+ int t, tend;
+ const int *e;
+ const float *v[3];
+ float normal[3];
+ vec3_t p[3];
+ float bias;
+ int mask, surfacemask = 0;
+ if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
+ return 0;
+ bias = r_shadow_shadowmapborder / (float)(r_shadow_shadowmapmaxsize - r_shadow_shadowmapborder);
+ tend = firsttriangle + numtris;
+ if (BoxInsideBox(surfacemins, surfacemaxs, lightmins, lightmaxs))
+ {
+ // surface box entirely inside light box, no box cull
+ if (projectdirection)
+ {
+ for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+ {
+ v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3, v[2] = invertex3f + e[2] * 3;
+ TriangleNormal(v[0], v[1], v[2], normal);
+ if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0))
+ {
+ Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]);
+ mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias);
+ surfacemask |= mask;
+ if(totals)
+ {
+ totals[0] += mask&1, totals[1] += (mask>>1)&1, totals[2] += (mask>>2)&1, totals[3] += (mask>>3)&1, totals[4] += (mask>>4)&1, totals[5] += mask>>5;
+ shadowsides[numshadowsides] = mask;
+ shadowsideslist[numshadowsides++] = t;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+ {
+ v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3, v[2] = invertex3f + e[2] * 3;
+ if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]))
+ {
+ Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]);
+ mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias);
+ surfacemask |= mask;
+ if(totals)
+ {
+ totals[0] += mask&1, totals[1] += (mask>>1)&1, totals[2] += (mask>>2)&1, totals[3] += (mask>>3)&1, totals[4] += (mask>>4)&1, totals[5] += mask>>5;
+ shadowsides[numshadowsides] = mask;
+ shadowsideslist[numshadowsides++] = t;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // surface box not entirely inside light box, cull each triangle
+ if (projectdirection)
+ {
+ for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+ {
+ v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3, v[2] = invertex3f + e[2] * 3;
+ TriangleNormal(v[0], v[1], v[2], normal);
+ if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
+ && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
+ {
+ Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]);
+ mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias);
+ surfacemask |= mask;
+ if(totals)
+ {
+ totals[0] += mask&1, totals[1] += (mask>>1)&1, totals[2] += (mask>>2)&1, totals[3] += (mask>>3)&1, totals[4] += (mask>>4)&1, totals[5] += mask>>5;
+ shadowsides[numshadowsides] = mask;
+ shadowsideslist[numshadowsides++] = t;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+ {
+ v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3, v[2] = invertex3f + e[2] * 3;
+ if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
+ && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
+ {
+ Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]);
+ mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias);
+ surfacemask |= mask;
+ if(totals)
+ {
+ totals[0] += mask&1, totals[1] += (mask>>1)&1, totals[2] += (mask>>2)&1, totals[3] += (mask>>3)&1, totals[4] += (mask>>4)&1, totals[5] += mask>>5;
+ shadowsides[numshadowsides] = mask;
+ shadowsideslist[numshadowsides++] = t;
+ }
+ }
+ }
+ }
+ }
+ return surfacemask;
+}
+
+void R_Shadow_ShadowMapFromList(int numverts, int numtris, const float *vertex3f, const int *elements, int numsidetris, const int *sidetotals, const unsigned char *sides, const int *sidetris)
+{
+ int i, j, outtriangles = 0;
+ int *outelement3i[6];
+ if (!numverts || !numsidetris || !r_shadow_compilingrtlight)
+ return;
+ outtriangles = sidetotals[0] + sidetotals[1] + sidetotals[2] + sidetotals[3] + sidetotals[4] + sidetotals[5];
+ // make sure shadowelements is big enough for this mesh
+ if (maxshadowtriangles < outtriangles)
+ R_Shadow_ResizeShadowArrays(0, outtriangles, 0, 1);
+
+ // compute the offset and size of the separate index lists for each cubemap side
+ outtriangles = 0;
+ for (i = 0;i < 6;i++)
+ {
+ outelement3i[i] = shadowelements + outtriangles * 3;
+ r_shadow_compilingrtlight->static_meshchain_shadow_shadowmap->sideoffsets[i] = outtriangles;
+ r_shadow_compilingrtlight->static_meshchain_shadow_shadowmap->sidetotals[i] = sidetotals[i];
+ outtriangles += sidetotals[i];
+ }
+
+ // gather up the (sparse) triangles into separate index lists for each cubemap side
+ for (i = 0;i < numsidetris;i++)
+ {
+ const int *element = elements + sidetris[i] * 3;
+ for (j = 0;j < 6;j++)
+ {
+ if (sides[i] & (1 << j))
+ {
+ outelement3i[j][0] = element[0];
+ outelement3i[j][1] = element[1];
+ outelement3i[j][2] = element[2];
+ outelement3i[j] += 3;
+ }
+ }
+ }
+
+ Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow_shadowmap, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, outtriangles, shadowelements);
+}
+
+static void R_Shadow_MakeTextures_MakeCorona(void)
+{
+ float dx, dy;
+ int x, y, a;
+ unsigned char pixels[32][32][4];
+ for (y = 0;y < 32;y++)
+ {
+ dy = (y - 15.5f) * (1.0f / 16.0f);
+ for (x = 0;x < 32;x++)
+ {
+ dx = (x - 15.5f) * (1.0f / 16.0f);
+ a = (int)(((1.0f / (dx * dx + dy * dy + 0.2f)) - (1.0f / (1.0f + 0.2))) * 32.0f / (1.0f / (1.0f + 0.2)));
+ a = bound(0, a, 255);
+ pixels[y][x][0] = a;
+ pixels[y][x][1] = a;
+ pixels[y][x][2] = a;
+ pixels[y][x][3] = 255;
+ }
+ }
+ r_shadow_lightcorona = R_LoadTexture2D(r_shadow_texturepool, "lightcorona", 32, 32, &pixels[0][0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR, NULL);
+}
+
+static unsigned int R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
+{
+ float dist = sqrt(x*x+y*y+z*z);
+ float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
+ // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
+ return (unsigned char)bound(0, intensity * 256.0f, 255) * 0x01010101;
+}
+
+static void R_Shadow_MakeTextures(void)
+{
+ int x, y, z;
+ float intensity, dist;
+ unsigned int *data;
+ R_Shadow_FreeShadowMaps();
+ R_FreeTexturePool(&r_shadow_texturepool);
+ r_shadow_texturepool = R_AllocTexturePool();
+ r_shadow_attenlinearscale = r_shadow_lightattenuationlinearscale.value;
+ r_shadow_attendividebias = r_shadow_lightattenuationdividebias.value;
+ data = (unsigned int *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE) * 4);
+ // the table includes one additional value to avoid the need to clamp indexing due to minor math errors
+ for (x = 0;x <= ATTENTABLESIZE;x++)
+ {
+ dist = (x + 0.5f) * (1.0f / ATTENTABLESIZE) * (1.0f / 0.9375);
+ intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
+ r_shadow_attentable[x] = bound(0, intensity, 1);
+ }
+ // 1D gradient texture
+ for (x = 0;x < ATTEN1DSIZE;x++)
+ data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0);
+ r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, NULL);
+ // 2D circle texture
+ for (y = 0;y < ATTEN2DSIZE;y++)
+ for (x = 0;x < ATTEN2DSIZE;x++)
+ data[y*ATTEN2DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), 0);
+ r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, NULL);
+ // 3D sphere texture
+ if (r_shadow_texture3d.integer && gl_texture3d)
+ {
+ for (z = 0;z < ATTEN3DSIZE;z++)
+ for (y = 0;y < ATTEN3DSIZE;y++)
+ for (x = 0;x < ATTEN3DSIZE;x++)
+ data[(z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375));
+ r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, NULL);
+ }
+ else
+ r_shadow_attenuation3dtexture = NULL;
+ Mem_Free(data);
+
+ R_Shadow_MakeTextures_MakeCorona();
+
+ // Editor light sprites
+ r_editlights_sprcursor = Draw_CachePic ("gfx/editlights/cursor");
+ r_editlights_sprlight = Draw_CachePic ("gfx/editlights/light");
+ r_editlights_sprnoshadowlight = Draw_CachePic ("gfx/editlights/noshadow");
+ r_editlights_sprcubemaplight = Draw_CachePic ("gfx/editlights/cubemaplight");
+ r_editlights_sprcubemapnoshadowlight = Draw_CachePic ("gfx/editlights/cubemapnoshadowlight");
+ r_editlights_sprselection = Draw_CachePic ("gfx/editlights/selection");
+}
+
+void R_Shadow_ValidateCvars(void)
+{
+ if (r_shadow_texture3d.integer && !gl_texture3d)
+ Cvar_SetValueQuick(&r_shadow_texture3d, 0);
+ if (gl_ext_separatestencil.integer && !gl_support_separatestencil)
+ Cvar_SetValueQuick(&gl_ext_separatestencil, 0);
+ if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
+ Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
+}
+
+void R_Shadow_RenderMode_Begin(void)
+{
+#if 0
+ GLint drawbuffer;
+ GLint readbuffer;
+#endif
+ R_Shadow_ValidateCvars();
+
+ if (!r_shadow_attenuation2dtexture
+ || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
+ || r_shadow_lightattenuationdividebias.value != r_shadow_attendividebias
+ || r_shadow_lightattenuationlinearscale.value != r_shadow_attenlinearscale)
+ R_Shadow_MakeTextures();
+
+ CHECKGLERROR
+ R_Mesh_ColorPointer(NULL, 0, 0);
+ R_Mesh_ResetTextureState();