]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
fixed CopyEntity builtin, now only copies the vars (not the physics info and such)
[divverent/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3 #include "r_shadow.h"
4 #include "cl_collision.h"
5
6 extern void R_Shadow_EditLights_Init(void);
7
8 #define SHADOWSTAGE_NONE 0
9 #define SHADOWSTAGE_STENCIL 1
10 #define SHADOWSTAGE_LIGHT 2
11 #define SHADOWSTAGE_ERASESTENCIL 3
12
13 int r_shadowstage = SHADOWSTAGE_NONE;
14 int r_shadow_reloadlights = false;
15
16 int r_shadow_lightingmode = 0;
17
18 mempool_t *r_shadow_mempool;
19
20 int maxshadowelements;
21 int *shadowelements;
22 int maxtrianglefacinglight;
23 qbyte *trianglefacinglight;
24
25 rtexturepool_t *r_shadow_texturepool;
26 rtexture_t *r_shadow_normalsattenuationtexture;
27 rtexture_t *r_shadow_normalscubetexture;
28 rtexture_t *r_shadow_attenuation2dtexture;
29 rtexture_t *r_shadow_blankbumptexture;
30 rtexture_t *r_shadow_blankglosstexture;
31 rtexture_t *r_shadow_blankwhitetexture;
32
33 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "2"};
34 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
35 cvar_t r_shadow_realtime = {0, "r_shadow_realtime", "0"};
36 cvar_t r_shadow_erasebydrawing = {0, "r_shadow_erasebydrawing", "0"};
37 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "0"};
38 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
39 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
40 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
41
42 void R_Shadow_ClearWorldLights(void);
43 void r_shadow_start(void)
44 {
45         // allocate vertex processing arrays
46         r_shadow_mempool = Mem_AllocPool("R_Shadow");
47         maxshadowelements = 0;
48         shadowelements = NULL;
49         maxtrianglefacinglight = 0;
50         trianglefacinglight = NULL;
51         r_shadow_normalsattenuationtexture = NULL;
52         r_shadow_normalscubetexture = NULL;
53         r_shadow_attenuation2dtexture = NULL;
54         r_shadow_blankbumptexture = NULL;
55         r_shadow_blankglosstexture = NULL;
56         r_shadow_blankwhitetexture = NULL;
57         r_shadow_texturepool = NULL;
58         R_Shadow_ClearWorldLights();
59         r_shadow_reloadlights = true;
60 }
61
62 void r_shadow_shutdown(void)
63 {
64         R_Shadow_ClearWorldLights();
65         r_shadow_reloadlights = true;
66         r_shadow_normalsattenuationtexture = NULL;
67         r_shadow_normalscubetexture = NULL;
68         r_shadow_attenuation2dtexture = NULL;
69         r_shadow_blankbumptexture = NULL;
70         r_shadow_blankglosstexture = NULL;
71         r_shadow_blankwhitetexture = NULL;
72         R_FreeTexturePool(&r_shadow_texturepool);
73         maxshadowelements = 0;
74         shadowelements = NULL;
75         maxtrianglefacinglight = 0;
76         trianglefacinglight = NULL;
77         Mem_FreePool(&r_shadow_mempool);
78 }
79
80 void R_Shadow_LoadWorldLights(const char *mapname);
81 void r_shadow_newmap(void)
82 {
83         R_Shadow_ClearWorldLights();
84         r_shadow_reloadlights = true;
85 }
86
87 void R_Shadow_Init(void)
88 {
89         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
90         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
91         Cvar_RegisterVariable(&r_shadow_realtime);
92         Cvar_RegisterVariable(&r_shadow_texture3d);
93         Cvar_RegisterVariable(&r_shadow_gloss);
94         Cvar_RegisterVariable(&r_shadow_debuglight);
95         Cvar_RegisterVariable(&r_shadow_erasebydrawing);
96         Cvar_RegisterVariable(&r_shadow_scissor);
97         R_Shadow_EditLights_Init();
98         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
99 }
100
101 void R_Shadow_ProjectVertices(const float *in, float *out, int numverts, const float *relativelightorigin, float projectdistance)
102 {
103         int i;
104         for (i = 0;i < numverts;i++, in += 4, out += 4)
105         {
106 #if 1
107                 out[0] = in[0] + 1000000.0f * (in[0] - relativelightorigin[0]);
108                 out[1] = in[1] + 1000000.0f * (in[1] - relativelightorigin[1]);
109                 out[2] = in[2] + 1000000.0f * (in[2] - relativelightorigin[2]);
110 #elif 0
111                 VectorSubtract(in, relativelightorigin, temp);
112                 f = lightradius / sqrt(DotProduct(temp,temp));
113                 if (f < 1)
114                         f = 1;
115                 VectorMA(relativelightorigin, f, temp, out);
116 #else
117                 VectorSubtract(in, relativelightorigin, temp);
118                 f = projectdistance / sqrt(DotProduct(temp,temp));
119                 VectorMA(in, f, temp, out);
120 #endif
121         }
122 }
123
124 void R_Shadow_MakeTriangleShadowFlags(const int *elements, const float *vertex, int numtris, qbyte *trianglefacinglight, const float *relativelightorigin, float lightradius)
125 {
126         int i;
127         const float *v0, *v1, *v2;
128         for (i = 0;i < numtris;i++, elements += 3)
129         {
130                 // calculate triangle facing flag
131                 v0 = vertex + elements[0] * 4;
132                 v1 = vertex + elements[1] * 4;
133                 v2 = vertex + elements[2] * 4;
134                 // we do not need to normalize the surface normal because both sides
135                 // of the comparison use it, therefore they are both multiplied the
136                 // same amount...  furthermore the subtract can be done on the
137                 // vectors, saving a little bit of math in the dotproducts
138 #if 0
139                 // fast version
140                 // subtracts v1 from v0 and v2, combined into a crossproduct,
141                 // combined with a dotproduct of the light location relative to the
142                 // first point of the triangle (any point works, since the triangle
143                 // is obviously flat), and finally a comparison to determine if the
144                 // light is infront of the triangle (the goal of this statement)
145                 trianglefacinglight[i] =
146                    (relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
147                  + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
148                  + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
149 #else
150                 // readable version
151                 {
152                 float dir0[3], dir1[3], temp[3], f;
153
154                 // calculate two mostly perpendicular edge directions
155                 VectorSubtract(v0, v1, dir0);
156                 VectorSubtract(v2, v1, dir1);
157
158                 // we have two edge directions, we can calculate a third vector from
159                 // them, which is the direction of the surface normal (it's magnitude
160                 // is not 1 however)
161                 CrossProduct(dir0, dir1, temp);
162
163                 // this is entirely unnecessary, but kept for clarity
164                 //VectorNormalize(temp);
165
166                 // compare distance of light along normal, with distance of any point
167                 // of the triangle along the same normal (the triangle is planar,
168                 // I.E. flat, so all points give the same answer)
169                 // the normal is not normalized because it is used on both sides of
170                 // the comparison, so it's magnitude does not matter
171                 //trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
172                 f = DotProduct(relativelightorigin, temp) - DotProduct(v0, temp);
173                 trianglefacinglight[i] = f > 0 && f < lightradius * sqrt(DotProduct(temp, temp));
174                 }
175 #endif
176         }
177 }
178
179 int R_Shadow_BuildShadowVolumeTriangles(const int *elements, const int *neighbors, int numtris, int numverts, const qbyte *trianglefacinglight, int *out)
180 {
181         int i, tris;
182         // check each frontface for bordering backfaces,
183         // and cast shadow polygons from those edges,
184         // also create front and back caps for shadow volume
185         tris = 0;
186         for (i = 0;i < numtris;i++, elements += 3, neighbors += 3)
187         {
188                 if (trianglefacinglight[i])
189                 {
190                         // triangle is frontface and therefore casts shadow,
191                         // output front and back caps for shadow volume
192                         // front cap
193                         out[0] = elements[0];
194                         out[1] = elements[1];
195                         out[2] = elements[2];
196                         // rear cap (with flipped winding order)
197                         out[3] = elements[0] + numverts;
198                         out[4] = elements[2] + numverts;
199                         out[5] = elements[1] + numverts;
200                         out += 6;
201                         tris += 2;
202                         // check the edges
203                         if (neighbors[0] < 0 || !trianglefacinglight[neighbors[0]])
204                         {
205                                 out[0] = elements[1];
206                                 out[1] = elements[0];
207                                 out[2] = elements[0] + numverts;
208                                 out[3] = elements[1];
209                                 out[4] = elements[0] + numverts;
210                                 out[5] = elements[1] + numverts;
211                                 out += 6;
212                                 tris += 2;
213                         }
214                         if (neighbors[1] < 0 || !trianglefacinglight[neighbors[1]])
215                         {
216                                 out[0] = elements[2];
217                                 out[1] = elements[1];
218                                 out[2] = elements[1] + numverts;
219                                 out[3] = elements[2];
220                                 out[4] = elements[1] + numverts;
221                                 out[5] = elements[2] + numverts;
222                                 out += 6;
223                                 tris += 2;
224                         }
225                         if (neighbors[2] < 0 || !trianglefacinglight[neighbors[2]])
226                         {
227                                 out[0] = elements[0];
228                                 out[1] = elements[2];
229                                 out[2] = elements[2] + numverts;
230                                 out[3] = elements[0];
231                                 out[4] = elements[2] + numverts;
232                                 out[5] = elements[0] + numverts;
233                                 out += 6;
234                                 tris += 2;
235                         }
236                 }
237         }
238         return tris;
239 }
240
241 void R_Shadow_ResizeTriangleFacingLight(int numtris)
242 {
243         // make sure trianglefacinglight is big enough for this volume
244         if (maxtrianglefacinglight < numtris)
245         {
246                 maxtrianglefacinglight = numtris;
247                 if (trianglefacinglight)
248                         Mem_Free(trianglefacinglight);
249                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
250         }
251 }
252
253 void R_Shadow_ResizeShadowElements(int numtris)
254 {
255         // make sure shadowelements is big enough for this volume
256         if (maxshadowelements < numtris * 24)
257         {
258                 maxshadowelements = numtris * 24;
259                 if (shadowelements)
260                         Mem_Free(shadowelements);
261                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
262         }
263 }
264
265 void R_Shadow_Volume(int numverts, int numtris, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
266 {
267         int tris;
268         if (projectdistance < 0.1)
269         {
270                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
271                 return;
272         }
273 // terminology:
274 //
275 // frontface:
276 // a triangle facing the light source
277 //
278 // backface:
279 // a triangle not facing the light source
280 //
281 // shadow volume:
282 // an extrusion of the frontfaces, beginning at the original geometry and
283 // ending further from the light source than the original geometry
284 // (presumably at least as far as the light's radius, if the light has a
285 // radius at all), capped at both front and back to avoid any problems
286 //
287 // description:
288 // draws the shadow volumes of the model.
289 // requirements:
290 // vertex locations must already be in varray_vertex before use.
291 // varray_vertex must have capacity for numverts * 2.
292
293         // make sure trianglefacinglight is big enough for this volume
294         if (maxtrianglefacinglight < numtris)
295                 R_Shadow_ResizeTriangleFacingLight(numtris);
296
297         // make sure shadowelements is big enough for this volume
298         if (maxshadowelements < numtris * 24)
299                 R_Shadow_ResizeShadowElements(numtris);
300
301         // generate projected vertices
302         // by clever use of elements we'll construct the whole shadow from
303         // the unprojected vertices and these projected vertices
304         R_Shadow_ProjectVertices(varray_vertex, varray_vertex + numverts * 4, numverts, relativelightorigin, projectdistance);
305
306         // check which triangles are facing the light
307         R_Shadow_MakeTriangleShadowFlags(elements, varray_vertex, numtris, trianglefacinglight, relativelightorigin, lightradius);
308
309         // output triangle elements
310         tris = R_Shadow_BuildShadowVolumeTriangles(elements, neighbors, numtris, numverts, trianglefacinglight, shadowelements);
311         R_Shadow_RenderVolume(numverts * 2, tris, shadowelements);
312 }
313
314 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements)
315 {
316         if (!numverts || !numtris)
317                 return;
318         if (r_shadowstage == SHADOWSTAGE_STENCIL)
319         {
320                 // increment stencil if backface is behind depthbuffer
321                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
322                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
323                 R_Mesh_Draw(numverts, numtris, elements);
324                 // decrement stencil if frontface is behind depthbuffer
325                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
326                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
327         }
328         R_Mesh_Draw(numverts, numtris, elements);
329 }
330
331 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
332 {
333         shadowmesh_t *mesh;
334         if (r_shadowstage == SHADOWSTAGE_STENCIL)
335         {
336                 // increment stencil if backface is behind depthbuffer
337                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
338                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
339                 for (mesh = firstmesh;mesh;mesh = mesh->next)
340                 {
341                         R_Mesh_ResizeCheck(mesh->numverts);
342                         memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
343                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
344                 }
345                 // decrement stencil if frontface is behind depthbuffer
346                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
347                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
348         }
349         for (mesh = firstmesh;mesh;mesh = mesh->next)
350         {
351                 R_Mesh_ResizeCheck(mesh->numverts);
352                 memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
353                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
354         }
355 }
356
357 float r_shadow_atten1;
358 #define ATTEN3DSIZE 64
359 static void R_Shadow_Make3DTextures(void)
360 {
361         int x, y, z;
362         float v[3], intensity, ilen, bordercolor[4];
363         qbyte *data;
364         if (r_shadow_texture3d.integer != 1 || !gl_texture3d)
365                 return;
366         data = Mem_Alloc(tempmempool, ATTEN3DSIZE * ATTEN3DSIZE * ATTEN3DSIZE * 4);
367         for (z = 0;z < ATTEN3DSIZE;z++)
368         {
369                 for (y = 0;y < ATTEN3DSIZE;y++)
370                 {
371                         for (x = 0;x < ATTEN3DSIZE;x++)
372                         {
373                                 v[0] = (x + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
374                                 v[1] = (y + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
375                                 v[2] = (z + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
376                                 intensity = 1.0f - sqrt(DotProduct(v, v));
377                                 if (intensity > 0)
378                                         intensity *= intensity;
379                                 ilen = 127.0f * bound(0, intensity * r_shadow_atten1, 1) / sqrt(DotProduct(v, v));
380                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = 128.0f + ilen * v[0];
381                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = 128.0f + ilen * v[1];
382                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = 128.0f + ilen * v[2];
383                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = 255;
384                         }
385                 }
386         }
387         r_shadow_normalsattenuationtexture = R_LoadTexture3D(r_shadow_texturepool, "normalsattenuation", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
388         bordercolor[0] = 0.5f;
389         bordercolor[1] = 0.5f;
390         bordercolor[2] = 0.5f;
391         bordercolor[3] = 1.0f;
392         qglTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, bordercolor);
393         Mem_Free(data);
394 }
395
396 static void R_Shadow_MakeTextures(void)
397 {
398         int x, y, d, side;
399         float v[3], s, t, intensity;
400         qbyte *data;
401         data = Mem_Alloc(tempmempool, 6*128*128*4);
402         R_FreeTexturePool(&r_shadow_texturepool);
403         r_shadow_texturepool = R_AllocTexturePool();
404         r_shadow_atten1 = r_shadow_lightattenuationscale.value;
405         data[0] = 128;
406         data[1] = 128;
407         data[2] = 255;
408         data[3] = 255;
409         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
410         data[0] = 255;
411         data[1] = 255;
412         data[2] = 255;
413         data[3] = 255;
414         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
415         data[0] = 255;
416         data[1] = 255;
417         data[2] = 255;
418         data[3] = 255;
419         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
420         for (side = 0;side < 6;side++)
421         {
422                 for (y = 0;y < 128;y++)
423                 {
424                         for (x = 0;x < 128;x++)
425                         {
426                                 s = (x + 0.5f) * (2.0f / 128.0f) - 1.0f;
427                                 t = (y + 0.5f) * (2.0f / 128.0f) - 1.0f;
428                                 switch(side)
429                                 {
430                                 case 0:
431                                         v[0] = 1;
432                                         v[1] = -t;
433                                         v[2] = -s;
434                                         break;
435                                 case 1:
436                                         v[0] = -1;
437                                         v[1] = -t;
438                                         v[2] = s;
439                                         break;
440                                 case 2:
441                                         v[0] = s;
442                                         v[1] = 1;
443                                         v[2] = t;
444                                         break;
445                                 case 3:
446                                         v[0] = s;
447                                         v[1] = -1;
448                                         v[2] = -t;
449                                         break;
450                                 case 4:
451                                         v[0] = s;
452                                         v[1] = -t;
453                                         v[2] = 1;
454                                         break;
455                                 case 5:
456                                         v[0] = -s;
457                                         v[1] = -t;
458                                         v[2] = -1;
459                                         break;
460                                 }
461                                 intensity = 127.0f / sqrt(DotProduct(v, v));
462                                 data[((side*128+y)*128+x)*4+0] = 128.0f + intensity * v[0];
463                                 data[((side*128+y)*128+x)*4+1] = 128.0f + intensity * v[1];
464                                 data[((side*128+y)*128+x)*4+2] = 128.0f + intensity * v[2];
465                                 data[((side*128+y)*128+x)*4+3] = 255;
466                         }
467                 }
468         }
469         r_shadow_normalscubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalscube", 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
470         for (y = 0;y < 128;y++)
471         {
472                 for (x = 0;x < 128;x++)
473                 {
474                         v[0] = (x + 0.5f) * (2.0f / 128.0f) - 1.0f;
475                         v[1] = (y + 0.5f) * (2.0f / 128.0f) - 1.0f;
476                         v[2] = 0;
477                         intensity = 1.0f - sqrt(DotProduct(v, v));
478                         if (intensity > 0)
479                                 intensity *= intensity;
480                         intensity = bound(0, intensity * r_shadow_atten1 * 256.0f, 255.0f);
481                         d = bound(0, intensity, 255);
482                         data[((0*128+y)*128+x)*4+0] = d;
483                         data[((0*128+y)*128+x)*4+1] = d;
484                         data[((0*128+y)*128+x)*4+2] = d;
485                         data[((0*128+y)*128+x)*4+3] = d;
486                 }
487         }
488         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", 128, 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_MIPMAP, NULL);
489         Mem_Free(data);
490         R_Shadow_Make3DTextures();
491 }
492
493 void R_Shadow_Stage_Begin(void)
494 {
495         rmeshstate_t m;
496
497         if (r_shadow_texture3d.integer == 1 && !gl_texture3d)
498         {
499                 Con_Printf("3D texture support not detected, falling back on slower 2D + 1D + normalization lighting\n");
500                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
501         }
502         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
503         if (!r_shadow_attenuation2dtexture
504          || (r_shadow_texture3d.integer == 1 && !r_shadow_normalsattenuationtexture)
505          || r_shadow_lightattenuationscale.value != r_shadow_atten1)
506                 R_Shadow_MakeTextures();
507         if (r_shadow_reloadlights && cl.worldmodel)
508         {
509                 r_shadow_reloadlights = false;
510                 R_Shadow_LoadWorldLights(cl.worldmodel->name);
511         }
512
513         memset(&m, 0, sizeof(m));
514         m.blendfunc1 = GL_ONE;
515         m.blendfunc2 = GL_ZERO;
516         R_Mesh_State(&m);
517         GL_Color(0, 0, 0, 1);
518         r_shadowstage = SHADOWSTAGE_NONE;
519 }
520
521 void R_Shadow_Stage_ShadowVolumes(void)
522 {
523         rmeshstate_t m;
524         memset(&m, 0, sizeof(m));
525         R_Mesh_TextureState(&m);
526         GL_Color(1, 1, 1, 1);
527         qglColorMask(0, 0, 0, 0);
528         qglDisable(GL_BLEND);
529         qglDepthMask(0);
530         qglDepthFunc(GL_LESS);
531         qglEnable(GL_STENCIL_TEST);
532         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
533         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
534         qglEnable(GL_CULL_FACE);
535         qglEnable(GL_DEPTH_TEST);
536         r_shadowstage = SHADOWSTAGE_STENCIL;
537         if (!r_shadow_erasebydrawing.integer)
538                 qglClear(GL_STENCIL_BUFFER_BIT);
539 }
540
541 void R_Shadow_Stage_Light(void)
542 {
543         rmeshstate_t m;
544         memset(&m, 0, sizeof(m));
545         R_Mesh_TextureState(&m);
546         qglActiveTexture(GL_TEXTURE0_ARB);
547
548         qglEnable(GL_BLEND);
549         qglBlendFunc(GL_ONE, GL_ONE);
550         GL_Color(1, 1, 1, 1);
551         qglColorMask(1, 1, 1, 1);
552         qglDepthMask(0);
553         qglDepthFunc(GL_EQUAL);
554         qglEnable(GL_STENCIL_TEST);
555         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
556         // only draw light where this geometry was already rendered AND the
557         // stencil is 0 (non-zero means shadow)
558         qglStencilFunc(GL_EQUAL, 0, 0xFF);
559         qglEnable(GL_CULL_FACE);
560         qglEnable(GL_DEPTH_TEST);
561         r_shadowstage = SHADOWSTAGE_LIGHT;
562 }
563
564 int R_Shadow_Stage_EraseShadowVolumes(void)
565 {
566         if (r_shadow_erasebydrawing.integer)
567         {
568                 rmeshstate_t m;
569                 memset(&m, 0, sizeof(m));
570                 R_Mesh_TextureState(&m);
571                 GL_Color(1, 1, 1, 1);
572                 qglColorMask(0, 0, 0, 0);
573                 qglDisable(GL_BLEND);
574                 qglDepthMask(0);
575                 qglDepthFunc(GL_LESS);
576                 qglEnable(GL_STENCIL_TEST);
577                 qglStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
578                 qglStencilFunc(GL_ALWAYS, 0, 0xFF);
579                 qglDisable(GL_CULL_FACE);
580                 qglDisable(GL_DEPTH_TEST);
581                 r_shadowstage = SHADOWSTAGE_ERASESTENCIL;
582                 return true;
583         }
584         else
585                 return false;
586 }
587
588 void R_Shadow_Stage_End(void)
589 {
590         rmeshstate_t m;
591         // attempt to restore state to what Mesh_State thinks it is
592         qglDisable(GL_BLEND);
593         qglBlendFunc(GL_ONE, GL_ZERO);
594         qglDepthMask(1);
595         // now restore the rest of the state to normal
596         GL_Color(1, 1, 1, 1);
597         qglColorMask(1, 1, 1, 1);
598         qglDisable(GL_SCISSOR_TEST);
599         qglDepthFunc(GL_LEQUAL);
600         qglDisable(GL_STENCIL_TEST);
601         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
602         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
603         qglEnable(GL_CULL_FACE);
604         qglEnable(GL_DEPTH_TEST);
605         // force mesh state to reset by using various combinations of features
606         memset(&m, 0, sizeof(m));
607         m.blendfunc1 = GL_SRC_ALPHA;
608         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
609         R_Mesh_State(&m);
610         m.blendfunc1 = GL_ONE;
611         m.blendfunc2 = GL_ZERO;
612         R_Mesh_State(&m);
613         r_shadowstage = SHADOWSTAGE_NONE;
614 }
615
616 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
617 {
618         int i, ix1, iy1, ix2, iy2;
619         float x1, y1, x2, y2, x, y;
620         vec3_t smins, smaxs;
621         vec4_t v, v2;
622         if (!r_shadow_scissor.integer)
623                 return false;
624         // if view is inside the box, just say yes it's visible
625         if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
626          && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
627          && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
628         {
629                 qglDisable(GL_SCISSOR_TEST);
630                 return false;
631         }
632         VectorSubtract(r_origin, origin, v);
633         if (DotProduct(v, v) < radius * radius)
634         {
635                 qglDisable(GL_SCISSOR_TEST);
636                 return false;
637         }
638         // create viewspace bbox
639         for (i = 0;i < 8;i++)
640         {
641                 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
642                 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
643                 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
644                 v2[0] = DotProduct(v, vright);
645                 v2[1] = DotProduct(v, vup);
646                 v2[2] = DotProduct(v, vpn);
647                 if (i)
648                 {
649                         if (smins[0] > v2[0]) smins[0] = v2[0];
650                         if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
651                         if (smins[1] > v2[1]) smins[1] = v2[1];
652                         if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
653                         if (smins[2] > v2[2]) smins[2] = v2[2];
654                         if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
655                 }
656                 else
657                 {
658                         smins[0] = smaxs[0] = v2[0];
659                         smins[1] = smaxs[1] = v2[1];
660                         smins[2] = smaxs[2] = v2[2];
661                 }
662         }
663         // now we have a bbox in viewspace
664         // clip it to the viewspace version of the sphere
665         v[0] = origin[0] - r_origin[0];
666         v[1] = origin[1] - r_origin[1];
667         v[2] = origin[2] - r_origin[2];
668         v2[0] = DotProduct(v, vright);
669         v2[1] = DotProduct(v, vup);
670         v2[2] = DotProduct(v, vpn);
671         if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
672         if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
673         if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
674         if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
675         if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
676         if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
677         // clip it to the view plane
678         if (smins[2] < 1)
679                 smins[2] = 1;
680         // return true if that culled the box
681         if (smins[2] >= smaxs[2])
682                 return true;
683         // ok some of it is infront of the view, transform each corner back to
684         // worldspace and then to screenspace and make screen rect
685         for (i = 0;i < 8;i++)
686         {
687                 v2[0] = (i & 1) ? smins[0] : smaxs[0];
688                 v2[1] = (i & 2) ? smins[1] : smaxs[1];
689                 v2[2] = (i & 4) ? smins[2] : smaxs[2];
690                 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
691                 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
692                 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
693                 v[3] = 1.0f;
694                 GL_TransformToScreen(v, v2);
695                 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
696                 x = v2[0];
697                 y = v2[1];
698                 if (i)
699                 {
700                         if (x1 > x) x1 = x;
701                         if (x2 < x) x2 = x;
702                         if (y1 > y) y1 = y;
703                         if (y2 < y) y2 = y;
704                 }
705                 else
706                 {
707                         x1 = x2 = x;
708                         y1 = y2 = y;
709                 }
710         }
711         /*
712         // this code doesn't handle boxes with any points behind view properly
713         x1 = 1000;x2 = -1000;
714         y1 = 1000;y2 = -1000;
715         for (i = 0;i < 8;i++)
716         {
717                 v[0] = (i & 1) ? mins[0] : maxs[0];
718                 v[1] = (i & 2) ? mins[1] : maxs[1];
719                 v[2] = (i & 4) ? mins[2] : maxs[2];
720                 v[3] = 1.0f;
721                 GL_TransformToScreen(v, v2);
722                 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
723                 if (v2[2] > 0)
724                 {
725                         x = v2[0];
726                         y = v2[1];
727
728                         if (x1 > x) x1 = x;
729                         if (x2 < x) x2 = x;
730                         if (y1 > y) y1 = y;
731                         if (y2 < y) y2 = y;
732                 }
733         }
734         */
735         ix1 = x1 - 1.0f;
736         iy1 = y1 - 1.0f;
737         ix2 = x2 + 1.0f;
738         iy2 = y2 + 1.0f;
739         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
740         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
741         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
742         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
743         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
744         if (ix2 <= ix1 || iy2 <= iy1)
745                 return true;
746         // set up the scissor rectangle
747         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
748         qglEnable(GL_SCISSOR_TEST);
749         return false;
750 }
751
752 void R_Shadow_GenTexCoords_Attenuation2D1D(float *out2d, float *out1d, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius)
753 {
754         int i;
755         float lightvec[3], iradius;
756         iradius = 0.5f / lightradius;
757         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out2d += 4, out1d += 4)
758         {
759                 VectorSubtract(vertex, relativelightorigin, lightvec);
760                 out2d[0] = 0.5f + DotProduct(svectors, lightvec) * iradius;
761                 out2d[1] = 0.5f + DotProduct(tvectors, lightvec) * iradius;
762                 out2d[2] = 0;
763                 out1d[0] = 0.5f + DotProduct(normals, lightvec) * iradius;
764                 out1d[1] = 0.5f;
765                 out1d[2] = 0;
766         }
767 }
768
769 void R_Shadow_GenTexCoords_Diffuse_Attenuation3D(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius)
770 {
771         int i;
772         float lightvec[3], iradius;
773         iradius = 0.5f / lightradius;
774         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
775         {
776                 VectorSubtract(vertex, relativelightorigin, lightvec);
777                 out[0] = 0.5f + DotProduct(svectors, lightvec) * iradius;
778                 out[1] = 0.5f + DotProduct(tvectors, lightvec) * iradius;
779                 out[2] = 0.5f + DotProduct(normals, lightvec) * iradius;
780         }
781 }
782
783 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin)
784 {
785         int i;
786         float lightdir[3];
787         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
788         {
789                 VectorSubtract(vertex, relativelightorigin, lightdir);
790                 // the cubemap normalizes this for us
791                 out[0] = DotProduct(svectors, lightdir);
792                 out[1] = DotProduct(tvectors, lightdir);
793                 out[2] = DotProduct(normals, lightdir);
794         }
795 }
796
797 void R_Shadow_GenTexCoords_Specular_Attenuation3D(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin, float lightradius)
798 {
799         int i;
800         float lightdir[3], eyedir[3], halfdir[3], lightdirlen, iradius;
801         iradius = 0.5f / lightradius;
802         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
803         {
804                 VectorSubtract(vertex, relativelightorigin, lightdir);
805                 // this is used later to make the attenuation correct
806                 lightdirlen = sqrt(DotProduct(lightdir, lightdir)) * iradius;
807                 VectorNormalizeFast(lightdir);
808                 VectorSubtract(vertex, relativeeyeorigin, eyedir);
809                 VectorNormalizeFast(eyedir);
810                 VectorAdd(lightdir, eyedir, halfdir);
811                 VectorNormalizeFast(halfdir);
812                 out[0] = 0.5f + DotProduct(svectors, halfdir) * lightdirlen;
813                 out[1] = 0.5f + DotProduct(tvectors, halfdir) * lightdirlen;
814                 out[2] = 0.5f + DotProduct(normals, halfdir) * lightdirlen;
815         }
816 }
817
818 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
819 {
820         int i;
821         float lightdir[3], eyedir[3], halfdir[3];
822         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
823         {
824                 VectorSubtract(vertex, relativelightorigin, lightdir);
825                 VectorNormalizeFast(lightdir);
826                 VectorSubtract(vertex, relativeeyeorigin, eyedir);
827                 VectorNormalizeFast(eyedir);
828                 VectorAdd(lightdir, eyedir, halfdir);
829                 // the cubemap normalizes this for us
830                 out[0] = DotProduct(svectors, halfdir);
831                 out[1] = DotProduct(tvectors, halfdir);
832                 out[2] = DotProduct(normals, halfdir);
833         }
834 }
835
836 void R_Shadow_GenTexCoords_LightCubeMap(float *out, int numverts, const float *vertex, const vec3_t relativelightorigin)
837 {
838         int i;
839         // FIXME: this needs to be written
840         // this code assumes the vertices are in worldspace (a false assumption)
841         for (i = 0;i < numverts;i++, vertex += 4, out += 4)
842                 VectorSubtract(vertex, relativelightorigin, out);
843 }
844
845 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
846 {
847         int renders, mult;
848         float scale, colorscale;
849         rmeshstate_t m;
850         memset(&m, 0, sizeof(m));
851         if (!bumptexture)
852                 bumptexture = r_shadow_blankbumptexture;
853         // colorscale accounts for how much we multiply the brightness during combine
854         // mult is how many times the final pass of the lighting will be
855         // performed to get more brightness than otherwise possible
856         // limit mult to 64 for sanity sake
857         if (r_shadow_texture3d.integer)
858         {
859                 if (r_textureunits.integer >= 4 && !lightcubemap)
860                 {
861                         // 4 texture 3D combine path, one pass, no light cubemap support
862                         m.tex[0] = R_GetTexture(bumptexture);
863                         m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
864                         m.tex[2] = R_GetTexture(basetexture);
865                         m.tex[3] = R_GetTexture(r_shadow_blankwhitetexture);
866                         m.texcombinergb[0] = GL_REPLACE;
867                         m.texcombinergb[1] = GL_DOT3_RGB_ARB;
868                         m.texcombinergb[2] = GL_MODULATE;
869                         m.texcombinergb[3] = GL_MODULATE;
870                         m.texrgbscale[1] = 1;
871                         m.texrgbscale[3] = 4;
872                         R_Mesh_TextureState(&m);
873                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
874                         memcpy(varray_texcoord[2], texcoords, numverts * sizeof(float[4]));
875                         R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
876                         qglActiveTexture(GL_TEXTURE3_ARB);
877                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
878                         colorscale = r_colorscale * 0.25f * r_shadow_lightintensityscale.value;
879                         for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
880                         colorscale *= scale;
881                         GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
882                         for (renders = 0;renders < mult;renders++)
883                                 R_Mesh_Draw(numverts, numtriangles, elements);
884                         qglActiveTexture(GL_TEXTURE3_ARB);
885                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
886                 }
887                 else
888                 {
889                         // 2 texture no3D combine path, two pass
890                         m.tex[0] = R_GetTexture(bumptexture);
891                         m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
892                         m.texcombinergb[0] = GL_REPLACE;
893                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
894                         m.texalphascale[1] = 1;
895                         R_Mesh_TextureState(&m);
896                         qglColorMask(0,0,0,1);
897                         qglDisable(GL_BLEND);
898                         GL_Color(1,1,1,1);
899                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
900                         R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
901                         R_Mesh_Draw(numverts, numtriangles, elements);
902
903                         m.tex[0] = R_GetTexture(basetexture);
904                         m.tex3d[1] = 0;
905                         m.texcubemap[1] = R_GetTexture(lightcubemap);
906                         m.texcombinergb[0] = GL_MODULATE;
907                         m.texcombinergb[1] = GL_MODULATE;
908                         m.texrgbscale[1] = 1;
909                         m.texalphascale[1] = 1;
910                         R_Mesh_TextureState(&m);
911                         qglColorMask(1,1,1,1);
912                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
913                         qglEnable(GL_BLEND);
914                         if (lightcubemap)
915                                 R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
916
917                         colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
918                         for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
919                         colorscale *= scale;
920                         GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
921                         for (renders = 0;renders < mult;renders++)
922                                 R_Mesh_Draw(numverts, numtriangles, elements);
923                 }
924         }
925         else if (r_textureunits.integer >= 4)
926         {
927                 // 4 texture no3D combine path, two pass
928                 m.tex[0] = R_GetTexture(bumptexture);
929                 m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
930                 m.texcombinergb[0] = GL_REPLACE;
931                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
932                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
933                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
934                 R_Mesh_TextureState(&m);
935                 qglColorMask(0,0,0,1);
936                 qglDisable(GL_BLEND);
937                 GL_Color(1,1,1,1);
938                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
939                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
940                 R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[2], varray_texcoord[3], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
941                 R_Mesh_Draw(numverts, numtriangles, elements);
942
943                 m.tex[0] = R_GetTexture(basetexture);
944                 m.texcubemap[1] = R_GetTexture(lightcubemap);
945                 m.texcombinergb[0] = GL_MODULATE;
946                 m.texcombinergb[1] = GL_MODULATE;
947                 m.tex[2] = 0;
948                 m.tex[3] = 0;
949                 R_Mesh_TextureState(&m);
950                 qglColorMask(1,1,1,1);
951                 qglBlendFunc(GL_DST_ALPHA, GL_ONE);
952                 qglEnable(GL_BLEND);
953                 if (lightcubemap)
954                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
955
956                 colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
957                 for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
958                 colorscale *= scale;
959                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
960                 for (renders = 0;renders < mult;renders++)
961                         R_Mesh_Draw(numverts, numtriangles, elements);
962         }
963         else
964         {
965                 // 2 texture no3D combine path, three pass
966                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
967                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
968                 R_Mesh_TextureState(&m);
969                 qglColorMask(0,0,0,1);
970                 qglDisable(GL_BLEND);
971                 GL_Color(1,1,1,1);
972                 R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
973                 R_Mesh_Draw(numverts, numtriangles, elements);
974
975                 m.tex[0] = R_GetTexture(bumptexture);
976                 m.tex[1] = 0;
977                 m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
978                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
979                 R_Mesh_TextureState(&m);
980                 qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
981                 qglEnable(GL_BLEND);
982                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
983                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
984                 R_Mesh_Draw(numverts, numtriangles, elements);
985
986                 m.tex[0] = R_GetTexture(basetexture);
987                 m.texcubemap[1] = R_GetTexture(lightcubemap);
988                 m.texcombinergb[1] = GL_MODULATE;
989                 R_Mesh_TextureState(&m);
990                 qglColorMask(1,1,1,1);
991                 qglBlendFunc(GL_DST_ALPHA, GL_ONE);
992                 if (lightcubemap)
993                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
994
995                 colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
996                 for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
997                 colorscale *= scale;
998                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
999                 for (renders = 0;renders < mult;renders++)
1000                         R_Mesh_Draw(numverts, numtriangles, elements);
1001         }
1002 }
1003
1004 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1005 {
1006         int renders, mult;
1007         float scale, colorscale;
1008         rmeshstate_t m;
1009         memset(&m, 0, sizeof(m));
1010         if (!bumptexture)
1011                 bumptexture = r_shadow_blankbumptexture;
1012         if (!glosstexture)
1013                 glosstexture = r_shadow_blankglosstexture;
1014         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1015         {
1016                 // 2 texture no3D combine path, five pass
1017                 memset(&m, 0, sizeof(m));
1018
1019                 m.tex[0] = R_GetTexture(bumptexture);
1020                 m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1021                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1022                 R_Mesh_TextureState(&m);
1023                 qglColorMask(0,0,0,1);
1024                 qglDisable(GL_BLEND);
1025                 GL_Color(1,1,1,1);
1026                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1027                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
1028                 R_Mesh_Draw(numverts, numtriangles, elements);
1029
1030                 m.tex[0] = 0;
1031                 m.texcubemap[1] = 0;
1032                 m.texcombinergb[1] = GL_MODULATE;
1033                 R_Mesh_TextureState(&m);
1034                 // square alpha in framebuffer a few times to make it shiny
1035                 qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1036                 qglEnable(GL_BLEND);
1037                 // these comments are a test run through this math for intensity 0.5
1038                 // 0.5 * 0.5 = 0.25
1039                 R_Mesh_Draw(numverts, numtriangles, elements);
1040                 // 0.25 * 0.25 = 0.0625
1041                 R_Mesh_Draw(numverts, numtriangles, elements);
1042                 // 0.0625 * 0.0625 = 0.00390625
1043                 R_Mesh_Draw(numverts, numtriangles, elements);
1044
1045                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1046                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1047                 R_Mesh_TextureState(&m);
1048                 qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1049                 R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
1050                 R_Mesh_Draw(numverts, numtriangles, elements);
1051
1052                 m.tex[0] = R_GetTexture(glosstexture);
1053                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1054                 R_Mesh_TextureState(&m);
1055                 qglColorMask(1,1,1,1);
1056                 qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1057                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1058                 if (lightcubemap)
1059                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
1060
1061                 // the 0.25f makes specular lighting much dimmer than diffuse (intentionally)
1062                 colorscale = r_colorscale * 0.25f * r_shadow_lightintensityscale.value;
1063                 for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
1064                 colorscale *= scale;
1065                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
1066                 for (renders = 0;renders < mult;renders++)
1067                         R_Mesh_Draw(numverts, numtriangles, elements);
1068         }
1069 }
1070
1071 #define PRECOMPUTEDSHADOWVOLUMES 1
1072 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1073 {
1074 #if PRECOMPUTEDSHADOWVOLUMES
1075         R_Mesh_Matrix(matrix);
1076         R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1077 #else
1078         shadowmesh_t *mesh;
1079         R_Mesh_Matrix(matrix);
1080         for (mesh = light->shadowvolume;mesh;mesh = mesh->next)
1081         {
1082                 R_Mesh_ResizeCheck(mesh->numverts * 2);
1083                 memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
1084                 R_Shadow_Volume(mesh->numverts, mesh->numtriangles, varray_vertex, mesh->elements, mesh->neighbors, light->origin, light->lightradius, light->lightradius);
1085         }
1086 #endif
1087 }
1088
1089 cvar_t r_editlights = {0, "r_editlights", "0"};
1090 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1091 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1092 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1093 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1094 worldlight_t *r_shadow_worldlightchain;
1095 worldlight_t *r_shadow_selectedlight;
1096 vec3_t r_editlights_cursorlocation;
1097
1098 static int castshadowcount = 1;
1099 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname)
1100 {
1101         int i, j, k, l, maxverts, *mark;
1102         float *verts, *v, *v0, *v1, f, projectdistance, temp[3], temp2[3], temp3[3], radius2;
1103         worldlight_t *e;
1104         shadowmesh_t *mesh;
1105         mleaf_t *leaf;
1106         msurface_t *surf;
1107         qbyte *pvs;
1108
1109         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1110         VectorCopy(origin, e->origin);
1111         VectorCopy(color, e->light);
1112         e->lightradius = radius;
1113         VectorCopy(origin, e->mins);
1114         VectorCopy(origin, e->maxs);
1115         e->cullradius = 0;
1116         e->style = style;
1117         e->next = r_shadow_worldlightchain;
1118         r_shadow_worldlightchain = e;
1119         if (cubemapname)
1120         {
1121                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1122                 strcpy(e->cubemapname, cubemapname);
1123                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1124         }
1125         if (cl.worldmodel)
1126         {
1127                 castshadowcount++;
1128                 leaf = Mod_PointInLeaf(origin, cl.worldmodel);
1129                 pvs = Mod_LeafPVS(leaf, cl.worldmodel);
1130                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1131                 {
1132                         if (pvs[i >> 3] & (1 << (i & 7)))
1133                         {
1134                                 VectorCopy(origin, temp);
1135                                 if (temp[0] < leaf->mins[0]) temp[0] = leaf->mins[0];
1136                                 if (temp[0] > leaf->maxs[0]) temp[0] = leaf->maxs[0];
1137                                 if (temp[1] < leaf->mins[1]) temp[1] = leaf->mins[1];
1138                                 if (temp[1] > leaf->maxs[1]) temp[1] = leaf->maxs[1];
1139                                 if (temp[2] < leaf->mins[2]) temp[2] = leaf->mins[2];
1140                                 if (temp[2] > leaf->maxs[2]) temp[2] = leaf->maxs[2];
1141                                 VectorSubtract(temp, origin, temp);
1142                                 if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
1143                                 {
1144                                         leaf->worldnodeframe = castshadowcount;
1145                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1146                                         {
1147                                                 surf = cl.worldmodel->surfaces + *mark;
1148                                                 if (surf->castshadow != castshadowcount)
1149                                                 {
1150                                                         f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1151                                                         if (surf->flags & SURF_PLANEBACK)
1152                                                                 f = -f;
1153                                                         if (f > 0 && f < e->lightradius)
1154                                                         {
1155                                                                 VectorSubtract(e->origin, surf->poly_center, temp);
1156                                                                 if (DotProduct(temp, temp) - surf->poly_radius2 < e->lightradius * e->lightradius)
1157                                                                         surf->castshadow = castshadowcount;
1158                                                         }
1159                                                 }
1160                                         }
1161                                 }
1162                         }
1163                 }
1164
1165                 e->numleafs = 0;
1166                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1167                         if (leaf->worldnodeframe == castshadowcount)
1168                                 e->numleafs++;
1169                 e->numsurfaces = 0;
1170                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1171                         if (surf->castshadow == castshadowcount)
1172                                 e->numsurfaces++;
1173
1174                 if (e->numleafs)
1175                         e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1176                 if (e->numsurfaces)
1177                         e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1178                 e->numleafs = 0;
1179                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1180                         if (leaf->worldnodeframe == castshadowcount)
1181                                 e->leafs[e->numleafs++] = leaf;
1182                 e->numsurfaces = 0;
1183                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1184                         if (surf->castshadow == castshadowcount)
1185                                 e->surfaces[e->numsurfaces++] = surf;
1186                 // find bounding box and sphere of lit surfaces
1187                 // (these will be used for creating a shape to clip the light)
1188                 radius2 = 0;
1189                 VectorCopy(e->origin, e->mins);
1190                 VectorCopy(e->origin, e->maxs);
1191                 for (j = 0;j < e->numsurfaces;j++)
1192                 {
1193                         surf = e->surfaces[j];
1194                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
1195                         {
1196                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
1197                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
1198                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1199                                 VectorSubtract(v, e->origin, temp);
1200                                 f = DotProduct(temp, temp);
1201                                 if (radius2 < f)
1202                                         radius2 = f;
1203                         }
1204                 }
1205                 e->cullradius = sqrt(radius2);
1206                 if (e->cullradius > e->lightradius)
1207                         e->cullradius = e->lightradius;
1208                 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
1209                 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
1210                 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
1211                 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
1212                 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
1213                 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
1214                 Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
1215                 // clip shadow volumes against eachother to remove unnecessary
1216                 // polygons (and sections of polygons)
1217                 maxverts = 256;
1218                 verts = NULL;
1219                 castshadowcount++;
1220                 for (j = 0;j < e->numsurfaces;j++)
1221                 {
1222                         surf = e->surfaces[j];
1223                         if (surf->flags & SURF_SHADOWCAST)
1224                         {
1225                                 surf->castshadow = castshadowcount;
1226                                 if (maxverts < surf->poly_numverts)
1227                                         maxverts = surf->poly_numverts;
1228                         }
1229                 }
1230                 e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 32768);
1231 #if !PRECOMPUTEDSHADOWVOLUMES
1232                 // make a mesh to cast a shadow volume from
1233                 for (j = 0;j < e->numsurfaces;j++)
1234                         if (e->surfaces[j]->castshadow == castshadowcount)
1235                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, e->surfaces[j]->poly_numverts, e->surfaces[j]->poly_verts);
1236 #else
1237 #if 1
1238                 {
1239                 int tris;
1240                 shadowmesh_t *castmesh, *mesh;
1241                 surfmesh_t *surfmesh;
1242                 // make a mesh to cast a shadow volume from
1243                 castmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, 32768);
1244                 for (j = 0;j < e->numsurfaces;j++)
1245                         if (e->surfaces[j]->castshadow == castshadowcount)
1246                                 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1247                                         Mod_ShadowMesh_AddMesh(loadmodel->mempool, castmesh, surfmesh->numverts, surfmesh->verts, surfmesh->numtriangles, surfmesh->index);
1248                 castmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, castmesh);
1249
1250                 // cast shadow volume from castmesh
1251                 for (mesh = castmesh;mesh;mesh = mesh->next)
1252                 {
1253                         R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
1254                         R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1255
1256                         if (maxverts < castmesh->numverts * 2)
1257                         {
1258                                 maxverts = castmesh->numverts * 2;
1259                                 if (verts)
1260                                         Mem_Free(verts);
1261                                 verts = NULL;
1262                         }
1263                         if (verts == NULL && maxverts > 0)
1264                                 verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[4]));
1265
1266                         // now that we have the buffers big enough, construct shadow volume mesh
1267                         memcpy(verts, castmesh->verts, castmesh->numverts * sizeof(float[4]));
1268                         R_Shadow_ProjectVertices(verts, verts + castmesh->numverts * 4, castmesh->numverts, e->origin, e->lightradius);
1269                         R_Shadow_MakeTriangleShadowFlags(castmesh->elements, verts, castmesh->numtriangles, trianglefacinglight, e->origin, e->lightradius);
1270                         tris = R_Shadow_BuildShadowVolumeTriangles(castmesh->elements, castmesh->neighbors, castmesh->numtriangles, castmesh->numverts, trianglefacinglight, shadowelements);
1271                         // add the constructed shadow volume mesh
1272                         Mod_ShadowMesh_AddMesh(loadmodel->mempool, e->shadowvolume, castmesh->numverts, verts, tris, shadowelements);
1273                 }
1274                 // we're done with castmesh now
1275                 Mod_ShadowMesh_Free(castmesh);
1276                 }
1277 #else
1278                 // make a shadow volume mesh
1279                 if (verts == NULL && maxverts > 0)
1280                         verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[4]));
1281                 for (j = 0;j < e->numsurfaces;j++)
1282                 {
1283                         surf = e->surfaces[j];
1284                         if (surf->castshadow != castshadowcount)
1285                                 continue;
1286                         projectdistance = 1000000.0f;//e->lightradius;
1287                         // copy the original polygon, for the front cap of the volume
1288                         for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1289                                 VectorCopy(v0, v1);
1290                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1291                         // project the original polygon, reversed, for the back cap of the volume
1292                         for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1293                         {
1294                                 VectorSubtract(v0, e->origin, temp);
1295                                 //VectorNormalize(temp);
1296                                 VectorMA(v0, projectdistance, temp, v1);
1297                         }
1298                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1299                         // project the shadow volume sides
1300                         for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
1301                         {
1302                                 if (surf->neighborsurfaces == NULL || surf->neighborsurfaces[l] == NULL || surf->neighborsurfaces[l]->castshadow != castshadowcount)
1303                                 {
1304                                         VectorCopy(v1, &verts[0]);
1305                                         VectorCopy(v0, &verts[3]);
1306                                         VectorCopy(v0, &verts[6]);
1307                                         VectorCopy(v1, &verts[9]);
1308                                         VectorSubtract(&verts[6], e->origin, temp);
1309                                         //VectorNormalize(temp);
1310                                         VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1311                                         VectorSubtract(&verts[9], e->origin, temp);
1312                                         //VectorNormalize(temp);
1313                                         VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1314
1315 #if 0
1316                                         VectorSubtract(&verts[0], &verts[3], temp);
1317                                         VectorSubtract(&verts[6], &verts[3], temp2);
1318                                         CrossProduct(temp, temp2, temp3);
1319                                         VectorNormalize(temp3);
1320                                         if (DotProduct(surf->poly_center, temp3) > DotProduct(&verts[0], temp3))
1321                                         {
1322                                                 VectorCopy(v0, &verts[0]);
1323                                                 VectorCopy(v1, &verts[3]);
1324                                                 VectorCopy(v1, &verts[6]);
1325                                                 VectorCopy(v0, &verts[9]);
1326                                                 VectorSubtract(&verts[6], e->origin, temp);
1327                                                 //VectorNormalize(temp);
1328                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1329                                                 VectorSubtract(&verts[9], e->origin, temp);
1330                                                 //VectorNormalize(temp);
1331                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1332                                                 Con_Printf("flipped shadow volume edge %8p %i\n", surf, l);
1333                                         }
1334 #endif
1335
1336                                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1337                                 }
1338                         }
1339                 }
1340 #endif
1341 #endif
1342                 e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
1343                 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1344                         l += mesh->numtriangles;
1345                 Con_Printf("static shadow volume built containing %i triangles\n", l);
1346         }
1347 }
1348
1349 void R_Shadow_FreeWorldLight(worldlight_t *light)
1350 {
1351         worldlight_t **lightpointer;
1352         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1353         if (*lightpointer != light)
1354                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1355         *lightpointer = light->next;
1356         if (light->cubemapname)
1357                 Mem_Free(light->cubemapname);
1358         if (light->shadowvolume)
1359                 Mod_ShadowMesh_Free(light->shadowvolume);
1360         if (light->surfaces)
1361                 Mem_Free(light->surfaces);
1362         if (light->leafs)
1363                 Mem_Free(light->leafs);
1364         Mem_Free(light);
1365 }
1366
1367 void R_Shadow_ClearWorldLights(void)
1368 {
1369         while (r_shadow_worldlightchain)
1370                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1371         r_shadow_selectedlight = NULL;
1372 }
1373
1374 void R_Shadow_SelectLight(worldlight_t *light)
1375 {
1376         if (r_shadow_selectedlight)
1377                 r_shadow_selectedlight->selected = false;
1378         r_shadow_selectedlight = light;
1379         if (r_shadow_selectedlight)
1380                 r_shadow_selectedlight->selected = true;
1381 }
1382
1383 void R_Shadow_FreeSelectedWorldLight(void)
1384 {
1385         if (r_shadow_selectedlight)
1386         {
1387                 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
1388                 r_shadow_selectedlight = NULL;
1389         }
1390 }
1391
1392 void R_Shadow_SelectLightInView(void)
1393 {
1394         float bestrating, rating, temp[3], dist;
1395         worldlight_t *best, *light;
1396         best = NULL;
1397         bestrating = 1e30;
1398         for (light = r_shadow_worldlightchain;light;light = light->next)
1399         {
1400                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1401                 dist = sqrt(DotProduct(temp, temp));
1402                 if (DotProduct(temp, vpn) >= 0.97 * dist && bestrating > dist && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
1403                 {
1404                         bestrating = dist;
1405                         best = light;
1406                 }
1407         }
1408         R_Shadow_SelectLight(best);
1409 }
1410
1411 void R_Shadow_LoadWorldLights(const char *mapname)
1412 {
1413         int n, a, style;
1414         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1415         float origin[3], radius, color[3];
1416         COM_StripExtension(mapname, name);
1417         strcat(name, ".rtlights");
1418         lightsstring = COM_LoadFile(name, false);
1419         if (lightsstring)
1420         {
1421                 s = lightsstring;
1422                 n = 0;
1423                 while (*s)
1424                 {
1425                         t = s;
1426                         while (*s && *s != '\n')
1427                                 s++;
1428                         if (!*s)
1429                                 break;
1430                         *s = 0;
1431                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, &cubemapname);
1432                         if (a < 9)
1433                                 cubemapname[0] = 0;
1434                         *s = '\n';
1435                         if (a < 8)
1436                         {
1437                                 Con_Printf("found %d parameters on line %i, should be 8 or 9 parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style cubemapname)\n", a, n + 1);
1438                                 break;
1439                         }
1440                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
1441                         s++;
1442                         n++;
1443                 }
1444                 if (*s)
1445                         Con_Printf("invalid rtlights file \"%s\"\n", name);
1446                 Mem_Free(lightsstring);
1447         }
1448 }
1449
1450 void R_Shadow_SaveWorldLights(const char *mapname)
1451 {
1452         worldlight_t *light;
1453         int bufchars, bufmaxchars;
1454         char *buf, *oldbuf;
1455         char name[MAX_QPATH];
1456         char line[1024];
1457         if (!r_shadow_worldlightchain)
1458                 return;
1459         COM_StripExtension(mapname, name);
1460         strcat(name, ".rtlights");
1461         bufchars = bufmaxchars = 0;
1462         buf = NULL;
1463         for (light = r_shadow_worldlightchain;light;light = light->next)
1464         {
1465                 sprintf(line, "%g %g %g %g %g %g %g %d %s\n", light->origin[0], light->origin[1], light->origin[2], light->lightradius, light->light[0], light->light[1], light->light[2], light->style, light->cubemapname ? light->cubemapname : "");
1466                 if (bufchars + strlen(line) > bufmaxchars)
1467                 {
1468                         bufmaxchars = bufchars + strlen(line) + 2048;
1469                         oldbuf = buf;
1470                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
1471                         if (oldbuf)
1472                         {
1473                                 if (bufchars)
1474                                         memcpy(buf, oldbuf, bufchars);
1475                                 Mem_Free(oldbuf);
1476                         }
1477                 }
1478                 if (strlen(line))
1479                 {
1480                         memcpy(buf + bufchars, line, strlen(line));
1481                         bufchars += strlen(line);
1482                 }
1483         }
1484         if (bufchars)
1485                 COM_WriteFile(name, buf, bufchars);
1486         if (buf)
1487                 Mem_Free(buf);
1488 }
1489
1490 void R_Shadow_SetCursorLocationForView(void)
1491 {
1492         vec_t dist, push, frac;
1493         vec3_t dest, endpos, normal;
1494         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
1495         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
1496         if (frac < 1)
1497         {
1498                 dist = frac * r_editlights_cursordistance.value;
1499                 push = r_editlights_cursorpushback.value;
1500                 if (push > dist)
1501                         push = dist;
1502                 push = -push;
1503                 VectorMA(endpos, push, vpn, endpos);
1504                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
1505         }
1506         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
1507         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
1508         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
1509 }
1510
1511 extern void R_DrawCrosshairSprite(rtexture_t *texture, vec3_t origin, vec_t scale, float cr, float cg, float cb, float ca);
1512 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1513 {
1514         cachepic_t *pic;
1515         pic = Draw_CachePic("gfx/crosshair1.tga");
1516         if (pic)
1517                 R_DrawCrosshairSprite(pic->tex, r_editlights_cursorlocation, r_editlights_cursorgrid.value * 0.5f, 1, 1, 1, 1);
1518 }
1519
1520 void R_Shadow_DrawCursor(void)
1521 {
1522         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1523 }
1524
1525 void R_Shadow_UpdateLightingMode(void)
1526 {
1527         r_shadow_lightingmode = 0;
1528         if (r_shadow_realtime.integer)
1529         {
1530                 if (r_shadow_worldlightchain)
1531                         r_shadow_lightingmode = 2;
1532                 else
1533                         r_shadow_lightingmode = 1;
1534         }
1535 }
1536
1537 void R_Shadow_UpdateWorldLightSelection(void)
1538 {
1539         if (r_editlights.integer)
1540         {
1541                 R_Shadow_SelectLightInView();
1542                 R_Shadow_SetCursorLocationForView();
1543                 R_Shadow_DrawCursor();
1544         }
1545         else
1546                 R_Shadow_SelectLight(NULL);
1547 }
1548
1549 void R_Shadow_EditLights_Clear_f(void)
1550 {
1551         R_Shadow_ClearWorldLights();
1552 }
1553
1554 void R_Shadow_EditLights_Reload_f(void)
1555 {
1556         if (cl.worldmodel)
1557         {
1558                 R_Shadow_ClearWorldLights();
1559                 R_Shadow_LoadWorldLights(cl.worldmodel->name);
1560         }
1561 }
1562
1563 void R_Shadow_EditLights_Save_f(void)
1564 {
1565         if (cl.worldmodel)
1566                 R_Shadow_SaveWorldLights(cl.worldmodel->name);
1567 }
1568
1569 void R_Shadow_EditLights_Spawn_f(void)
1570 {
1571         vec3_t origin, color;
1572         vec_t radius;
1573         int style;
1574         const char *cubemapname;
1575         if (!r_editlights.integer)
1576         {
1577                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
1578                 return;
1579         }
1580         if (Cmd_Argc() <= 7)
1581         {
1582                 radius = 200;
1583                 color[0] = color[1] = color[2] = 1;
1584                 style = 0;
1585                 cubemapname = NULL;
1586                 if (Cmd_Argc() >= 2)
1587                 {
1588                         radius = atof(Cmd_Argv(1));
1589                         if (Cmd_Argc() >= 3)
1590                         {
1591                                 color[0] = atof(Cmd_Argv(2));
1592                                 color[1] = color[0];
1593                                 color[2] = color[0];
1594                                 if (Cmd_Argc() >= 5)
1595                                 {
1596                                         color[1] = atof(Cmd_Argv(3));
1597                                         color[2] = atof(Cmd_Argv(4));
1598                                         if (Cmd_Argc() >= 6)
1599                                         {
1600                                                 style = atoi(Cmd_Argv(5));
1601                                                 if (Cmd_Argc() >= 7)
1602                                                         cubemapname = Cmd_Argv(6);
1603                                         }
1604                                 }
1605                         }
1606                 }
1607                 if (cubemapname && !cubemapname[0])
1608                         cubemapname = NULL;
1609                 if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
1610                 {
1611                         VectorCopy(r_editlights_cursorlocation, origin);
1612                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
1613                         return;
1614                 }
1615         }
1616         Con_Printf("usage: r_editlights_spawn radius red green blue [style [cubemap]]\n");
1617 }
1618
1619 void R_Shadow_EditLights_Edit_f(void)
1620 {
1621         vec3_t origin, color;
1622         vec_t radius;
1623         int style;
1624         const char *cubemapname;
1625         if (!r_editlights.integer)
1626         {
1627                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
1628                 return;
1629         }
1630         if (!r_shadow_selectedlight)
1631         {
1632                 Con_Printf("No selected light.\n");
1633                 return;
1634         }
1635         if (Cmd_Argc() <= 7)
1636         {
1637                 radius = 200;
1638                 color[0] = color[1] = color[2] = 1;
1639                 style = 0;
1640                 cubemapname = NULL;
1641                 if (Cmd_Argc() >= 2)
1642                 {
1643                         radius = atof(Cmd_Argv(1));
1644                         if (Cmd_Argc() >= 3)
1645                         {
1646                                 color[0] = atof(Cmd_Argv(2));
1647                                 color[1] = color[0];
1648                                 color[2] = color[0];
1649                                 if (Cmd_Argc() >= 5)
1650                                 {
1651                                         color[1] = atof(Cmd_Argv(3));
1652                                         color[2] = atof(Cmd_Argv(4));
1653                                         if (Cmd_Argc() >= 6)
1654                                         {
1655                                                 style = atoi(Cmd_Argv(5));
1656                                                 if (Cmd_Argc() >= 7)
1657                                                         cubemapname = Cmd_Argv(6);
1658                                         }
1659                                 }
1660                         }
1661                 }
1662                 if (cubemapname && !cubemapname[0])
1663                         cubemapname = NULL;
1664                 if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
1665                 {
1666                         VectorCopy(r_shadow_selectedlight->origin, origin);
1667                         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
1668                         r_shadow_selectedlight = NULL;
1669                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
1670                         return;
1671                 }
1672         }
1673         Con_Printf("usage: r_editlights_edit radius red green blue [style [cubemap]]\n");
1674 }
1675
1676 void R_Shadow_EditLights_Remove_f(void)
1677 {
1678         if (!r_editlights.integer)
1679         {
1680                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
1681                 return;
1682         }
1683         if (!r_shadow_selectedlight)
1684         {
1685                 Con_Printf("No selected light.\n");
1686                 return;
1687         }
1688         R_Shadow_FreeSelectedWorldLight();
1689 }
1690
1691 void R_Shadow_EditLights_Init(void)
1692 {
1693         Cvar_RegisterVariable(&r_editlights);
1694         Cvar_RegisterVariable(&r_editlights_cursordistance);
1695         Cvar_RegisterVariable(&r_editlights_cursorpushback);
1696         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
1697         Cvar_RegisterVariable(&r_editlights_cursorgrid);
1698         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
1699         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
1700         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
1701         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
1702         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
1703         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
1704 }