]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
R_Mesh_State_Texture and GL_VertexPointer merge once again to become the reborn R_Mes...
[divverent/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
15
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
20
21 Patent warning:
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
26 shadows do not lie.
27
28
29
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
36
37
38
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
44 in some ideal cases).
45
46
47
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however.  Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
57
58
59
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
65
66
67
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
76
77
78
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
82
83
84
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
89
90
91
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
96
97
98
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
105
106
107
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
112 */
113
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
117 #include "portals.h"
118 #include "image.h"
119
120 extern void R_Shadow_EditLights_Init(void);
121
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_STENCILTWOSIDE 3
126
127 int r_shadowstage = SHADOWSTAGE_NONE;
128 int r_shadow_reloadlights = false;
129
130 mempool_t *r_shadow_mempool;
131
132 int maxshadowelements;
133 int *shadowelements;
134
135 int maxshadowmark;
136 int numshadowmark;
137 int *shadowmark;
138 int *shadowmarklist;
139 int shadowmarkcount;
140
141 int maxvertexupdate;
142 int *vertexupdate;
143 int *vertexremap;
144 int vertexupdatenum;
145
146 rtexturepool_t *r_shadow_texturepool;
147 rtexture_t *r_shadow_normalcubetexture;
148 rtexture_t *r_shadow_attenuation2dtexture;
149 rtexture_t *r_shadow_attenuation3dtexture;
150 rtexture_t *r_shadow_blankbumptexture;
151 rtexture_t *r_shadow_blankglosstexture;
152 rtexture_t *r_shadow_blankwhitetexture;
153
154 // used only for light filters (cubemaps)
155 rtexturepool_t *r_shadow_filters_texturepool;
156
157 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
158 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
159 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
160 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
161 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
162 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
163 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
164 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
165 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
166 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
167 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
168 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
169 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
170 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
171 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
172 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
173 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
174 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
175 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
176 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
177 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
178 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
179 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
180 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
181 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
182 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
183
184 int c_rt_lights, c_rt_clears, c_rt_scissored;
185 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
186 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
187
188 void R_Shadow_ClearWorldLights(void);
189 void R_Shadow_SaveWorldLights(void);
190 void R_Shadow_LoadWorldLights(void);
191 void R_Shadow_LoadLightsFile(void);
192 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
193
194 void r_shadow_start(void)
195 {
196         // allocate vertex processing arrays
197         r_shadow_mempool = Mem_AllocPool("R_Shadow");
198         maxshadowelements = 0;
199         shadowelements = NULL;
200         maxvertexupdate = 0;
201         vertexupdate = NULL;
202         vertexremap = NULL;
203         vertexupdatenum = 0;
204         maxshadowmark = 0;
205         numshadowmark = 0;
206         shadowmark = NULL;
207         shadowmarklist = NULL;
208         shadowmarkcount = 0;
209         r_shadow_normalcubetexture = NULL;
210         r_shadow_attenuation2dtexture = NULL;
211         r_shadow_attenuation3dtexture = NULL;
212         r_shadow_blankbumptexture = NULL;
213         r_shadow_blankglosstexture = NULL;
214         r_shadow_blankwhitetexture = NULL;
215         r_shadow_texturepool = NULL;
216         r_shadow_filters_texturepool = NULL;
217         R_Shadow_ClearWorldLights();
218         r_shadow_reloadlights = true;
219 }
220
221 void r_shadow_shutdown(void)
222 {
223         R_Shadow_ClearWorldLights();
224         r_shadow_reloadlights = true;
225         r_shadow_normalcubetexture = NULL;
226         r_shadow_attenuation2dtexture = NULL;
227         r_shadow_attenuation3dtexture = NULL;
228         r_shadow_blankbumptexture = NULL;
229         r_shadow_blankglosstexture = NULL;
230         r_shadow_blankwhitetexture = NULL;
231         R_FreeTexturePool(&r_shadow_texturepool);
232         R_FreeTexturePool(&r_shadow_filters_texturepool);
233         maxshadowelements = 0;
234         shadowelements = NULL;
235         maxvertexupdate = 0;
236         vertexupdate = NULL;
237         vertexremap = NULL;
238         vertexupdatenum = 0;
239         maxshadowmark = 0;
240         numshadowmark = 0;
241         shadowmark = NULL;
242         shadowmarklist = NULL;
243         shadowmarkcount = 0;
244         Mem_FreePool(&r_shadow_mempool);
245 }
246
247 void r_shadow_newmap(void)
248 {
249         R_Shadow_ClearWorldLights();
250         r_shadow_reloadlights = true;
251 }
252
253 void R_Shadow_Help_f(void)
254 {
255         Con_Printf(
256 "Documentation on r_shadow system:\n"
257 "Settings:\n"
258 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
259 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
260 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
261 "r_shadow_realtime_world : use realtime world light rendering\n"
262 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
263 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n"
264 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
265 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
266 "r_shadow_glossintensity : brightness of textured gloss\n"
267 "r_shadow_gloss2intensity : brightness of forced gloss\n"
268 "r_shadow_debuglight : render only this light number (-1 = all)\n"
269 "r_shadow_scissor : use scissor optimization\n"
270 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
271 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
272 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
273 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
274 "r_shadow_portallight : use portal visibility for static light precomputation\n"
275 "r_shadow_projectdistance : shadow volume projection distance\n"
276 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
277 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
278 "r_shadow_worldshadows : enable world shadows\n"
279 "r_shadow_dlightshadows : enable dlight shadows\n"
280 "Commands:\n"
281 "r_shadow_help : this help\n"
282         );
283 }
284
285 void R_Shadow_Init(void)
286 {
287         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
288         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
289         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
290         Cvar_RegisterVariable(&r_shadow_realtime_world);
291         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
292         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
293         Cvar_RegisterVariable(&r_shadow_visiblevolumes);
294         Cvar_RegisterVariable(&r_shadow_gloss);
295         Cvar_RegisterVariable(&r_shadow_glossintensity);
296         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
297         Cvar_RegisterVariable(&r_shadow_debuglight);
298         Cvar_RegisterVariable(&r_shadow_scissor);
299         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
300         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
301         Cvar_RegisterVariable(&r_shadow_polygonfactor);
302         Cvar_RegisterVariable(&r_shadow_polygonoffset);
303         Cvar_RegisterVariable(&r_shadow_portallight);
304         Cvar_RegisterVariable(&r_shadow_projectdistance);
305         Cvar_RegisterVariable(&r_shadow_texture3d);
306         Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
307         Cvar_RegisterVariable(&r_shadow_worldshadows);
308         Cvar_RegisterVariable(&r_shadow_dlightshadows);
309         Cvar_RegisterVariable(&r_shadow_showtris);
310         Cvar_RegisterVariable(&r_shadow_staticworldlights);
311         Cvar_RegisterVariable(&r_shadow_cull);
312         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
313         if (gamemode == GAME_TENEBRAE)
314         {
315                 Cvar_SetValue("r_shadow_gloss", 2);
316                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
317         }
318         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
319         R_Shadow_EditLights_Init();
320         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
321 }
322
323 matrix4x4_t matrix_attenuationxyz =
324 {
325         {
326                 {0.5, 0.0, 0.0, 0.5},
327                 {0.0, 0.5, 0.0, 0.5},
328                 {0.0, 0.0, 0.5, 0.5},
329                 {0.0, 0.0, 0.0, 1.0}
330         }
331 };
332
333 matrix4x4_t matrix_attenuationz =
334 {
335         {
336                 {0.0, 0.0, 0.5, 0.5},
337                 {0.0, 0.0, 0.0, 0.0},
338                 {0.0, 0.0, 0.0, 0.0},
339                 {0.0, 0.0, 0.0, 1.0}
340         }
341 };
342
343 int *R_Shadow_ResizeShadowElements(int numtris)
344 {
345         // make sure shadowelements is big enough for this volume
346         if (maxshadowelements < numtris * 24)
347         {
348                 maxshadowelements = numtris * 24;
349                 if (shadowelements)
350                         Mem_Free(shadowelements);
351                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
352         }
353         return shadowelements;
354 }
355
356 void R_Shadow_PrepareShadowMark(int numtris)
357 {
358         // make sure shadowmark is big enough for this volume
359         if (maxshadowmark < numtris)
360         {
361                 maxshadowmark = numtris;
362                 if (shadowmark)
363                         Mem_Free(shadowmark);
364                 if (shadowmarklist)
365                         Mem_Free(shadowmarklist);
366                 shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
367                 shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
368                 shadowmarkcount = 0;
369         }
370         shadowmarkcount++;
371         // if shadowmarkcount wrapped we clear the array and adjust accordingly
372         if (shadowmarkcount == 0)
373         {
374                 shadowmarkcount = 1;
375                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
376         }
377         numshadowmark = 0;
378 }
379
380 int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
381 {
382         int i, j, tris = 0, vr[3], t, outvertices = 0;
383         const int *e, *n;
384         float f, temp[3];
385
386         if (maxvertexupdate < innumvertices)
387         {
388                 maxvertexupdate = innumvertices;
389                 if (vertexupdate)
390                         Mem_Free(vertexupdate);
391                 if (vertexremap)
392                         Mem_Free(vertexremap);
393                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
394                 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
395                 vertexupdatenum = 0;
396         }
397         vertexupdatenum++;
398         if (vertexupdatenum == 0)
399         {
400                 vertexupdatenum = 1;
401                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
402                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
403         }
404         
405         for (i = 0;i < numshadowmarktris;i++)
406         {
407                 t = shadowmarktris[i];
408                 shadowmark[t] = shadowmarkcount;
409                 e = inelement3i + t * 3;
410                 // make sure the vertices are created
411                 for (j = 0;j < 3;j++)
412                 {
413                         if (vertexupdate[e[j]] != vertexupdatenum)
414                         {
415                                 vertexupdate[e[j]] = vertexupdatenum;
416                                 vertexremap[e[j]] = outvertices;
417                                 VectorSubtract(invertex3f + e[j] * 3, projectorigin, temp);
418                                 f = projectdistance / VectorLength(temp);
419                                 VectorCopy(invertex3f + e[j] * 3, outvertex3f);
420                                 VectorMA(projectorigin, f, temp, (outvertex3f + 3));
421                                 outvertex3f += 6;
422                                 outvertices += 2;
423                         }
424                 }
425                 // output the front and back triangles
426                 outelement3i[0] = vertexremap[e[0]];
427                 outelement3i[1] = vertexremap[e[1]];
428                 outelement3i[2] = vertexremap[e[2]];
429                 outelement3i[3] = vertexremap[e[2]] + 1;
430                 outelement3i[4] = vertexremap[e[1]] + 1;
431                 outelement3i[5] = vertexremap[e[0]] + 1;
432                 outelement3i += 6;
433                 tris += 2;
434         }
435
436         for (i = 0;i < numshadowmarktris;i++)
437         {
438                 t = shadowmarktris[i];
439                 e = inelement3i + t * 3;
440                 n = inneighbor3i + t * 3;
441                 // output the sides (facing outward from this triangle)
442                 if (shadowmark[n[0]] != shadowmarkcount)
443                 {
444                         vr[0] = vertexremap[e[0]];
445                         vr[1] = vertexremap[e[1]];
446                         outelement3i[0] = vr[1];
447                         outelement3i[1] = vr[0];
448                         outelement3i[2] = vr[0] + 1;
449                         outelement3i[3] = vr[1];
450                         outelement3i[4] = vr[0] + 1;
451                         outelement3i[5] = vr[1] + 1;
452                         outelement3i += 6;
453                         tris += 2;
454                 }
455                 if (shadowmark[n[1]] != shadowmarkcount)
456                 {
457                         vr[1] = vertexremap[e[1]];
458                         vr[2] = vertexremap[e[2]];
459                         outelement3i[0] = vr[2];
460                         outelement3i[1] = vr[1];
461                         outelement3i[2] = vr[1] + 1;
462                         outelement3i[3] = vr[2];
463                         outelement3i[4] = vr[1] + 1;
464                         outelement3i[5] = vr[2] + 1;
465                         outelement3i += 6;
466                         tris += 2;
467                 }
468                 if (shadowmark[n[2]] != shadowmarkcount)
469                 {
470                         vr[0] = vertexremap[e[0]];
471                         vr[2] = vertexremap[e[2]];
472                         outelement3i[0] = vr[0];
473                         outelement3i[1] = vr[2];
474                         outelement3i[2] = vr[2] + 1;
475                         outelement3i[3] = vr[0];
476                         outelement3i[4] = vr[2] + 1;
477                         outelement3i[5] = vr[0] + 1;
478                         outelement3i += 6;
479                         tris += 2;
480                 }
481         }
482         if (outnumvertices)
483                 *outnumvertices = outvertices;
484         return tris;
485 }
486
487 float varray_vertex3f2[65536*3];
488
489 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris)
490 {
491         int tris, outverts;
492         if (projectdistance < 0.1)
493         {
494                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
495                 return;
496         }
497         if (!numverts || !nummarktris)
498                 return;
499         // make sure shadowelements is big enough for this volume
500         if (maxshadowelements < nummarktris * 24)
501                 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
502         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
503         R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
504 }
505
506 void R_Shadow_VolumeFromBox(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, const vec3_t mins, const vec3_t maxs)
507 {
508         int i;
509         const float *v[3];
510
511         // check which triangles are facing the , and then output
512         // triangle elements and vertices...  by clever use of elements we
513         // can construct the whole shadow from the unprojected vertices and
514         // the projected vertices
515
516         // identify lit faces within the bounding box
517         R_Shadow_PrepareShadowMark(numtris);
518         for (i = 0;i < numtris;i++)
519         {
520                 v[0] = invertex3f + elements[i*3+0] * 3;
521                 v[1] = invertex3f + elements[i*3+1] * 3;
522                 v[2] = invertex3f + elements[i*3+2] * 3;
523                 if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) && maxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && mins[0] < max(v[0][0], max(v[1][0], v[2][0])) && maxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && mins[1] < max(v[0][1], max(v[1][1], v[2][1])) && maxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && mins[2] < max(v[0][2], max(v[1][2], v[2][2])))
524                         shadowmarklist[numshadowmark++] = i;
525         }
526         R_Shadow_VolumeFromList(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, numshadowmark, shadowmarklist);
527 }
528
529 void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, float radius)
530 {
531         vec3_t mins, maxs;
532         mins[0] = projectorigin[0] - radius;
533         mins[1] = projectorigin[1] - radius;
534         mins[2] = projectorigin[2] - radius;
535         maxs[0] = projectorigin[0] + radius;
536         maxs[1] = projectorigin[1] + radius;
537         maxs[2] = projectorigin[2] + radius;
538         R_Shadow_VolumeFromBox(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, mins, maxs);
539 }
540
541 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
542 {
543         rmeshstate_t m;
544         memset(&m, 0, sizeof(m));
545         m.pointer_vertex = vertex3f;
546         R_Mesh_State(&m);
547         if (r_shadowstage == SHADOWSTAGE_STENCIL)
548         {
549                 // decrement stencil if frontface is behind depthbuffer
550                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
551                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
552                 R_Mesh_Draw(numvertices, numtriangles, element3i);
553                 c_rt_shadowmeshes++;
554                 c_rt_shadowtris += numtriangles;
555                 // increment stencil if backface is behind depthbuffer
556                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
557                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
558         }
559         R_Mesh_Draw(numvertices, numtriangles, element3i);
560         c_rt_shadowmeshes++;
561         c_rt_shadowtris += numtriangles;
562 }
563
564 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
565 {
566         shadowmesh_t *mesh;
567         rmeshstate_t m;
568         memset(&m, 0, sizeof(m));
569         for (mesh = firstmesh;mesh;mesh = mesh->next)
570         {
571                 m.pointer_vertex = mesh->vertex3f;
572                 R_Mesh_State(&m);
573                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
574                 {
575                         // decrement stencil if frontface is behind depthbuffer
576                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
577                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
578                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
579                         c_rtcached_shadowmeshes++;
580                         c_rtcached_shadowtris += mesh->numtriangles;
581                         // increment stencil if backface is behind depthbuffer
582                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
583                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
584                 }
585                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
586                 c_rtcached_shadowmeshes++;
587                 c_rtcached_shadowtris += mesh->numtriangles;
588         }
589 }
590
591 float r_shadow_attenpower, r_shadow_attenscale;
592 static void R_Shadow_MakeTextures(void)
593 {
594         int x, y, z, d, side;
595         float v[3], s, t, intensity;
596         qbyte *data;
597         R_FreeTexturePool(&r_shadow_texturepool);
598         r_shadow_texturepool = R_AllocTexturePool();
599         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
600         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
601 #define NORMSIZE 64
602 #define ATTEN2DSIZE 64
603 #define ATTEN3DSIZE 32
604         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
605         data[0] = 128;
606         data[1] = 128;
607         data[2] = 255;
608         data[3] = 255;
609         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
610         data[0] = 255;
611         data[1] = 255;
612         data[2] = 255;
613         data[3] = 255;
614         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
615         data[0] = 255;
616         data[1] = 255;
617         data[2] = 255;
618         data[3] = 255;
619         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
620         if (gl_texturecubemap)
621         {
622                 for (side = 0;side < 6;side++)
623                 {
624                         for (y = 0;y < NORMSIZE;y++)
625                         {
626                                 for (x = 0;x < NORMSIZE;x++)
627                                 {
628                                         s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
629                                         t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
630                                         switch(side)
631                                         {
632                                         case 0:
633                                                 v[0] = 1;
634                                                 v[1] = -t;
635                                                 v[2] = -s;
636                                                 break;
637                                         case 1:
638                                                 v[0] = -1;
639                                                 v[1] = -t;
640                                                 v[2] = s;
641                                                 break;
642                                         case 2:
643                                                 v[0] = s;
644                                                 v[1] = 1;
645                                                 v[2] = t;
646                                                 break;
647                                         case 3:
648                                                 v[0] = s;
649                                                 v[1] = -1;
650                                                 v[2] = -t;
651                                                 break;
652                                         case 4:
653                                                 v[0] = s;
654                                                 v[1] = -t;
655                                                 v[2] = 1;
656                                                 break;
657                                         case 5:
658                                                 v[0] = -s;
659                                                 v[1] = -t;
660                                                 v[2] = -1;
661                                                 break;
662                                         }
663                                         intensity = 127.0f / sqrt(DotProduct(v, v));
664                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
665                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
666                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
667                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
668                                 }
669                         }
670                 }
671                 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
672         }
673         else
674                 r_shadow_normalcubetexture = NULL;
675         for (y = 0;y < ATTEN2DSIZE;y++)
676         {
677                 for (x = 0;x < ATTEN2DSIZE;x++)
678                 {
679                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
680                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
681                         v[2] = 0;
682                         intensity = 1.0f - sqrt(DotProduct(v, v));
683                         if (intensity > 0)
684                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
685                         d = bound(0, intensity, 255);
686                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
687                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
688                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
689                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
690                 }
691         }
692         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
693         if (r_shadow_texture3d.integer)
694         {
695                 for (z = 0;z < ATTEN3DSIZE;z++)
696                 {
697                         for (y = 0;y < ATTEN3DSIZE;y++)
698                         {
699                                 for (x = 0;x < ATTEN3DSIZE;x++)
700                                 {
701                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
702                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
703                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
704                                         intensity = 1.0f - sqrt(DotProduct(v, v));
705                                         if (intensity > 0)
706                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
707                                         d = bound(0, intensity, 255);
708                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
709                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
710                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
711                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
712                                 }
713                         }
714                 }
715                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
716         }
717         Mem_Free(data);
718 }
719
720 void R_Shadow_Stage_Begin(void)
721 {
722         rmeshstate_t m;
723
724         if (r_shadow_texture3d.integer && !gl_texture3d)
725                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
726         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
727                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
728
729         if (!r_shadow_attenuation2dtexture
730          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
731          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
732          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
733                 R_Shadow_MakeTextures();
734
735         memset(&m, 0, sizeof(m));
736         GL_BlendFunc(GL_ONE, GL_ZERO);
737         GL_DepthMask(false);
738         GL_DepthTest(true);
739         R_Mesh_State(&m);
740         GL_ColorPointer(NULL);
741         GL_Color(0, 0, 0, 1);
742         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
743         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
744         r_shadowstage = SHADOWSTAGE_NONE;
745
746         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
747         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
748         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
749 }
750
751 void R_Shadow_LoadWorldLightsIfNeeded(void)
752 {
753         if (r_shadow_reloadlights && cl.worldmodel)
754         {
755                 R_Shadow_ClearWorldLights();
756                 r_shadow_reloadlights = false;
757                 R_Shadow_LoadWorldLights();
758                 if (r_shadow_worldlightchain == NULL)
759                 {
760                         R_Shadow_LoadLightsFile();
761                         if (r_shadow_worldlightchain == NULL)
762                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
763                 }
764         }
765 }
766
767 void R_Shadow_Stage_ShadowVolumes(void)
768 {
769         rmeshstate_t m;
770         memset(&m, 0, sizeof(m));
771         R_Mesh_State(&m);
772         GL_ColorPointer(NULL);
773         GL_Color(1, 1, 1, 1);
774         GL_ColorMask(0, 0, 0, 0);
775         GL_BlendFunc(GL_ONE, GL_ZERO);
776         GL_DepthMask(false);
777         GL_DepthTest(true);
778         qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
779         //if (r_shadow_polygonoffset.value != 0)
780         //{
781         //      qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
782         //      qglEnable(GL_POLYGON_OFFSET_FILL);
783         //}
784         //else
785         //      qglDisable(GL_POLYGON_OFFSET_FILL);
786         qglDepthFunc(GL_LESS);
787         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
788         qglEnable(GL_STENCIL_TEST);
789         qglStencilFunc(GL_ALWAYS, 128, ~0);
790         if (gl_ext_stenciltwoside.integer)
791         {
792                 r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
793                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
794         qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
795                 qglStencilMask(~0);
796                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
797         qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
798                 qglStencilMask(~0);
799                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
800         }
801         else
802         {
803                 r_shadowstage = SHADOWSTAGE_STENCIL;
804                 qglStencilMask(~0);
805                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
806         }
807         GL_Clear(GL_STENCIL_BUFFER_BIT);
808         c_rt_clears++;
809         // LordHavoc note: many shadow volumes reside entirely inside the world
810         // (that is to say they are entirely bounded by their lit surfaces),
811         // which can be optimized by handling things as an inverted light volume,
812         // with the shadow boundaries of the world being simulated by an altered
813         // (129) bias to stencil clearing on such lights
814         // FIXME: generate inverted light volumes for use as shadow volumes and
815         // optimize for them as noted above
816 }
817
818 void R_Shadow_Stage_LightWithoutShadows(void)
819 {
820         rmeshstate_t m;
821         memset(&m, 0, sizeof(m));
822         R_Mesh_State(&m);
823         GL_BlendFunc(GL_ONE, GL_ONE);
824         GL_DepthMask(false);
825         GL_DepthTest(true);
826         qglPolygonOffset(0, 0);
827         //qglDisable(GL_POLYGON_OFFSET_FILL);
828         GL_ColorPointer(NULL);
829         GL_Color(1, 1, 1, 1);
830         GL_ColorMask(1, 1, 1, 1);
831         qglDepthFunc(GL_EQUAL);
832         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
833         qglDisable(GL_STENCIL_TEST);
834         if (gl_support_stenciltwoside)
835                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
836         qglStencilMask(~0);
837         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
838         qglStencilFunc(GL_EQUAL, 128, 0xFF);
839         r_shadowstage = SHADOWSTAGE_LIGHT;
840         c_rt_lights++;
841 }
842
843 void R_Shadow_Stage_LightWithShadows(void)
844 {
845         rmeshstate_t m;
846         memset(&m, 0, sizeof(m));
847         R_Mesh_State(&m);
848         GL_BlendFunc(GL_ONE, GL_ONE);
849         GL_DepthMask(false);
850         GL_DepthTest(true);
851         qglPolygonOffset(0, 0);
852         //qglDisable(GL_POLYGON_OFFSET_FILL);
853         GL_ColorPointer(NULL);
854         GL_Color(1, 1, 1, 1);
855         GL_ColorMask(1, 1, 1, 1);
856         qglDepthFunc(GL_EQUAL);
857         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
858         qglEnable(GL_STENCIL_TEST);
859         if (gl_support_stenciltwoside)
860                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
861         qglStencilMask(~0);
862         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
863         // only draw light where this geometry was already rendered AND the
864         // stencil is 128 (values other than this mean shadow)
865         qglStencilFunc(GL_EQUAL, 128, 0xFF);
866         r_shadowstage = SHADOWSTAGE_LIGHT;
867         c_rt_lights++;
868 }
869
870 void R_Shadow_Stage_End(void)
871 {
872         rmeshstate_t m;
873         memset(&m, 0, sizeof(m));
874         R_Mesh_State(&m);
875         GL_BlendFunc(GL_ONE, GL_ZERO);
876         GL_DepthMask(true);
877         GL_DepthTest(true);
878         qglPolygonOffset(0, 0);
879         //qglDisable(GL_POLYGON_OFFSET_FILL);
880         GL_ColorPointer(NULL);
881         GL_Color(1, 1, 1, 1);
882         GL_ColorMask(1, 1, 1, 1);
883         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
884         qglDepthFunc(GL_LEQUAL);
885         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
886         qglDisable(GL_STENCIL_TEST);
887         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
888         if (gl_support_stenciltwoside)
889                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
890         qglStencilMask(~0);
891         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
892         r_shadowstage = SHADOWSTAGE_NONE;
893 }
894
895 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
896 {
897         int i, ix1, iy1, ix2, iy2;
898         float x1, y1, x2, y2, x, y, f;
899         vec3_t smins, smaxs;
900         vec4_t v, v2;
901         if (!r_shadow_scissor.integer)
902                 return false;
903         // if view is inside the box, just say yes it's visible
904         // LordHavoc: for some odd reason scissor seems broken without stencil
905         // (?!?  seems like a driver bug) so abort if gl_stencil is false
906         if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
907         {
908                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
909                 return false;
910         }
911         for (i = 0;i < 3;i++)
912         {
913                 if (r_viewforward[i] >= 0)
914                 {
915                         v[i] = mins[i];
916                         v2[i] = maxs[i];
917                 }
918                 else
919                 {
920                         v[i] = maxs[i];
921                         v2[i] = mins[i];
922                 }
923         }
924         f = DotProduct(r_viewforward, r_vieworigin) + 1;
925         if (DotProduct(r_viewforward, v2) <= f)
926         {
927                 // entirely behind nearclip plane
928                 return true;
929         }
930         if (DotProduct(r_viewforward, v) >= f)
931         {
932                 // entirely infront of nearclip plane
933                 x1 = y1 = x2 = y2 = 0;
934                 for (i = 0;i < 8;i++)
935                 {
936                         v[0] = (i & 1) ? mins[0] : maxs[0];
937                         v[1] = (i & 2) ? mins[1] : maxs[1];
938                         v[2] = (i & 4) ? mins[2] : maxs[2];
939                         v[3] = 1.0f;
940                         GL_TransformToScreen(v, v2);
941                         //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]);
942                         x = v2[0];
943                         y = v2[1];
944                         if (i)
945                         {
946                                 if (x1 > x) x1 = x;
947                                 if (x2 < x) x2 = x;
948                                 if (y1 > y) y1 = y;
949                                 if (y2 < y) y2 = y;
950                         }
951                         else
952                         {
953                                 x1 = x2 = x;
954                                 y1 = y2 = y;
955                         }
956                 }
957         }
958         else
959         {
960                 // clipped by nearclip plane
961                 // this is nasty and crude...
962                 // create viewspace bbox
963                 for (i = 0;i < 8;i++)
964                 {
965                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
966                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
967                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
968                         v2[0] = -DotProduct(v, r_viewleft);
969                         v2[1] = DotProduct(v, r_viewup);
970                         v2[2] = DotProduct(v, r_viewforward);
971                         if (i)
972                         {
973                                 if (smins[0] > v2[0]) smins[0] = v2[0];
974                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
975                                 if (smins[1] > v2[1]) smins[1] = v2[1];
976                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
977                                 if (smins[2] > v2[2]) smins[2] = v2[2];
978                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
979                         }
980                         else
981                         {
982                                 smins[0] = smaxs[0] = v2[0];
983                                 smins[1] = smaxs[1] = v2[1];
984                                 smins[2] = smaxs[2] = v2[2];
985                         }
986                 }
987                 // now we have a bbox in viewspace
988                 // clip it to the view plane
989                 if (smins[2] < 1)
990                         smins[2] = 1;
991                 // return true if that culled the box
992                 if (smins[2] >= smaxs[2])
993                         return true;
994                 // ok some of it is infront of the view, transform each corner back to
995                 // worldspace and then to screenspace and make screen rect
996                 // initialize these variables just to avoid compiler warnings
997                 x1 = y1 = x2 = y2 = 0;
998                 for (i = 0;i < 8;i++)
999                 {
1000                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1001                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1002                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1003                         v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1004                         v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1005                         v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1006                         v[3] = 1.0f;
1007                         GL_TransformToScreen(v, v2);
1008                         //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]);
1009                         x = v2[0];
1010                         y = v2[1];
1011                         if (i)
1012                         {
1013                                 if (x1 > x) x1 = x;
1014                                 if (x2 < x) x2 = x;
1015                                 if (y1 > y) y1 = y;
1016                                 if (y2 < y) y2 = y;
1017                         }
1018                         else
1019                         {
1020                                 x1 = x2 = x;
1021                                 y1 = y2 = y;
1022                         }
1023                 }
1024                 /*
1025                 // this code doesn't handle boxes with any points behind view properly
1026                 x1 = 1000;x2 = -1000;
1027                 y1 = 1000;y2 = -1000;
1028                 for (i = 0;i < 8;i++)
1029                 {
1030                         v[0] = (i & 1) ? mins[0] : maxs[0];
1031                         v[1] = (i & 2) ? mins[1] : maxs[1];
1032                         v[2] = (i & 4) ? mins[2] : maxs[2];
1033                         v[3] = 1.0f;
1034                         GL_TransformToScreen(v, v2);
1035                         //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]);
1036                         if (v2[2] > 0)
1037                         {
1038                                 x = v2[0];
1039                                 y = v2[1];
1040
1041                                 if (x1 > x) x1 = x;
1042                                 if (x2 < x) x2 = x;
1043                                 if (y1 > y) y1 = y;
1044                                 if (y2 < y) y2 = y;
1045                         }
1046                 }
1047                 */
1048         }
1049         ix1 = x1 - 1.0f;
1050         iy1 = y1 - 1.0f;
1051         ix2 = x2 + 1.0f;
1052         iy2 = y2 + 1.0f;
1053         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1054         if (ix1 < r_view_x) ix1 = r_view_x;
1055         if (iy1 < r_view_y) iy1 = r_view_y;
1056         if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1057         if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1058         if (ix2 <= ix1 || iy2 <= iy1)
1059                 return true;
1060         // set up the scissor rectangle
1061         GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1062         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1063         //qglEnable(GL_SCISSOR_TEST);
1064         c_rt_scissored++;
1065         return false;
1066 }
1067
1068 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1069 {
1070         float *color4f = varray_color4f;
1071         float dist, dot, intensity, v[3], n[3];
1072         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1073         {
1074                 Matrix4x4_Transform(m, vertex3f, v);
1075                 if ((dist = DotProduct(v, v)) < 1)
1076                 {
1077                         Matrix4x4_Transform3x3(m, normal3f, n);
1078                         if ((dot = DotProduct(n, v)) > 0)
1079                         {
1080                                 dist = sqrt(dist);
1081                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1082                                 VectorScale(lightcolor, intensity, color4f);
1083                                 color4f[3] = 1;
1084                         }
1085                         else
1086                         {
1087                                 VectorClear(color4f);
1088                                 color4f[3] = 1;
1089                         }
1090                 }
1091                 else
1092                 {
1093                         VectorClear(color4f);
1094                         color4f[3] = 1;
1095                 }
1096         }
1097 }
1098
1099 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1100 {
1101         float *color4f = varray_color4f;
1102         float dist, dot, intensity, v[3], n[3];
1103         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1104         {
1105                 Matrix4x4_Transform(m, vertex3f, v);
1106                 if ((dist = fabs(v[2])) < 1)
1107                 {
1108                         Matrix4x4_Transform3x3(m, normal3f, n);
1109                         if ((dot = DotProduct(n, v)) > 0)
1110                         {
1111                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1112                                 VectorScale(lightcolor, intensity, color4f);
1113                                 color4f[3] = 1;
1114                         }
1115                         else
1116                         {
1117                                 VectorClear(color4f);
1118                                 color4f[3] = 1;
1119                         }
1120                 }
1121                 else
1122                 {
1123                         VectorClear(color4f);
1124                         color4f[3] = 1;
1125                 }
1126         }
1127 }
1128
1129 // FIXME: this should be done in a vertex program when possible
1130 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1131 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1132 {
1133         do
1134         {
1135                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1136                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1137                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1138                 vertex3f += 3;
1139                 tc3f += 3;
1140         }
1141         while (--numverts);
1142 }
1143
1144 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1145 {
1146         do
1147         {
1148                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1149                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1150                 vertex3f += 3;
1151                 tc2f += 2;
1152         }
1153         while (--numverts);
1154 }
1155
1156 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
1157 {
1158         int i;
1159         float lightdir[3];
1160         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1161         {
1162                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1163                 // the cubemap normalizes this for us
1164                 out3f[0] = DotProduct(svector3f, lightdir);
1165                 out3f[1] = DotProduct(tvector3f, lightdir);
1166                 out3f[2] = DotProduct(normal3f, lightdir);
1167         }
1168 }
1169
1170 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
1171 {
1172         int i;
1173         float lightdir[3], eyedir[3], halfdir[3];
1174         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1175         {
1176                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1177                 VectorNormalizeFast(lightdir);
1178                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1179                 VectorNormalizeFast(eyedir);
1180                 VectorAdd(lightdir, eyedir, halfdir);
1181                 // the cubemap normalizes this for us
1182                 out3f[0] = DotProduct(svector3f, halfdir);
1183                 out3f[1] = DotProduct(tvector3f, halfdir);
1184                 out3f[2] = DotProduct(normal3f, halfdir);
1185         }
1186 }
1187
1188 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1189 {
1190         int renders;
1191         float color[3], color2[3];
1192         rmeshstate_t m;
1193         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1194         {
1195                 if (!bumptexture)
1196                         bumptexture = r_shadow_blankbumptexture;
1197                 GL_ColorPointer(NULL);
1198                 GL_Color(1,1,1,1);
1199                 // colorscale accounts for how much we multiply the brightness during combine
1200                 // mult is how many times the final pass of the lighting will be
1201                 // performed to get more brightness than otherwise possible
1202                 // limit mult to 64 for sanity sake
1203                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1204                 {
1205                         // 3/2 3D combine path (Geforce3, Radeon 8500)
1206                         memset(&m, 0, sizeof(m));
1207                         m.pointer_vertex = vertex3f;
1208                         m.tex[0] = R_GetTexture(bumptexture);
1209                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1210                         m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1211                         m.texcombinergb[0] = GL_REPLACE;
1212                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1213                         m.pointer_texcoord[0] = texcoord2f;
1214                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1215                         m.pointer_texcoord[2] = varray_texcoord3f[2];
1216                         R_Mesh_State(&m);
1217                         GL_ColorMask(0,0,0,1);
1218                         GL_BlendFunc(GL_ONE, GL_ZERO);
1219                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1220                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1221                         R_Mesh_Draw(numverts, numtriangles, elements);
1222                         c_rt_lightmeshes++;
1223                         c_rt_lighttris += numtriangles;
1224
1225                         memset(&m, 0, sizeof(m));
1226                         m.pointer_vertex = vertex3f;
1227                         m.tex[0] = R_GetTexture(basetexture);
1228                         m.pointer_texcoord[0] = texcoord2f;
1229                         if (lightcubemap)
1230                         {
1231                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1232                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1233                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1234                         }
1235                         R_Mesh_State(&m);
1236                         GL_ColorMask(1,1,1,0);
1237                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1238                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1239                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1240                         {
1241                                 color[0] = bound(0, color2[0], 1);
1242                                 color[1] = bound(0, color2[1], 1);
1243                                 color[2] = bound(0, color2[2], 1);
1244                                 GL_Color(color[0], color[1], color[2], 1);
1245                                 R_Mesh_Draw(numverts, numtriangles, elements);
1246                                 c_rt_lightmeshes++;
1247                                 c_rt_lighttris += numtriangles;
1248                         }
1249                 }
1250                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1251                 {
1252                         // 1/2/2 3D combine path (original Radeon)
1253                         memset(&m, 0, sizeof(m));
1254                         m.pointer_vertex = vertex3f;
1255                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1256                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1257                         R_Mesh_State(&m);
1258                         GL_ColorMask(0,0,0,1);
1259                         GL_BlendFunc(GL_ONE, GL_ZERO);
1260                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1261                         R_Mesh_Draw(numverts, numtriangles, elements);
1262                         c_rt_lightmeshes++;
1263                         c_rt_lighttris += numtriangles;
1264
1265                         memset(&m, 0, sizeof(m));
1266                         m.pointer_vertex = vertex3f;
1267                         m.tex[0] = R_GetTexture(bumptexture);
1268                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1269                         m.texcombinergb[0] = GL_REPLACE;
1270                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1271                         m.pointer_texcoord[0] = texcoord2f;
1272                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1273                         R_Mesh_State(&m);
1274                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1275                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1276                         R_Mesh_Draw(numverts, numtriangles, elements);
1277                         c_rt_lightmeshes++;
1278                         c_rt_lighttris += numtriangles;
1279
1280                         memset(&m, 0, sizeof(m));
1281                         m.pointer_vertex = vertex3f;
1282                         m.tex[0] = R_GetTexture(basetexture);
1283                         m.pointer_texcoord[0] = texcoord2f;
1284                         if (lightcubemap)
1285                         {
1286                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1287                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1288                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1289                         }
1290                         R_Mesh_State(&m);
1291                         GL_ColorMask(1,1,1,0);
1292                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1293                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1294                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1295                         {
1296                                 color[0] = bound(0, color2[0], 1);
1297                                 color[1] = bound(0, color2[1], 1);
1298                                 color[2] = bound(0, color2[2], 1);
1299                                 GL_Color(color[0], color[1], color[2], 1);
1300                                 R_Mesh_Draw(numverts, numtriangles, elements);
1301                                 c_rt_lightmeshes++;
1302                                 c_rt_lighttris += numtriangles;
1303                         }
1304                 }
1305                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1306                 {
1307                         // 2/2 3D combine path (original Radeon)
1308                         memset(&m, 0, sizeof(m));
1309                         m.pointer_vertex = vertex3f;
1310                         m.tex[0] = R_GetTexture(bumptexture);
1311                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1312                         m.texcombinergb[0] = GL_REPLACE;
1313                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1314                         m.pointer_texcoord[0] = texcoord2f;
1315                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1316                         R_Mesh_State(&m);
1317                         GL_ColorMask(0,0,0,1);
1318                         GL_BlendFunc(GL_ONE, GL_ZERO);
1319                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1320                         R_Mesh_Draw(numverts, numtriangles, elements);
1321                         c_rt_lightmeshes++;
1322                         c_rt_lighttris += numtriangles;
1323
1324                         memset(&m, 0, sizeof(m));
1325                         m.pointer_vertex = vertex3f;
1326                         m.tex[0] = R_GetTexture(basetexture);
1327                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1328                         m.pointer_texcoord[0] = texcoord2f;
1329                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1330                         R_Mesh_State(&m);
1331                         GL_ColorMask(1,1,1,0);
1332                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1333                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1334                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1335                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1336                         {
1337                                 color[0] = bound(0, color2[0], 1);
1338                                 color[1] = bound(0, color2[1], 1);
1339                                 color[2] = bound(0, color2[2], 1);
1340                                 GL_Color(color[0], color[1], color[2], 1);
1341                                 R_Mesh_Draw(numverts, numtriangles, elements);
1342                                 c_rt_lightmeshes++;
1343                                 c_rt_lighttris += numtriangles;
1344                         }
1345                 }
1346                 else if (r_textureunits.integer >= 4)
1347                 {
1348                         // 4/2 2D combine path (Geforce3, Radeon 8500)
1349                         memset(&m, 0, sizeof(m));
1350                         m.pointer_vertex = vertex3f;
1351                         m.tex[0] = R_GetTexture(bumptexture);
1352                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1353                         m.texcombinergb[0] = GL_REPLACE;
1354                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1355                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1356                         m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1357                         m.pointer_texcoord[0] = texcoord2f;
1358                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1359                         m.pointer_texcoord[2] = varray_texcoord2f[2];
1360                         m.pointer_texcoord[3] = varray_texcoord2f[3];
1361                         R_Mesh_State(&m);
1362                         GL_ColorMask(0,0,0,1);
1363                         GL_BlendFunc(GL_ONE, GL_ZERO);
1364                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1365                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1366                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1367                         R_Mesh_Draw(numverts, numtriangles, elements);
1368                         c_rt_lightmeshes++;
1369                         c_rt_lighttris += numtriangles;
1370
1371                         memset(&m, 0, sizeof(m));
1372                         m.pointer_vertex = vertex3f;
1373                         m.tex[0] = R_GetTexture(basetexture);
1374                         m.pointer_texcoord[0] = texcoord2f;
1375                         if (lightcubemap)
1376                         {
1377                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1378                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1379                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1380                         }
1381                         R_Mesh_State(&m);
1382                         GL_ColorMask(1,1,1,0);
1383                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1384                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1385                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1386                         {
1387                                 color[0] = bound(0, color2[0], 1);
1388                                 color[1] = bound(0, color2[1], 1);
1389                                 color[2] = bound(0, color2[2], 1);
1390                                 GL_Color(color[0], color[1], color[2], 1);
1391                                 R_Mesh_Draw(numverts, numtriangles, elements);
1392                                 c_rt_lightmeshes++;
1393                                 c_rt_lighttris += numtriangles;
1394                         }
1395                 }
1396                 else
1397                 {
1398                         // 2/2/2 2D combine path (any dot3 card)
1399                         memset(&m, 0, sizeof(m));
1400                         m.pointer_vertex = vertex3f;
1401                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1402                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1403                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1404                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1405                         R_Mesh_State(&m);
1406                         GL_ColorMask(0,0,0,1);
1407                         GL_BlendFunc(GL_ONE, GL_ZERO);
1408                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1409                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1410                         R_Mesh_Draw(numverts, numtriangles, elements);
1411                         c_rt_lightmeshes++;
1412                         c_rt_lighttris += numtriangles;
1413
1414                         memset(&m, 0, sizeof(m));
1415                         m.pointer_vertex = vertex3f;
1416                         m.tex[0] = R_GetTexture(bumptexture);
1417                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1418                         m.texcombinergb[0] = GL_REPLACE;
1419                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1420                         m.pointer_texcoord[0] = texcoord2f;
1421                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1422                         R_Mesh_State(&m);
1423                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1424                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1425                         R_Mesh_Draw(numverts, numtriangles, elements);
1426                         c_rt_lightmeshes++;
1427                         c_rt_lighttris += numtriangles;
1428
1429                         memset(&m, 0, sizeof(m));
1430                         m.pointer_vertex = vertex3f;
1431                         m.tex[0] = R_GetTexture(basetexture);
1432                         m.pointer_texcoord[0] = texcoord2f;
1433                         if (lightcubemap)
1434                         {
1435                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1436                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1437                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1438                         }
1439                         R_Mesh_State(&m);
1440                         GL_ColorMask(1,1,1,0);
1441                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1442                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1443                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1444                         {
1445                                 color[0] = bound(0, color2[0], 1);
1446                                 color[1] = bound(0, color2[1], 1);
1447                                 color[2] = bound(0, color2[2], 1);
1448                                 GL_Color(color[0], color[1], color[2], 1);
1449                                 R_Mesh_Draw(numverts, numtriangles, elements);
1450                                 c_rt_lightmeshes++;
1451                                 c_rt_lighttris += numtriangles;
1452                         }
1453                 }
1454         }
1455         else
1456         {
1457                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1458                 GL_DepthMask(false);
1459                 GL_DepthTest(true);
1460                 GL_ColorPointer(varray_color4f);
1461                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1462                 memset(&m, 0, sizeof(m));
1463                 m.pointer_vertex = vertex3f;
1464                 m.tex[0] = R_GetTexture(basetexture);
1465                 m.pointer_texcoord[0] = texcoord2f;
1466                 if (r_textureunits.integer >= 2)
1467                 {
1468                         // voodoo2
1469                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1470                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1471                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1472                 }
1473                 R_Mesh_State(&m);
1474                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1475                 {
1476                         color[0] = bound(0, color2[0], 1);
1477                         color[1] = bound(0, color2[1], 1);
1478                         color[2] = bound(0, color2[2], 1);
1479                         if (r_textureunits.integer >= 2)
1480                                 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1481                         else
1482                                 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1483                         R_Mesh_Draw(numverts, numtriangles, elements);
1484                         c_rt_lightmeshes++;
1485                         c_rt_lighttris += numtriangles;
1486                 }
1487         }
1488 }
1489
1490 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1491 {
1492         int renders;
1493         float color[3], color2[3], colorscale;
1494         rmeshstate_t m;
1495         if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1496                 return;
1497         if (!glosstexture)
1498                 glosstexture = r_shadow_blankglosstexture;
1499         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1500         {
1501                 colorscale = r_shadow_glossintensity.value;
1502                 if (!bumptexture)
1503                         bumptexture = r_shadow_blankbumptexture;
1504                 if (glosstexture == r_shadow_blankglosstexture)
1505                         colorscale *= r_shadow_gloss2intensity.value;
1506                 GL_ColorPointer(NULL);
1507                 GL_Color(1,1,1,1);
1508                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1509                 {
1510                         // 2/0/0/1/2 3D combine blendsquare path
1511                         memset(&m, 0, sizeof(m));
1512                         m.pointer_vertex = vertex3f;
1513                         m.tex[0] = R_GetTexture(bumptexture);
1514                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1515                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1516                         m.pointer_texcoord[0] = texcoord2f;
1517                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1518                         R_Mesh_State(&m);
1519                         GL_ColorMask(0,0,0,1);
1520                         // this squares the result
1521                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1522                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1523                         R_Mesh_Draw(numverts, numtriangles, elements);
1524                         c_rt_lightmeshes++;
1525                         c_rt_lighttris += numtriangles;
1526
1527                         memset(&m, 0, sizeof(m));
1528                         m.pointer_vertex = vertex3f;
1529                         R_Mesh_State(&m);
1530                         // square alpha in framebuffer a few times to make it shiny
1531                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1532                         // these comments are a test run through this math for intensity 0.5
1533                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1534                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1535                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1536                         R_Mesh_Draw(numverts, numtriangles, elements);
1537                         c_rt_lightmeshes++;
1538                         c_rt_lighttris += numtriangles;
1539                         R_Mesh_Draw(numverts, numtriangles, elements);
1540                         c_rt_lightmeshes++;
1541                         c_rt_lighttris += numtriangles;
1542
1543                         memset(&m, 0, sizeof(m));
1544                         m.pointer_vertex = vertex3f;
1545                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1546                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1547                         R_Mesh_State(&m);
1548                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1549                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1550                         R_Mesh_Draw(numverts, numtriangles, elements);
1551                         c_rt_lightmeshes++;
1552                         c_rt_lighttris += numtriangles;
1553
1554                         memset(&m, 0, sizeof(m));
1555                         m.pointer_vertex = vertex3f;
1556                         m.tex[0] = R_GetTexture(glosstexture);
1557                         if (lightcubemap)
1558                         {
1559                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1560                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1561                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1562                         }
1563                         m.pointer_texcoord[0] = texcoord2f;
1564                         R_Mesh_State(&m);
1565                         GL_ColorMask(1,1,1,0);
1566                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1567                         VectorScale(lightcolor, colorscale, color2);
1568                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1569                         {
1570                                 color[0] = bound(0, color2[0], 1);
1571                                 color[1] = bound(0, color2[1], 1);
1572                                 color[2] = bound(0, color2[2], 1);
1573                                 GL_Color(color[0], color[1], color[2], 1);
1574                                 R_Mesh_Draw(numverts, numtriangles, elements);
1575                                 c_rt_lightmeshes++;
1576                                 c_rt_lighttris += numtriangles;
1577                         }
1578                 }
1579                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1580                 {
1581                         // 2/0/0/2 3D combine blendsquare path
1582                         memset(&m, 0, sizeof(m));
1583                         m.pointer_vertex = vertex3f;
1584                         m.tex[0] = R_GetTexture(bumptexture);
1585                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1586                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1587                         m.pointer_texcoord[0] = texcoord2f;
1588                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1589                         R_Mesh_State(&m);
1590                         GL_ColorMask(0,0,0,1);
1591                         // this squares the result
1592                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1593                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1594                         R_Mesh_Draw(numverts, numtriangles, elements);
1595                         c_rt_lightmeshes++;
1596                         c_rt_lighttris += numtriangles;
1597
1598                         memset(&m, 0, sizeof(m));
1599                         m.pointer_vertex = vertex3f;
1600                         R_Mesh_State(&m);
1601                         // square alpha in framebuffer a few times to make it shiny
1602                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1603                         // these comments are a test run through this math for intensity 0.5
1604                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1605                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1606                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1607                         R_Mesh_Draw(numverts, numtriangles, elements);
1608                         c_rt_lightmeshes++;
1609                         c_rt_lighttris += numtriangles;
1610                         R_Mesh_Draw(numverts, numtriangles, elements);
1611                         c_rt_lightmeshes++;
1612                         c_rt_lighttris += numtriangles;
1613
1614                         memset(&m, 0, sizeof(m));
1615                         m.pointer_vertex = vertex3f;
1616                         m.tex[0] = R_GetTexture(glosstexture);
1617                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1618                         m.pointer_texcoord[0] = texcoord2f;
1619                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1620                         R_Mesh_State(&m);
1621                         GL_ColorMask(1,1,1,0);
1622                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1623                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1624                         VectorScale(lightcolor, colorscale, color2);
1625                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1626                         {
1627                                 color[0] = bound(0, color2[0], 1);
1628                                 color[1] = bound(0, color2[1], 1);
1629                                 color[2] = bound(0, color2[2], 1);
1630                                 GL_Color(color[0], color[1], color[2], 1);
1631                                 R_Mesh_Draw(numverts, numtriangles, elements);
1632                                 c_rt_lightmeshes++;
1633                                 c_rt_lighttris += numtriangles;
1634                         }
1635                 }
1636                 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1637                 {
1638                         // 2/0/0/2/2 2D combine blendsquare path
1639                         memset(&m, 0, sizeof(m));
1640                         m.pointer_vertex = vertex3f;
1641                         m.tex[0] = R_GetTexture(bumptexture);
1642                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1643                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1644                         m.pointer_texcoord[0] = texcoord2f;
1645                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1646                         R_Mesh_State(&m);
1647                         GL_ColorMask(0,0,0,1);
1648                         // this squares the result
1649                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1650                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1651                         R_Mesh_Draw(numverts, numtriangles, elements);
1652                         c_rt_lightmeshes++;
1653                         c_rt_lighttris += numtriangles;
1654
1655                         memset(&m, 0, sizeof(m));
1656                         m.pointer_vertex = vertex3f;
1657                         R_Mesh_State(&m);
1658                         // square alpha in framebuffer a few times to make it shiny
1659                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1660                         // these comments are a test run through this math for intensity 0.5
1661                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1662                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1663                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1664                         R_Mesh_Draw(numverts, numtriangles, elements);
1665                         c_rt_lightmeshes++;
1666                         c_rt_lighttris += numtriangles;
1667                         R_Mesh_Draw(numverts, numtriangles, elements);
1668                         c_rt_lightmeshes++;
1669                         c_rt_lighttris += numtriangles;
1670
1671                         memset(&m, 0, sizeof(m));
1672                         m.pointer_vertex = vertex3f;
1673                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1674                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1675                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1676                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1677                         R_Mesh_State(&m);
1678                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1679                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1680                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1681                         R_Mesh_Draw(numverts, numtriangles, elements);
1682                         c_rt_lightmeshes++;
1683                         c_rt_lighttris += numtriangles;
1684
1685                         memset(&m, 0, sizeof(m));
1686                         m.pointer_vertex = vertex3f;
1687                         m.tex[0] = R_GetTexture(glosstexture);
1688                         if (lightcubemap)
1689                         {
1690                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1691                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1692                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1693                         }
1694                         m.pointer_texcoord[0] = texcoord2f;
1695                         R_Mesh_State(&m);
1696                         GL_ColorMask(1,1,1,0);
1697                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1698                         VectorScale(lightcolor, colorscale, color2);
1699                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1700                         {
1701                                 color[0] = bound(0, color2[0], 1);
1702                                 color[1] = bound(0, color2[1], 1);
1703                                 color[2] = bound(0, color2[2], 1);
1704                                 GL_Color(color[0], color[1], color[2], 1);
1705                                 R_Mesh_Draw(numverts, numtriangles, elements);
1706                                 c_rt_lightmeshes++;
1707                                 c_rt_lighttris += numtriangles;
1708                         }
1709                 }
1710         }
1711 }
1712
1713 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1714 {
1715         int j, k;
1716         float scale;
1717         R_RTLight_Uncompile(rtlight);
1718         memset(rtlight, 0, sizeof(*rtlight));
1719
1720         VectorCopy(light->origin, rtlight->shadoworigin);
1721         VectorCopy(light->color, rtlight->color);
1722         rtlight->radius = light->radius;
1723         rtlight->cullradius = rtlight->radius;
1724         rtlight->cullradius2 = rtlight->cullradius * rtlight->cullradius;
1725         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->cullradius;
1726         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->cullradius;
1727         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->cullradius;
1728         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->cullradius;
1729         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->cullradius;
1730         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->cullradius;
1731         rtlight->cubemapname[0] = 0;
1732         if (light->cubemapname[0])
1733                 strcpy(rtlight->cubemapname, light->cubemapname);
1734         else if (light->cubemapnum > 0)
1735                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1736         rtlight->shadow = light->shadow;
1737         rtlight->corona = light->corona;
1738         rtlight->style = light->style;
1739         rtlight->isstatic = isstatic;
1740         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1741         // ConcatScale won't work here because this needs to scale rotate and
1742         // translate, not just rotate
1743         scale = 1.0f / rtlight->radius;
1744         for (k = 0;k < 3;k++)
1745                 for (j = 0;j < 4;j++)
1746                         rtlight->matrix_worldtolight.m[k][j] *= scale;
1747         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1748         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1749
1750         rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1751         rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1752         VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.25f, rtlight->lightmap_light);
1753         rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1754 }
1755
1756 // compiles rtlight geometry
1757 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1758 void R_RTLight_Compile(rtlight_t *rtlight)
1759 {
1760         int i, j, k, l, maxverts = 256, tris;
1761         float *vertex3f = NULL, mins[3], maxs[3];
1762         shadowmesh_t *mesh, *castmesh = NULL;
1763         int lightpvsbytes;
1764         qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1765         qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1766
1767         // compile the light
1768         rtlight->compiled = true;
1769         VectorCopy(rtlight->cullmins, mins);
1770         VectorCopy(rtlight->cullmaxs, maxs);
1771         if (rtlight->shadow)
1772                 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1773         rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1774         if (cl.worldmodel)
1775         {
1776                 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, rtlight->shadoworigin, 0, lightfullpvs, sizeof(lightfullpvs));
1777                 memset(lightpvs, 0, lightpvsbytes);
1778                 if (cl.worldmodel->brushq3.num_leafs)
1779                 {
1780                         q3mleaf_t *leaf;
1781                         q3mface_t *face;
1782
1783                         // make a pvs that only includes things within the box
1784                         for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1785                                 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1786                                         SETPVSBIT(lightpvs, leaf->clusterindex);
1787
1788                         // make a cluster list for fast visibility checking during rendering
1789                         for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1790                                 if (CHECKPVSBIT(lightpvs, i))
1791                                         rtlight->static_numclusters++;
1792                         rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1793                         for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1794                                 if (CHECKPVSBIT(lightpvs, i))
1795                                         rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1796
1797                         VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1798                         VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1799                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1800                                 face->lighttemp_castshadow = false;
1801                         for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1802                         {
1803                                 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1804                                 {
1805                                         for (k = 0;k < 3;k++)
1806                                         {
1807                                                 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1808                                                 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1809                                         }
1810                                         for (j = 0;j < leaf->numleaffaces;j++)
1811                                         {
1812                                                 face = leaf->firstleafface[j];
1813                                                 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1814                                                         face->lighttemp_castshadow = true;
1815                                         }
1816                                 }
1817                         }
1818
1819                         // add surfaces to shadow casting mesh and light mesh
1820                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1821                         {
1822                                 if (face->lighttemp_castshadow)
1823                                 {
1824                                         face->lighttemp_castshadow = false;
1825                                         if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1826                                         {
1827                                                 if (rtlight->shadow)
1828                                                         if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1829                                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1830                                                 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1831                                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
1832                                         }
1833                                 }
1834                         }
1835                 }
1836                 else if (cl.worldmodel->brushq1.num_leafs)
1837                 {
1838                         mleaf_t *leaf;
1839                         msurface_t *surf;
1840                         VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1841                         VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1842                         i = CL_PointQ1Contents(rtlight->shadoworigin);
1843
1844                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1845                                 surf->lighttemp_castshadow = false;
1846
1847                         if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1848                         {
1849                                 qbyte *byteleafpvs;
1850                                 qbyte *bytesurfacepvs;
1851
1852                                 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
1853                                 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1854
1855                                 Portal_Visibility(cl.worldmodel, rtlight->shadoworigin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, rtlight->cullmins, rtlight->cullmaxs);
1856
1857                                 // make a pvs that only includes things within the box
1858                                 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1859                                 {
1860                                         if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1861                                         {
1862                                                 SETPVSBIT(lightpvs, leaf->clusterindex);
1863                                                 for (k = 0;k < 3;k++)
1864                                                 {
1865                                                         if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1866                                                         if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1867                                                 }
1868                                         }
1869                                 }
1870         
1871                                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1872                                         if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1873                                                 surf->lighttemp_castshadow = true;
1874
1875                                 Mem_Free(byteleafpvs);
1876                                 Mem_Free(bytesurfacepvs);
1877         
1878                                 // make a cluster list for fast visibility checking during rendering
1879                                 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1880                                         if (CHECKPVSBIT(lightpvs, i))
1881                                                 rtlight->static_numclusters++;
1882                                 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1883                                 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1884                                         if (CHECKPVSBIT(lightpvs, i))
1885                                                 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1886                         }
1887                         else
1888                         {
1889                                 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1890                                 {
1891                                         if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1892                                         {
1893                                                 // make a pvs that only includes things within the box
1894                                                 SETPVSBIT(lightpvs, leaf->clusterindex);
1895                                                 for (k = 0;k < 3;k++)
1896                                                 {
1897                                                         if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1898                                                         if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1899                                                 }
1900                                                 for (j = 0;j < leaf->nummarksurfaces;j++)
1901                                                 {
1902                                                         surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1903                                                         if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1904                                                                 surf->lighttemp_castshadow = true;
1905                                                 }
1906                                         }
1907                                 }
1908
1909                                 // make a pvs that only includes things within the box
1910                                 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1911                                         if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1912                                                 SETPVSBIT(lightpvs, leaf->clusterindex);
1913
1914                                 // make a cluster list for fast visibility checking during rendering
1915                                 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1916                                         if (CHECKPVSBIT(lightpvs, i))
1917                                                 rtlight->static_numclusters++;
1918                                 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1919                                 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1920                                         if (CHECKPVSBIT(lightpvs, i))
1921                                                 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1922                         }
1923
1924                         // add surfaces to shadow casting mesh and light mesh
1925                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1926                         {
1927                                 if (surf->lighttemp_castshadow)
1928                                 {
1929                                         surf->lighttemp_castshadow = false;
1930                                         if (rtlight->shadow && (surf->flags & SURF_SHADOWCAST))
1931                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
1932                                         if (!(surf->flags & SURF_DRAWSKY))
1933                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
1934                                 }
1935                         }
1936                 }
1937         }
1938
1939         // limit box to light bounds (in case it grew larger)
1940         for (k = 0;k < 3;k++)
1941         {
1942                 if (rtlight->cullmins[k] < rtlight->shadoworigin[k] - rtlight->radius) rtlight->cullmins[k] = rtlight->shadoworigin[k] - rtlight->radius;
1943                 if (rtlight->cullmaxs[k] > rtlight->shadoworigin[k] + rtlight->radius) rtlight->cullmaxs[k] = rtlight->shadoworigin[k] + rtlight->radius;
1944         }
1945         rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1946         rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1947
1948         // cast shadow volume from castmesh
1949         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
1950         if (castmesh)
1951         {
1952                 maxverts = 0;
1953                 for (mesh = castmesh;mesh;mesh = mesh->next)
1954                 {
1955                         R_Shadow_ResizeShadowElements(mesh->numtriangles);
1956                         maxverts = max(maxverts, mesh->numverts * 2);
1957                 }
1958
1959                 if (maxverts > 0)
1960                 {
1961                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1962                         // now that we have the buffers big enough, construct and add
1963                         // the shadow volume mesh
1964                         if (rtlight->shadow)
1965                                 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1966                         for (mesh = castmesh;mesh;mesh = mesh->next)
1967                         {
1968                                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1969                                 R_Shadow_PrepareShadowMark(mesh->numtriangles);
1970                                 for (i = 0;i < mesh->numtriangles;i++)
1971                                 {
1972                                         const float *v[3];
1973                                         v[0] = mesh->vertex3f + mesh->element3i[i*3+0] * 3;
1974                                         v[1] = mesh->vertex3f + mesh->element3i[i*3+1] * 3;
1975                                         v[2] = mesh->vertex3f + mesh->element3i[i*3+2] * 3;
1976                                         if (PointInfrontOfTriangle(rtlight->shadoworigin, v[0], v[1], v[2]) && rtlight->cullmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && rtlight->cullmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && rtlight->cullmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && rtlight->cullmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && rtlight->cullmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && rtlight->cullmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
1977                                                 shadowmarklist[numshadowmark++] = i;
1978                                 }
1979                                 if (maxshadowelements < numshadowmark * 24)
1980                                         R_Shadow_ResizeShadowElements((numshadowmark + 256) * 24);
1981                                 if ((tris = R_Shadow_ConstructShadowVolume(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->neighbor3i, mesh->vertex3f, NULL, shadowelements, vertex3f, rtlight->shadoworigin, r_shadow_projectdistance.value, numshadowmark, shadowmarklist)))
1982                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
1983                         }
1984                         Mem_Free(vertex3f);
1985                         vertex3f = NULL;
1986                 }
1987                 // we're done with castmesh now
1988                 Mod_ShadowMesh_Free(castmesh);
1989         }
1990
1991         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
1992         rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
1993
1994         k = 0;
1995         if (rtlight->static_meshchain_shadow)
1996                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
1997                         k += mesh->numtriangles;
1998         l = 0;
1999         if (rtlight->static_meshchain_light)
2000                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2001                         l += mesh->numtriangles;
2002         Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], k, l);
2003 }
2004
2005 void R_RTLight_Uncompile(rtlight_t *rtlight)
2006 {
2007         if (rtlight->compiled)
2008         {
2009                 if (rtlight->static_meshchain_shadow)
2010                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2011                 rtlight->static_meshchain_shadow = NULL;
2012                 if (rtlight->static_meshchain_light)
2013                         Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2014                 rtlight->static_meshchain_light = NULL;
2015                 if (rtlight->static_clusterindices)
2016                         Mem_Free(rtlight->static_clusterindices);
2017                 rtlight->static_clusterindices = NULL;
2018                 rtlight->static_numclusters = 0;
2019                 rtlight->compiled = false;
2020         }
2021 }
2022
2023 int shadowframecount = 0;
2024
2025 void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t shadoworigin, vec_t shadowradius, vec3_t cullmins, vec3_t cullmaxs)
2026 {
2027         // rough checks
2028         if ((BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) || !r_shadow_cull.integer) && (ent->flags & RENDER_SHADOW) && ent->model && ent->model->DrawShadowVolume)
2029         {
2030                 vec3_t relativeshadoworigin;
2031                 Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativeshadoworigin);
2032                 ent->model->DrawShadowVolume (ent, relativeshadoworigin, shadowradius);
2033         }
2034 }
2035
2036 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
2037
2038 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2039 {
2040         int i, shadow;
2041         entity_render_t *ent;
2042         float f;
2043         vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2044         rtexture_t *cubemaptexture;
2045         matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2046
2047         if (d_lightstylevalue[rtlight->style] <= 0)
2048                 return;
2049         if (rtlight->compiled)
2050         {
2051                 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2052                         return;
2053                 for (i = 0;i < rtlight->static_numclusters;i++)
2054                         if (CHECKPVSBIT(r_pvsbits, rtlight->static_clusterindices[i]))
2055                                 break;
2056                 if (i == rtlight->static_numclusters)
2057                         return;
2058         }
2059         else if (VIS_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2060                 return;
2061         if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2062                 return;
2063
2064         if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2065                 R_RTLight_Compile(rtlight);
2066         
2067         f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2068         VectorScale(rtlight->color, f, lightcolor);
2069         /*
2070         if (rtlight->selected)
2071         {
2072                 f = 2 + sin(realtime * M_PI * 4.0);
2073                 VectorScale(lightcolor, f, lightcolor);
2074         }
2075         */
2076
2077         if (rtlight->cubemapname[0])
2078                 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2079         else
2080                 cubemaptexture = NULL;
2081
2082         shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer);
2083         if (shadow && (gl_stencil || visiblevolumes))
2084         {
2085                 if (!visiblevolumes)
2086                         R_Shadow_Stage_ShadowVolumes();
2087                 ent = &cl_entities[0].render;
2088                 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2089                 {
2090                         R_Mesh_Matrix(&ent->matrix);
2091                         if (r_shadow_showtris.integer)
2092                         {
2093                                 shadowmesh_t *mesh;
2094                                 rmeshstate_t m;
2095                                 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2096                                 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2097                                 qglDisable(GL_DEPTH_TEST);
2098                                 qglDisable(GL_STENCIL_TEST);
2099                                 //qglDisable(GL_CULL_FACE);
2100                                 GL_ColorMask(1,1,1,1);
2101                                 GL_ColorPointer(NULL);
2102                                 GL_Color(0,0.1,0,1);
2103                                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2104                                 memset(&m, 0, sizeof(m));
2105                                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2106                                 {
2107                                         m.pointer_vertex = mesh->vertex3f;
2108                                         R_Mesh_State(&m);
2109                                         R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2110                                 }
2111                                 //qglEnable(GL_CULL_FACE);
2112                                 if (depthenabled)
2113                                         qglEnable(GL_DEPTH_TEST);
2114                                 if (stencilenabled)
2115                                 {
2116                                         qglEnable(GL_STENCIL_TEST);
2117                                         GL_ColorMask(0,0,0,0);
2118                                 }
2119                         }
2120                         R_Shadow_RenderShadowMeshVolume(rtlight->static_meshchain_shadow);
2121                 }
2122                 else
2123                         R_TestAndDrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2124                 if (r_drawentities.integer)
2125                         for (i = 0;i < r_refdef.numentities;i++)
2126                                 if (r_refdef.entities[i]->flags & RENDER_SHADOW)
2127                                         R_TestAndDrawShadowVolume(r_refdef.entities[i], rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2128         }
2129
2130         if (!visiblevolumes)
2131         {
2132                 if (shadow && gl_stencil)
2133                         R_Shadow_Stage_LightWithShadows();
2134                 else
2135                         R_Shadow_Stage_LightWithoutShadows();
2136
2137                 ent = &cl_entities[0].render;
2138                 if (ent->model && ent->model->DrawLight)
2139                 {
2140                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2141                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2142                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2143                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2144                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2145                         if (r_shadow_staticworldlights.integer && rtlight->compiled)
2146                         {
2147                                 //R_Shadow_DrawStaticWorldLight_Light(rtlight, &ent->matrix, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2148                                 shadowmesh_t *mesh;
2149                                 R_Mesh_Matrix(&ent->matrix);
2150                                 if (r_shadow_showtris.integer)
2151                                 {
2152                                         rmeshstate_t m;
2153                                         int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2154                                         int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2155                                         qglDisable(GL_DEPTH_TEST);
2156                                         qglDisable(GL_STENCIL_TEST);
2157                                         //qglDisable(GL_CULL_FACE);
2158                                         memset(&m, 0, sizeof(m));
2159                                         GL_ColorPointer(NULL);
2160                                         GL_Color(0.2,0,0,1);
2161                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2162                                         for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2163                                         {
2164                                                 m.pointer_vertex = mesh->vertex3f;
2165                                                 R_Mesh_State(&m);
2166                                                 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2167                                         }
2168                                         //qglEnable(GL_CULL_FACE);
2169                                         if (depthenabled)
2170                                                 qglEnable(GL_DEPTH_TEST);
2171                                         if (stencilenabled)
2172                                                 qglEnable(GL_STENCIL_TEST);
2173                                 }
2174                                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2175                                 {
2176                                         R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, cubemaptexture);
2177                                         R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, cubemaptexture);
2178                                 }
2179                         }
2180                         else
2181                                 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2182                 }
2183                 if (r_drawentities.integer)
2184                 {
2185                         for (i = 0;i < r_refdef.numentities;i++)
2186                         {
2187                                 ent = r_refdef.entities[i];
2188                                 if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
2189                                  && BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2190                                  && (ent->flags & RENDER_LIGHT))
2191                                 {
2192                                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2193                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2194                                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2195                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2196                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2197                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2198                                 }
2199                         }
2200                 }
2201         }
2202 }
2203
2204 void R_ShadowVolumeLighting(int visiblevolumes)
2205 {
2206         int lnum;
2207         dlight_t *light;
2208         rmeshstate_t m;
2209
2210         if (visiblevolumes)
2211         {
2212                 memset(&m, 0, sizeof(m));
2213                 R_Mesh_State(&m);
2214
2215                 GL_BlendFunc(GL_ONE, GL_ONE);
2216                 GL_DepthMask(false);
2217                 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2218                 qglDisable(GL_CULL_FACE);
2219                 GL_ColorPointer(NULL);
2220                 GL_Color(0.0, 0.0125, 0.1, 1);
2221         }
2222         else
2223                 R_Shadow_Stage_Begin();
2224         shadowframecount++;
2225         if (r_shadow_realtime_world.integer)
2226         {
2227                 R_Shadow_LoadWorldLightsIfNeeded();
2228                 if (r_shadow_debuglight.integer >= 0)
2229                 {
2230                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2231                                 if (lnum == r_shadow_debuglight.integer)
2232                                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2233                 }
2234                 else
2235                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2236                                 R_DrawRTLight(&light->rtlight, visiblevolumes);
2237         }
2238         if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2239                 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2240                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2241
2242         if (visiblevolumes)
2243         {
2244                 qglEnable(GL_CULL_FACE);
2245                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2246         }
2247         else
2248                 R_Shadow_Stage_End();
2249 }
2250
2251 cvar_t r_editlights = {0, "r_editlights", "0"};
2252 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
2253 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
2254 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
2255 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
2256 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
2257 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
2258 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
2259 dlight_t *r_shadow_worldlightchain;
2260 dlight_t *r_shadow_selectedlight;
2261 vec3_t r_editlights_cursorlocation;
2262
2263 typedef struct cubemapinfo_s
2264 {
2265         char basename[64];
2266         rtexture_t *texture;
2267 }
2268 cubemapinfo_t;
2269
2270 #define MAX_CUBEMAPS 128
2271 static int numcubemaps;
2272 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
2273
2274 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2275 typedef struct suffixinfo_s
2276 {
2277         char *suffix;
2278         int flipx, flipy, flipdiagonal;
2279 }
2280 suffixinfo_t;
2281 static suffixinfo_t suffix[3][6] =
2282 {
2283         {
2284                 {"posx", false, false, false},
2285                 {"negx", false, false, false},
2286                 {"posy", false, false, false},
2287                 {"negy", false, false, false},
2288                 {"posz", false, false, false},
2289                 {"negz", false, false, false}
2290         },
2291         {
2292                 {"px", false, false, false},
2293                 {"nx", false, false, false},
2294                 {"py", false, false, false},
2295                 {"ny", false, false, false},
2296                 {"pz", false, false, false},
2297                 {"nz", false, false, false}
2298         },
2299         {
2300                 {"ft", true, false, true},
2301                 {"bk", false, true, true},
2302                 {"lf", true, true, false},
2303                 {"rt", false, false, false},
2304                 {"up", false, false, false},
2305                 {"dn", false, false, false}
2306         }
2307 };
2308
2309 static int componentorder[4] = {0, 1, 2, 3};
2310
2311 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2312 {
2313         int i, j, cubemapsize;
2314         qbyte *cubemappixels, *image_rgba;
2315         rtexture_t *cubemaptexture;
2316         char name[256];
2317         // must start 0 so the first loadimagepixels has no requested width/height
2318         cubemapsize = 0;
2319         cubemappixels = NULL;
2320         cubemaptexture = NULL;
2321         for (j = 0;j < 3 && !cubemappixels;j++)
2322         {
2323                 for (i = 0;i < 6;i++)
2324                 {
2325                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2326                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2327                         {
2328                                 if (image_width == image_height)
2329                                 {
2330                                         if (!cubemappixels && image_width >= 1)
2331                                         {
2332                                                 cubemapsize = image_width;
2333                                                 // note this clears to black, so unavailable sizes are black
2334                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2335                                         }
2336                                         if (cubemappixels)
2337                                                 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
2338                                 }
2339                                 else
2340                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2341                                 Mem_Free(image_rgba);
2342                         }
2343                 }
2344         }
2345         if (cubemappixels)
2346         {
2347                 if (!r_shadow_filters_texturepool)
2348                         r_shadow_filters_texturepool = R_AllocTexturePool();
2349                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2350                 Mem_Free(cubemappixels);
2351         }
2352         else
2353         {
2354                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2355                 for (j = 0;j < 3;j++)
2356                         for (i = 0;i < 6;i++)
2357                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2358                 Con_Print(" and was unable to find any of them.\n");
2359         }
2360         return cubemaptexture;
2361 }
2362
2363 rtexture_t *R_Shadow_Cubemap(const char *basename)
2364 {
2365         int i;
2366         for (i = 0;i < numcubemaps;i++)
2367                 if (!strcasecmp(cubemaps[i].basename, basename))
2368                         return cubemaps[i].texture;
2369         if (i >= MAX_CUBEMAPS)
2370                 return NULL;
2371         numcubemaps++;
2372         strcpy(cubemaps[i].basename, basename);
2373         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2374         return cubemaps[i].texture;
2375 }
2376
2377 void R_Shadow_FreeCubemaps(void)
2378 {
2379         numcubemaps = 0;
2380         R_FreeTexturePool(&r_shadow_filters_texturepool);
2381 }
2382
2383 void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
2384 {
2385         dlight_t *light;
2386
2387         if (radius < 15 || DotProduct(color, color) < 0.03)
2388         {
2389                 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2390                 return;
2391         }
2392
2393         light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2394         VectorCopy(origin, light->origin);
2395         VectorCopy(angles, light->angles);
2396         VectorCopy(color, light->color);
2397         light->radius = radius;
2398         light->style = style;
2399         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2400         {
2401                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2402                 light->style = 0;
2403         }
2404         light->shadow = shadowenable;
2405         light->corona = corona;
2406         if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2407                 strcpy(light->cubemapname, cubemapname);
2408         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2409         light->next = r_shadow_worldlightchain;
2410         r_shadow_worldlightchain = light;
2411
2412         R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2413         if (r_shadow_staticworldlights.integer)
2414                 R_RTLight_Compile(&light->rtlight);
2415 }
2416
2417 void R_Shadow_FreeWorldLight(dlight_t *light)
2418 {
2419         dlight_t **lightpointer;
2420         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2421         if (*lightpointer != light)
2422                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2423         *lightpointer = light->next;
2424         R_RTLight_Uncompile(&light->rtlight);
2425         Mem_Free(light);
2426 }
2427
2428 void R_Shadow_ClearWorldLights(void)
2429 {
2430         while (r_shadow_worldlightchain)
2431                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2432         r_shadow_selectedlight = NULL;
2433         R_Shadow_FreeCubemaps();
2434 }
2435
2436 void R_Shadow_SelectLight(dlight_t *light)
2437 {
2438         if (r_shadow_selectedlight)
2439                 r_shadow_selectedlight->selected = false;
2440         r_shadow_selectedlight = light;
2441         if (r_shadow_selectedlight)
2442                 r_shadow_selectedlight->selected = true;
2443 }
2444
2445 rtexture_t *lighttextures[5];
2446
2447 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2448 {
2449         float scale = r_editlights_cursorgrid.value * 0.5f;
2450         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2451 }
2452
2453 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2454 {
2455         float intensity;
2456         const dlight_t *light;
2457         light = calldata1;
2458         intensity = 0.5;
2459         if (light->selected)
2460                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2461         if (!light->shadow)
2462                 intensity *= 0.5f;
2463         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2464 }
2465
2466 void R_Shadow_DrawLightSprites(void)
2467 {
2468         int i;
2469         cachepic_t *pic;
2470         dlight_t *light;
2471
2472         for (i = 0;i < 5;i++)
2473         {
2474                 lighttextures[i] = NULL;
2475                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2476                         lighttextures[i] = pic->tex;
2477         }
2478
2479         for (light = r_shadow_worldlightchain;light;light = light->next)
2480                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2481         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2482 }
2483
2484 void R_Shadow_SelectLightInView(void)
2485 {
2486         float bestrating, rating, temp[3];
2487         dlight_t *best, *light;
2488         best = NULL;
2489         bestrating = 0;
2490         for (light = r_shadow_worldlightchain;light;light = light->next)
2491         {
2492                 VectorSubtract(light->origin, r_vieworigin, temp);
2493                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2494                 if (rating >= 0.95)
2495                 {
2496                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2497                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2498                         {
2499                                 bestrating = rating;
2500                                 best = light;
2501                         }
2502                 }
2503         }
2504         R_Shadow_SelectLight(best);
2505 }
2506
2507 void R_Shadow_LoadWorldLights(void)
2508 {
2509         int n, a, style, shadow;
2510         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2511         float origin[3], radius, color[3], angles[3], corona;
2512         if (cl.worldmodel == NULL)
2513         {
2514                 Con_Print("No map loaded.\n");
2515                 return;
2516         }
2517         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2518         strlcat (name, ".rtlights", sizeof (name));
2519         lightsstring = FS_LoadFile(name, false);
2520         if (lightsstring)
2521         {
2522                 s = lightsstring;
2523                 n = 0;
2524                 while (*s)
2525                 {
2526                         t = s;
2527                         /*
2528                         shadow = true;
2529                         for (;COM_Parse(t, true) && strcmp(
2530                         if (COM_Parse(t, true))
2531                         {
2532                                 if (com_token[0] == '!')
2533                                 {
2534                                         shadow = false;
2535                                         origin[0] = atof(com_token+1);
2536                                 }
2537                                 else
2538                                         origin[0] = atof(com_token);
2539                                 if (Com_Parse(t
2540                         }
2541                         */
2542                         t = s;
2543                         while (*s && *s != '\n')
2544                                 s++;
2545                         if (!*s)
2546                                 break;
2547                         *s = 0;
2548                         shadow = true;
2549                         // check for modifier flags
2550                         if (*t == '!')
2551                         {
2552                                 shadow = false;
2553                                 t++;
2554                         }
2555                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2]);
2556                         if (a < 13)
2557                                 VectorClear(angles);
2558                         if (a < 10)
2559                                 corona = 0;
2560                         if (a < 9 || !strcmp(cubemapname, "\"\""))
2561                                 cubemapname[0] = 0;
2562                         *s = '\n';
2563                         if (a < 8)
2564                         {
2565                                 Con_Printf("found %d parameters on line %i, should be 8 or more parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style \"cubemapname\" corona angles[0] angles[1] angles[2])\n", a, n + 1);
2566                                 break;
2567                         }
2568                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2569                         radius *= r_editlights_rtlightssizescale.value;
2570                         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2571                         s++;
2572                         n++;
2573                 }
2574                 if (*s)
2575                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2576                 Mem_Free(lightsstring);
2577         }
2578 }
2579
2580 void R_Shadow_SaveWorldLights(void)
2581 {
2582         dlight_t *light;
2583         int bufchars, bufmaxchars;
2584         char *buf, *oldbuf;
2585         char name[MAX_QPATH];
2586         char line[1024];
2587         if (!r_shadow_worldlightchain)
2588                 return;
2589         if (cl.worldmodel == NULL)
2590         {
2591                 Con_Print("No map loaded.\n");
2592                 return;
2593         }
2594         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2595         strlcat (name, ".rtlights", sizeof (name));
2596         bufchars = bufmaxchars = 0;
2597         buf = NULL;
2598         for (light = r_shadow_worldlightchain;light;light = light->next)
2599         {
2600                 sprintf(line, "%s%f %f %f %f %f %f %f %d %s %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname[0] ? light->cubemapname : "\"\"", light->corona, light->angles[0], light->angles[1], light->angles[2]);
2601                 if (bufchars + (int) strlen(line) > bufmaxchars)
2602                 {
2603                         bufmaxchars = bufchars + strlen(line) + 2048;
2604                         oldbuf = buf;
2605                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2606                         if (oldbuf)
2607                         {
2608                                 if (bufchars)
2609                                         memcpy(buf, oldbuf, bufchars);
2610                                 Mem_Free(oldbuf);
2611                         }
2612                 }
2613                 if (strlen(line))
2614                 {
2615                         memcpy(buf + bufchars, line, strlen(line));
2616                         bufchars += strlen(line);
2617                 }
2618         }
2619         if (bufchars)
2620                 FS_WriteFile(name, buf, bufchars);
2621         if (buf)
2622                 Mem_Free(buf);
2623 }
2624
2625 void R_Shadow_LoadLightsFile(void)
2626 {
2627         int n, a, style;
2628         char name[MAX_QPATH], *lightsstring, *s, *t;
2629         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2630         if (cl.worldmodel == NULL)
2631         {
2632                 Con_Print("No map loaded.\n");
2633                 return;
2634         }
2635         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2636         strlcat (name, ".lights", sizeof (name));
2637         lightsstring = FS_LoadFile(name, false);
2638         if (lightsstring)
2639         {
2640                 s = lightsstring;
2641                 n = 0;
2642                 while (*s)
2643                 {
2644                         t = s;
2645                         while (*s && *s != '\n')
2646                                 s++;
2647                         if (!*s)
2648                                 break;
2649                         *s = 0;
2650                         a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &origin[0], &origin[1], &origin[2], &falloff, &color[0], &color[1], &color[2], &subtract, &spotdir[0], &spotdir[1], &spotdir[2], &spotcone, &distbias, &style);
2651                         *s = '\n';
2652                         if (a < 14)
2653                         {
2654                                 Con_Printf("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
2655                                 break;
2656                         }
2657                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2658                         radius = bound(15, radius, 4096);
2659                         VectorScale(color, (2.0f / (8388608.0f)), color);
2660                         R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2661                         s++;
2662                         n++;
2663                 }
2664                 if (*s)
2665                         Con_Printf("invalid lights file \"%s\"\n", name);
2666                 Mem_Free(lightsstring);
2667         }
2668 }
2669
2670 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2671 {
2672         int entnum, style, islight, skin, pflags, effects;
2673         char key[256], value[1024];
2674         float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2675         const char *data;
2676
2677         if (cl.worldmodel == NULL)
2678         {
2679                 Con_Print("No map loaded.\n");
2680                 return;
2681         }
2682         data = cl.worldmodel->brush.entities;
2683         if (!data)
2684                 return;
2685         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2686         {
2687                 light = 0;
2688                 origin[0] = origin[1] = origin[2] = 0;
2689                 originhack[0] = originhack[1] = originhack[2] = 0;
2690                 angles[0] = angles[1] = angles[2] = 0;
2691                 color[0] = color[1] = color[2] = 1;
2692                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2693                 fadescale = 1;
2694                 lightscale = 1;
2695                 style = 0;
2696                 skin = 0;
2697                 pflags = 0;
2698                 effects = 0;
2699                 islight = false;
2700                 while (1)
2701                 {
2702                         if (!COM_ParseToken(&data, false))
2703                                 break; // error
2704                         if (com_token[0] == '}')
2705                                 break; // end of entity
2706                         if (com_token[0] == '_')
2707                                 strcpy(key, com_token + 1);
2708                         else
2709                                 strcpy(key, com_token);
2710                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2711                                 key[strlen(key)-1] = 0;
2712                         if (!COM_ParseToken(&data, false))
2713                                 break; // error
2714                         strcpy(value, com_token);
2715
2716                         // now that we have the key pair worked out...
2717                         if (!strcmp("light", key))
2718                                 light = atof(value);
2719                         else if (!strcmp("origin", key))
2720                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2721                         else if (!strcmp("angle", key))
2722                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2723                         else if (!strcmp("angles", key))
2724                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2725                         else if (!strcmp("color", key))
2726                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2727                         else if (!strcmp("wait", key))
2728                                 fadescale = atof(value);
2729                         else if (!strcmp("classname", key))
2730                         {
2731                                 if (!strncmp(value, "light", 5))
2732                                 {
2733                                         islight = true;
2734                                         if (!strcmp(value, "light_fluoro"))
2735                                         {
2736                                                 originhack[0] = 0;
2737                                                 originhack[1] = 0;
2738                                                 originhack[2] = 0;
2739                                                 overridecolor[0] = 1;
2740                                                 overridecolor[1] = 1;
2741                                                 overridecolor[2] = 1;
2742                                         }
2743                                         if (!strcmp(value, "light_fluorospark"))
2744                                         {
2745                                                 originhack[0] = 0;
2746                                                 originhack[1] = 0;
2747                                                 originhack[2] = 0;
2748                                                 overridecolor[0] = 1;
2749                                                 overridecolor[1] = 1;
2750                                                 overridecolor[2] = 1;
2751                                         }
2752                                         if (!strcmp(value, "light_globe"))
2753                                         {
2754                                                 originhack[0] = 0;
2755                                                 originhack[1] = 0;
2756                                                 originhack[2] = 0;
2757                                                 overridecolor[0] = 1;
2758                                                 overridecolor[1] = 0.8;
2759                                                 overridecolor[2] = 0.4;
2760                                         }
2761                                         if (!strcmp(value, "light_flame_large_yellow"))
2762                                         {
2763                                                 originhack[0] = 0;
2764                                                 originhack[1] = 0;
2765                                                 originhack[2] = 48;
2766                                                 overridecolor[0] = 1;
2767                                                 overridecolor[1] = 0.5;
2768                                                 overridecolor[2] = 0.1;
2769                                         }
2770                                         if (!strcmp(value, "light_flame_small_yellow"))
2771                                         {
2772                                                 originhack[0] = 0;
2773                                                 originhack[1] = 0;
2774                                                 originhack[2] = 40;
2775                                                 overridecolor[0] = 1;
2776                                                 overridecolor[1] = 0.5;
2777                                                 overridecolor[2] = 0.1;
2778                                         }
2779                                         if (!strcmp(value, "light_torch_small_white"))
2780                                         {
2781                                                 originhack[0] = 0;
2782                                                 originhack[1] = 0;
2783                                                 originhack[2] = 40;
2784                                                 overridecolor[0] = 1;
2785                                                 overridecolor[1] = 0.5;
2786                                                 overridecolor[2] = 0.1;
2787                                         }
2788                                         if (!strcmp(value, "light_torch_small_walltorch"))
2789                                         {
2790                                                 originhack[0] = 0;
2791                                                 originhack[1] = 0;
2792                                                 originhack[2] = 40;
2793                                                 overridecolor[0] = 1;
2794                                                 overridecolor[1] = 0.5;
2795                                                 overridecolor[2] = 0.1;
2796                                         }
2797                                 }
2798                         }
2799                         else if (!strcmp("style", key))
2800                                 style = atoi(value);
2801                         else if (cl.worldmodel->type == mod_brushq3)
2802                         {
2803                                 if (!strcmp("scale", key))
2804                                         lightscale = atof(value);
2805                                 if (!strcmp("fade", key))
2806                                         fadescale = atof(value);
2807                         }
2808                         else if (!strcmp("skin", key))
2809                                 skin = (int)atof(value);
2810                         else if (!strcmp("pflags", key))
2811                                 pflags = (int)atof(value);
2812                         else if (!strcmp("effects", key))
2813                                 effects = (int)atof(value);
2814                 }
2815                 if (light <= 0 && islight)
2816                         light = 300;
2817                 if (lightscale <= 0)
2818                         lightscale = 1;
2819                 if (fadescale <= 0)
2820                         fadescale = 1;
2821                 if (gamemode == GAME_TENEBRAE)
2822                 {
2823                         if (effects & EF_NODRAW)
2824                         {
2825                                 pflags |= PFLAGS_FULLDYNAMIC;
2826                                 effects &= ~EF_NODRAW;
2827                         }
2828                 }
2829                 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2830                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2831                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2832                         VectorCopy(overridecolor, color);
2833                 VectorScale(color, light, color);
2834                 VectorAdd(origin, originhack, origin);
2835                 if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC))
2836                         R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2837         }
2838 }
2839
2840
2841 void R_Shadow_SetCursorLocationForView(void)
2842 {
2843         vec_t dist, push, frac;
2844         vec3_t dest, endpos, normal;
2845         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2846         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2847         if (frac < 1)
2848         {
2849                 dist = frac * r_editlights_cursordistance.value;
2850                 push = r_editlights_cursorpushback.value;
2851                 if (push > dist)
2852                         push = dist;
2853                 push = -push;
2854                 VectorMA(endpos, push, r_viewforward, endpos);
2855                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2856         }
2857         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2858         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2859         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2860 }
2861
2862 void R_Shadow_UpdateWorldLightSelection(void)
2863 {
2864         if (r_editlights.integer)
2865         {
2866                 R_Shadow_SetCursorLocationForView();
2867                 R_Shadow_SelectLightInView();
2868                 R_Shadow_DrawLightSprites();
2869         }
2870         else
2871                 R_Shadow_SelectLight(NULL);
2872 }
2873
2874 void R_Shadow_EditLights_Clear_f(void)
2875 {
2876         R_Shadow_ClearWorldLights();
2877 }
2878
2879 void R_Shadow_EditLights_Reload_f(void)
2880 {
2881         r_shadow_reloadlights = true;
2882 }
2883
2884 void R_Shadow_EditLights_Save_f(void)
2885 {
2886         if (cl.worldmodel)
2887                 R_Shadow_SaveWorldLights();
2888 }
2889
2890 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2891 {
2892         R_Shadow_ClearWorldLights();
2893         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2894 }
2895
2896 void R_Shadow_EditLights_ImportLightsFile_f(void)
2897 {
2898         R_Shadow_ClearWorldLights();
2899         R_Shadow_LoadLightsFile();
2900 }
2901
2902 void R_Shadow_EditLights_Spawn_f(void)
2903 {
2904         vec3_t color;
2905         if (!r_editlights.integer)
2906         {
2907                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2908                 return;
2909         }
2910         if (Cmd_Argc() != 1)
2911         {
2912                 Con_Print("r_editlights_spawn does not take parameters\n");
2913                 return;
2914         }
2915         color[0] = color[1] = color[2] = 1;
2916         R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2917 }
2918
2919 void R_Shadow_EditLights_Edit_f(void)
2920 {
2921         vec3_t origin, angles, color;
2922         vec_t radius, corona;
2923         int style, shadows;
2924         char cubemapname[1024];
2925         if (!r_editlights.integer)
2926         {
2927                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2928                 return;
2929         }
2930         if (!r_shadow_selectedlight)
2931         {
2932                 Con_Print("No selected light.\n");
2933                 return;
2934         }
2935         VectorCopy(r_shadow_selectedlight->origin, origin);
2936         VectorCopy(r_shadow_selectedlight->angles, angles);
2937         VectorCopy(r_shadow_selectedlight->color, color);
2938         radius = r_shadow_selectedlight->radius;
2939         style = r_shadow_selectedlight->style;
2940         if (r_shadow_selectedlight->cubemapname)
2941                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2942         else
2943                 cubemapname[0] = 0;
2944         shadows = r_shadow_selectedlight->shadow;
2945         corona = r_shadow_selectedlight->corona;
2946         if (!strcmp(Cmd_Argv(1), "origin"))
2947         {
2948                 if (Cmd_Argc() != 5)
2949                 {
2950                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2951                         return;
2952                 }
2953                 origin[0] = atof(Cmd_Argv(2));
2954                 origin[1] = atof(Cmd_Argv(3));
2955                 origin[2] = atof(Cmd_Argv(4));
2956         }
2957         else if (!strcmp(Cmd_Argv(1), "originx"))
2958         {
2959                 if (Cmd_Argc() != 3)
2960                 {
2961                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2962                         return;
2963                 }
2964                 origin[0] = atof(Cmd_Argv(2));
2965         }
2966         else if (!strcmp(Cmd_Argv(1), "originy"))
2967         {
2968                 if (Cmd_Argc() != 3)
2969                 {
2970                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2971                         return;
2972                 }
2973                 origin[1] = atof(Cmd_Argv(2));
2974         }
2975         else if (!strcmp(Cmd_Argv(1), "originz"))
2976         {
2977                 if (Cmd_Argc() != 3)
2978                 {
2979                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2980                         return;
2981                 }
2982                 origin[2] = atof(Cmd_Argv(2));
2983         }
2984         else if (!strcmp(Cmd_Argv(1), "move"))
2985         {
2986                 if (Cmd_Argc() != 5)
2987                 {
2988                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2989                         return;
2990                 }
2991                 origin[0] += atof(Cmd_Argv(2));
2992                 origin[1] += atof(Cmd_Argv(3));
2993                 origin[2] += atof(Cmd_Argv(4));
2994         }
2995         else if (!strcmp(Cmd_Argv(1), "movex"))
2996         {
2997                 if (Cmd_Argc() != 3)
2998                 {
2999                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3000                         return;
3001                 }
3002                 origin[0] += atof(Cmd_Argv(2));
3003         }
3004         else if (!strcmp(Cmd_Argv(1), "movey"))
3005         {
3006                 if (Cmd_Argc() != 3)
3007                 {
3008                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3009                         return;
3010                 }
3011                 origin[1] += atof(Cmd_Argv(2));
3012         }
3013         else if (!strcmp(Cmd_Argv(1), "movez"))
3014         {
3015                 if (Cmd_Argc() != 3)
3016                 {
3017                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3018                         return;
3019                 }
3020                 origin[2] += atof(Cmd_Argv(2));
3021         }
3022         else if (!strcmp(Cmd_Argv(1), "angles"))
3023         {
3024                 if (Cmd_Argc() != 5)
3025                 {
3026                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3027                         return;
3028                 }
3029                 angles[0] = atof(Cmd_Argv(2));
3030                 angles[1] = atof(Cmd_Argv(3));
3031                 angles[2] = atof(Cmd_Argv(4));
3032         }
3033         else if (!strcmp(Cmd_Argv(1), "anglesx"))
3034         {
3035                 if (Cmd_Argc() != 3)
3036                 {
3037                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3038                         return;
3039                 }
3040                 angles[0] = atof(Cmd_Argv(2));
3041         }
3042         else if (!strcmp(Cmd_Argv(1), "anglesy"))
3043         {
3044                 if (Cmd_Argc() != 3)
3045                 {
3046                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3047                         return;
3048                 }
3049                 angles[1] = atof(Cmd_Argv(2));
3050         }
3051         else if (!strcmp(Cmd_Argv(1), "anglesz"))
3052         {
3053                 if (Cmd_Argc() != 3)
3054                 {
3055                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3056                         return;
3057                 }
3058                 angles[2] = atof(Cmd_Argv(2));
3059         }
3060         else if (!strcmp(Cmd_Argv(1), "color"))
3061         {
3062                 if (Cmd_Argc() != 5)
3063                 {
3064                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3065                         return;
3066                 }
3067                 color[0] = atof(Cmd_Argv(2));
3068                 color[1] = atof(Cmd_Argv(3));
3069                 color[2] = atof(Cmd_Argv(4));
3070         }
3071         else if (!strcmp(Cmd_Argv(1), "radius"))
3072         {
3073                 if (Cmd_Argc() != 3)
3074                 {
3075                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3076                         return;
3077                 }
3078                 radius = atof(Cmd_Argv(2));
3079         }
3080         else if (!strcmp(Cmd_Argv(1), "style"))
3081         {
3082                 if (Cmd_Argc() != 3)
3083                 {
3084                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3085                         return;
3086                 }
3087                 style = atoi(Cmd_Argv(2));
3088         }
3089         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3090         {
3091                 if (Cmd_Argc() > 3)
3092                 {
3093                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3094                         return;
3095                 }
3096                 if (Cmd_Argc() == 3)
3097                         strcpy(cubemapname, Cmd_Argv(2));
3098                 else
3099                         cubemapname[0] = 0;
3100         }
3101         else if (!strcmp(Cmd_Argv(1), "shadows"))
3102         {
3103                 if (Cmd_Argc() != 3)
3104                 {
3105                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3106                         return;
3107                 }
3108                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3109         }
3110         else if (!strcmp(Cmd_Argv(1), "corona"))
3111         {
3112                 if (Cmd_Argc() != 3)
3113                 {
3114                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3115                         return;
3116                 }
3117                 corona = atof(Cmd_Argv(2));
3118         }
3119         else
3120         {
3121                 Con_Print("usage: r_editlights_edit [property] [value]\n");
3122                 Con_Print("Selected light's properties:\n");
3123                 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3124                 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3125                 Con_Printf("Color  : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3126                 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3127                 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3128                 Con_Printf("Style  : %i\n", r_shadow_selectedlight->style);
3129                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3130                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3131                 return;
3132         }
3133         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3134         r_shadow_selectedlight = NULL;
3135         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3136 }
3137
3138 extern int con_vislines;
3139 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3140 {
3141         float x, y;
3142         char temp[256];
3143         if (r_shadow_selectedlight == NULL)
3144                 return;
3145         x = 0;
3146         y = con_vislines;
3147         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3148         sprintf(temp, "Origin  %f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3149         sprintf(temp, "Angles  %f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3150         sprintf(temp, "Color   %f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3151         sprintf(temp, "Radius  %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3152         sprintf(temp, "Corona  %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3153         sprintf(temp, "Style   %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3154         sprintf(temp, "Shadows %s", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3155         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3156 }
3157
3158 void R_Shadow_EditLights_ToggleShadow_f(void)
3159 {
3160         if (!r_editlights.integer)
3161         {
3162                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3163                 return;
3164         }
3165         if (!r_shadow_selectedlight)
3166         {
3167                 Con_Print("No selected light.\n");
3168                 return;
3169         }
3170         R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3171         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3172         r_shadow_selectedlight = NULL;
3173 }
3174
3175 void R_Shadow_EditLights_ToggleCorona_f(void)
3176 {
3177         if (!r_editlights.integer)
3178         {
3179                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3180                 return;
3181         }
3182         if (!r_shadow_selectedlight)
3183         {
3184                 Con_Print("No selected light.\n");
3185                 return;
3186         }
3187         R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3188         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3189         r_shadow_selectedlight = NULL;
3190 }
3191
3192 void R_Shadow_EditLights_Remove_f(void)
3193 {
3194         if (!r_editlights.integer)
3195         {
3196                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
3197                 return;
3198         }
3199         if (!r_shadow_selectedlight)
3200         {
3201                 Con_Print("No selected light.\n");
3202                 return;
3203         }
3204         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3205         r_shadow_selectedlight = NULL;
3206 }
3207
3208 void R_Shadow_EditLights_Help_f(void)
3209 {
3210         Con_Print(
3211 "Documentation on r_editlights system:\n"
3212 "Settings:\n"
3213 "r_editlights : enable/disable editing mode\n"
3214 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3215 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3216 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3217 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3218 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3219 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3220 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3221 "Commands:\n"
3222 "r_editlights_help : this help\n"
3223 "r_editlights_clear : remove all lights\n"
3224 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3225 "r_editlights_save : save to .rtlights file\n"
3226 "r_editlights_spawn : create a light with default settings\n"
3227 "r_editlights_edit command : edit selected light - more documentation below\n"
3228 "r_editlights_remove : remove selected light\n"
3229 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3230 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3231 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3232 "Edit commands:\n"
3233 "origin x y z : set light location\n"
3234 "originx x: set x component of light location\n"
3235 "originy y: set y component of light location\n"
3236 "originz z: set z component of light location\n"
3237 "move x y z : adjust light location\n"
3238 "movex x: adjust x component of light location\n"
3239 "movey y: adjust y component of light location\n"
3240 "movez z: adjust z component of light location\n"
3241 "angles x y z : set light angles\n"
3242 "anglesx x: set x component of light angles\n"
3243 "anglesy y: set y component of light angles\n"
3244 "anglesz z: set z component of light angles\n"
3245 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3246 "radius radius : set radius (size) of light\n"
3247 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3248 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3249 "shadows 1/0 : turn on/off shadows\n"
3250 "corona n : set corona intensity\n"
3251 "<nothing> : print light properties to console\n"
3252         );
3253 }
3254
3255 void R_Shadow_EditLights_Init(void)
3256 {
3257         Cvar_RegisterVariable(&r_editlights);
3258         Cvar_RegisterVariable(&r_editlights_cursordistance);
3259         Cvar_RegisterVariable(&r_editlights_cursorpushback);
3260         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3261         Cvar_RegisterVariable(&r_editlights_cursorgrid);
3262         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3263         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3264         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3265         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3266         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3267         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3268         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3269         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3270         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3271         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3272         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3273         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3274         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3275         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
3276 }
3277