]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
now uploads a 3D attenuation texture (not used yet)
[divverent/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3 #include "r_shadow.h"
4
5 mempool_t *r_shadow_mempool;
6
7 int maxshadowelements;
8 int *shadowelements;
9 int maxtrianglefacinglight;
10 qbyte *trianglefacinglight;
11
12 rtexturepool_t *r_shadow_texturepool;
13 rtexture_t *r_shadow_attenuationtexture;
14
15 void r_shadow_start(void)
16 {
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;
25 }
26
27 void r_shadow_shutdown(void)
28 {
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);
36 }
37
38 void r_shadow_newmap(void)
39 {
40 }
41
42 void R_Shadow_Init(void)
43 {
44         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
45 }
46
47 void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance, int visiblevolume)
48 {
49         int i, *e, *n, *out, tris;
50         float *v0, *v1, *v2, temp[3], f;
51         if (projectdistance < 0.1)
52         {
53                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
54                 return;
55         }
56 // terminology:
57 //
58 // frontface:
59 // a triangle facing the light source
60 //
61 // backface:
62 // a triangle not facing the light source
63 //
64 // shadow volume:
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
69 //
70 // description:
71 // draws the shadow volumes of the model.
72 // requirements:
73 // vertex loations must already be in vertex before use.
74 // vertex must have capacity for numverts * 2.
75
76         // make sure trianglefacinglight is big enough for this volume
77         if (maxtrianglefacinglight < numtris)
78         {
79                 maxtrianglefacinglight = numtris;
80                 if (trianglefacinglight)
81                         Mem_Free(trianglefacinglight);
82                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
83         }
84
85         // make sure shadowelements is big enough for this volume
86         if (maxshadowelements < numtris * 24)
87         {
88                 maxshadowelements = numtris * 24;
89                 if (shadowelements)
90                         Mem_Free(shadowelements);
91                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
92         }
93
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)
98         {
99                 VectorSubtract(v0, relativelightorigin, temp);
100 #if 0
101                 f = lightradius / sqrt(DotProduct(temp,temp));
102                 if (f < 1)
103                         f = 1;
104                 VectorMA(relativelightorigin, f, temp, v1);
105 #else
106                 f = projectdistance / sqrt(DotProduct(temp,temp));
107                 VectorMA(v0, f, temp, v1);
108 #endif
109         }
110
111         // check which triangles are facing the light
112         for (i = 0, e = elements;i < numtris;i++, e += 3)
113         {
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
122 #if 1
123                 // fast version
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;
133 #else
134                 // readable version
135                 {
136                 float dir0[3], dir1[3];
137
138                 // calculate two mostly perpendicular edge directions
139                 VectorSubtract(v0, v1, dir0);
140                 VectorSubtract(v2, v1, dir1);
141
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
144                 // is not 1 however)
145                 CrossProduct(dir0, dir1, temp);
146
147                 // this is entirely unnecessary, but kept for clarity
148                 //VectorNormalize(temp);
149
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);
156 #endif
157         }
158
159         // output triangle elements
160         out = shadowelements;
161         tris = 0;
162
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)
167         {
168                 if (!trianglefacinglight[i])
169                 {
170                         // triangle is backface and therefore casts shadow,
171                         // output front and back caps for shadow volume
172 #if 1
173                         // front cap (with flipped winding order)
174                         out[0] = e[0];
175                         out[1] = e[2];
176                         out[2] = e[1];
177                         // rear cap
178                         out[3] = e[0] + numverts;
179                         out[4] = e[1] + numverts;
180                         out[5] = e[2] + numverts;
181                         out += 6;
182                         tris += 2;
183 #else
184                         // rear cap
185                         out[0] = e[0] + numverts;
186                         out[1] = e[1] + numverts;
187                         out[2] = e[2] + numverts;
188                         out += 3;
189                         tris += 1;
190 #endif
191                         // check the edges
192                         if (n[0] < 0 || trianglefacinglight[n[0]])
193                         {
194                                 out[0] = e[0];
195                                 out[1] = e[1];
196                                 out[2] = e[1] + numverts;
197                                 out[3] = e[0];
198                                 out[4] = e[1] + numverts;
199                                 out[5] = e[0] + numverts;
200                                 out += 6;
201                                 tris += 2;
202                         }
203                         if (n[1] < 0 || trianglefacinglight[n[1]])
204                         {
205                                 out[0] = e[1];
206                                 out[1] = e[2];
207                                 out[2] = e[2] + numverts;
208                                 out[3] = e[1];
209                                 out[4] = e[2] + numverts;
210                                 out[5] = e[1] + numverts;
211                                 out += 6;
212                                 tris += 2;
213                         }
214                         if (n[2] < 0 || trianglefacinglight[n[2]])
215                         {
216                                 out[0] = e[2];
217                                 out[1] = e[0];
218                                 out[2] = e[0] + numverts;
219                                 out[3] = e[2];
220                                 out[4] = e[0] + numverts;
221                                 out[5] = e[2] + numverts;
222                                 out += 6;
223                                 tris += 2;
224                         }
225                 }
226         }
227         R_Shadow_RenderVolume(numverts * 2, tris, shadowelements, visiblevolume);
228 }
229
230 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements, int visiblevolume)
231 {
232         // draw the volume
233         if (visiblevolume)
234         {
235                 qglDisable(GL_CULL_FACE);
236                 R_Mesh_Draw(numverts, numtris, elements);
237                 qglEnable(GL_CULL_FACE);
238         }
239         else
240         {
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);
249         }
250 }
251
252 static void R_Shadow_MakeTextures(void)
253 {
254         int x, y, z, d;
255         float v[3];
256         qbyte data[32][32][32][4];
257         r_shadow_texturepool = R_AllocTexturePool();
258         for (z = 0;z < 32;z++)
259         {
260                 v[2] = z - 16.0f;
261                 for (y = 0;y < 32;y++)
262                 {
263                         v[1] = y - 16.0f;
264                         for (x = 0;x < 32;x++)
265                         {
266                                 v[0] = x - 16.0f;
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;
270                         }
271                 }
272         }
273         r_shadow_attenuationtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation", 32, 32, 32, &data[0][0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_ALPHA);
274 }
275
276 void R_Shadow_Stage_Depth(void)
277 {
278         rmeshstate_t m;
279
280         if (!r_shadow_attenuationtexture)
281                 R_Shadow_MakeTextures();
282
283         memset(&m, 0, sizeof(m));
284         m.blendfunc1 = GL_ONE;
285         m.blendfunc2 = GL_ZERO;
286         R_Mesh_State(&m);
287         GL_Color(0, 0, 0, 1);
288 }
289
290 void R_Shadow_Stage_ShadowVolumes(void)
291 {
292         GL_Color(1, 1, 1, 1);
293         qglColorMask(0, 0, 0, 0);
294         qglDisable(GL_BLEND);
295         qglDepthMask(0);
296         qglDepthFunc(GL_LEQUAL);
297         qglClearStencil(0);
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);
302 }
303
304 void R_Shadow_Stage_Light(void)
305 {
306         qglEnable(GL_BLEND);
307         qglBlendFunc(GL_ONE, GL_ONE);
308         GL_Color(1, 1, 1, 1);
309         qglColorMask(1, 1, 1, 1);
310         qglDepthMask(0);
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);
317 }
318
319 void R_Shadow_Stage_Textures(void)
320 {
321         rmeshstate_t m;
322         // attempt to restore state to what Mesh_State thinks it is
323         qglDisable(GL_BLEND);
324         qglBlendFunc(GL_ONE, GL_ZERO);
325         qglDepthMask(1);
326
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;
331         R_Mesh_State(&m);
332
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);
341 }
342
343 void R_Shadow_Stage_End(void)
344 {
345         rmeshstate_t m;
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);
352
353         // now change to a more useful state
354         memset(&m, 0, sizeof(m));
355         m.blendfunc1 = GL_ONE;
356         m.blendfunc2 = GL_ZERO;
357         R_Mesh_State(&m);
358 }
359
360 void R_Shadow_VertexLight(int numverts, float *vertex, float *normals, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor)
361 {
362         int i;
363         float *n, *v, *c, f, dist, temp[3], light[3];
364         // calculate vertex colors
365         VectorCopy(lightcolor, light);
366
367         for (i = 0, v = vertex, c = varray_color, n = normals;i < numverts;i++, v += 4, c += 4, n += 3)
368         {
369                 VectorSubtract(relativelightorigin, v, temp);
370                 c[0] = 0;
371                 c[1] = 0;
372                 c[2] = 0;
373                 c[3] = 1;
374                 f = DotProduct(n, temp);
375                 if (f > 0)
376                 {
377                         dist = DotProduct(temp, temp);
378                         if (dist < lightradius2)
379                         {
380                                 f = ((1.0f / (dist + lightdistbias)) - lightsubtract) * (f / sqrt(dist));
381                                 c[0] = f * light[0];
382                                 c[1] = f * light[1];
383                                 c[2] = f * light[2];
384                         }
385                 }
386         }
387 }