]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
ee32a61baf979ef8df0fc9e80ae253c76d5664b1
[divverent/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3
4 mempool_t *r_shadow_mempool;
5
6 int maxshadowelements;
7 int *shadowelements;
8 int maxtrianglefacinglight;
9 qbyte *trianglefacinglight;
10
11 void r_shadow_start(void)
12 {
13         // allocate vertex processing arrays
14         r_shadow_mempool = Mem_AllocPool("R_Shadow");
15         maxshadowelements = 0;
16         shadowelements = NULL;
17         maxtrianglefacinglight = 0;
18         trianglefacinglight = NULL;
19 }
20
21 void r_shadow_shutdown(void)
22 {
23         maxshadowelements = 0;
24         shadowelements = NULL;
25         maxtrianglefacinglight = 0;
26         trianglefacinglight = NULL;
27         Mem_FreePool(&r_shadow_mempool);
28 }
29
30 void r_shadow_newmap(void)
31 {
32 }
33
34 void R_Shadow_Init(void)
35 {
36         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
37 }
38
39 void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance, int visiblevolume)
40 {
41         int i, *e, *n, *out, tris;
42         float *v0, *v1, *v2, temp[3], f;
43         if (projectdistance < 0.1)
44         {
45                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
46                 return;
47         }
48 // terminology:
49 //
50 // frontface:
51 // a triangle facing the light source
52 //
53 // backface:
54 // a triangle not facing the light source
55 //
56 // shadow volume:
57 // an extrusion of the backfaces, beginning at the original geometry and
58 // ending further from the light source than the original geometry
59 // (presumably at least as far as the light's radius, if the light has a
60 // radius at all), capped at both front and back to avoid any problems
61 //
62 // description:
63 // draws the shadow volumes of the model.
64 // requirements:
65 // vertex loations must already be in vertex before use.
66 // vertex must have capacity for numverts * 2.
67
68         // make sure trianglefacinglight is big enough for this volume
69         if (maxtrianglefacinglight < numtris)
70         {
71                 maxtrianglefacinglight = numtris;
72                 if (trianglefacinglight)
73                         Mem_Free(trianglefacinglight);
74                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
75         }
76
77         // make sure shadowelements is big enough for this volume
78         if (maxshadowelements < numtris * 24)
79         {
80                 maxshadowelements = numtris * 24;
81                 if (shadowelements)
82                         Mem_Free(shadowelements);
83                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
84         }
85
86         // make projected vertices
87         // by clever use of elements we'll construct the whole shadow from
88         // the unprojected vertices and these projected vertices
89         for (i = 0, v0 = vertex, v1 = vertex + numverts * 4;i < numverts;i++, v0 += 4, v1 += 4)
90         {
91                 VectorSubtract(v0, relativelightorigin, temp);
92 #if 0
93                 f = lightradius / sqrt(DotProduct(temp,temp));
94                 if (f < 1)
95                         f = 1;
96                 VectorMA(relativelightorigin, f, temp, v1);
97 #else
98                 f = projectdistance / sqrt(DotProduct(temp,temp));
99                 VectorMA(v0, f, temp, v1);
100 #endif
101         }
102
103         // check which triangles are facing the light
104         for (i = 0, e = elements;i < numtris;i++, e += 3)
105         {
106                 // calculate triangle facing flag
107                 v0 = vertex + e[0] * 4;
108                 v1 = vertex + e[1] * 4;
109                 v2 = vertex + e[2] * 4;
110                 // we do not need to normalize the surface normal because both sides
111                 // of the comparison use it, therefore they are both multiplied the
112                 // same amount...  furthermore the subtract can be done on the
113                 // vectors, saving a little bit of math in the dotproducts
114 #if 1
115                 // fast version
116                 // subtracts v1 from v0 and v2, combined into a crossproduct,
117                 // combined with a dotproduct of the light location relative to the
118                 // first point of the triangle (any point works, since the triangle
119                 // is obviously flat), and finally a comparison to determine if the
120                 // light is infront of the triangle (the goal of this statement)
121                 trianglefacinglight[i] =
122                    (relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
123                  + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
124                  + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
125 #else
126                 // readable version
127                 {
128                 float dir0[3], dir1[3];
129
130                 // calculate two mostly perpendicular edge directions
131                 VectorSubtract(v0, v1, dir0);
132                 VectorSubtract(v2, v1, dir1);
133
134                 // we have two edge directions, we can calculate a third vector from
135                 // them, which is the direction of the surface normal (it's magnitude
136                 // is not 1 however)
137                 CrossProduct(dir0, dir1, temp);
138
139                 // this is entirely unnecessary, but kept for clarity
140                 //VectorNormalize(temp);
141
142                 // compare distance of light along normal, with distance of any point
143                 // of the triangle along the same normal (the triangle is planar,
144                 // I.E. flat, so all points give the same answer)
145                 // the normal is not normalized because it is used on both sides of
146                 // the comparison, so it's magnitude does not matter
147                 trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
148 #endif
149         }
150
151         // output triangle elements
152         out = shadowelements;
153         tris = 0;
154
155         // check each backface for bordering frontfaces,
156         // and cast shadow polygons from those edges,
157         // also create front and back caps for shadow volume
158         for (i = 0, e = elements, n = neighbors;i < numtris;i++, e += 3, n += 3)
159         {
160                 if (!trianglefacinglight[i])
161                 {
162                         // triangle is backface and therefore casts shadow,
163                         // output front and back caps for shadow volume
164 #if 1
165                         // front cap (with flipped winding order)
166                         out[0] = e[0];
167                         out[1] = e[2];
168                         out[2] = e[1];
169                         // rear cap
170                         out[3] = e[0] + numverts;
171                         out[4] = e[1] + numverts;
172                         out[5] = e[2] + numverts;
173                         out += 6;
174                         tris += 2;
175 #else
176                         // rear cap
177                         out[0] = e[0] + numverts;
178                         out[1] = e[1] + numverts;
179                         out[2] = e[2] + numverts;
180                         out += 3;
181                         tris += 1;
182 #endif
183                         // check the edges
184                         if (n[0] < 0 || trianglefacinglight[n[0]])
185                         {
186                                 out[0] = e[0];
187                                 out[1] = e[1];
188                                 out[2] = e[1] + numverts;
189                                 out[3] = e[0];
190                                 out[4] = e[1] + numverts;
191                                 out[5] = e[0] + numverts;
192                                 out += 6;
193                                 tris += 2;
194                         }
195                         if (n[1] < 0 || trianglefacinglight[n[1]])
196                         {
197                                 out[0] = e[1];
198                                 out[1] = e[2];
199                                 out[2] = e[2] + numverts;
200                                 out[3] = e[1];
201                                 out[4] = e[2] + numverts;
202                                 out[5] = e[1] + numverts;
203                                 out += 6;
204                                 tris += 2;
205                         }
206                         if (n[2] < 0 || trianglefacinglight[n[2]])
207                         {
208                                 out[0] = e[2];
209                                 out[1] = e[0];
210                                 out[2] = e[0] + numverts;
211                                 out[3] = e[2];
212                                 out[4] = e[0] + numverts;
213                                 out[5] = e[2] + numverts;
214                                 out += 6;
215                                 tris += 2;
216                         }
217                 }
218         }
219         // draw the volume
220         if (visiblevolume)
221         {
222                 //qglDisable(GL_CULL_FACE);
223                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
224                 //qglEnable(GL_CULL_FACE);
225         }
226         else
227         {
228                 qglColorMask(0,0,0,0);
229                 qglDepthMask(0);
230                 qglEnable(GL_STENCIL_TEST);
231
232                 // increment stencil if backface is behind depthbuffer
233                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
234                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
235                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
236                 // decrement stencil if frontface is infront of depthbuffer
237                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
238                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
239                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
240
241                 // restore to normal quake rendering
242                 qglDisable(GL_STENCIL_TEST);
243                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
244                 qglDepthMask(1);
245                 qglColorMask(1,1,1,1);
246         }
247 }
248
249 void R_Shadow_VertexLight(int numverts, float *vertex, float *normals, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor)
250 {
251         int i;
252         float *n, *v, *c, f, dist, temp[3];
253         // calculate vertex colors
254         for (i = 0, v = vertex, c = varray_color, n = normals;i < numverts;i++, v += 4, c += 4, n += 3)
255         {
256                 VectorSubtract(relativelightorigin, v, temp);
257                 c[0] = 0;
258                 c[1] = 0;
259                 c[2] = 0;
260                 c[3] = 1;
261                 f = DotProduct(n, temp);
262                 if (f > 0)
263                 {
264                         dist = DotProduct(temp, temp);
265                         if (dist < lightradius2)
266                         {
267                                 f = ((1.0f / (dist + lightdistbias)) - lightsubtract) * (f / sqrt(dist));
268                                 c[0] = f * lightcolor[0];
269                                 c[1] = f * lightcolor[1];
270                                 c[2] = f * lightcolor[2];
271                         }
272                 }
273         }
274 }
275
276 void R_Shadow_RenderLightThroughStencil(int numverts, int numtris, int *elements, vec3_t relativelightorigin, float *normals)
277 {
278         // only draw light where this geometry was already rendered AND the
279         // stencil is 0 (non-zero means shadow)
280         qglDepthFunc(GL_EQUAL);
281         qglEnable(GL_STENCIL_TEST);
282         qglStencilFunc(GL_EQUAL, 0, 0xFF);
283         R_Mesh_Draw(numverts, numtris, elements);
284         qglDisable(GL_STENCIL_TEST);
285         qglDepthFunc(GL_LEQUAL);
286 }
287
288 void R_Shadow_ClearStencil(void)
289 {
290         qglClearStencil(0);
291         qglClear(GL_STENCIL_BUFFER_BIT);
292 }