backport 864691defbc55453071050b5cc8408079a8f3199 to fix llvm 13 build
engine: use inthash_t for _prprograminfo lookups instead of a 300MB array

Index: source/build/src/polymer.cpp
--- source/build/src/polymer.cpp.orig
+++ source/build/src/polymer.cpp
@@ -84,6 +84,9 @@ const GLbitfield prindexringmapflags = GL_MAP_WRITE_BI
 _prbucket       *prbuckethead;
 int32_t         prcanbucket;
 
+static inthashtable_t prprogramtable = { nullptr, INTHASH_SIZE(256) };
+static GrowArray<_prprograminfo *> prprogramptrs;
+
 static const _prvert  vertsprite[4] =
 {
     {
@@ -691,8 +694,6 @@ static const _prprogrambit   prprogrambits[PR_BIT_COUN
     }
 };
 
-_prprograminfo  prprograms[1 << PR_BIT_COUNT];
-
 int32_t         overridematerial;
 int32_t         globaloldoverridematerial;
 
@@ -797,17 +798,12 @@ int32_t             polymer_init(void)
         i++;
     }
 
-    i = 0;
-    while (i < (1 << PR_BIT_COUNT))
-    {
-        prprograms[i].handle = 0;
-        i++;
-    }
-
     overridematerial = 0xFFFFFFFF;
 
     polymersearching = FALSE;
 
+    inthash_init(&prprogramtable);
+
     polymer_initrendertargets(pr_shadowcount + 1);
 
     // Prime highpalookup maps
@@ -868,7 +864,6 @@ void                polymer_uninit(void)
     }
 
     polymer_freeboard();
-
     polymer_initrendertargets(0);
 
     i = 0;
@@ -901,6 +896,19 @@ void                polymer_uninit(void)
 
     if (pr_verbosity >= 3)
         OSD_Printf("PR: freed %d planelists\n", i);
+
+    inthash_free(&prprogramtable);
+
+    for (auto &pr : prprogramptrs)
+    {
+        glDeleteProgram(pr->handle);
+        DO_FREE_AND_NULL(pr);
+    }
+
+    if (pr_verbosity >= 2)
+        OSD_Printf("PR: freed %" PRIi64 " programs\n", prprogramptrs.size());
+
+    prprogramptrs.clear();
 }
 
 void                polymer_setaspect(int32_t ang)
@@ -2307,6 +2315,12 @@ static void         polymer_bucketplane(_prplane* plan
     }
 }
 
+static inline _prprograminfo *polymer_getprogram(int32_t materialbits)
+{
+    intptr_t progptr = inthash_find(&prprogramtable, materialbits);
+    return (progptr != -1) ? (_prprograminfo *)progptr : polymer_compileprogram(materialbits);
+}
+
 static void         polymer_drawplane(_prplane* plane)
 {
     int32_t         materialbits;
@@ -2390,12 +2404,13 @@ static void         polymer_drawplane(_prplane* plane)
     curlight = 0;
     do {
         materialbits = polymer_bindmaterial(&plane->material, plane->lights, plane->lightcount);
+        auto &prprogram = *polymer_getprogram(materialbits);
 
         if (materialbits & prprogrambits[PR_BIT_NORMAL_MAP].bit)
         {
-            glVertexAttrib3fv(prprograms[materialbits].attrib_T, &plane->tbn[0][0]);
-            glVertexAttrib3fv(prprograms[materialbits].attrib_B, &plane->tbn[1][0]);
-            glVertexAttrib3fv(prprograms[materialbits].attrib_N, &plane->tbn[2][0]);
+            glVertexAttrib3fv(prprogram.attrib_T, &plane->tbn[0][0]);
+            glVertexAttrib3fv(prprogram.attrib_B, &plane->tbn[1][0]);
+            glVertexAttrib3fv(prprogram.attrib_N, &plane->tbn[2][0]);
         }
 
         if (plane->indices)
@@ -5228,10 +5243,9 @@ static int32_t      polymer_bindmaterial(const _prmate
     programbits |= prprogrambits[PR_BIT_FOOTER].bit;
 
     // --------- program compiling
-    if (!prprograms[programbits].handle)
-        polymer_compileprogram(programbits);
+    auto &prprogram = *polymer_getprogram(programbits);
 
-    polymost_useShaderProgram(prprograms[programbits].handle);
+    polymost_useShaderProgram(prprogram.handle);
 
     // --------- bit setup
 
@@ -5240,20 +5254,20 @@ static int32_t      polymer_bindmaterial(const _prmate
     // PR_BIT_ANIM_INTERPOLATION
     if (programbits & prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit)
     {
-        glEnableVertexAttribArray(prprograms[programbits].attrib_nextFrameData);
-        if (prprograms[programbits].attrib_nextFrameNormal != -1)
-            glEnableVertexAttribArray(prprograms[programbits].attrib_nextFrameNormal);
-        glVertexAttribPointer(prprograms[programbits].attrib_nextFrameData,
+        glEnableVertexAttribArray(prprogram.attrib_nextFrameData);
+        if (prprogram.attrib_nextFrameNormal != -1)
+            glEnableVertexAttribArray(prprogram.attrib_nextFrameNormal);
+        glVertexAttribPointer(prprogram.attrib_nextFrameData,
                                3, GL_FLOAT, GL_FALSE,
                                sizeof(float) * 15,
                                material->nextframedata);
-        if (prprograms[programbits].attrib_nextFrameNormal != -1)
-            glVertexAttribPointer(prprograms[programbits].attrib_nextFrameNormal,
+        if (prprogram.attrib_nextFrameNormal != -1)
+            glVertexAttribPointer(prprogram.attrib_nextFrameNormal,
                                    3, GL_FLOAT, GL_FALSE,
                                    sizeof(float) * 15,
                                    material->nextframedata + 3);
 
-        glUniform1f(prprograms[programbits].uniform_frameProgress, material->frameprogress);
+        glUniform1f(prprogram.uniform_frameProgress, material->frameprogress);
     }
 
     // PR_BIT_LIGHTING_PASS
@@ -5283,31 +5297,31 @@ static int32_t      polymer_bindmaterial(const _prmate
         if (material->mdspritespace == GL_TRUE) {
             float mdspritespacepos[3];
             polymer_transformpoint(pos, mdspritespacepos, (float *)mdspritespace);
-            glUniform3fv(prprograms[programbits].uniform_eyePosition, 1, mdspritespacepos);
+            glUniform3fv(prprogram.uniform_eyePosition, 1, mdspritespacepos);
         } else
-            glUniform3fv(prprograms[programbits].uniform_eyePosition, 1, pos);
-        glUniform1i(prprograms[programbits].uniform_normalMap, texunit);
+            glUniform3fv(prprogram.uniform_eyePosition, 1, pos);
+        glUniform1i(prprogram.uniform_normalMap, texunit);
         if (pr_overrideparallax) {
             bias[0] = pr_parallaxscale;
             bias[1] = pr_parallaxbias;
-            glUniform2fv(prprograms[programbits].uniform_normalBias, 1, bias);
+            glUniform2fv(prprogram.uniform_normalBias, 1, bias);
         } else
-            glUniform2fv(prprograms[programbits].uniform_normalBias, 1, material->normalbias);
+            glUniform2fv(prprogram.uniform_normalBias, 1, material->normalbias);
 
         if (material->tbn) {
-            glEnableVertexAttribArray(prprograms[programbits].attrib_T);
-            glEnableVertexAttribArray(prprograms[programbits].attrib_B);
-            glEnableVertexAttribArray(prprograms[programbits].attrib_N);
+            glEnableVertexAttribArray(prprogram.attrib_T);
+            glEnableVertexAttribArray(prprogram.attrib_B);
+            glEnableVertexAttribArray(prprogram.attrib_N);
 
-            glVertexAttribPointer(prprograms[programbits].attrib_T,
+            glVertexAttribPointer(prprogram.attrib_T,
                                       3, GL_FLOAT, GL_FALSE,
                                       sizeof(float) * 15,
                                       material->tbn);
-            glVertexAttribPointer(prprograms[programbits].attrib_B,
+            glVertexAttribPointer(prprogram.attrib_B,
                                       3, GL_FLOAT, GL_FALSE,
                                       sizeof(float) * 15,
                                       material->tbn + 3);
-            glVertexAttribPointer(prprograms[programbits].attrib_N,
+            glVertexAttribPointer(prprogram.attrib_N,
                                       3, GL_FLOAT, GL_FALSE,
                                       sizeof(float) * 15,
                                       material->tbn + 6);
@@ -5322,29 +5336,29 @@ static int32_t      polymer_bindmaterial(const _prmate
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_2D, material->artmap);
 
-        glUniform1i(prprograms[programbits].uniform_artMap, texunit);
+        glUniform1i(prprogram.uniform_artMap, texunit);
 
         texunit++;
 
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_2D, material->basepalmap);
 
-        glUniform1i(prprograms[programbits].uniform_basePalMap, texunit);
+        glUniform1i(prprogram.uniform_basePalMap, texunit);
 
         texunit++;
 
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_RECTANGLE_ARB, material->lookupmap);
 
-        glUniform1i(prprograms[programbits].uniform_lookupMap, texunit);
+        glUniform1i(prprogram.uniform_lookupMap, texunit);
 
         texunit++;
 
-        glUniform1f(prprograms[programbits].uniform_shadeOffset, (GLfloat)material->shadeoffset);
+        glUniform1f(prprogram.uniform_shadeOffset, (GLfloat)material->shadeoffset);
         if (r_usenewshading == 4)
         {
             // the fog in Polymer is a sphere insted of a plane, the furthest visible point should be the same as Polymost
-            glUniform1f(prprograms[programbits].uniform_visibility, globalvisibility / 262144.f * material->visibility);
+            glUniform1f(prprogram.uniform_visibility, globalvisibility / 262144.f * material->visibility);
         }
         else
         {
@@ -5358,7 +5372,7 @@ static int32_t      polymer_bindmaterial(const _prmate
 
             static constexpr float factor_old = 1.f / ((2048.f * (1.07f / 1.024f) / 35.f) * material_visibility_divisor);
 
-            glUniform1f(prprograms[programbits].uniform_visibility, globalvisibility * material->visibility * r_usenewshading > 1 ? factor_new : factor_old);
+            glUniform1f(prprogram.uniform_visibility, globalvisibility * material->visibility * r_usenewshading > 1 ? factor_new : factor_old);
         }
     }
 
@@ -5368,8 +5382,8 @@ static int32_t      polymer_bindmaterial(const _prmate
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_2D, material->diffusemap);
 
-        glUniform1i(prprograms[programbits].uniform_diffuseMap, texunit);
-        glUniform2fv(prprograms[programbits].uniform_diffuseScale, 1, material->diffusescale);
+        glUniform1i(prprogram.uniform_diffuseMap, texunit);
+        glUniform2fv(prprogram.uniform_diffuseScale, 1, material->diffusescale);
 
         texunit++;
     }
@@ -5380,7 +5394,7 @@ static int32_t      polymer_bindmaterial(const _prmate
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_3D, material->highpalookupmap);
 
-        glUniform1i(prprograms[programbits].uniform_highPalookupMap, texunit);
+        glUniform1i(prprogram.uniform_highPalookupMap, texunit);
 
         texunit++;
     }
@@ -5403,8 +5417,8 @@ static int32_t      polymer_bindmaterial(const _prmate
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_2D, material->detailmap);
 
-        glUniform1i(prprograms[programbits].uniform_detailMap, texunit);
-        glUniform2fv(prprograms[programbits].uniform_detailScale, 1, scale);
+        glUniform1i(prprogram.uniform_detailMap, texunit);
+        glUniform2fv(prprogram.uniform_detailScale, 1, scale);
 
         texunit++;
     }
@@ -5424,7 +5438,7 @@ static int32_t      polymer_bindmaterial(const _prmate
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_2D, material->specmap);
 
-        glUniform1i(prprograms[programbits].uniform_specMap, texunit);
+        glUniform1i(prprogram.uniform_specMap, texunit);
 
         texunit++;
     }
@@ -5437,9 +5451,9 @@ static int32_t      polymer_bindmaterial(const _prmate
         if (pr_overridespecular) {
             specmaterial[0] = pr_specularpower;
             specmaterial[1] = pr_specularfactor;
-            glUniform2fv(prprograms[programbits].uniform_specMaterial, 1, specmaterial);
+            glUniform2fv(prprogram.uniform_specMaterial, 1, specmaterial);
         } else
-            glUniform2fv(prprograms[programbits].uniform_specMaterial, 1, material->specmaterial);
+            glUniform2fv(prprogram.uniform_specMaterial, 1, material->specmaterial);
     }
 
     // PR_BIT_MIRROR_MAP
@@ -5448,14 +5462,14 @@ static int32_t      polymer_bindmaterial(const _prmate
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_RECTANGLE_ARB, material->mirrormap);
 
-        glUniform1i(prprograms[programbits].uniform_mirrorMap, texunit);
+        glUniform1i(prprogram.uniform_mirrorMap, texunit);
 
         texunit++;
     }
 #ifdef PR_LINEAR_FOG
     if (programbits & prprogrambits[PR_BIT_FOG].bit)
     {
-        glUniform1i(prprograms[programbits].uniform_linearFog, r_usenewshading >= 2);
+        glUniform1i(prprogram.uniform_linearFog, r_usenewshading >= 2);
     }
 #endif
     // PR_BIT_GLOW_MAP
@@ -5464,7 +5478,7 @@ static int32_t      polymer_bindmaterial(const _prmate
         polymost_activeTexture(texunit + GL_TEXTURE0);
         polymost_bindTexture(GL_TEXTURE_2D, material->glowmap);
 
-        glUniform1i(prprograms[programbits].uniform_glowMap, texunit);
+        glUniform1i(prprogram.uniform_glowMap, texunit);
 
         texunit++;
     }
@@ -5507,8 +5521,8 @@ static int32_t      polymer_bindmaterial(const _prmate
             indir[1] = (float)(sintable[(prlights[lights[curlight]].faderadius+512)&2047]) / 16383.0f;
             indir[1] = 1.0 / (indir[1] - indir[0]);
 
-            glUniform3fv(prprograms[programbits].uniform_spotDir, 1, dir);
-            glUniform2fv(prprograms[programbits].uniform_spotRadius, 1, indir);
+            glUniform3fv(prprogram.uniform_spotDir, 1, dir);
+            glUniform2fv(prprogram.uniform_spotRadius, 1, indir);
 
             // PR_BIT_PROJECTION_MAP
             if (programbits & prprogrambits[PR_BIT_PROJECTION_MAP].bit)
@@ -5525,7 +5539,7 @@ static int32_t      polymer_bindmaterial(const _prmate
                 glLoadIdentity();
                 glMatrixMode(GL_MODELVIEW);
 
-                glUniformMatrix4fv(prprograms[programbits].uniform_shadowProjMatrix, 1, GL_FALSE, matrix);
+                glUniformMatrix4fv(prprogram.uniform_shadowProjMatrix, 1, GL_FALSE, matrix);
 
                 // PR_BIT_SHADOW_MAP
                 if (programbits & prprogrambits[PR_BIT_SHADOW_MAP].bit)
@@ -5533,7 +5547,7 @@ static int32_t      polymer_bindmaterial(const _prmate
                     polymost_activeTexture(texunit + GL_TEXTURE0);
                     polymost_bindTexture(prrts[prlights[lights[curlight]].rtindex].target, prrts[prlights[lights[curlight]].rtindex].z);
 
-                    glUniform1i(prprograms[programbits].uniform_shadowMap, texunit);
+                    glUniform1i(prprogram.uniform_shadowMap, texunit);
 
                     texunit++;
                 }
@@ -5544,7 +5558,7 @@ static int32_t      polymer_bindmaterial(const _prmate
                     polymost_activeTexture(texunit + GL_TEXTURE0);
                     polymost_bindTexture(GL_TEXTURE_2D, prlights[lights[curlight]].lightmap);
 
-                    glUniform1i(prprograms[programbits].uniform_lightMap, texunit);
+                    glUniform1i(prprogram.uniform_lightMap, texunit);
 
                     texunit++;
                 }
@@ -5584,14 +5598,15 @@ static int32_t      polymer_bindmaterial(const _prmate
 
 static void         polymer_unbindmaterial(int32_t programbits)
 {
+    auto &prprogram = *polymer_getprogram(programbits);
     // repair any dirty GL state here
 
     // PR_BIT_ANIM_INTERPOLATION
     if (programbits & prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit)
     {
-        if (prprograms[programbits].attrib_nextFrameNormal != -1)
-            glDisableVertexAttribArray(prprograms[programbits].attrib_nextFrameNormal);
-        glDisableVertexAttribArray(prprograms[programbits].attrib_nextFrameData);
+        if (prprogram.attrib_nextFrameNormal != -1)
+            glDisableVertexAttribArray(prprogram.attrib_nextFrameNormal);
+        glDisableVertexAttribArray(prprogram.attrib_nextFrameData);
     }
 
     // PR_BIT_LIGHTING_PASS
@@ -5603,15 +5618,15 @@ static void         polymer_unbindmaterial(int32_t pro
     // PR_BIT_NORMAL_MAP
     if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit)
     {
-        glDisableVertexAttribArray(prprograms[programbits].attrib_T);
-        glDisableVertexAttribArray(prprograms[programbits].attrib_B);
-        glDisableVertexAttribArray(prprograms[programbits].attrib_N);
+        glDisableVertexAttribArray(prprogram.attrib_T);
+        glDisableVertexAttribArray(prprogram.attrib_B);
+        glDisableVertexAttribArray(prprogram.attrib_N);
     }
 
     polymost_useShaderProgram(0);
 }
 
-static void         polymer_compileprogram(int32_t programbits)
+static _prprograminfo *polymer_compileprogram(int32_t programbits)
 {
     int32_t         i, enabledbits;
     GLuint          vert, frag, program;
@@ -5675,8 +5690,10 @@ static void         polymer_compileprogram(int32_t pro
 
     glGetProgramInfoLog(program, PR_INFO_LOG_BUFFER_SIZE, NULL, infobuffer);
 
-    prprograms[programbits].handle = program;
+    auto &prprogram = *(_prprograminfo *)Xcalloc(1, sizeof(_prprograminfo));
 
+    prprogram.handle = program;
+
 #ifdef DEBUGGINGAIDS
     if (pr_verbosity >= 1)
 #else
@@ -5697,105 +5714,109 @@ static void         polymer_compileprogram(int32_t pro
     // PR_BIT_ANIM_INTERPOLATION
     if (programbits & prprogrambits[PR_BIT_ANIM_INTERPOLATION].bit)
     {
-        prprograms[programbits].attrib_nextFrameData = glGetAttribLocation(program, "nextFrameData");
-        prprograms[programbits].attrib_nextFrameNormal = glGetAttribLocation(program, "nextFrameNormal");
-        prprograms[programbits].uniform_frameProgress = glGetUniformLocation(program, "frameProgress");
+        prprogram.attrib_nextFrameData = glGetAttribLocation(program, "nextFrameData");
+        prprogram.attrib_nextFrameNormal = glGetAttribLocation(program, "nextFrameNormal");
+        prprogram.uniform_frameProgress = glGetUniformLocation(program, "frameProgress");
     }
 
     // PR_BIT_NORMAL_MAP
     if (programbits & prprogrambits[PR_BIT_NORMAL_MAP].bit)
     {
-        prprograms[programbits].attrib_T = glGetAttribLocation(program, "T");
-        prprograms[programbits].attrib_B = glGetAttribLocation(program, "B");
-        prprograms[programbits].attrib_N = glGetAttribLocation(program, "N");
-        prprograms[programbits].uniform_eyePosition = glGetUniformLocation(program, "eyePosition");
-        prprograms[programbits].uniform_normalMap = glGetUniformLocation(program, "normalMap");
-        prprograms[programbits].uniform_normalBias = glGetUniformLocation(program, "normalBias");
+        prprogram.attrib_T = glGetAttribLocation(program, "T");
+        prprogram.attrib_B = glGetAttribLocation(program, "B");
+        prprogram.attrib_N = glGetAttribLocation(program, "N");
+        prprogram.uniform_eyePosition = glGetUniformLocation(program, "eyePosition");
+        prprogram.uniform_normalMap = glGetUniformLocation(program, "normalMap");
+        prprogram.uniform_normalBias = glGetUniformLocation(program, "normalBias");
     }
 
     // PR_BIT_ART_MAP
     if (programbits & prprogrambits[PR_BIT_ART_MAP].bit)
     {
-        prprograms[programbits].uniform_artMap = glGetUniformLocation(program, "artMap");
-        prprograms[programbits].uniform_basePalMap = glGetUniformLocation(program, "basePalMap");
-        prprograms[programbits].uniform_lookupMap = glGetUniformLocation(program, "lookupMap");
-        prprograms[programbits].uniform_shadeOffset = glGetUniformLocation(program, "shadeOffset");
-        prprograms[programbits].uniform_visibility = glGetUniformLocation(program, "visibility");
+        prprogram.uniform_artMap = glGetUniformLocation(program, "artMap");
+        prprogram.uniform_basePalMap = glGetUniformLocation(program, "basePalMap");
+        prprogram.uniform_lookupMap = glGetUniformLocation(program, "lookupMap");
+        prprogram.uniform_shadeOffset = glGetUniformLocation(program, "shadeOffset");
+        prprogram.uniform_visibility = glGetUniformLocation(program, "visibility");
     }
 
     // PR_BIT_DIFFUSE_MAP
     if (programbits & prprogrambits[PR_BIT_DIFFUSE_MAP].bit)
     {
-        prprograms[programbits].uniform_diffuseMap = glGetUniformLocation(program, "diffuseMap");
-        prprograms[programbits].uniform_diffuseScale = glGetUniformLocation(program, "diffuseScale");
+        prprogram.uniform_diffuseMap = glGetUniformLocation(program, "diffuseMap");
+        prprogram.uniform_diffuseScale = glGetUniformLocation(program, "diffuseScale");
     }
 
     // PR_BIT_HIGHPALOOKUP_MAP
     if (programbits & prprogrambits[PR_BIT_HIGHPALOOKUP_MAP].bit)
     {
-        prprograms[programbits].uniform_highPalookupMap = glGetUniformLocation(program, "highPalookupMap");
+        prprogram.uniform_highPalookupMap = glGetUniformLocation(program, "highPalookupMap");
     }
 
     // PR_BIT_DIFFUSE_DETAIL_MAP
     if (programbits & prprogrambits[PR_BIT_DIFFUSE_DETAIL_MAP].bit)
     {
-        prprograms[programbits].uniform_detailMap = glGetUniformLocation(program, "detailMap");
-        prprograms[programbits].uniform_detailScale = glGetUniformLocation(program, "detailScale");
+        prprogram.uniform_detailMap = glGetUniformLocation(program, "detailMap");
+        prprogram.uniform_detailScale = glGetUniformLocation(program, "detailScale");
     }
 
     // PR_BIT_SPECULAR_MAP
     if (programbits & prprogrambits[PR_BIT_SPECULAR_MAP].bit)
     {
-        prprograms[programbits].uniform_specMap = glGetUniformLocation(program, "specMap");
+        prprogram.uniform_specMap = glGetUniformLocation(program, "specMap");
     }
 
     // PR_BIT_SPECULAR_MATERIAL
     if (programbits & prprogrambits[PR_BIT_SPECULAR_MATERIAL].bit)
     {
-        prprograms[programbits].uniform_specMaterial = glGetUniformLocation(program, "specMaterial");
+        prprogram.uniform_specMaterial = glGetUniformLocation(program, "specMaterial");
     }
 
     // PR_BIT_MIRROR_MAP
     if (programbits & prprogrambits[PR_BIT_MIRROR_MAP].bit)
     {
-        prprograms[programbits].uniform_mirrorMap = glGetUniformLocation(program, "mirrorMap");
+        prprogram.uniform_mirrorMap = glGetUniformLocation(program, "mirrorMap");
     }
 #ifdef PR_LINEAR_FOG
     if (programbits & prprogrambits[PR_BIT_FOG].bit)
     {
-        prprograms[programbits].uniform_linearFog = glGetUniformLocation(program, "linearFog");
+        prprogram.uniform_linearFog = glGetUniformLocation(program, "linearFog");
     }
 #endif
     // PR_BIT_GLOW_MAP
     if (programbits & prprogrambits[PR_BIT_GLOW_MAP].bit)
     {
-        prprograms[programbits].uniform_glowMap = glGetUniformLocation(program, "glowMap");
+        prprogram.uniform_glowMap = glGetUniformLocation(program, "glowMap");
     }
 
     // PR_BIT_PROJECTION_MAP
     if (programbits & prprogrambits[PR_BIT_PROJECTION_MAP].bit)
     {
-        prprograms[programbits].uniform_shadowProjMatrix = glGetUniformLocation(program, "shadowProjMatrix");
+        prprogram.uniform_shadowProjMatrix = glGetUniformLocation(program, "shadowProjMatrix");
     }
 
     // PR_BIT_SHADOW_MAP
     if (programbits & prprogrambits[PR_BIT_SHADOW_MAP].bit)
     {
-        prprograms[programbits].uniform_shadowMap = glGetUniformLocation(program, "shadowMap");
+        prprogram.uniform_shadowMap = glGetUniformLocation(program, "shadowMap");
     }
 
     // PR_BIT_LIGHT_MAP
     if (programbits & prprogrambits[PR_BIT_LIGHT_MAP].bit)
     {
-        prprograms[programbits].uniform_lightMap = glGetUniformLocation(program, "lightMap");
+        prprogram.uniform_lightMap = glGetUniformLocation(program, "lightMap");
     }
 
     // PR_BIT_SPOT_LIGHT
     if (programbits & prprogrambits[PR_BIT_SPOT_LIGHT].bit)
     {
-        prprograms[programbits].uniform_spotDir = glGetUniformLocation(program, "spotDir");
-        prprograms[programbits].uniform_spotRadius = glGetUniformLocation(program, "spotRadius");
+        prprogram.uniform_spotDir = glGetUniformLocation(program, "spotDir");
+        prprogram.uniform_spotRadius = glGetUniformLocation(program, "spotRadius");
     }
+
+    inthash_add(&prprogramtable, programbits, (intptr_t)&prprogram, 0);
+    prprogramptrs.append(&prprogram);
+    return prprogramptrs.last();
 }
 
 // LIGHTS
