5 mempool_t *r_shadow_mempool;
9 int maxtrianglefacinglight;
10 qbyte *trianglefacinglight;
12 void r_shadow_start(void)
14 // allocate vertex processing arrays
15 r_shadow_mempool = Mem_AllocPool("R_Shadow");
16 maxshadowelements = 0;
17 shadowelements = NULL;
18 maxtrianglefacinglight = 0;
19 trianglefacinglight = NULL;
22 void r_shadow_shutdown(void)
24 maxshadowelements = 0;
25 shadowelements = NULL;
26 maxtrianglefacinglight = 0;
27 trianglefacinglight = NULL;
28 Mem_FreePool(&r_shadow_mempool);
31 void r_shadow_newmap(void)
35 void R_Shadow_Init(void)
37 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
40 void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance, int visiblevolume)
42 int i, *e, *n, *out, tris;
43 float *v0, *v1, *v2, temp[3], f;
44 if (projectdistance < 0.1)
46 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
52 // a triangle facing the light source
55 // a triangle not facing the light source
58 // an extrusion of the backfaces, beginning at the original geometry and
59 // ending further from the light source than the original geometry
60 // (presumably at least as far as the light's radius, if the light has a
61 // radius at all), capped at both front and back to avoid any problems
64 // draws the shadow volumes of the model.
66 // vertex loations must already be in vertex before use.
67 // vertex must have capacity for numverts * 2.
69 // make sure trianglefacinglight is big enough for this volume
70 if (maxtrianglefacinglight < numtris)
72 maxtrianglefacinglight = numtris;
73 if (trianglefacinglight)
74 Mem_Free(trianglefacinglight);
75 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
78 // make sure shadowelements is big enough for this volume
79 if (maxshadowelements < numtris * 24)
81 maxshadowelements = numtris * 24;
83 Mem_Free(shadowelements);
84 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
87 // make projected vertices
88 // by clever use of elements we'll construct the whole shadow from
89 // the unprojected vertices and these projected vertices
90 for (i = 0, v0 = vertex, v1 = vertex + numverts * 4;i < numverts;i++, v0 += 4, v1 += 4)
92 VectorSubtract(v0, relativelightorigin, temp);
94 f = lightradius / sqrt(DotProduct(temp,temp));
97 VectorMA(relativelightorigin, f, temp, v1);
99 f = projectdistance / sqrt(DotProduct(temp,temp));
100 VectorMA(v0, f, temp, v1);
104 // check which triangles are facing the light
105 for (i = 0, e = elements;i < numtris;i++, e += 3)
107 // calculate triangle facing flag
108 v0 = vertex + e[0] * 4;
109 v1 = vertex + e[1] * 4;
110 v2 = vertex + e[2] * 4;
111 // we do not need to normalize the surface normal because both sides
112 // of the comparison use it, therefore they are both multiplied the
113 // same amount... furthermore the subtract can be done on the
114 // vectors, saving a little bit of math in the dotproducts
117 // subtracts v1 from v0 and v2, combined into a crossproduct,
118 // combined with a dotproduct of the light location relative to the
119 // first point of the triangle (any point works, since the triangle
120 // is obviously flat), and finally a comparison to determine if the
121 // light is infront of the triangle (the goal of this statement)
122 trianglefacinglight[i] =
123 (relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
124 + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
125 + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
129 float dir0[3], dir1[3];
131 // calculate two mostly perpendicular edge directions
132 VectorSubtract(v0, v1, dir0);
133 VectorSubtract(v2, v1, dir1);
135 // we have two edge directions, we can calculate a third vector from
136 // them, which is the direction of the surface normal (it's magnitude
138 CrossProduct(dir0, dir1, temp);
140 // this is entirely unnecessary, but kept for clarity
141 //VectorNormalize(temp);
143 // compare distance of light along normal, with distance of any point
144 // of the triangle along the same normal (the triangle is planar,
145 // I.E. flat, so all points give the same answer)
146 // the normal is not normalized because it is used on both sides of
147 // the comparison, so it's magnitude does not matter
148 trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
152 // output triangle elements
153 out = shadowelements;
156 // check each backface for bordering frontfaces,
157 // and cast shadow polygons from those edges,
158 // also create front and back caps for shadow volume
159 for (i = 0, e = elements, n = neighbors;i < numtris;i++, e += 3, n += 3)
161 if (!trianglefacinglight[i])
163 // triangle is backface and therefore casts shadow,
164 // output front and back caps for shadow volume
166 // front cap (with flipped winding order)
171 out[3] = e[0] + numverts;
172 out[4] = e[1] + numverts;
173 out[5] = e[2] + numverts;
178 out[0] = e[0] + numverts;
179 out[1] = e[1] + numverts;
180 out[2] = e[2] + numverts;
185 if (n[0] < 0 || trianglefacinglight[n[0]])
189 out[2] = e[1] + numverts;
191 out[4] = e[1] + numverts;
192 out[5] = e[0] + numverts;
196 if (n[1] < 0 || trianglefacinglight[n[1]])
200 out[2] = e[2] + numverts;
202 out[4] = e[2] + numverts;
203 out[5] = e[1] + numverts;
207 if (n[2] < 0 || trianglefacinglight[n[2]])
211 out[2] = e[0] + numverts;
213 out[4] = e[0] + numverts;
214 out[5] = e[2] + numverts;
220 R_Shadow_RenderVolume(numverts * 2, tris, shadowelements, visiblevolume);
223 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements, int visiblevolume)
228 qglDisable(GL_CULL_FACE);
229 R_Mesh_Draw(numverts, numtris, elements);
230 qglEnable(GL_CULL_FACE);
234 // increment stencil if backface is behind depthbuffer
235 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
236 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
237 R_Mesh_Draw(numverts, numtris, elements);
238 // decrement stencil if frontface is behind depthbuffer
239 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
240 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
241 R_Mesh_Draw(numverts, numtris, elements);
245 void R_Shadow_Stage_Depth(void)
248 memset(&m, 0, sizeof(m));
249 m.blendfunc1 = GL_ONE;
250 m.blendfunc2 = GL_ZERO;
252 GL_Color(0, 0, 0, 1);
255 void R_Shadow_Stage_ShadowVolumes(void)
257 GL_Color(1, 1, 1, 1);
258 qglColorMask(0, 0, 0, 0);
259 qglDisable(GL_BLEND);
261 qglDepthFunc(GL_LEQUAL);
263 qglClear(GL_STENCIL_BUFFER_BIT);
264 qglEnable(GL_STENCIL_TEST);
265 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
266 qglStencilFunc(GL_ALWAYS, 0, 0xFF);
269 void R_Shadow_Stage_Light(void)
272 qglBlendFunc(GL_ONE, GL_ONE);
273 GL_Color(1, 1, 1, 1);
274 qglColorMask(1, 1, 1, 1);
276 qglDepthFunc(GL_EQUAL);
277 qglEnable(GL_STENCIL_TEST);
278 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
279 // only draw light where this geometry was already rendered AND the
280 // stencil is 0 (non-zero means shadow)
281 qglStencilFunc(GL_EQUAL, 0, 0xFF);
284 void R_Shadow_Stage_Textures(void)
287 // attempt to restore state to what Mesh_State thinks it is
288 qglDisable(GL_BLEND);
289 qglBlendFunc(GL_ONE, GL_ZERO);
292 // now change to a more useful state
293 memset(&m, 0, sizeof(m));
294 m.blendfunc1 = GL_DST_COLOR;
295 m.blendfunc2 = GL_SRC_COLOR;
298 // now hack some more
299 GL_Color(1, 1, 1, 1);
300 qglColorMask(1, 1, 1, 1);
301 qglDepthFunc(GL_EQUAL);
302 qglEnable(GL_STENCIL_TEST);
303 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
304 // only draw in lit areas
305 qglStencilFunc(GL_EQUAL, 0, 0xFF);
308 void R_Shadow_Stage_End(void)
311 GL_Color(1, 1, 1, 1);
312 qglColorMask(1, 1, 1, 1);
313 qglDepthFunc(GL_LEQUAL);
314 qglDisable(GL_STENCIL_TEST);
315 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
316 qglStencilFunc(GL_ALWAYS, 0, 0xFF);
318 // now change to a more useful state
319 memset(&m, 0, sizeof(m));
320 m.blendfunc1 = GL_ONE;
321 m.blendfunc2 = GL_ZERO;
325 void R_Shadow_VertexLight(int numverts, float *vertex, float *normals, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor)
328 float *n, *v, *c, f, dist, temp[3], light[3];
329 // calculate vertex colors
330 VectorCopy(lightcolor, light);
331 for (i = 0, v = vertex, c = varray_color, n = normals;i < numverts;i++, v += 4, c += 4, n += 3)
333 VectorSubtract(relativelightorigin, v, temp);
338 f = DotProduct(n, temp);
341 dist = DotProduct(temp, temp);
342 if (dist < lightradius2)
344 f = ((1.0f / (dist + lightdistbias)) - lightsubtract) * (f / sqrt(dist));