1 /*
2 Copyright (C) 1997-2001 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 //
21 // rf_light.c
22 // Dynamic lights
23 // Lightmaps
24 // Alias model lighting
25 //
26
27 #include "rf_local.h"
28
29 /*
30 =============================================================================
31
32 QUAKE II DYNAMIC LIGHTS
33
34 =============================================================================
35 */
36
37 // The lightmap texture data needs to be kept in
38 // main memory so texsubimage can update properly
39 static float r_q2_blockLights[34*34*3];
40
41 static vec3_t r_q2_pointColor;
42 static vec3_t r_q2_lightSpot;
43
44 /*
45 =============
46 R_Q2BSP_MarkWorldLights
47 =============
48 */
R_Q2BSP_r_MarkWorldLights(mBspNode_t * node,refDLight_t * lt,uint32 bit)49 static void R_Q2BSP_r_MarkWorldLights (mBspNode_t *node, refDLight_t *lt, uint32 bit)
50 {
51 mBspSurface_t **mark, *surf;
52 float dist;
53
54 loc0:
55 if (node->c.q2_contents != -1)
56 return;
57 if (node->c.visFrame != ri.scn.visFrameCount)
58 return;
59
60 dist = PlaneDiff (lt->origin, node->c.plane);
61 if (dist > lt->intensity) {
62 node = node->children[0];
63 goto loc0;
64 }
65 if (dist < -lt->intensity) {
66 node = node->children[1];
67 goto loc0;
68 }
69 if (!BoundsIntersect (node->c.mins, node->c.maxs, lt->mins, lt->maxs))
70 return;
71
72 // Mark the polygons
73 if (node->q2_firstLitSurface) {
74 mark = node->q2_firstLitSurface;
75 do {
76 surf = *mark++;
77 if (!BoundsIntersect (surf->mins, surf->maxs, lt->mins, lt->maxs))
78 continue;
79
80 if (surf->dLightFrame != ri.frameCount) {
81 surf->dLightFrame = ri.frameCount;
82 surf->dLightBits = 0;
83 }
84 surf->dLightBits |= bit;
85 } while (*mark);
86 }
87
88 R_Q2BSP_r_MarkWorldLights (node->children[0], lt, bit);
89 R_Q2BSP_r_MarkWorldLights (node->children[1], lt, bit);
90 }
R_Q2BSP_MarkWorldLights(void)91 void R_Q2BSP_MarkWorldLights (void)
92 {
93 refDLight_t *lt;
94 uint32 i;
95
96 if (gl_flashblend->intVal)
97 return;
98
99 for (lt=ri.scn.dLightList, i=0 ; i<ri.scn.numDLights ; i++, lt++)
100 R_Q2BSP_r_MarkWorldLights (ri.scn.worldModel->bspModel.nodes, lt, 1<<i);
101 }
102
103
104 /*
105 =============
106 R_Q2BSP_MarkBModelLights
107 =============
108 */
R_Q2BSP_r_MarkBModelLights(mBspNode_t * node,refDLight_t * lt,uint32 bit)109 static void R_Q2BSP_r_MarkBModelLights (mBspNode_t *node, refDLight_t *lt, uint32 bit)
110 {
111 mBspSurface_t **mark, *surf;
112 float dist;
113
114 loc0:
115 if (node->c.q2_contents != -1)
116 return;
117
118 dist = PlaneDiff (lt->origin, node->c.plane);
119 if (dist > lt->intensity) {
120 node = node->children[0];
121 goto loc0;
122 }
123 if (dist < -lt->intensity) {
124 node = node->children[1];
125 goto loc0;
126 }
127 if (!BoundsIntersect (node->c.mins, node->c.maxs, lt->mins, lt->maxs))
128 return;
129
130 // Mark the polygons
131 if (node->q2_firstLitSurface) {
132 mark = node->q2_firstLitSurface;
133 do {
134 surf = *mark++;
135 if (!BoundsIntersect (surf->mins, surf->maxs, lt->mins, lt->maxs))
136 continue;
137
138 if (surf->dLightFrame != ri.frameCount) {
139 surf->dLightFrame = ri.frameCount;
140 surf->dLightBits = 0;
141 }
142 surf->dLightBits |= bit;
143 } while (*mark);
144 }
145
146 R_Q2BSP_r_MarkBModelLights (node->children[0], lt, bit);
147 R_Q2BSP_r_MarkBModelLights (node->children[1], lt, bit);
148 }
R_Q2BSP_MarkBModelLights(refEntity_t * ent,vec3_t mins,vec3_t maxs)149 void R_Q2BSP_MarkBModelLights (refEntity_t *ent, vec3_t mins, vec3_t maxs)
150 {
151 refDLight_t *lt;
152 refModel_t *model = ent->model;
153 mBspNode_t *node;
154 uint32 i;
155
156 if (!ri.scn.numDLights || gl_flashblend->intVal || !gl_dynamic->intVal || r_fullbright->intVal)
157 return;
158
159 node = ent->model->bspModel.nodes + ent->model->q2BspModel.firstNode;
160 for (i=0, lt=ri.scn.dLightList ; i<ri.scn.numDLights ; lt++, i++) {
161 if (!BoundsIntersect (mins, maxs, lt->mins, lt->maxs))
162 continue;
163
164 R_Q2BSP_r_MarkBModelLights (node, lt, 1<<i);
165 }
166 }
167
168 /*
169 =============================================================================
170
171 QUAKE II LIGHT SAMPLING
172
173 =============================================================================
174 */
175
176 /*
177 ===============
178 R_Q2BSP_RecursiveLightPoint
179 ===============
180 */
Q2BSP_RecursiveLightPoint(mBspNode_t * node,vec3_t start,vec3_t end)181 static int Q2BSP_RecursiveLightPoint (mBspNode_t *node, vec3_t start, vec3_t end)
182 {
183 float front, back, frac;
184 int i, s, t, ds, dt, r;
185 int side, map;
186 cBspPlane_t *plane;
187 vec3_t mid;
188 mBspSurface_t **mark, *surf;
189 byte *lightmap;
190
191 // Didn't hit anything
192 if (node->c.q2_contents != -1)
193 return -1;
194
195 // Calculate mid point
196 plane = node->c.plane;
197 if (plane->type < 3) {
198 front = start[plane->type] - plane->dist;
199 back = end[plane->type] - plane->dist;
200 }
201 else {
202 front = DotProduct (start, plane->normal) - plane->dist;
203 back = DotProduct (end, plane->normal) - plane->dist;
204 }
205
206 side = front < 0;
207 if ((back < 0) == side)
208 return Q2BSP_RecursiveLightPoint (node->children[side], start, end);
209
210 frac = front / (front - back);
211 mid[0] = start[0] + (end[0] - start[0]) * frac;
212 mid[1] = start[1] + (end[1] - start[1]) * frac;
213 mid[2] = start[2] + (end[2] - start[2]) * frac;
214
215 // Go down front side
216 r = Q2BSP_RecursiveLightPoint (node->children[side], start, mid);
217 if (r >= 0)
218 return r; // Hit something
219
220 if ((back < 0) == side)
221 return -1; // Didn't hit anything
222
223 // Check for impact on this node
224 Vec3Copy (mid, r_q2_lightSpot);
225
226 if (node->q2_firstLitSurface) {
227 mark = node->q2_firstLitSurface;
228 do {
229 surf = *mark++;
230
231 s = Q_ftol (DotProduct (mid, surf->q2_texInfo->vecs[0]) + surf->q2_texInfo->vecs[0][3]);
232 t = Q_ftol (DotProduct (mid, surf->q2_texInfo->vecs[1]) + surf->q2_texInfo->vecs[1][3]);
233 if (s < surf->q2_textureMins[0] || t < surf->q2_textureMins[1])
234 continue;
235
236 ds = s - surf->q2_textureMins[0];
237 dt = t - surf->q2_textureMins[1];
238 if (ds > surf->q2_extents[0] || dt > surf->q2_extents[1])
239 continue;
240
241 ds >>= 4;
242 dt >>= 4;
243
244 lightmap = surf->q2_lmSamples;
245 Vec3Clear (r_q2_pointColor);
246 if (lightmap) {
247 vec3_t scale;
248
249 lightmap += 3 * (dt*surf->q2_lmWidth + ds);
250
251 for (map=0 ; map<surf->q2_numStyles ; map++) {
252 Vec3Scale (ri.scn.lightStyles[surf->q2_styles[map]].rgb, gl_modulate->floatVal, scale);
253 for (i=0 ; i<3 ; i++)
254 r_q2_pointColor[i] += lightmap[i] * scale[i] * (1.0f/255.0f);
255
256 lightmap += 3*surf->q2_lmWidth*surf->q2_lmWidth;
257 }
258 }
259
260 return 1;
261 } while (*mark);
262 }
263
264 // Go down back side
265 return Q2BSP_RecursiveLightPoint (node->children[!side], mid, end);
266 }
R_Q2BSP_RecursiveLightPoint(vec3_t point,vec3_t end)267 static qBool R_Q2BSP_RecursiveLightPoint (vec3_t point, vec3_t end)
268 {
269 int r;
270
271 if (ri.def.rdFlags & RDF_NOWORLDMODEL || !ri.scn.worldModel->q2BspModel.lightData) {
272 Vec3Set (r_q2_pointColor, 1, 1, 1);
273 return qFalse;
274 }
275
276 r = Q2BSP_RecursiveLightPoint (ri.scn.worldModel->bspModel.nodes, point, end);
277
278 if (r == -1) {
279 Vec3Clear (r_q2_pointColor);
280 return qFalse;
281 }
282
283 return qTrue;
284 }
285
286
287 /*
288 ===============
289 R_Q2BSP_ShadowForEntity
290 ===============
291 */
R_Q2BSP_ShadowForEntity(refEntity_t * ent,vec3_t shadowSpot)292 static qBool R_Q2BSP_ShadowForEntity (refEntity_t *ent, vec3_t shadowSpot)
293 {
294 vec3_t end;
295
296 Vec3Set (end, ent->origin[0], ent->origin[1], ent->origin[2] - 2048);
297
298 if (R_Q2BSP_RecursiveLightPoint (ent->origin, end)) {
299 // Found!
300 Vec3Copy (r_q2_lightSpot, shadowSpot);
301 return qTrue;
302 }
303
304 // Not found!
305 Vec3Clear (shadowSpot);
306 return qFalse;
307
308 }
309
310
311 /*
312 ===============
313 R_Q2BSP_LightForEntity
314 ===============
315 */
R_Q2BSP_LightForEntity(refEntity_t * ent,int numVerts,byte * bArray)316 static void R_Q2BSP_LightForEntity (refEntity_t *ent, int numVerts, byte *bArray)
317 {
318 static vec3_t tempColorsArray[RB_MAX_VERTS];
319 float *cArray;
320 vec3_t end;
321 vec3_t ambientLight;
322 vec3_t directedLight;
323 int r, g, b, i;
324 vec3_t dir, direction;
325 float dot;
326
327 if (!(ent->flags & RF_WEAPONMODEL) && (r_fullbright->intVal || ent->flags & RF_FULLBRIGHT))
328 goto fullBright;
329
330 //
331 // Get the lighting from below
332 //
333 Vec3Set (end, ent->origin[0], ent->origin[1], ent->origin[2] - 2048);
334 if (!R_Q2BSP_RecursiveLightPoint (ent->origin, end)) {
335 end[2] = ent->origin[2] + 16;
336 if (!(ent->flags & RF_WEAPONMODEL) && !R_Q2BSP_RecursiveLightPoint (ent->origin, end)) {
337 // Not found!
338 Vec3Copy (r_q2_pointColor, ambientLight);
339 Vec3Copy (r_q2_pointColor, directedLight);
340 }
341 else {
342 // Found!
343 Vec3Copy (r_q2_pointColor, directedLight);
344 Vec3Scale (r_q2_pointColor, 0.6f, ambientLight);
345 }
346 }
347 else {
348 // Found!
349 Vec3Copy (r_q2_pointColor, directedLight);
350 Vec3Scale (r_q2_pointColor, 0.6f, ambientLight);
351 }
352
353 // Save off light value for server to look at (BIG HACK!)
354 if (ent->flags & RF_WEAPONMODEL) {
355 // Pick the greatest component, which should be
356 // the same as the mono value returned by software
357 if (r_q2_pointColor[0] > r_q2_pointColor[1]) {
358 if (r_q2_pointColor[0] > r_q2_pointColor[2])
359 Cvar_VariableSetValue (r_lightlevel, 150 * r_q2_pointColor[0], qTrue);
360 else
361 Cvar_VariableSetValue (r_lightlevel, 150 * r_q2_pointColor[2], qTrue);
362 }
363 else {
364 if (r_q2_pointColor[1] > r_q2_pointColor[2])
365 Cvar_VariableSetValue (r_lightlevel, 150 * r_q2_pointColor[1], qTrue);
366 else
367 Cvar_VariableSetValue (r_lightlevel, 150 * r_q2_pointColor[2], qTrue);
368 }
369
370 }
371
372 // Fullbright entity
373 if (r_fullbright->intVal || ent->flags & RF_FULLBRIGHT) {
374 fullBright:
375 for (i=0 ; i<numVerts ; i++, bArray+=4)
376 *(int *)bArray = *(int *)ent->color;
377 return;
378 }
379
380 //
381 // Flag effects
382 //
383 if (ent->flags & RF_MINLIGHT) {
384 for (i=0 ; i<3 ; i++)
385 if (ambientLight[i] > 0.1f)
386 break;
387
388 if (i == 3) {
389 ambientLight[0] += 0.1f;
390 ambientLight[1] += 0.1f;
391 ambientLight[2] += 0.1f;
392 }
393 }
394
395 if (ent->flags & RF_GLOW) {
396 float scale;
397 float min;
398
399 // Bonus items will pulse with time
400 scale = 0.1f * (float)sin (ri.def.time * 7);
401 for (i=0 ; i<3 ; i++) {
402 min = ambientLight[i] * 0.8f;
403 ambientLight[i] += scale;
404 if (ambientLight[i] < min)
405 ambientLight[i] = min;
406 }
407 }
408
409 //
410 // Add ambient lights
411 //
412 Vec3Set (dir, -1, 0, 1);
413 Matrix3_TransformVector (ent->axis, dir, direction);
414
415 for (i=0 ; i<numVerts; i++) {
416 dot = DotProduct (rb.batch.normals[i], direction);
417 if (dot <= 0)
418 Vec3Copy (ambientLight, tempColorsArray[i]);
419 else
420 Vec3MA (ambientLight, dot, directedLight, tempColorsArray[i]);
421 }
422
423 //
424 // Add dynamic lights
425 //
426 if (gl_dynamic->intVal && ri.scn.numDLights) {
427 refDLight_t *lt;
428 float dist, add, intensity8, intensity;
429 vec3_t dlOrigin;
430 uint32 num;
431
432 for (lt=ri.scn.dLightList, num=0 ; num<ri.scn.numDLights ; num++, lt++) {
433 // FIXME: use BoundsIntersect for a performance boost, though this will meen storing bounds in the entity or something...
434 if (!BoundsAndSphereIntersect (lt->mins, lt->maxs, ent->origin, ent->model->radius * ent->scale))
435 continue;
436
437 // Translate
438 Vec3Subtract (lt->origin, ent->origin, dir);
439 dist = Vec3LengthFast (dir);
440
441 if (!dist || dist > lt->intensity + ent->model->radius * ent->scale)
442 continue;
443
444 // Rotate
445 Matrix3_TransformVector (ent->axis, dir, dlOrigin);
446
447 // Calculate intensity
448 intensity = lt->intensity - dist;
449 if (intensity <= 0)
450 continue;
451 intensity8 = lt->intensity * 8;
452
453 for (i=0 ; i<numVerts ; i++) {
454 Vec3Subtract (dlOrigin, rb.batch.vertices[i], dir);
455 add = DotProduct (rb.batch.normals[i], dir);
456
457 // Add some ambience
458 Vec3MA (tempColorsArray[i], intensity * 0.4f * (1.0f/256.0f), lt->color, tempColorsArray[i]);
459
460 // Shade the verts
461 if (add > 0) {
462 dot = DotProduct (dir, dir);
463 add *= (intensity8 / dot) * Q_RSqrtf (dot);
464 if (add > 255.0f)
465 add = 255.0f / add;
466
467 Vec3MA (tempColorsArray[i], add, lt->color, tempColorsArray[i]);
468 }
469 }
470 }
471 }
472
473 //
474 // Clamp
475 //
476 cArray = tempColorsArray[0];
477 for (i=0 ; i<numVerts ; i++, bArray+=4, cArray+=3) {
478 r = Q_ftol (cArray[0] * ent->color[0]);
479 g = Q_ftol (cArray[1] * ent->color[1]);
480 b = Q_ftol (cArray[2] * ent->color[2]);
481
482 bArray[0] = clamp (r, 0, 255);
483 bArray[1] = clamp (g, 0, 255);
484 bArray[2] = clamp (b, 0, 255);
485 }
486 }
487
488
489 /*
490 ===============
491 R_Q2BSP_LightPoint
492 ===============
493 */
R_Q2BSP_LightPoint(vec3_t point,vec3_t light)494 static void R_Q2BSP_LightPoint (vec3_t point, vec3_t light)
495 {
496 vec3_t end;
497 vec3_t dist;
498 refDLight_t *lt;
499 float add;
500 uint32 num;
501
502 Vec3Set (end, point[0], point[1], point[2] - 2048);
503 if (!R_Q2BSP_RecursiveLightPoint (point, end)) {
504 end[2] = point[2] + 16;
505 R_Q2BSP_RecursiveLightPoint (point, end);
506 }
507 Vec3Copy (r_q2_pointColor, light);
508
509 //
510 // Add dynamic lights
511 //
512 for (lt=ri.scn.dLightList, num=0 ; num<ri.scn.numDLights ; num++, lt++) {
513 Vec3Subtract (point, lt->origin, dist);
514 add = (lt->intensity - Vec3Length (dist)) * (1.0f/256.0f);
515
516 if (add > 0)
517 Vec3MA (light, add, lt->color, light);
518 }
519 }
520
521
522 /*
523 ====================
524 R_Q2BSP_SetLightLevel
525
526 Save off light value for server to look at (BIG HACK!)
527 ====================
528 */
R_Q2BSP_SetLightLevel(void)529 static void R_Q2BSP_SetLightLevel (void)
530 {
531 vec3_t shadelight;
532
533 R_Q2BSP_LightPoint (ri.def.viewOrigin, shadelight);
534
535 // Pick the greatest component, which should be
536 // the same as the mono value returned by software
537 if (shadelight[0] > shadelight[1]) {
538 if (shadelight[0] > shadelight[2])
539 Cvar_VariableSetValue (r_lightlevel, 150 * shadelight[0], qTrue);
540 else
541 Cvar_VariableSetValue (r_lightlevel, 150 * shadelight[2], qTrue);
542 }
543 else {
544 if (shadelight[1] > shadelight[2])
545 Cvar_VariableSetValue (r_lightlevel, 150 * shadelight[1], qTrue);
546 else
547 Cvar_VariableSetValue (r_lightlevel, 150 * shadelight[2], qTrue);
548 }
549
550 }
551
552 /*
553 =============================================================================
554
555 QUAKE II LIGHTMAP
556
557 =============================================================================
558 */
559
560 static byte *r_q2_lmBuffer;
561 static int r_q2_lmNumUploaded;
562 static int *r_q2_lmAllocated;
563 int r_q2_lmSize;
564
565 /*
566 ===============
567 R_Q2BSP_AddDynamicLights
568 ===============
569 */
R_Q2BSP_AddDynamicLights(mBspSurface_t * surf)570 static void R_Q2BSP_AddDynamicLights (mBspSurface_t *surf)
571 {
572 int sd, td, s, t;
573 float fDist, fDist2, fRad;
574 float scale, sl, st;
575 float fsacc, ftacc;
576 float *bl;
577 vec3_t impact;
578 refDLight_t *lt;
579 uint32 num;
580
581 for (num=0, lt=ri.scn.dLightList ; num<ri.scn.numDLights ; num++, lt++) {
582 if (!(surf->dLightBits & (1<<num)))
583 continue; // Not lit by this light
584
585 fDist = PlaneDiff (lt->origin, surf->q2_plane);
586 fRad = lt->intensity - (float)fabs (fDist); // fRad is now the highest intensity on the plane
587 if (fRad < 0)
588 continue;
589
590 impact[0] = lt->origin[0] - (surf->q2_plane->normal[0] * fDist);
591 impact[1] = lt->origin[1] - (surf->q2_plane->normal[1] * fDist);
592 impact[2] = lt->origin[2] - (surf->q2_plane->normal[2] * fDist);
593
594 sl = DotProduct (impact, surf->q2_texInfo->vecs[0]) + surf->q2_texInfo->vecs[0][3] - surf->q2_textureMins[0];
595 st = DotProduct (impact, surf->q2_texInfo->vecs[1]) + surf->q2_texInfo->vecs[1][3] - surf->q2_textureMins[1];
596
597 bl = r_q2_blockLights;
598 for (t=0, ftacc=0 ; t<surf->q2_lmHeight ; t++) {
599 td = Q_ftol (st - ftacc);
600 if (td < 0)
601 td = -td;
602
603 for (s=0, fsacc=0 ; s<surf->q2_lmWidth ; s++) {
604 sd = Q_ftol (sl - fsacc);
605 if (sd < 0)
606 sd = -sd;
607
608 if (sd > td) {
609 fDist = (float)(sd + (td>>1));
610 fDist2 = (float)(sd + (td<<1));
611 }
612 else {
613 fDist = (float)(td + (sd>>1));
614 fDist2 = (float)(td + (sd<<1));
615 }
616
617 if (fDist < fRad) {
618 scale = fRad - fDist;
619
620 bl[0] += lt->color[0] * scale;
621 bl[1] += lt->color[1] * scale;
622 bl[2] += lt->color[2] * scale;
623
624 // Amplify the center a little
625 if (fDist2 < fRad) {
626 scale = fRad - fDist2;
627 bl[0] += lt->color[0] * scale * 0.5f;
628 bl[1] += lt->color[1] * scale * 0.5f;
629 bl[2] += lt->color[2] * scale * 0.5f;
630 }
631 }
632
633 fsacc += 16;
634 bl += 3;
635 }
636
637 ftacc += 16;
638 }
639 }
640 }
641
642
643 /*
644 ===============
645 R_Q2BSP_BuildLightMap
646
647 Combine and scale multiple lightmaps into the floating format in blocklights
648 ===============
649 */
R_Q2BSP_BuildLightMap(mBspSurface_t * surf,byte * dest,int stride)650 static void R_Q2BSP_BuildLightMap (mBspSurface_t *surf, byte *dest, int stride)
651 {
652 int i, j, size;
653 int map;
654 float *bl, max;
655 vec3_t scale;
656 byte *lightMap;
657
658 if (surf->q2_texInfo->flags & (SURF_TEXINFO_SKY|SURF_TEXINFO_WARP))
659 Com_Error (ERR_DROP, "LM_BuildLightMap called for non-lit surface");
660
661 size = surf->q2_lmWidth*surf->q2_lmHeight;
662 if (size > sizeof (r_q2_blockLights) >> 4)
663 Com_Error (ERR_DROP, "Bad r_q2_blockLights size");
664
665 // Set to full bright if no light data
666 if (!surf->q2_lmSamples || r_fullbright->intVal) {
667 for (i=0 ; i<size*3 ; i++)
668 r_q2_blockLights[i] = 255.0f;
669 }
670 else {
671 lightMap = surf->q2_lmSamples;
672
673 // Add all the lightmaps
674 if (surf->q2_numStyles == 1) {
675 bl = r_q2_blockLights;
676
677 // Optimal case
678 Vec3Scale (ri.scn.lightStyles[surf->q2_styles[0]].rgb, gl_modulate->floatVal, scale);
679 if (scale[0] == 1.0f && scale[1] == 1.0f && scale[2] == 1.0f) {
680 for (i=0 ; i<size ; i++) {
681 bl[0] = lightMap[i*3+0];
682 bl[1] = lightMap[i*3+1];
683 bl[2] = lightMap[i*3+2];
684
685 bl += 3;
686 }
687 }
688 else {
689 for (i=0 ; i<size ; i++) {
690 bl[0] = lightMap[i*3+0] * scale[0];
691 bl[1] = lightMap[i*3+1] * scale[1];
692 bl[2] = lightMap[i*3+2] * scale[2];
693
694 bl += 3;
695 }
696 }
697
698 // Skip to next lightmap
699 lightMap += size*3;
700 }
701 else {
702 map = 0;
703 bl = r_q2_blockLights;
704 Vec3Scale (ri.scn.lightStyles[surf->q2_styles[map]].rgb, gl_modulate->floatVal, scale);
705 if (scale[0] == 1.0f && scale[1] == 1.0f && scale[2] == 1.0f) {
706 for (i=0 ; i<size ; i++, bl+=3) {
707 bl[0] = lightMap[i*3+0];
708 bl[1] = lightMap[i*3+1];
709 bl[2] = lightMap[i*3+2];
710 }
711 }
712 else {
713 for (i=0 ; i<size ; i++, bl+=3) {
714 bl[0] = lightMap[i*3+0] * scale[0];
715 bl[1] = lightMap[i*3+1] * scale[1];
716 bl[2] = lightMap[i*3+2] * scale[2];
717 }
718 }
719
720 // Skip to next lightmap
721 lightMap += size*3;
722
723 for (map=1 ; map<surf->q2_numStyles ; map++) {
724 bl = r_q2_blockLights;
725
726 Vec3Scale (ri.scn.lightStyles[surf->q2_styles[map]].rgb, gl_modulate->floatVal, scale);
727 if (scale[0] == 1.0f && scale[1] == 1.0f && scale[2] == 1.0f) {
728 for (i=0 ; i<size ; i++, bl+=3) {
729 bl[0] += lightMap[i*3+0];
730 bl[1] += lightMap[i*3+1];
731 bl[2] += lightMap[i*3+2];
732 }
733 }
734 else {
735 for (i=0 ; i<size ; i++, bl+=3) {
736 bl[0] += lightMap[i*3+0] * scale[0];
737 bl[1] += lightMap[i*3+1] * scale[1];
738 bl[2] += lightMap[i*3+2] * scale[2];
739 }
740 }
741
742 // Skip to next lightmap
743 lightMap += size*3;
744 }
745 }
746
747 // Add all the dynamic lights
748 if (surf->dLightFrame == ri.frameCount)
749 R_Q2BSP_AddDynamicLights (surf);
750 }
751
752 // Put into texture format
753 stride -= (surf->q2_lmWidth << 2);
754 bl = r_q2_blockLights;
755
756 for (i=0 ; i<surf->q2_lmHeight ; i++) {
757 for (j=0 ; j<surf->q2_lmWidth ; j++) {
758 // Catch negative lights
759 if (bl[0] < 0)
760 bl[0] = 0;
761 if (bl[1] < 0)
762 bl[1] = 0;
763 if (bl[2] < 0)
764 bl[2] = 0;
765
766 // Determine the brightest of the three color components
767 max = bl[0];
768 if (bl[1] > max)
769 max = bl[1];
770 if (bl[2] > max)
771 max = bl[2];
772
773 // Normalize the color components to the highest channel
774 if (max > 255) {
775 max = 255.0f / max;
776
777 dest[0] = (byte)(bl[0]*max);
778 dest[1] = (byte)(bl[1]*max);
779 dest[2] = (byte)(bl[2]*max);
780 dest[3] = (byte)(255*max);
781 }
782 else {
783 dest[0] = (byte)bl[0];
784 dest[1] = (byte)bl[1];
785 dest[2] = (byte)bl[2];
786 dest[3] = 255;
787 }
788
789 bl += 3;
790 dest += 4;
791 }
792
793 dest += stride;
794 }
795 }
796
797
798 /*
799 ===============
800 R_Q2BSP_SetLMCacheState
801 ===============
802 */
R_Q2BSP_SetLMCacheState(mBspSurface_t * surf)803 static void R_Q2BSP_SetLMCacheState (mBspSurface_t *surf)
804 {
805 int map;
806
807 for (map=0 ; map<surf->q2_numStyles ; map++)
808 surf->q2_cachedLight[map] = ri.scn.lightStyles[surf->q2_styles[map]].white;
809 }
810
811
812 /*
813 =======================
814 R_Q2BSP_UpdateLightmap
815 =======================
816 */
R_Q2BSP_UpdateLightmap(mBspSurface_t * surf)817 void R_Q2BSP_UpdateLightmap (mBspSurface_t *surf)
818 {
819 static uint32 temp[Q2LIGHTMAP_WIDTH*Q2LIGHTMAP_WIDTH];
820 int map;
821
822 // Don't attempt a surface more than once a frame
823 // FIXME: This is just a nasty work-around at best
824 if (surf->q2_lmFrame == ri.frameCount)
825 return;
826 surf->q2_lmFrame = ri.frameCount;
827
828 // Is this surface allowed to have a lightmap?
829 if (surf->q2_texInfo->flags & (SURF_TEXINFO_SKY|SURF_TEXINFO_WARP)) {
830 surf->q2_lmTexNumActive = -1;
831 return;
832 }
833
834 // Dynamic this frame or dynamic previously
835 if (gl_dynamic->intVal) {
836 for (map=0 ; map<surf->q2_numStyles ; map++) {
837 if (ri.scn.lightStyles[surf->q2_styles[map]].white != surf->q2_cachedLight[map])
838 goto dynamic;
839 }
840
841 if (surf->dLightFrame == ri.frameCount)
842 goto dynamic;
843 }
844
845 // No need to update
846 surf->q2_lmTexNumActive = surf->lmTexNum;
847 return;
848
849 dynamic:
850 // Update texture
851 R_Q2BSP_BuildLightMap (surf, (void *)temp, surf->q2_lmWidth*4);
852 if ((surf->q2_styles[map] >= 32 || surf->q2_styles[map] == 0) && surf->dLightFrame != ri.frameCount) {
853 R_Q2BSP_SetLMCacheState (surf);
854
855 RB_BindTexture (r_lmTextures[surf->lmTexNum]);
856 surf->q2_lmTexNumActive = surf->lmTexNum;
857 }
858 else {
859 RB_BindTexture (r_lmTextures[0]);
860 surf->q2_lmTexNumActive = 0;
861 }
862
863 qglTexSubImage2D (GL_TEXTURE_2D, 0,
864 surf->q2_lmCoords[0], surf->q2_lmCoords[1],
865 surf->q2_lmWidth, surf->q2_lmHeight,
866 GL_RGBA,
867 GL_UNSIGNED_BYTE,
868 temp);
869 }
870
871
872 /*
873 ================
874 R_Q2BSP_UploadLMBlock
875 ================
876 */
R_Q2BSP_UploadLMBlock(void)877 static void R_Q2BSP_UploadLMBlock (void)
878 {
879 if (r_q2_lmNumUploaded+1 >= R_MAX_LIGHTMAPS)
880 Com_Error (ERR_DROP, "R_Q2BSP_UploadLMBlock: - R_MAX_LIGHTMAPS exceeded\n");
881
882 r_lmTextures[r_q2_lmNumUploaded++] = R_Load2DImage (Q_VarArgs ("*lm%i", r_q2_lmNumUploaded), (byte **)(&r_q2_lmBuffer),
883 r_q2_lmSize, r_q2_lmSize, IF_NOPICMIP|IF_NOMIPMAP_LINEAR|IF_NOGAMMA|IF_NOINTENS|IF_NOCOMPRESS|IT_LIGHTMAP, 3);
884 }
885
886
887 /*
888 ================
889 R_Q2BSP_AllocLMBlock
890
891 Returns a texture number and the position inside it
892 ================
893 */
R_Q2BSP_AllocLMBlock(int w,int h,int * x,int * y)894 static qBool R_Q2BSP_AllocLMBlock (int w, int h, int *x, int *y)
895 {
896 int i, j;
897 int best, best2;
898
899 best = r_q2_lmSize;
900 for (i=0 ; i<r_q2_lmSize-w ; i++) {
901 best2 = 0;
902
903 for (j=0 ; j<w ; j++) {
904 if (r_q2_lmAllocated[i+j] >= best)
905 break;
906
907 if (r_q2_lmAllocated[i+j] > best2)
908 best2 = r_q2_lmAllocated[i+j];
909 }
910
911 if (j == w) {
912 // This is a valid spot
913 *x = i;
914 *y = best = best2;
915 }
916 }
917
918 if (best + h > r_q2_lmSize)
919 return qFalse;
920
921 for (i=0 ; i<w ; i++)
922 r_q2_lmAllocated[*x + i] = best + h;
923
924 return qTrue;
925 }
926
927
928 /*
929 ==================
930 R_Q2BSP_BeginBuildingLightmaps
931 ==================
932 */
R_Q2BSP_BeginBuildingLightmaps(void)933 void R_Q2BSP_BeginBuildingLightmaps (void)
934 {
935 int size, i;
936
937 // Should be no lightmaps at this point
938 r_q2_lmNumUploaded = 0;
939
940 // Find the maximum size
941 for (size=1 ; size<Q2LIGHTMAP_WIDTH && size<ri.config.maxTexSize ; size<<=1);
942 r_q2_lmSize = size;
943
944 // Allocate buffers and clear values
945 r_q2_lmAllocated = Mem_PoolAllocExt (sizeof (int) * r_q2_lmSize, qTrue, ri.lightSysPool, 0);
946 r_q2_lmBuffer = Mem_PoolAllocExt (r_q2_lmSize*r_q2_lmSize*4, qFalse, ri.lightSysPool, 0);
947 memset (r_q2_lmBuffer, 255, r_q2_lmSize*r_q2_lmSize*4);
948
949 // Setup the base light styles
950 for (i=0 ; i<MAX_CS_LIGHTSTYLES ; i++) {
951 Vec3Set (ri.scn.lightStyles[i].rgb, 1, 1, 1);
952 ri.scn.lightStyles[i].white = 3;
953 }
954
955 // Initialize the base dynamic lightmap texture
956 R_Q2BSP_UploadLMBlock ();
957 }
958
959
960 /*
961 ========================
962 R_Q2BSP_CreateSurfaceLightmap
963 ========================
964 */
R_Q2BSP_CreateSurfaceLightmap(mBspSurface_t * surf)965 void R_Q2BSP_CreateSurfaceLightmap (mBspSurface_t *surf)
966 {
967 byte *base;
968
969 if (!R_Q2BSP_AllocLMBlock (surf->q2_lmWidth, surf->q2_lmHeight, &surf->q2_lmCoords[0], &surf->q2_lmCoords[1])) {
970 R_Q2BSP_UploadLMBlock ();
971 memset (r_q2_lmAllocated, 0, sizeof (int) * r_q2_lmSize);
972
973 if (!R_Q2BSP_AllocLMBlock (surf->q2_lmWidth, surf->q2_lmHeight, &surf->q2_lmCoords[0], &surf->q2_lmCoords[1]))
974 Com_Error (ERR_FATAL, "Consecutive calls to R_Q2BSP_AllocLMBlock (%d, %d) failed\n", surf->q2_lmWidth, surf->q2_lmHeight);
975 }
976
977 surf->lmTexNum = r_q2_lmNumUploaded;
978 surf->q2_lmTexNumActive = -1;
979 surf->q2_lmFrame = ri.frameCount - 1; // Force an update
980
981 base = r_q2_lmBuffer + ((surf->q2_lmCoords[1] * r_q2_lmSize + surf->q2_lmCoords[0]) * 4);
982
983 R_Q2BSP_SetLMCacheState (surf);
984 R_Q2BSP_BuildLightMap (surf, base, r_q2_lmSize*4);
985 }
986
987
988 /*
989 =======================
990 R_Q2BSP_EndBuildingLightmaps
991 =======================
992 */
R_Q2BSP_EndBuildingLightmaps(void)993 void R_Q2BSP_EndBuildingLightmaps (void)
994 {
995 // Upload the final block
996 R_Q2BSP_UploadLMBlock ();
997
998 // Release allocated memory
999 Mem_Free (r_q2_lmAllocated);
1000 Mem_Free (r_q2_lmBuffer);
1001 }
1002
1003
1004 /*
1005 =======================
1006 R_Q2BSP_TouchLightmaps
1007 =======================
1008 */
R_Q2BSP_TouchLightmaps(void)1009 static void R_Q2BSP_TouchLightmaps (void)
1010 {
1011 int i;
1012
1013 for (i=0 ; i<r_q2_lmNumUploaded ; i++)
1014 R_TouchImage (r_lmTextures[i]);
1015 }
1016
1017 /*
1018 =============================================================================
1019
1020 QUAKE III DYNAMIC LIGHTS
1021
1022 =============================================================================
1023 */
1024
1025 #define Q3_DLIGHT_SCALE 0.5f
1026
1027 /*
1028 =================
1029 R_Q3BSP_MarkLitSurfaces
1030 =================
1031 */
R_Q3BSP_MarkLitSurfaces(refDLight_t * lt,float lightIntensity,uint32 bit,mBspNode_t * node)1032 static void R_Q3BSP_MarkLitSurfaces (refDLight_t *lt, float lightIntensity, uint32 bit, mBspNode_t *node)
1033 {
1034 mBspLeaf_t *leaf;
1035 mBspSurface_t *surf, **mark;
1036 float dist;
1037
1038 for ( ; ; ) {
1039 if (node->c.visFrame != ri.scn.visFrameCount)
1040 return;
1041 if (node->c.plane == NULL)
1042 break;
1043
1044 dist = PlaneDiff (lt->origin, node->c.plane);
1045 if (dist > lightIntensity) {
1046 node = node->children[0];
1047 continue;
1048 }
1049
1050 if (dist >= -lightIntensity)
1051 R_Q3BSP_MarkLitSurfaces (lt, lightIntensity, bit, node->children[0]);
1052 node = node->children[1];
1053 }
1054
1055 leaf = (mBspLeaf_t *)node;
1056
1057 // Check for door connected areas
1058 if (ri.def.areaBits) {
1059 if (!(ri.def.areaBits[leaf->area>>3] & (1<<(leaf->area&7))))
1060 return; // Not visible
1061 }
1062 if (!leaf->q3_firstLitSurface)
1063 return;
1064 if (!BoundsIntersect (leaf->c.mins, leaf->c.maxs, lt->mins, lt->maxs))
1065 return;
1066
1067 mark = leaf->q3_firstLitSurface;
1068 do {
1069 surf = *mark++;
1070 if (!BoundsIntersect (surf->mins, surf->maxs, lt->mins, lt->maxs))
1071 continue;
1072
1073 if (surf->dLightFrame != ri.frameCount) {
1074 surf->dLightBits = 0;
1075 surf->dLightFrame = ri.frameCount;
1076 }
1077 surf->dLightBits |= bit;
1078 } while (*mark);
1079 }
1080
1081
1082 /*
1083 =================
1084 R_Q3BSP_MarkWorldLights
1085 =================
1086 */
R_Q3BSP_MarkWorldLights(void)1087 void R_Q3BSP_MarkWorldLights (void)
1088 {
1089 refDLight_t *lt;
1090 uint32 i;
1091
1092 if (gl_flashblend->intVal || !gl_dynamic->intVal || !ri.scn.numDLights || r_vertexLighting->intVal || r_fullbright->intVal)
1093 return;
1094
1095 lt = ri.scn.dLightList;
1096 for (i=0 ; i<ri.scn.numDLights ; i++, lt++)
1097 R_Q3BSP_MarkLitSurfaces (lt, lt->intensity*Q3_DLIGHT_SCALE, 1<<i, ri.scn.worldModel->bspModel.nodes);
1098 }
1099
1100
1101 /*
1102 =================
1103 R_Q3BSP_MarkBModelLights
1104 =================
1105 */
R_Q3BSP_MarkBModelLights(refEntity_t * ent,vec3_t mins,vec3_t maxs)1106 void R_Q3BSP_MarkBModelLights (refEntity_t *ent, vec3_t mins, vec3_t maxs)
1107 {
1108 refDLight_t *lt;
1109 refModel_t *model = ent->model;
1110 mBspSurface_t *surf;
1111 uint32 i;
1112 int j;
1113
1114 if (!gl_dynamic->intVal || !ri.scn.numDLights || r_fullbright->intVal)
1115 return;
1116
1117 for (i=0, lt=ri.scn.dLightList ; i<ri.scn.numDLights ; i++, lt++) {
1118 if (!BoundsIntersect (mins, maxs, lt->mins, lt->maxs))
1119 continue;
1120
1121 for (j=0, surf=model->bspModel.firstModelSurface ; j<model->bspModel.numModelSurfaces ; j++, surf++) {
1122 if (R_Q3BSP_SurfPotentiallyLit (surf)) {
1123 if (surf->dLightFrame != ri.frameCount) {
1124 surf->dLightBits = 0;
1125 surf->dLightFrame = ri.frameCount;
1126 }
1127 surf->dLightBits |= 1<<i;
1128 }
1129 }
1130 }
1131 }
1132
1133 /*
1134 =============================================================================
1135
1136 QUAKE III LIGHT SAMPLING
1137
1138 =============================================================================
1139 */
1140
1141 /*
1142 ===============
1143 R_Q3BSP_LightForEntity
1144 ===============
1145 */
R_Q3BSP_LightForEntity(refEntity_t * ent,int numVerts,byte * bArray)1146 static void R_Q3BSP_LightForEntity (refEntity_t *ent, int numVerts, byte *bArray)
1147 {
1148 static vec3_t tempColorsArray[RB_MAX_VERTS];
1149 vec3_t vf, vf2;
1150 float *cArray;
1151 float t[8], direction_uv[2], dot;
1152 int r, g, b, vi[3], i, j, index[4];
1153 vec3_t dlorigin, ambient, diffuse, dir, direction;
1154 float *gridSize, *gridMins;
1155 int *gridBounds;
1156
1157 // Fullbright entity
1158 if (r_fullbright->intVal || ent->flags & RF_FULLBRIGHT) {
1159 for (i=0 ; i<numVerts ; i++, bArray+=4)
1160 *(int *)bArray = *(int *)ent->color;
1161 return;
1162 }
1163
1164 // Probably a weird shader, see mpteam4 for example
1165 if (!ent->model || ent->model->type == MODEL_Q3BSP) {
1166 memset (bArray, 0, sizeof (bvec4_t)*numVerts);
1167 return;
1168 }
1169
1170 Vec3Set (ambient, 0, 0, 0);
1171 Vec3Set (diffuse, 0, 0, 0);
1172 Vec3Set (direction, 1, 1, 1);
1173
1174 gridSize = ri.scn.worldModel->q3BspModel.gridSize;
1175 gridMins = ri.scn.worldModel->q3BspModel.gridMins;
1176 gridBounds = ri.scn.worldModel->q3BspModel.gridBounds;
1177
1178 if (!ri.scn.worldModel->q3BspModel.lightGrid || !ri.scn.worldModel->q3BspModel.numLightGridElems)
1179 goto dynamic;
1180
1181 for (i=0 ; i<3 ; i++) {
1182 vf[i] = (ent->origin[i] - ri.scn.worldModel->q3BspModel.gridMins[i]) / ri.scn.worldModel->q3BspModel.gridSize[i];
1183 vi[i] = (int)vf[i];
1184 vf[i] = vf[i] - floor(vf[i]);
1185 vf2[i] = 1.0f - vf[i];
1186 }
1187
1188 index[0] = vi[2]*ri.scn.worldModel->q3BspModel.gridBounds[3] + vi[1]*ri.scn.worldModel->q3BspModel.gridBounds[0] + vi[0];
1189 index[1] = index[0] + ri.scn.worldModel->q3BspModel.gridBounds[0];
1190 index[2] = index[0] + ri.scn.worldModel->q3BspModel.gridBounds[3];
1191 index[3] = index[2] + ri.scn.worldModel->q3BspModel.gridBounds[0];
1192 for (i=0 ; i<4 ; i++) {
1193 if (index[i] < 0 || index[i] >= ri.scn.worldModel->q3BspModel.numLightGridElems-1)
1194 goto dynamic;
1195 }
1196
1197 t[0] = vf2[0] * vf2[1] * vf2[2];
1198 t[1] = vf[0] * vf2[1] * vf2[2];
1199 t[2] = vf2[0] * vf[1] * vf2[2];
1200 t[3] = vf[0] * vf[1] * vf2[2];
1201 t[4] = vf2[0] * vf2[1] * vf[2];
1202 t[5] = vf[0] * vf2[1] * vf[2];
1203 t[6] = vf2[0] * vf[1] * vf[2];
1204 t[7] = vf[0] * vf[1] * vf[2];
1205
1206 for (j=0 ; j<3 ; j++) {
1207 ambient[j] = 0;
1208 diffuse[j] = 0;
1209
1210 for (i=0 ; i<4 ; i++) {
1211 ambient[j] += t[i*2] * ri.scn.worldModel->q3BspModel.lightGrid[index[i]].ambient[j];
1212 ambient[j] += t[i*2+1] * ri.scn.worldModel->q3BspModel.lightGrid[index[i]+1].ambient[j];
1213
1214 diffuse[j] += t[i*2] * ri.scn.worldModel->q3BspModel.lightGrid[index[i]].diffuse[j];
1215 diffuse[j] += t[i*2+1] * ri.scn.worldModel->q3BspModel.lightGrid[index[i]+1].diffuse[j];
1216 }
1217 }
1218
1219 for (j=0 ; j<2 ; j++) {
1220 direction_uv[j] = 0;
1221
1222 for (i=0 ; i<4 ; i++) {
1223 direction_uv[j] += t[i*2] * ri.scn.worldModel->q3BspModel.lightGrid[index[i]].direction[j];
1224 direction_uv[j] += t[i*2+1] * ri.scn.worldModel->q3BspModel.lightGrid[index[i]+1].direction[j];
1225 }
1226
1227 direction_uv[j] = AngleModf (direction_uv[j]);
1228 }
1229
1230 dot = bound(0.0f, /*r_ambientscale->floatVal*/ 1.0f, 1.0f) * ri.pow2MapOvrbr;
1231 Vec3Scale (ambient, dot, ambient);
1232
1233 dot = bound(0.0f, /*r_directedscale->floatVal*/ 1.0f, 1.0f) * ri.pow2MapOvrbr;
1234 Vec3Scale (diffuse, dot, diffuse);
1235
1236 if (ent->flags & RF_MINLIGHT) {
1237 for (i=0 ; i<3 ; i++)
1238 if (ambient[i] > 0.1)
1239 break;
1240
1241 if (i == 3) {
1242 ambient[0] = 0.1f;
1243 ambient[1] = 0.1f;
1244 ambient[2] = 0.1f;
1245 }
1246 }
1247
1248 dot = direction_uv[0] * (1.0 / 255.0);
1249 t[0] = RB_FastSin (dot + 0.25f);
1250 t[1] = RB_FastSin (dot);
1251
1252 dot = direction_uv[1] * (1.0 / 255.0);
1253 t[2] = RB_FastSin (dot + 0.25f);
1254 t[3] = RB_FastSin (dot);
1255
1256 Vec3Set (dir, t[2] * t[1], t[3] * t[1], t[0]);
1257
1258 // Rotate direction
1259 Matrix3_TransformVector (ent->axis, dir, direction);
1260
1261 cArray = tempColorsArray[0];
1262 for (i=0 ; i<numVerts ; i++, cArray+=3) {
1263 dot = DotProduct (rb.batch.normals[i], direction);
1264
1265 if (dot <= 0)
1266 Vec3Copy (ambient, cArray);
1267 else
1268 Vec3MA (ambient, dot, diffuse, cArray);
1269 }
1270
1271 dynamic:
1272 //
1273 // Add dynamic lights
1274 //
1275 if (gl_dynamic->intVal && ri.scn.numDLights) {
1276 refDLight_t *dl;
1277 float dist, add, intensity8;
1278 uint32 num;
1279
1280 for (num=0, dl=ri.scn.dLightList ; num<ri.scn.numDLights ; dl++, num++) {
1281 // FIXME: use BoundsIntersect for a performance boost, though this will meen storing bounds in the entity or something...
1282 if (!BoundsAndSphereIntersect (dl->mins, dl->maxs, ent->origin, ent->model->radius * ent->scale))
1283 continue;
1284
1285 // Translate
1286 Vec3Subtract ( dl->origin, ent->origin, dir );
1287 dist = Vec3Length ( dir );
1288
1289 if (dist > dl->intensity + ent->model->radius * ent->scale)
1290 continue;
1291
1292 // Rotate
1293 Matrix3_TransformVector ( ent->axis, dir, dlorigin );
1294
1295 intensity8 = dl->intensity * 8;
1296
1297 cArray = tempColorsArray[0];
1298 for (i=0 ; i<numVerts ; i++, cArray+=3) {
1299 Vec3Subtract (dlorigin, rb.batch.vertices[i], dir);
1300 add = DotProduct (rb.batch.normals[i], dir);
1301
1302 if (add > 0) {
1303 dot = DotProduct (dir, dir);
1304 add *= (intensity8 / dot) * Q_RSqrtf (dot);
1305 Vec3MA (cArray, add, dl->color, cArray);
1306 }
1307 }
1308 }
1309 }
1310
1311 cArray = tempColorsArray[0];
1312 for (i=0 ; i<numVerts ; i++, bArray+=4, cArray+=3) {
1313 r = Q_ftol (cArray[0] * ent->color[0]);
1314 g = Q_ftol (cArray[1] * ent->color[1]);
1315 b = Q_ftol (cArray[2] * ent->color[2]);
1316
1317 bArray[0] = clamp (r, 0, 255);
1318 bArray[1] = clamp (g, 0, 255);
1319 bArray[2] = clamp (b, 0, 255);
1320 }
1321 }
1322
1323 /*
1324 =============================================================================
1325
1326 QUAKE III LIGHTMAP
1327
1328 =============================================================================
1329 */
1330
1331 static byte *r_q3_lmBuffer;
1332 static int r_q3_lmBufferSize;
1333 static int r_q3_lmNumUploaded;
1334 static int r_q3_lmMaxBlockSize;
1335
1336 /*
1337 =======================
1338 R_Q3BSP_BuildLightmap
1339 =======================
1340 */
R_Q3BSP_BuildLightmap(int w,int h,const byte * data,byte * dest,int blockWidth)1341 static void R_Q3BSP_BuildLightmap (int w, int h, const byte *data, byte *dest, int blockWidth)
1342 {
1343 int x, y;
1344 float scale;
1345 byte *rgba;
1346 float scaled[3];
1347
1348 if (!data || r_fullbright->intVal) {
1349 for (y=0 ; y<h ; y++, dest)
1350 memset (dest + y * blockWidth, 255, w * 4);
1351 return;
1352 }
1353
1354 scale = ri.pow2MapOvrbr;
1355 for (y=0 ; y<h ; y++) {
1356 for (x=0, rgba=dest+y*blockWidth ; x<w ; x++, rgba+=4) {
1357 scaled[0] = data[(y*w+x) * Q3LIGHTMAP_BYTES+0] * scale;
1358 scaled[1] = data[(y*w+x) * Q3LIGHTMAP_BYTES+1] * scale;
1359 scaled[2] = data[(y*w+x) * Q3LIGHTMAP_BYTES+2] * scale;
1360
1361 ColorNormalizeb (scaled, rgba);
1362 }
1363 }
1364 }
1365
1366
1367 /*
1368 =======================
1369 R_Q3BSP_PackLightmaps
1370 =======================
1371 */
R_Q3BSP_PackLightmaps(int num,int w,int h,int size,const byte * data,mQ3BspLightmapRect_t * rects)1372 static int R_Q3BSP_PackLightmaps (int num, int w, int h, int size, const byte *data, mQ3BspLightmapRect_t *rects)
1373 {
1374 int i, x, y, root;
1375 byte *block;
1376 image_t *image;
1377 int rectX, rectY, rectSize;
1378 int maxX, maxY, max, xStride;
1379 double tw, th, tx, ty;
1380
1381 maxX = r_q3_lmMaxBlockSize / w;
1382 maxY = r_q3_lmMaxBlockSize / h;
1383 max = maxY;
1384 if (maxY > maxX)
1385 max = maxX;
1386
1387 if (r_q3_lmNumUploaded >= R_MAX_LIGHTMAPS-1)
1388 Com_Error (ERR_DROP, "R_Q3BSP_PackLightmaps: - R_MAX_LIGHTMAPS exceeded\n");
1389
1390 Com_DevPrintf (0, "Packing %i lightmap(s) -> ", num);
1391
1392 if (!max || num == 1 || !r_lmPacking->intVal) {
1393 // Process as it is
1394 R_Q3BSP_BuildLightmap (w, h, data, r_q3_lmBuffer, w * 4);
1395
1396 image = R_Load2DImage (Q_VarArgs ("*lm%i", r_q3_lmNumUploaded), (byte **)(&r_q3_lmBuffer),
1397 w, h, IF_CLAMP|IF_NOPICMIP|IF_NOMIPMAP_LINEAR|IF_NOGAMMA|IF_NOINTENS|IF_NOCOMPRESS|IT_LIGHTMAP, Q3LIGHTMAP_BYTES);
1398
1399 r_lmTextures[r_q3_lmNumUploaded] = image;
1400 rects[0].texNum = r_q3_lmNumUploaded;
1401
1402 rects[0].w = 1; rects[0].x = 0;
1403 rects[0].h = 1; rects[0].y = 0;
1404
1405 Com_DevPrintf (0, "%ix%i\n", 1, 1);
1406
1407 r_q3_lmNumUploaded++;
1408 return 1;
1409 }
1410
1411 // Find the nearest square block size
1412 root = (int)sqrt (num);
1413 if (root > max)
1414 root = max;
1415
1416 // Keep row size a power of two
1417 for (i=1 ; i<root ; i <<= 1);
1418 if (i > root)
1419 i >>= 1;
1420 root = i;
1421
1422 num -= root * root;
1423 rectX = rectY = root;
1424
1425 if (maxY > maxX) {
1426 for ( ; num>=root && rectY<maxY ; rectY++, num-=root);
1427
1428 // Sample down if not a power of two
1429 for (y=1 ; y<rectY ; y <<= 1);
1430 if (y > rectY)
1431 y >>= 1;
1432 rectY = y;
1433 }
1434 else {
1435 for ( ; num>=root && rectX<maxX ; rectX++, num-=root);
1436
1437 // Sample down if not a power of two
1438 for (x=1 ; x<rectX ; x<<=1);
1439 if (x > rectX)
1440 x >>= 1;
1441 rectX = x;
1442 }
1443
1444 tw = 1.0 / (double)rectX;
1445 th = 1.0 / (double)rectY;
1446
1447 xStride = w * 4;
1448 rectSize = (rectX * w) * (rectY * h) * 4;
1449 if (rectSize > r_q3_lmBufferSize) {
1450 if (r_q3_lmBuffer)
1451 Mem_Free (r_q3_lmBuffer);
1452 r_q3_lmBuffer = Mem_PoolAllocExt (rectSize, qFalse, ri.lightSysPool, 0);
1453 memset (r_q3_lmBuffer, 255, rectSize);
1454 r_q3_lmBufferSize = rectSize;
1455 }
1456
1457 block = r_q3_lmBuffer;
1458 for (y=0, ty=0.0f, num=0 ; y<rectY ; y++, ty+=th, block+=rectX*xStride*h) {
1459 for (x=0, tx=0.0f ; x<rectX ; x++, tx+=tw, num++) {
1460 R_Q3BSP_BuildLightmap (w, h, data + num * size, block + x * xStride, rectX * xStride);
1461
1462 rects[num].w = tw; rects[num].x = tx;
1463 rects[num].h = th; rects[num].y = ty;
1464 }
1465 }
1466
1467 image = R_Load2DImage (Q_VarArgs ("*lm%i", r_q3_lmNumUploaded), (byte **)(&r_q3_lmBuffer),
1468 rectX * w, rectY * h, IF_CLAMP|IF_NOPICMIP|IF_NOMIPMAP_LINEAR|IF_NOGAMMA|IF_NOINTENS|IF_NOCOMPRESS|IT_LIGHTMAP, Q3LIGHTMAP_BYTES);
1469
1470 r_lmTextures[r_q3_lmNumUploaded] = image;
1471 for (i=0 ; i<num ; i++)
1472 rects[i].texNum = r_q3_lmNumUploaded;
1473
1474 Com_DevPrintf (0, "%ix%i\n", rectX, rectY);
1475
1476 r_q3_lmNumUploaded++;
1477 return num;
1478 }
1479
1480
1481 /*
1482 =======================
1483 R_Q3BSP_BuildLightmaps
1484 =======================
1485 */
R_Q3BSP_BuildLightmaps(int numLightmaps,int w,int h,const byte * data,mQ3BspLightmapRect_t * rects)1486 void R_Q3BSP_BuildLightmaps (int numLightmaps, int w, int h, const byte *data, mQ3BspLightmapRect_t *rects)
1487 {
1488 int i;
1489 int size;
1490
1491 // Pack lightmaps
1492 for (size=1 ; size<r_lmMaxBlockSize->intVal && size<ri.config.maxTexSize ; size<<=1);
1493
1494 r_q3_lmNumUploaded = 0;
1495 r_q3_lmMaxBlockSize = size;
1496 size = w * h * Q3LIGHTMAP_BYTES;
1497 r_q3_lmBufferSize = w * h * 4;
1498 r_q3_lmBuffer = Mem_PoolAllocExt (r_q3_lmBufferSize, qFalse, ri.lightSysPool, 0);
1499
1500 for (i=0 ; i<numLightmaps ; )
1501 i += R_Q3BSP_PackLightmaps (numLightmaps - i, w, h, size, data + i * size, &rects[i]);
1502
1503 if (r_q3_lmBuffer)
1504 Mem_Free (r_q3_lmBuffer);
1505
1506 Com_DevPrintf (0, "Packed %i lightmaps into %i texture(s)\n", numLightmaps, r_q3_lmNumUploaded);
1507 }
1508
1509
1510 /*
1511 =======================
1512 R_Q3BSP_TouchLightmaps
1513 =======================
1514 */
R_Q3BSP_TouchLightmaps(void)1515 static void R_Q3BSP_TouchLightmaps (void)
1516 {
1517 int i;
1518
1519 for (i=0 ; i<r_q3_lmNumUploaded ; i++)
1520 R_TouchImage (r_lmTextures[i]);
1521 }
1522
1523 /*
1524 =============================================================================
1525
1526 FUNCTION WRAPPING
1527
1528 =============================================================================
1529 */
1530
1531 /*
1532 =============
1533 R_LightBounds
1534 =============
1535 */
R_LightBounds(const vec3_t origin,float intensity,vec3_t mins,vec3_t maxs)1536 void R_LightBounds (const vec3_t origin, float intensity, vec3_t mins, vec3_t maxs)
1537 {
1538 if (ri.scn.worldModel->type == MODEL_Q2BSP) {
1539 Vec3Set (mins, origin[0] - (intensity * 2), origin[1] - (intensity * 2), origin[2] - (intensity * 2));
1540 Vec3Set (maxs, origin[0] + (intensity * 2), origin[1] + (intensity * 2), origin[2] + (intensity * 2));
1541 return;
1542 }
1543
1544 Vec3Set (mins, origin[0] - intensity, origin[1] - intensity, origin[2] - intensity);
1545 Vec3Set (maxs, origin[0] + intensity, origin[1] + intensity, origin[2] + intensity);
1546 }
1547
1548
1549 /*
1550 =============
1551 R_LightPoint
1552 =============
1553 */
R_LightPoint(vec3_t point,vec3_t light)1554 void R_LightPoint (vec3_t point, vec3_t light)
1555 {
1556 if (ri.def.rdFlags & RDF_NOWORLDMODEL)
1557 return;
1558
1559 if (ri.scn.worldModel->type == MODEL_Q2BSP) {
1560 R_Q2BSP_LightPoint (point, light);
1561 return;
1562 }
1563
1564 // FIXME
1565 Vec3Set (light, 1, 1, 1);
1566 }
1567
1568
1569 /*
1570 =============
1571 R_SetLightLevel
1572 =============
1573 */
R_SetLightLevel(void)1574 void R_SetLightLevel (void)
1575 {
1576 if (ri.def.rdFlags & RDF_NOWORLDMODEL)
1577 return;
1578
1579 if (ri.scn.worldModel->type == MODEL_Q2BSP) {
1580 R_Q2BSP_SetLightLevel ();
1581 return;
1582 }
1583 }
1584
1585
1586 /*
1587 =======================
1588 R_TouchLightmaps
1589 =======================
1590 */
R_TouchLightmaps(void)1591 void R_TouchLightmaps (void)
1592 {
1593 if (ri.def.rdFlags & RDF_NOWORLDMODEL)
1594 return;
1595
1596 if (ri.scn.worldModel->type == MODEL_Q2BSP) {
1597 R_Q2BSP_TouchLightmaps ();
1598 return;
1599 }
1600
1601 R_Q3BSP_TouchLightmaps ();
1602 }
1603
1604
1605 /*
1606 =============
1607 R_ShadowForEntity
1608 =============
1609 */
R_ShadowForEntity(refEntity_t * ent,vec3_t shadowSpot)1610 qBool R_ShadowForEntity (refEntity_t *ent, vec3_t shadowSpot)
1611 {
1612 if (ri.def.rdFlags & RDF_NOWORLDMODEL)
1613 return qFalse;
1614
1615 if (ri.scn.worldModel->type == MODEL_Q2BSP)
1616 return R_Q2BSP_ShadowForEntity (ent, shadowSpot);
1617
1618 return qFalse;
1619 }
1620
1621
1622 /*
1623 =============
1624 R_LightForEntity
1625 =============
1626 */
R_LightForEntity(refEntity_t * ent,int numVerts,byte * bArray)1627 void R_LightForEntity (refEntity_t *ent, int numVerts, byte *bArray)
1628 {
1629 if (ri.def.rdFlags & RDF_NOWORLDMODEL || ri.scn.worldModel->type == MODEL_Q2BSP) {
1630 R_Q2BSP_LightForEntity (ent, numVerts, bArray);
1631 return;
1632 }
1633
1634 R_Q3BSP_LightForEntity (ent, numVerts, bArray);
1635 }
1636