5 mempool_t *r_shadow_mempool;
9 int maxtrianglefacinglight;
10 qbyte *trianglefacinglight;
12 rtexturepool_t *r_shadow_texturepool;
13 rtexture_t *r_shadow_attenuationtexture;
15 void r_shadow_start(void)
17 // allocate vertex processing arrays
18 r_shadow_mempool = Mem_AllocPool("R_Shadow");
19 maxshadowelements = 0;
20 shadowelements = NULL;
21 maxtrianglefacinglight = 0;
22 trianglefacinglight = NULL;
23 r_shadow_attenuationtexture = NULL;
24 r_shadow_texturepool = NULL;
27 void r_shadow_shutdown(void)
29 r_shadow_attenuationtexture = NULL;
30 R_FreeTexturePool(&r_shadow_texturepool);
31 maxshadowelements = 0;
32 shadowelements = NULL;
33 maxtrianglefacinglight = 0;
34 trianglefacinglight = NULL;
35 Mem_FreePool(&r_shadow_mempool);
38 void r_shadow_newmap(void)
42 void R_Shadow_Init(void)
44 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
47 void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance, int visiblevolume)
49 int i, *e, *n, *out, tris;
50 float *v0, *v1, *v2, temp[3], f;
51 if (projectdistance < 0.1)
53 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
59 // a triangle facing the light source
62 // a triangle not facing the light source
65 // an extrusion of the backfaces, beginning at the original geometry and
66 // ending further from the light source than the original geometry
67 // (presumably at least as far as the light's radius, if the light has a
68 // radius at all), capped at both front and back to avoid any problems
71 // draws the shadow volumes of the model.
73 // vertex loations must already be in vertex before use.
74 // vertex must have capacity for numverts * 2.
76 // make sure trianglefacinglight is big enough for this volume
77 if (maxtrianglefacinglight < numtris)
79 maxtrianglefacinglight = numtris;
80 if (trianglefacinglight)
81 Mem_Free(trianglefacinglight);
82 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
85 // make sure shadowelements is big enough for this volume
86 if (maxshadowelements < numtris * 24)
88 maxshadowelements = numtris * 24;
90 Mem_Free(shadowelements);
91 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
94 // make projected vertices
95 // by clever use of elements we'll construct the whole shadow from
96 // the unprojected vertices and these projected vertices
97 for (i = 0, v0 = vertex, v1 = vertex + numverts * 4;i < numverts;i++, v0 += 4, v1 += 4)
99 VectorSubtract(v0, relativelightorigin, temp);
101 f = lightradius / sqrt(DotProduct(temp,temp));
104 VectorMA(relativelightorigin, f, temp, v1);
106 f = projectdistance / sqrt(DotProduct(temp,temp));
107 VectorMA(v0, f, temp, v1);
111 // check which triangles are facing the light
112 for (i = 0, e = elements;i < numtris;i++, e += 3)
114 // calculate triangle facing flag
115 v0 = vertex + e[0] * 4;
116 v1 = vertex + e[1] * 4;
117 v2 = vertex + e[2] * 4;
118 // we do not need to normalize the surface normal because both sides
119 // of the comparison use it, therefore they are both multiplied the
120 // same amount... furthermore the subtract can be done on the
121 // vectors, saving a little bit of math in the dotproducts
124 // subtracts v1 from v0 and v2, combined into a crossproduct,
125 // combined with a dotproduct of the light location relative to the
126 // first point of the triangle (any point works, since the triangle
127 // is obviously flat), and finally a comparison to determine if the
128 // light is infront of the triangle (the goal of this statement)
129 trianglefacinglight[i] =
130 (relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
131 + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
132 + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
136 float dir0[3], dir1[3];
138 // calculate two mostly perpendicular edge directions
139 VectorSubtract(v0, v1, dir0);
140 VectorSubtract(v2, v1, dir1);
142 // we have two edge directions, we can calculate a third vector from
143 // them, which is the direction of the surface normal (it's magnitude
145 CrossProduct(dir0, dir1, temp);
147 // this is entirely unnecessary, but kept for clarity
148 //VectorNormalize(temp);
150 // compare distance of light along normal, with distance of any point
151 // of the triangle along the same normal (the triangle is planar,
152 // I.E. flat, so all points give the same answer)
153 // the normal is not normalized because it is used on both sides of
154 // the comparison, so it's magnitude does not matter
155 trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
159 // output triangle elements
160 out = shadowelements;
163 // check each backface for bordering frontfaces,
164 // and cast shadow polygons from those edges,
165 // also create front and back caps for shadow volume
166 for (i = 0, e = elements, n = neighbors;i < numtris;i++, e += 3, n += 3)
168 if (!trianglefacinglight[i])
170 // triangle is backface and therefore casts shadow,
171 // output front and back caps for shadow volume
173 // front cap (with flipped winding order)
178 out[3] = e[0] + numverts;
179 out[4] = e[1] + numverts;
180 out[5] = e[2] + numverts;
185 out[0] = e[0] + numverts;
186 out[1] = e[1] + numverts;
187 out[2] = e[2] + numverts;
192 if (n[0] < 0 || trianglefacinglight[n[0]])
196 out[2] = e[1] + numverts;
198 out[4] = e[1] + numverts;
199 out[5] = e[0] + numverts;
203 if (n[1] < 0 || trianglefacinglight[n[1]])
207 out[2] = e[2] + numverts;
209 out[4] = e[2] + numverts;
210 out[5] = e[1] + numverts;
214 if (n[2] < 0 || trianglefacinglight[n[2]])
218 out[2] = e[0] + numverts;
220 out[4] = e[0] + numverts;
221 out[5] = e[2] + numverts;
227 R_Shadow_RenderVolume(numverts * 2, tris, shadowelements, visiblevolume);
230 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements, int visiblevolume)
235 qglDisable(GL_CULL_FACE);
236 R_Mesh_Draw(numverts, numtris, elements);
237 qglEnable(GL_CULL_FACE);
241 // increment stencil if backface is behind depthbuffer
242 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
243 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
244 R_Mesh_Draw(numverts, numtris, elements);
245 // decrement stencil if frontface is behind depthbuffer
246 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
247 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
248 R_Mesh_Draw(numverts, numtris, elements);
252 static void R_Shadow_MakeTextures(void)
256 qbyte data[32][32][32][4];
257 r_shadow_texturepool = R_AllocTexturePool();
258 for (z = 0;z < 32;z++)
261 for (y = 0;y < 32;y++)
264 for (x = 0;x < 32;x++)
267 d = (int) ((1024.0f / (DotProduct(v, v)+1)) - 8.0f);
268 d = bound(0, d, 255);
269 data[z][y][x][0] = data[z][y][x][1] = data[z][y][x][2] = data[z][y][x][3] = d;
273 r_shadow_attenuationtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation", 32, 32, 32, &data[0][0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_ALPHA);
276 void R_Shadow_Stage_Depth(void)
280 if (!r_shadow_attenuationtexture)
281 R_Shadow_MakeTextures();
283 memset(&m, 0, sizeof(m));
284 m.blendfunc1 = GL_ONE;
285 m.blendfunc2 = GL_ZERO;
287 GL_Color(0, 0, 0, 1);
290 void R_Shadow_Stage_ShadowVolumes(void)
292 GL_Color(1, 1, 1, 1);
293 qglColorMask(0, 0, 0, 0);
294 qglDisable(GL_BLEND);
296 qglDepthFunc(GL_LEQUAL);
298 qglClear(GL_STENCIL_BUFFER_BIT);
299 qglEnable(GL_STENCIL_TEST);
300 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
301 qglStencilFunc(GL_ALWAYS, 0, 0xFF);
304 void R_Shadow_Stage_Light(void)
307 qglBlendFunc(GL_ONE, GL_ONE);
308 GL_Color(1, 1, 1, 1);
309 qglColorMask(1, 1, 1, 1);
311 qglDepthFunc(GL_EQUAL);
312 qglEnable(GL_STENCIL_TEST);
313 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
314 // only draw light where this geometry was already rendered AND the
315 // stencil is 0 (non-zero means shadow)
316 qglStencilFunc(GL_EQUAL, 0, 0xFF);
319 void R_Shadow_Stage_Textures(void)
322 // attempt to restore state to what Mesh_State thinks it is
323 qglDisable(GL_BLEND);
324 qglBlendFunc(GL_ONE, GL_ZERO);
327 // now change to a more useful state
328 memset(&m, 0, sizeof(m));
329 m.blendfunc1 = GL_DST_COLOR;
330 m.blendfunc2 = GL_SRC_COLOR;
333 // now hack some more
334 GL_Color(1, 1, 1, 1);
335 qglColorMask(1, 1, 1, 1);
336 qglDepthFunc(GL_EQUAL);
337 qglEnable(GL_STENCIL_TEST);
338 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
339 // only draw in lit areas
340 qglStencilFunc(GL_EQUAL, 0, 0xFF);
343 void R_Shadow_Stage_End(void)
346 GL_Color(1, 1, 1, 1);
347 qglColorMask(1, 1, 1, 1);
348 qglDepthFunc(GL_LEQUAL);
349 qglDisable(GL_STENCIL_TEST);
350 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
351 qglStencilFunc(GL_ALWAYS, 0, 0xFF);
353 // now change to a more useful state
354 memset(&m, 0, sizeof(m));
355 m.blendfunc1 = GL_ONE;
356 m.blendfunc2 = GL_ZERO;
360 void R_Shadow_VertexLight(int numverts, float *vertex, float *normals, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor)
363 float *n, *v, *c, f, dist, temp[3], light[3];
364 // calculate vertex colors
365 VectorCopy(lightcolor, light);
367 for (i = 0, v = vertex, c = varray_color, n = normals;i < numverts;i++, v += 4, c += 4, n += 3)
369 VectorSubtract(relativelightorigin, v, temp);
374 f = DotProduct(n, temp);
377 dist = DotProduct(temp, temp);
378 if (dist < lightradius2)
380 f = ((1.0f / (dist + lightdistbias)) - lightsubtract) * (f / sqrt(dist));