moved all type-specific model fields to respective structures (alias, sprite, brush)
[divverent/darkplaces.git] / r_light.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // r_light.c
21
22 #include "quakedef.h"
23 #include "cl_collision.h"
24 #include "r_shadow.h"
25
26 rdlight_t r_dlight[MAX_DLIGHTS];
27 int r_numdlights = 0;
28
29 cvar_t r_modellights = {CVAR_SAVE, "r_modellights", "4"};
30 cvar_t r_vismarklights = {0, "r_vismarklights", "1"};
31 cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1"};
32 cvar_t gl_flashblend = {CVAR_SAVE, "gl_flashblend", "1"};
33
34 static rtexture_t *lightcorona;
35 static rtexturepool_t *lighttexturepool;
36
37 void r_light_start(void)
38 {
39         float dx, dy;
40         int x, y, a;
41         qbyte pixels[32][32][4];
42         lighttexturepool = R_AllocTexturePool();
43         for (y = 0;y < 32;y++)
44         {
45                 dy = (y - 15.5f) * (1.0f / 16.0f);
46                 for (x = 0;x < 32;x++)
47                 {
48                         dx = (x - 15.5f) * (1.0f / 16.0f);
49                         a = ((1.0f / (dx * dx + dy * dy + 0.2f)) - (1.0f / (1.0f + 0.2))) * 32.0f / (1.0f / (1.0f + 0.2));
50                         a = bound(0, a, 255);
51                         pixels[y][x][0] = a;
52                         pixels[y][x][1] = a;
53                         pixels[y][x][2] = a;
54                         pixels[y][x][3] = 255;
55                 }
56         }
57         lightcorona = R_LoadTexture2D(lighttexturepool, "lightcorona", 32, 32, &pixels[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
58 }
59
60 void r_light_shutdown(void)
61 {
62         lighttexturepool = NULL;
63         lightcorona = NULL;
64 }
65
66 void r_light_newmap(void)
67 {
68         int i;
69         for (i = 0;i < 256;i++)
70                 d_lightstylevalue[i] = 264;             // normal light value
71 }
72
73 void R_Light_Init(void)
74 {
75         Cvar_RegisterVariable(&r_modellights);
76         Cvar_RegisterVariable(&r_vismarklights);
77         Cvar_RegisterVariable(&r_coronas);
78         Cvar_RegisterVariable(&gl_flashblend);
79         R_RegisterModule("R_Light", r_light_start, r_light_shutdown, r_light_newmap);
80 }
81
82 /*
83 ==================
84 R_AnimateLight
85 ==================
86 */
87 void R_AnimateLight (void)
88 {
89         int i, j, k;
90
91 //
92 // light animations
93 // 'm' is normal light, 'a' is no light, 'z' is double bright
94         i = (int)(cl.time * 10);
95         for (j = 0;j < MAX_LIGHTSTYLES;j++)
96         {
97                 if (!cl_lightstyle || !cl_lightstyle[j].length)
98                 {
99                         d_lightstylevalue[j] = 256;
100                         continue;
101                 }
102                 k = i % cl_lightstyle[j].length;
103                 k = cl_lightstyle[j].map[k] - 'a';
104                 k = k*22;
105                 d_lightstylevalue[j] = k;
106         }
107 }
108
109
110 void R_BuildLightList(void)
111 {
112         int i;
113         dlight_t *cd;
114         rdlight_t *rd;
115
116         r_numdlights = 0;
117         c_dlights = 0;
118
119         if (!r_dynamic.integer || !cl_dlights)
120                 return;
121
122         for (i = 0;i < MAX_DLIGHTS;i++)
123         {
124                 cd = cl_dlights + i;
125                 if (cd->radius <= 0)
126                         continue;
127                 rd = &r_dlight[r_numdlights++];
128                 VectorCopy(cd->origin, rd->origin);
129                 VectorScale(cd->color, cd->radius * 64.0f, rd->light);
130                 rd->cullradius2 = DotProduct(rd->light, rd->light) * (0.25f / (64.0f * 64.0f)) + 4096.0f;
131                 // clamp radius to avoid overflowing division table in lightmap code
132                 if (rd->cullradius2 > (2048.0f * 2048.0f))
133                         rd->cullradius2 = (2048.0f * 2048.0f);
134                 rd->cullradius = sqrt(rd->cullradius2);
135                 rd->subtract = 1.0f / rd->cullradius2;
136                 rd->ent = cd->ent;
137                 c_dlights++; // count every dlight in use
138         }
139 }
140
141 void R_DrawCoronas(void)
142 {
143         int i;
144         float cscale, scale, viewdist, dist;
145         rdlight_t *rd;
146         if (!r_coronas.integer)
147                 return;
148         R_Mesh_Matrix(&r_identitymatrix);
149         viewdist = DotProduct(r_origin, vpn);
150         for (i = 0;i < r_numdlights;i++)
151         {
152                 rd = r_dlight + i;
153                 dist = (DotProduct(rd->origin, vpn) - viewdist);
154                 if (dist >= 24.0f && CL_TraceLine(rd->origin, r_origin, NULL, NULL, 0, true, NULL) == 1)
155                 {
156                         cscale = (1.0f / 131072.0f);
157                         scale = rd->cullradius * 0.25f;
158                         if (gl_flashblend.integer)
159                         {
160                                 cscale *= 4.0f;
161                                 scale *= 2.0f;
162                         }
163                         R_DrawSprite(GL_ONE, GL_ONE, lightcorona, true, rd->origin, vright, vup, scale, -scale, -scale, scale, rd->light[0] * cscale, rd->light[1] * cscale, rd->light[2] * cscale, 1);
164                 }
165         }
166 }
167
168 /*
169 =============================================================================
170
171 DYNAMIC LIGHTS
172
173 =============================================================================
174 */
175
176 /*
177 =============
178 R_MarkLights
179 =============
180 */
181 static void R_OldMarkLights (entity_render_t *ent, vec3_t lightorigin, rdlight_t *rd, int bit, int bitindex, mnode_t *node)
182 {
183         float ndist, maxdist;
184         msurface_t *surf;
185         int i, *surfacepvsframes;
186         int d, impacts, impactt;
187         float dist, dist2, impact[3];
188
189         if (!r_dynamic.integer)
190                 return;
191
192         // for comparisons to minimum acceptable light
193         maxdist = rd->cullradius2;
194
195         surfacepvsframes = ent->model->brushq1.surfacepvsframes;
196 loc0:
197         if (node->contents < 0)
198                 return;
199
200         ndist = PlaneDiff(lightorigin, node->plane);
201
202         if (ndist > rd->cullradius)
203         {
204                 node = node->children[0];
205                 goto loc0;
206         }
207         if (ndist < -rd->cullradius)
208         {
209                 node = node->children[1];
210                 goto loc0;
211         }
212
213 // mark the polygons
214         surf = ent->model->brushq1.surfaces + node->firstsurface;
215         for (i = 0;i < node->numsurfaces;i++, surf++)
216         {
217                 if (surfacepvsframes[surf->number] != ent->model->brushq1.pvsframecount)
218                         continue;
219                 dist = ndist;
220                 if (surf->flags & SURF_PLANEBACK)
221                         dist = -dist;
222
223                 if (dist < -0.25f && !(surf->flags & SURF_LIGHTBOTHSIDES))
224                         continue;
225
226                 dist2 = dist * dist;
227                 if (dist2 >= maxdist)
228                         continue;
229
230                 if (node->plane->type < 3)
231                 {
232                         VectorCopy(lightorigin, impact);
233                         impact[node->plane->type] -= dist;
234                 }
235                 else
236                 {
237                         impact[0] = lightorigin[0] - surf->plane->normal[0] * dist;
238                         impact[1] = lightorigin[1] - surf->plane->normal[1] * dist;
239                         impact[2] = lightorigin[2] - surf->plane->normal[2] * dist;
240                 }
241
242                 impacts = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
243
244                 d = bound(0, impacts, surf->extents[0] + 16) - impacts;
245                 dist2 += d * d;
246                 if (dist2 > maxdist)
247                         continue;
248
249                 impactt = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
250
251                 d = bound(0, impactt, surf->extents[1] + 16) - impactt;
252                 dist2 += d * d;
253                 if (dist2 > maxdist)
254                         continue;
255
256                 if (surf->dlightframe != r_framecount) // not dynamic until now
257                 {
258                         surf->dlightbits[0] = surf->dlightbits[1] = surf->dlightbits[2] = surf->dlightbits[3] = surf->dlightbits[4] = surf->dlightbits[5] = surf->dlightbits[6] = surf->dlightbits[7] = 0;
259                         surf->dlightframe = r_framecount;
260                         if (r_dlightmap.integer)
261                                 surf->cached_dlight = true;
262                 }
263                 surf->dlightbits[bitindex] |= bit;
264         }
265
266         if (node->children[0]->contents >= 0)
267         {
268                 if (node->children[1]->contents >= 0)
269                 {
270                         R_OldMarkLights (ent, lightorigin, rd, bit, bitindex, node->children[0]);
271                         node = node->children[1];
272                         goto loc0;
273                 }
274                 else
275                 {
276                         node = node->children[0];
277                         goto loc0;
278                 }
279         }
280         else if (node->children[1]->contents >= 0)
281         {
282                 node = node->children[1];
283                 goto loc0;
284         }
285 }
286
287
288 static void R_VisMarkLights (entity_render_t *ent, rdlight_t *rd, int bit, int bitindex)
289 {
290         static int lightframe = 0;
291         mleaf_t *pvsleaf;
292         vec3_t lightorigin;
293         model_t *model;
294         int i, k, m, c, leafnum, *surfacepvsframes, *mark;
295         msurface_t *surf;
296         mleaf_t *leaf;
297         qbyte *in;
298         int row;
299         float low[3], high[3], dist, maxdist;
300
301         if (!r_dynamic.integer || !ent->model)
302                 return;
303
304         Matrix4x4_Transform(&ent->inversematrix, rd->origin, lightorigin);
305
306         model = ent->model;
307         pvsleaf = model->brushq1.PointInLeaf(model, lightorigin);
308         if (pvsleaf == NULL)
309                 return;
310
311         in = pvsleaf->compressed_vis;
312         if (!r_vismarklights.integer || !in)
313         {
314                 // told not to use pvs, or there's no pvs to use
315                 R_OldMarkLights(ent, lightorigin, rd, bit, bitindex, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
316                 return;
317         }
318
319         lightframe++;
320
321         low[0] = lightorigin[0] - rd->cullradius;low[1] = lightorigin[1] - rd->cullradius;low[2] = lightorigin[2] - rd->cullradius;
322         high[0] = lightorigin[0] + rd->cullradius;high[1] = lightorigin[1] + rd->cullradius;high[2] = lightorigin[2] + rd->cullradius;
323
324         // for comparisons to minimum acceptable light
325         maxdist = rd->cullradius2;
326
327         row = (model->brushq1.numleafs+7)>>3;
328         surfacepvsframes = model->brushq1.surfacepvsframes;
329
330         k = 0;
331         while (k < row)
332         {
333                 c = *in++;
334                 if (c)
335                 {
336                         for (i = 0;i < 8;i++)
337                         {
338                                 if (c & (1<<i))
339                                 {
340                                         // warning to the clumsy: numleafs is one less than it should be, it only counts leafs with vis bits (skips leaf 0)
341                                         leafnum = (k << 3)+i+1;
342                                         if (leafnum > model->brushq1.numleafs)
343                                                 return;
344                                         leaf = &model->brushq1.leafs[leafnum];
345                                         if (leaf->mins[0] > high[0] || leaf->maxs[0] < low[0]
346                                          || leaf->mins[1] > high[1] || leaf->maxs[1] < low[1]
347                                          || leaf->mins[2] > high[2] || leaf->maxs[2] < low[2])
348                                                 continue;
349                                         if ((m = leaf->nummarksurfaces))
350                                         {
351                                                 mark = leaf->firstmarksurface;
352                                                 do
353                                                 {
354                                                         surf = model->brushq1.surfaces + *mark++;
355                                                         // if not visible in current frame, or already marked because it was in another leaf we passed, skip
356                                                         if (surf->lightframe == lightframe)
357                                                                 continue;
358                                                         surf->lightframe = lightframe;
359                                                         if (surfacepvsframes[surf->number] != model->brushq1.pvsframecount)
360                                                                 continue;
361                                                         dist = PlaneDiff(lightorigin, surf->plane);
362                                                         if (surf->flags & SURF_PLANEBACK)
363                                                                 dist = -dist;
364                                                         // LordHavoc: make sure it is infront of the surface and not too far away
365                                                         if (dist < rd->cullradius && (dist > -0.25f || ((surf->flags & SURF_LIGHTBOTHSIDES) && dist > -rd->cullradius)))
366                                                         {
367                                                                 int d;
368                                                                 int impacts, impactt;
369                                                                 float dist2, impact[3];
370
371                                                                 dist2 = dist * dist;
372
373                                                                 if (surf->plane->type < 3)
374                                                                 {
375                                                                         VectorCopy(lightorigin, impact);
376                                                                         impact[surf->plane->type] -= dist;
377                                                                 }
378                                                                 else
379                                                                 {
380                                                                         impact[0] = lightorigin[0] - surf->plane->normal[0] * dist;
381                                                                         impact[1] = lightorigin[1] - surf->plane->normal[1] * dist;
382                                                                         impact[2] = lightorigin[2] - surf->plane->normal[2] * dist;
383                                                                 }
384
385                                                                 impacts = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
386                                                                 d = bound(0, impacts, surf->extents[0] + 16) - impacts;
387                                                                 dist2 += d * d;
388                                                                 if (dist2 > maxdist)
389                                                                         continue;
390
391                                                                 impactt = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
392                                                                 d = bound(0, impactt, surf->extents[1] + 16) - impactt;
393                                                                 dist2 += d * d;
394                                                                 if (dist2 > maxdist)
395                                                                         continue;
396
397                                                                 if (surf->dlightframe != r_framecount) // not dynamic until now
398                                                                 {
399                                                                         surf->dlightbits[0] = surf->dlightbits[1] = surf->dlightbits[2] = surf->dlightbits[3] = surf->dlightbits[4] = surf->dlightbits[5] = surf->dlightbits[6] = surf->dlightbits[7] = 0;
400                                                                         surf->dlightframe = r_framecount;
401                                                                         if (r_dlightmap.integer)
402                                                                                 surf->cached_dlight = true;
403                                                                 }
404                                                                 surf->dlightbits[bitindex] |= bit;
405                                                         }
406                                                 }
407                                                 while (--m);
408                                         }
409                                 }
410                         }
411                         k++;
412                         continue;
413                 }
414
415                 k += *in++;
416         }
417 }
418
419 void R_MarkLights(entity_render_t *ent)
420 {
421         int i;
422         if (!gl_flashblend.integer)
423                 for (i = 0;i < r_numdlights;i++)
424                         R_VisMarkLights (ent, r_dlight + i, 1 << (i & 31), i >> 5);
425 }
426
427 /*
428 =============================================================================
429
430 LIGHT SAMPLING
431
432 =============================================================================
433 */
434
435 static int RecursiveLightPoint (vec3_t color, const mnode_t *node, float x, float y, float startz, float endz)
436 {
437         int side, distz = endz - startz;
438         float front, back;
439         float mid;
440
441 loc0:
442         if (node->contents < 0)
443                 return false;           // didn't hit anything
444
445         switch (node->plane->type)
446         {
447         case PLANE_X:
448                 node = node->children[x < node->plane->dist];
449                 goto loc0;
450         case PLANE_Y:
451                 node = node->children[y < node->plane->dist];
452                 goto loc0;
453         case PLANE_Z:
454                 side = startz < node->plane->dist;
455                 if ((endz < node->plane->dist) == side)
456                 {
457                         node = node->children[side];
458                         goto loc0;
459                 }
460                 // found an intersection
461                 mid = node->plane->dist;
462                 break;
463         default:
464                 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
465                 front += startz * node->plane->normal[2];
466                 back += endz * node->plane->normal[2];
467                 side = front < node->plane->dist;
468                 if ((back < node->plane->dist) == side)
469                 {
470                         node = node->children[side];
471                         goto loc0;
472                 }
473                 // found an intersection
474                 mid = startz + distz * (front - node->plane->dist) / (front - back);
475                 break;
476         }
477
478         // go down front side
479         if (node->children[side]->contents >= 0 && RecursiveLightPoint (color, node->children[side], x, y, startz, mid))
480                 return true;    // hit something
481         else
482         {
483                 // check for impact on this node
484                 if (node->numsurfaces)
485                 {
486                         int i, ds, dt;
487                         msurface_t *surf;
488
489                         surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
490                         for (i = 0;i < node->numsurfaces;i++, surf++)
491                         {
492                                 if (!(surf->flags & SURF_LIGHTMAP))
493                                         continue;       // no lightmaps
494
495                                 ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
496                                 dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
497
498                                 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
499                                         continue;
500
501                                 ds -= surf->texturemins[0];
502                                 dt -= surf->texturemins[1];
503
504                                 if (ds > surf->extents[0] || dt > surf->extents[1])
505                                         continue;
506
507                                 if (surf->samples)
508                                 {
509                                         qbyte *lightmap;
510                                         int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
511                                         line3 = ((surf->extents[0]>>4)+1)*3;
512                                         size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
513
514                                         lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
515
516                                         for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
517                                         {
518                                                 scale = d_lightstylevalue[surf->styles[maps]];
519                                                 r00 += lightmap[      0] * scale;g00 += lightmap[      1] * scale;b00 += lightmap[      2] * scale;
520                                                 r01 += lightmap[      3] * scale;g01 += lightmap[      4] * scale;b01 += lightmap[      5] * scale;
521                                                 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
522                                                 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
523                                                 lightmap += size3;
524                                         }
525
526 /*
527 LordHavoc: here's the readable version of the interpolation
528 code, not quite as easy for the compiler to optimize...
529
530 dsfrac is the X position in the lightmap pixel, * 16
531 dtfrac is the Y position in the lightmap pixel, * 16
532 r00 is top left corner, r01 is top right corner
533 r10 is bottom left corner, r11 is bottom right corner
534 g and b are the same layout.
535 r0 and r1 are the top and bottom intermediate results
536
537 first we interpolate the top two points, to get the top
538 edge sample
539
540         r0 = (((r01-r00) * dsfrac) >> 4) + r00;
541         g0 = (((g01-g00) * dsfrac) >> 4) + g00;
542         b0 = (((b01-b00) * dsfrac) >> 4) + b00;
543
544 then we interpolate the bottom two points, to get the
545 bottom edge sample
546
547         r1 = (((r11-r10) * dsfrac) >> 4) + r10;
548         g1 = (((g11-g10) * dsfrac) >> 4) + g10;
549         b1 = (((b11-b10) * dsfrac) >> 4) + b10;
550
551 then we interpolate the top and bottom samples to get the
552 middle sample (the one which was requested)
553
554         r = (((r1-r0) * dtfrac) >> 4) + r0;
555         g = (((g1-g0) * dtfrac) >> 4) + g0;
556         b = (((b1-b0) * dtfrac) >> 4) + b0;
557 */
558
559                                         color[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
560                                         color[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
561                                         color[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
562                                 }
563                                 return true; // success
564                         }
565                 }
566
567                 // go down back side
568                 node = node->children[side ^ 1];
569                 startz = mid;
570                 distz = endz - startz;
571                 goto loc0;
572         }
573 }
574
575 void R_CompleteLightPoint (vec3_t color, const vec3_t p, int dynamic, const mleaf_t *leaf)
576 {
577         int i;
578         vec3_t v;
579         float f;
580         rdlight_t *rd;
581         mlight_t *sl;
582         if (leaf == NULL && cl.worldmodel != NULL)
583                 leaf = cl.worldmodel->brushq1.PointInLeaf(cl.worldmodel, p);
584         if (!leaf || leaf->contents == CONTENTS_SOLID || r_fullbright.integer || !cl.worldmodel->brushq1.lightdata)
585         {
586                 color[0] = color[1] = color[2] = 1;
587                 return;
588         }
589
590         color[0] = color[1] = color[2] = r_ambient.value * (2.0f / 128.0f);
591         if (cl.worldmodel->brushq1.numlights)
592         {
593                 for (i = 0;i < cl.worldmodel->brushq1.numlights;i++)
594                 {
595                         sl = cl.worldmodel->brushq1.lights + i;
596                         if (d_lightstylevalue[sl->style] > 0)
597                         {
598                                 VectorSubtract (p, sl->origin, v);
599                                 f = ((1.0f / (DotProduct(v, v) * sl->falloff + sl->distbias)) - sl->subtract);
600                                 if (f > 0 && CL_TraceLine(p, sl->origin, NULL, NULL, 0, false, NULL) == 1)
601                                 {
602                                         f *= d_lightstylevalue[sl->style] * (1.0f / 65536.0f);
603                                         VectorMA(color, f, sl->light, color);
604                                 }
605                         }
606                 }
607         }
608         else
609                 RecursiveLightPoint (color, cl.worldmodel->brushq1.nodes, p[0], p[1], p[2], p[2] - 65536);
610
611         if (dynamic)
612         {
613                 for (i = 0;i < r_numdlights;i++)
614                 {
615                         rd = r_dlight + i;
616                         VectorSubtract (p, rd->origin, v);
617                         f = DotProduct(v, v);
618                         if (f < rd->cullradius2 && CL_TraceLine(p, rd->origin, NULL, NULL, 0, false, NULL) == 1)
619                         {
620                                 f = (1.0f / (f + LIGHTOFFSET)) - rd->subtract;
621                                 VectorMA(color, f, rd->light, color);
622                         }
623                 }
624         }
625 }
626
627 typedef struct
628 {
629         vec3_t origin;
630         //vec_t cullradius2;
631         vec3_t light;
632         // how much this light would contribute to ambient if replaced
633         vec3_t ambientlight;
634         vec_t subtract;
635         vec_t falloff;
636         vec_t offset;
637         // used for choosing only the brightest lights
638         vec_t intensity;
639 }
640 nearlight_t;
641
642 static int nearlights;
643 static nearlight_t nearlight[MAX_DLIGHTS];
644
645 int R_LightModel(float *ambient4f, const entity_render_t *ent, float colorr, float colorg, float colorb, float colora, int worldcoords)
646 {
647         int i, j, maxnearlights;
648         float v[3], f, mscale, stylescale, intensity, ambientcolor[3];
649         nearlight_t *nl;
650         mlight_t *sl;
651         rdlight_t *rd;
652         mleaf_t *leaf;
653
654         nearlights = 0;
655         maxnearlights = r_modellights.integer;
656         if (r_fullbright.integer || (ent->effects & EF_FULLBRIGHT))
657         {
658                 // highly rare
659                 ambient4f[0] = colorr;
660                 ambient4f[1] = colorg;
661                 ambient4f[2] = colorb;
662                 ambient4f[3] = colora;
663                 return false;
664         }
665         if (r_shadow_realtime_world.integer)
666         {
667                 // user config choice
668                 ambient4f[0] = r_ambient.value * (2.0f / 128.0f) * colorr;
669                 ambient4f[1] = r_ambient.value * (2.0f / 128.0f) * colorg;
670                 ambient4f[2] = r_ambient.value * (2.0f / 128.0f) * colorb;
671                 ambient4f[3] = colora;
672                 return false;
673         }
674         if (maxnearlights == 0)
675         {
676                 // user config choice
677                 R_CompleteLightPoint (ambient4f, ent->origin, true, NULL);
678                 ambient4f[0] *= colorr;
679                 ambient4f[1] *= colorg;
680                 ambient4f[2] *= colorb;
681                 ambient4f[3] = colora;
682                 return false;
683         }
684         leaf = cl.worldmodel ? cl.worldmodel->brushq1.PointInLeaf(cl.worldmodel, ent->origin) : NULL;
685         if (!leaf || leaf->contents == CONTENTS_SOLID || !cl.worldmodel->brushq1.lightdata)
686                 ambient4f[0] = ambient4f[1] = ambient4f[2] = 1;
687         else
688         {
689                 ambient4f[0] = ambient4f[1] = ambient4f[2] = r_ambient.value * (2.0f / 128.0f);
690                 if (!cl.worldmodel->brushq1.numlights)
691                         RecursiveLightPoint (ambient4f, cl.worldmodel->brushq1.nodes, ent->origin[0], ent->origin[1], ent->origin[2], ent->origin[2] - 65536);
692         }
693         // scale of the model's coordinate space, to alter light attenuation to match
694         // make the mscale squared so it can scale the squared distance results
695         mscale = ent->scale * ent->scale;
696         nl = &nearlight[0];
697         for (i = 0;i < ent->numentlights;i++)
698         {
699                 sl = cl.worldmodel->brushq1.lights + ent->entlights[i];
700                 stylescale = d_lightstylevalue[sl->style] * (1.0f / 65536.0f);
701                 VectorSubtract (ent->origin, sl->origin, v);
702                 f = ((1.0f / (DotProduct(v, v) * sl->falloff + sl->distbias)) - sl->subtract) * stylescale;
703                 VectorScale(sl->light, f, ambientcolor);
704                 intensity = DotProduct(ambientcolor, ambientcolor);
705                 if (f < 0)
706                         intensity *= -1.0f;
707                 if (nearlights < maxnearlights)
708                         j = nearlights++;
709                 else
710                 {
711                         for (j = 0;j < maxnearlights;j++)
712                         {
713                                 if (nearlight[j].intensity < intensity)
714                                 {
715                                         if (nearlight[j].intensity > 0)
716                                                 VectorAdd(ambient4f, nearlight[j].ambientlight, ambient4f);
717                                         break;
718                                 }
719                         }
720                 }
721                 if (j >= maxnearlights)
722                 {
723                         // this light is less significant than all others,
724                         // add it to ambient
725                         if (intensity > 0)
726                                 VectorAdd(ambient4f, ambientcolor, ambient4f);
727                 }
728                 else
729                 {
730                         nl = nearlight + j;
731                         nl->intensity = intensity;
732                         // transform the light into the model's coordinate system
733                         if (worldcoords)
734                                 VectorCopy(sl->origin, nl->origin);
735                         else
736                                 Matrix4x4_Transform(&ent->inversematrix, sl->origin, nl->origin);
737                         // integrate mscale into falloff, for maximum speed
738                         nl->falloff = sl->falloff * mscale;
739                         VectorCopy(ambientcolor, nl->ambientlight);
740                         nl->light[0] = sl->light[0] * stylescale * colorr * 4.0f;
741                         nl->light[1] = sl->light[1] * stylescale * colorg * 4.0f;
742                         nl->light[2] = sl->light[2] * stylescale * colorb * 4.0f;
743                         nl->subtract = sl->subtract;
744                         nl->offset = sl->distbias;
745                 }
746         }
747         if (!r_shadow_realtime_dlight.integer)
748         {
749                 for (i = 0;i < r_numdlights;i++)
750                 {
751                         rd = r_dlight + i;
752                         VectorCopy(rd->origin, v);
753                         if (v[0] < ent->mins[0]) v[0] = ent->mins[0];if (v[0] > ent->maxs[0]) v[0] = ent->maxs[0];
754                         if (v[1] < ent->mins[1]) v[1] = ent->mins[1];if (v[1] > ent->maxs[1]) v[1] = ent->maxs[1];
755                         if (v[2] < ent->mins[2]) v[2] = ent->mins[2];if (v[2] > ent->maxs[2]) v[2] = ent->maxs[2];
756                         VectorSubtract (v, rd->origin, v);
757                         if (DotProduct(v, v) < rd->cullradius2)
758                         {
759                                 if (CL_TraceLine(ent->origin, rd->origin, NULL, NULL, 0, false, NULL) != 1)
760                                         continue;
761                                 VectorSubtract (ent->origin, rd->origin, v);
762                                 f = ((1.0f / (DotProduct(v, v) + LIGHTOFFSET)) - rd->subtract);
763                                 VectorScale(rd->light, f, ambientcolor);
764                                 intensity = DotProduct(ambientcolor, ambientcolor);
765                                 if (f < 0)
766                                         intensity *= -1.0f;
767                                 if (nearlights < maxnearlights)
768                                         j = nearlights++;
769                                 else
770                                 {
771                                         for (j = 0;j < maxnearlights;j++)
772                                         {
773                                                 if (nearlight[j].intensity < intensity)
774                                                 {
775                                                         if (nearlight[j].intensity > 0)
776                                                                 VectorAdd(ambient4f, nearlight[j].ambientlight, ambient4f);
777                                                         break;
778                                                 }
779                                         }
780                                 }
781                                 if (j >= maxnearlights)
782                                 {
783                                         // this light is less significant than all others,
784                                         // add it to ambient
785                                         if (intensity > 0)
786                                                 VectorAdd(ambient4f, ambientcolor, ambient4f);
787                                 }
788                                 else
789                                 {
790                                         nl = nearlight + j;
791                                         nl->intensity = intensity;
792                                         // transform the light into the model's coordinate system
793                                         if (worldcoords)
794                                                 VectorCopy(rd->origin, nl->origin);
795                                         else
796                                         {
797                                                 Matrix4x4_Transform(&ent->inversematrix, rd->origin, nl->origin);
798                                                 /*
799                                                 Con_Printf("%i %s : %f %f %f : %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n"
800                                                 , rd - r_dlight, ent->model->name
801                                                 , rd->origin[0], rd->origin[1], rd->origin[2]
802                                                 , nl->origin[0], nl->origin[1], nl->origin[2]
803                                                 , ent->inversematrix.m[0][0], ent->inversematrix.m[0][1], ent->inversematrix.m[0][2], ent->inversematrix.m[0][3]
804                                                 , ent->inversematrix.m[1][0], ent->inversematrix.m[1][1], ent->inversematrix.m[1][2], ent->inversematrix.m[1][3]
805                                                 , ent->inversematrix.m[2][0], ent->inversematrix.m[2][1], ent->inversematrix.m[2][2], ent->inversematrix.m[2][3]
806                                                 , ent->inversematrix.m[3][0], ent->inversematrix.m[3][1], ent->inversematrix.m[3][2], ent->inversematrix.m[3][3]);
807                                                 */
808                                         }
809                                         // integrate mscale into falloff, for maximum speed
810                                         nl->falloff = mscale;
811                                         VectorCopy(ambientcolor, nl->ambientlight);
812                                         nl->light[0] = rd->light[0] * colorr * 4.0f;
813                                         nl->light[1] = rd->light[1] * colorg * 4.0f;
814                                         nl->light[2] = rd->light[2] * colorb * 4.0f;
815                                         nl->subtract = rd->subtract;
816                                         nl->offset = LIGHTOFFSET;
817                                 }
818                         }
819                 }
820         }
821         ambient4f[0] *= colorr;
822         ambient4f[1] *= colorg;
823         ambient4f[2] *= colorb;
824         ambient4f[3] = colora;
825         return nearlights != 0;
826 }
827
828 void R_LightModel_CalcVertexColors(const float *ambientcolor4f, int numverts, const float *vertex3f, const float *normal3f, float *color4f)
829 {
830         int i, j;
831         float color[4], v[3], dot, dist2, f;
832         nearlight_t *nl;
833         // directional shading code here
834         for (i = 0;i < numverts;i++, vertex3f += 3, normal3f += 3, color4f += 4)
835         {
836                 VectorCopy4(ambientcolor4f, color);
837                 for (j = 0, nl = &nearlight[0];j < nearlights;j++, nl++)
838                 {
839                         VectorSubtract(vertex3f, nl->origin, v);
840                         // first eliminate negative lighting (back side)
841                         dot = DotProduct(normal3f, v);
842                         if (dot > 0)
843                         {
844                                 // we'll need this again later to normalize the dotproduct
845                                 dist2 = DotProduct(v,v);
846                                 // do the distance attenuation math
847                                 f = (1.0f / (dist2 * nl->falloff + nl->offset)) - nl->subtract;
848                                 if (f > 0)
849                                 {
850                                         // we must divide dot by sqrt(dist2) to compensate for
851                                         // the fact we did not normalize v before doing the
852                                         // dotproduct, the result is in the range 0 to 1 (we
853                                         // eliminated negative numbers already)
854                                         f *= dot / sqrt(dist2);
855                                         // blend in the lighting
856                                         VectorMA(color, f, nl->light, color);
857                                 }
858                         }
859                 }
860                 VectorCopy4(color, color4f);
861         }
862 }
863
864 void R_UpdateEntLights(entity_render_t *ent)
865 {
866         int i;
867         const mlight_t *sl;
868         vec3_t v;
869         if (r_shadow_realtime_dlight.integer)
870                 return;
871         VectorSubtract(ent->origin, ent->entlightsorigin, v);
872         if (ent->entlightsframe != (r_framecount - 1) || (realtime > ent->entlightstime && DotProduct(v,v) >= 1.0f))
873         {
874                 ent->entlightstime = realtime + 0.1;
875                 VectorCopy(ent->origin, ent->entlightsorigin);
876                 ent->numentlights = 0;
877                 if (cl.worldmodel)
878                         for (i = 0, sl = cl.worldmodel->brushq1.lights;i < cl.worldmodel->brushq1.numlights && ent->numentlights < MAXENTLIGHTS;i++, sl++)
879                                 if (CL_TraceLine(ent->origin, sl->origin, NULL, NULL, 0, false, NULL) == 1)
880                                         ent->entlights[ent->numentlights++] = i;
881         }
882         ent->entlightsframe = r_framecount;
883 }