1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: r_light.cpp 4354 2010-12-23 19:48:32Z dj_jl $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "r_local.h"
30 
31 // MACROS ------------------------------------------------------------------
32 
33 // TYPES -------------------------------------------------------------------
34 
35 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
36 
37 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
38 
39 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
40 
41 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
42 
43 // PUBLIC DATA DEFINITIONS -------------------------------------------------
44 
45 int					r_dlightframecount;
46 bool				r_light_add;
47 
48 vuint32				blocklights[18 * 18];
49 vuint32				blocklightsr[18 * 18];
50 vuint32				blocklightsg[18 * 18];
51 vuint32				blocklightsb[18 * 18];
52 vuint32				blockaddlightsr[18 * 18];
53 vuint32				blockaddlightsg[18 * 18];
54 vuint32				blockaddlightsb[18 * 18];
55 
56 byte				light_remap[256];
57 VCvarI				r_darken("r_darken", "0", CVAR_Archive);
58 VCvarI				r_ambient("r_ambient", "0");
59 int					light_mem;
60 VCvarI				r_extrasamples("r_extrasamples", "0", CVAR_Archive);
61 VCvarI				r_dynamic("r_dynamic", "1", CVAR_Archive);
62 VCvarI				r_dynamic_clip("r_dynamic_clip", "0", CVAR_Archive);
63 VCvarI				r_static_lights("r_static_lights", "1", CVAR_Archive);
64 VCvarI				r_static_add("r_static_add", "0", CVAR_Archive);
65 VCvarF				r_specular("r_specular", "0.1", CVAR_Archive);
66 
67 // PRIVATE DATA DEFINITIONS ------------------------------------------------
68 
69 static TVec			smins, smaxs;
70 static TVec			worldtotex[2];
71 static TVec			textoworld[2];
72 static TVec			texorg;
73 static TVec			surfpt[18 * 18 * 4];
74 static int			numsurfpt;
75 static bool			points_calculated;
76 static float		lightmap[18 * 18 * 4];
77 static float		lightmapr[18 * 18 * 4];
78 static float		lightmapg[18 * 18 * 4];
79 static float		lightmapb[18 * 18 * 4];
80 static bool			light_hit;
81 static byte			*facevis;
82 static bool			is_coloured;
83 
84 static int			c_bad;
85 
86 // CODE --------------------------------------------------------------------
87 
88 //==========================================================================
89 //
90 //	VRenderLevelShared::AddStaticLight
91 //
92 //==========================================================================
93 
AddStaticLight(const TVec & origin,float radius,vuint32 colour)94 void VRenderLevelShared::AddStaticLight(const TVec &origin, float radius,
95 	vuint32 colour)
96 {
97 	guard(VRenderLevelShared::AddStaticLight);
98 	light_t& L = Lights.Alloc();
99 	L.origin = origin;
100 	L.radius = radius;
101 	L.colour = colour;
102 	L.leafnum = Level->PointInSubsector(origin) - Level->Subsectors;
103 	unguard;
104 }
105 
106 //==========================================================================
107 //
108 //	VRenderLevel::CalcMinMaxs
109 //
110 //==========================================================================
111 
CalcMinMaxs(surface_t * surf)112 void VRenderLevel::CalcMinMaxs(surface_t *surf)
113 {
114 	guard(VRenderLevel::CalcMinMaxs);
115 	smins = TVec(99999.0, 99999.0, 99999.0);
116 	smaxs = TVec(-999999.0, -999999.0, -999999.0);
117 
118 	for (int i = 0; i < surf->count; i++)
119 	{
120 		TVec &v = surf->verts[i];
121 		if (smins.x > v.x)
122 			smins.x = v.x;
123 		if (smins.y > v.y)
124 			smins.y = v.y;
125 		if (smins.z > v.z)
126 			smins.z = v.z;
127 		if (smaxs.x < v.x)
128 			smaxs.x = v.x;
129 		if (smaxs.y < v.y)
130 			smaxs.y = v.y;
131 		if (smaxs.z < v.z)
132 			smaxs.z = v.z;
133 	}
134 	unguard;
135 }
136 
137 //==========================================================================
138 //
139 //	VRenderLevel::CastRay
140 //
141 //	Returns the distance between the points, or -1 if blocked
142 //
143 //==========================================================================
144 
CastRay(const TVec & p1,const TVec & p2,float squaredist)145 float VRenderLevel::CastRay(const TVec &p1, const TVec &p2,
146 	float squaredist)
147 {
148 	guard(VRenderLevel::CastRay);
149 	linetrace_t		Trace;
150 
151 	TVec delta = p2 - p1;
152 	float t = DotProduct(delta, delta);
153 	if (t > squaredist)
154 		return -1;		// too far away
155 
156 	if (!Level->TraceLine(Trace, p1, p2, SPF_NOBLOCKSIGHT))
157 		return -1;		// ray was blocked
158 
159 	if (t == 0)
160 		t = 1;			// don't blow up...
161 	return sqrt(t);
162 	unguard;
163 }
164 
165 //==========================================================================
166 //
167 //	VRenderLevel::CalcFaceVectors
168 //
169 //	Fills in texorg, worldtotex. and textoworld
170 //
171 //==========================================================================
172 
CalcFaceVectors(surface_t * surf)173 void VRenderLevel::CalcFaceVectors(surface_t *surf)
174 {
175 	guard(VRenderLevel::CalcFaceVectors);
176 	texinfo_t	*tex;
177 	int			i;
178 	TVec		texnormal;
179 	float		distscale;
180 	float		dist, len;
181 
182 	tex = surf->texinfo;
183 
184 	// convert from float to vec_t
185 	worldtotex[0] = tex->saxis;
186 	worldtotex[1] = tex->taxis;
187 
188 	//	Calculate a normal to the texture axis. Points can be moved along
189 	// this without changing their S/T
190 	texnormal.x = tex->taxis.y * tex->saxis.z
191 		- tex->taxis.z * tex->saxis.y;
192 	texnormal.y = tex->taxis.z * tex->saxis.x
193 		- tex->taxis.x * tex->saxis.z;
194 	texnormal.z = tex->taxis.x * tex->saxis.y
195 		- tex->taxis.y * tex->saxis.x;
196 	texnormal = Normalise(texnormal);
197 
198 	// flip it towards plane normal
199 	distscale = DotProduct(texnormal, surf->plane->normal);
200 	if (!distscale)
201 		Host_Error("Texture axis perpendicular to face");
202 	if (distscale < 0)
203 	{
204 		distscale = -distscale;
205 		texnormal = -texnormal;
206 	}
207 
208 	// distscale is the ratio of the distance along the texture normal to
209 	// the distance along the plane normal
210 	distscale = 1 / distscale;
211 
212 	for (i = 0; i < 2; i++)
213 	{
214 		len = Length(worldtotex[i]);
215 		dist = DotProduct(worldtotex[i], surf->plane->normal);
216 		dist *= distscale;
217 		textoworld[i] = worldtotex[i] - dist * texnormal;
218 		textoworld[i] = textoworld[i] * (1 / len) * (1 / len);
219 	}
220 
221 	// calculate texorg on the texture plane
222 	for (i = 0; i < 3; i++)
223 		texorg[i] = -tex->soffs * textoworld[0][i] - tex->toffs * textoworld[1][i];
224 
225 	// project back to the face plane
226 	dist = DotProduct(texorg, surf->plane->normal) - surf->plane->dist - 1;
227 	dist *= distscale;
228 	texorg = texorg - dist * texnormal;
229 	unguard;
230 }
231 
232 //==========================================================================
233 //
234 //	VRenderLevel::CalcPoints
235 //
236 //	For each texture aligned grid point, back project onto the plane
237 // to get the world xyz value of the sample point
238 //
239 //==========================================================================
240 
CalcPoints(surface_t * surf)241 void VRenderLevel::CalcPoints(surface_t *surf)
242 {
243 	guard(VRenderLevel::CalcPoints);
244 	int			i;
245 	int			s, t;
246 	int			w, h;
247 	int			step;
248 	float		starts, startt, us, ut;
249 	float		mids, midt;
250 	TVec*		spt;
251 	TVec		facemid;
252 	linetrace_t	Trace;
253 
254 
255 	//
256 	// fill in surforg
257 	// the points are biased towards the centre of the surface
258 	// to help avoid edge cases just inside walls
259 	//
260 	spt = surfpt;
261 	mids = surf->texturemins[0] + surf->extents[0] / 2;
262 	midt = surf->texturemins[1] + surf->extents[1] / 2;
263 
264 	facemid = texorg + textoworld[0] * mids + textoworld[1] * midt;
265 
266 	if (r_extrasamples)
267 	{
268 		// extra filtering
269 		w = ((surf->extents[0] >> 4) + 1) * 2;
270 		h = ((surf->extents[1] >> 4) + 1) * 2;
271 		starts = surf->texturemins[0] - 8;
272 		startt = surf->texturemins[1] - 8;
273 		step = 8;
274 	}
275 	else
276 	{
277 		w = (surf->extents[0] >> 4) + 1;
278 		h = (surf->extents[1] >> 4) + 1;
279 		starts = surf->texturemins[0];
280 		startt = surf->texturemins[1];
281 		step = 16;
282 	}
283 
284 	numsurfpt = w * h;
285 	for (t = 0; t < h; t++)
286 	{
287 		for (s = 0; s < w; s++, spt++)
288 		{
289 			us = starts + s * step;
290 			ut = startt + t * step;
291 
292 			// if a line can be traced from surf to facemid, the point is good
293 			for (i = 0; i < 6; i++)
294 			{
295 				// calculate texture point
296 				*spt = texorg + textoworld[0] * us + textoworld[1] * ut;
297 				if (Level->TraceLine(Trace, facemid, *spt, SPF_NOBLOCKSIGHT))
298 				{
299 					break;	// got it
300 				}
301 				if (i & 1)
302 				{
303 					if (us > mids)
304 					{
305 						us -= 8;
306 						if (us < mids)
307 							us = mids;
308 					}
309 					else
310 					{
311 						us += 8;
312 						if (us > mids)
313 							us = mids;
314 					}
315 				}
316 				else
317 				{
318 					if (ut > midt)
319 					{
320 						ut -= 8;
321 						if (ut < midt)
322 							ut = midt;
323 					}
324 					else
325 					{
326 						ut += 8;
327 						if (ut > midt)
328 							ut = midt;
329 					}
330 				}
331 
332 				// move surf 8 pixels towards the centre
333 				*spt += 8 * Normalise(facemid - *spt);
334 			}
335 			if (i == 2)
336 				c_bad++;
337 		}
338 	}
339 	unguard;
340 }
341 
342 //==========================================================================
343 //
344 //	VRenderLevel::SingleLightFace
345 //
346 //==========================================================================
347 
SingleLightFace(light_t * light,surface_t * surf)348 void VRenderLevel::SingleLightFace(light_t *light, surface_t *surf)
349 {
350 	guard(VRenderLevel::SingleLightFace);
351 	float	dist;
352 	TVec	incoming;
353 	float	angle;
354 	float	add;
355 	TVec	*spt;
356 	int		c;
357 	float	squaredist;
358 	float	rmul, gmul, bmul;
359 
360 	// Check potential visibility
361 	if (!(facevis[light->leafnum >> 3] & (1 << (light->leafnum & 7))))
362 		return;
363 
364 	// Check bounding box
365 	if (light->origin.x + light->radius < smins.x ||
366 		light->origin.x - light->radius > smaxs.x ||
367 		light->origin.y + light->radius < smins.y ||
368 		light->origin.y - light->radius > smaxs.y ||
369 		light->origin.z + light->radius < smins.z ||
370 		light->origin.z - light->radius > smaxs.z)
371 	{
372 		return;
373 	}
374 
375 	dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist;
376 
377 	// don't bother with lights behind the surface
378 	if (dist <= -0.1)
379 		return;
380 
381 	// don't bother with light too far away
382 	if (dist > light->radius)
383 	{
384 		return;
385 	}
386 
387 	//	Calc points only when surface may be lit by a light
388 	if (!points_calculated)
389 	{
390 		CalcFaceVectors(surf);
391 		CalcPoints(surf);
392 
393 		memset(lightmap, 0, numsurfpt * 4);
394 		memset(lightmapr, 0, numsurfpt * 4);
395 		memset(lightmapg, 0, numsurfpt * 4);
396 		memset(lightmapb, 0, numsurfpt * 4);
397 		points_calculated = true;
398 	}
399 
400 	//
401 	// check it for real
402 	//
403 	spt = surfpt;
404 	squaredist = light->radius * light->radius;
405 	rmul = ((light->colour >> 16) & 0xff) / 255.0;
406 	gmul = ((light->colour >> 8) & 0xff) / 255.0;
407 	bmul = (light->colour & 0xff) / 255.0;
408 	for (c = 0; c < numsurfpt; c++, spt++)
409 	{
410 		dist = CastRay(light->origin, *spt, squaredist);
411 		if (dist < 0)
412 			continue;	// light doesn't reach
413 
414 		incoming = Normalise(light->origin - *spt);
415 		angle = DotProduct(incoming, surf->plane->normal);
416 
417 		angle = 0.5 + 0.5 * angle;
418 		add = light->radius - dist;
419 		add *= angle;
420 		if (add < 0)
421 			continue;
422 		lightmap[c] += add;
423 		lightmapr[c] += add * rmul;
424 		lightmapg[c] += add * gmul;
425 		lightmapb[c] += add * bmul;
426 		if (lightmap[c] > 1)		// ignore real tiny lights
427 		{
428 			light_hit = true;
429 			if (light->colour != 0xffffffff)
430 				is_coloured = true;
431 		}
432 	}
433 	unguard;
434 }
435 
436 //==========================================================================
437 //
438 //	VRenderLevel::LightFace
439 //
440 //==========================================================================
441 
LightFace(surface_t * surf,subsector_t * leaf)442 void VRenderLevel::LightFace(surface_t *surf, subsector_t *leaf)
443 {
444 	guard(VRenderLevel::LightFace);
445 	int			i, s, t, w, h;
446 	float		total;
447 
448 	facevis = Level->LeafPVS(leaf);
449 	points_calculated = false;
450 	light_hit = false;
451 	is_coloured = false;
452 
453 	//
454 	// cast all lights
455 	//
456 	CalcMinMaxs(surf);
457 	if (r_static_lights)
458 	{
459 		for (i = 0; i < Lights.Num(); i++)
460 		{
461 			SingleLightFace(&Lights[i], surf);
462 		}
463 	}
464 
465 	if (!light_hit)
466 	{
467 		// no light hitting it
468 		if (surf->lightmap)
469 		{
470 			Z_Free(surf->lightmap);
471 			surf->lightmap = NULL;
472 		}
473 		if (surf->lightmap_rgb)
474 		{
475 			Z_Free(surf->lightmap_rgb);
476 			surf->lightmap_rgb = NULL;
477 		}
478 		return;
479 	}
480 
481 	w = (surf->extents[0] >> 4) + 1;
482 	h = (surf->extents[1] >> 4) + 1;
483 
484 	//	If the surface already has a lightmap, we will reuse it, otherwiese
485 	// we must allocate a new block
486 	if (is_coloured)
487 	{
488 		if (surf->lightmap_rgb)
489 		{
490 			Z_Free(surf->lightmap_rgb);
491 		}
492 		surf->lightmap_rgb = (rgb_t*)Z_Malloc(w * h * 3);
493 		light_mem += w * h * 3;
494 
495 		i = 0;
496 		for (t = 0; t < h; t++)
497 		{
498 			for (s = 0; s < w; s++, i++)
499 			{
500 				if (r_extrasamples)
501 				{
502 					// filtered sample
503 					total = lightmapr[t*w*4+s*2] +
504 							lightmapr[t*2*w*2+s*2+1] +
505 							lightmapr[(t*2+1)*w*2+s*2] +
506 							lightmapr[(t*2+1)*w*2+s*2+1];
507 					total *= 0.25;
508 				}
509 				else
510 					total = lightmapr[i];
511 				if (total > 255)
512 					total = 255;
513 				if (total < 0)
514 					Sys_Error("light < 0");
515 				surf->lightmap_rgb[i].r = byte(total);
516 
517 				if (r_extrasamples)
518 				{
519 					// filtered sample
520 					total = lightmapg[t*w*4+s*2] +
521 							lightmapg[t*2*w*2+s*2+1] +
522 							lightmapg[(t*2+1)*w*2+s*2] +
523 							lightmapg[(t*2+1)*w*2+s*2+1];
524 					total *= 0.25;
525 				}
526 				else
527 					total = lightmapg[i];
528 				if (total > 255)
529 					total = 255;
530 				if (total < 0)
531 					Sys_Error("light < 0");
532 				surf->lightmap_rgb[i].g = byte(total);
533 
534 				if (r_extrasamples)
535 				{
536 					// filtered sample
537 					total = lightmapb[t*w*4+s*2] +
538 							lightmapb[t*2*w*2+s*2+1] +
539 							lightmapb[(t*2+1)*w*2+s*2] +
540 							lightmapb[(t*2+1)*w*2+s*2+1];
541 					total *= 0.25;
542 				}
543 				else
544 					total = lightmapb[i];
545 				if (total > 255)
546 					total = 255;
547 				if (total < 0)
548 					Sys_Error("light < 0");
549 				surf->lightmap_rgb[i].b = byte(total);
550 			}
551 		}
552 	}
553 	else
554 	{
555 		if (surf->lightmap_rgb)
556 		{
557 			Z_Free(surf->lightmap_rgb);
558 			surf->lightmap_rgb = NULL;
559 		}
560 	}
561 
562 	if (surf->lightmap)
563 	{
564 		Z_Free(surf->lightmap);
565 	}
566 	surf->lightmap = (byte*)Z_Malloc(w * h);
567 	light_mem += w * h;
568 
569 	i = 0;
570 	for (t = 0; t < h; t++)
571 	{
572 		for (s = 0; s < w; s++, i++)
573 		{
574 			if (r_extrasamples)
575 			{
576 				// filtered sample
577 				total = lightmap[t*w*4+s*2] +
578 						lightmap[t*2*w*2+s*2+1] +
579 						lightmap[(t*2+1)*w*2+s*2] +
580 						lightmap[(t*2+1)*w*2+s*2+1];
581 				total *= 0.25;
582 			}
583 			else
584 				total = lightmap[i];
585 			if (total > 255)
586 				total = 255;
587 			if (total < 0)
588 				Sys_Error("light < 0");
589 			surf->lightmap[i] = byte(total);
590 		}
591 	}
592 	unguard;
593 }
594 
595 //**************************************************************************
596 //**
597 //**	DYNAMIC LIGHTS
598 //**
599 //**************************************************************************
600 
601 //==========================================================================
602 //
603 //	VRenderLevelShared::AllocDlight
604 //
605 //==========================================================================
606 
AllocDlight(VThinker * Owner)607 dlight_t* VRenderLevelShared::AllocDlight(VThinker* Owner)
608 {
609 	guard(VRenderLevelShared::AllocDlight);
610 	int			i;
611 	dlight_t*	dl;
612 
613 	// first look for an exact key match
614 	if (Owner)
615 	{
616 		dl = DLights;
617 		for (i = 0; i < MAX_DLIGHTS; i++, dl++)
618 		{
619 			if (dl->Owner == Owner)
620 			{
621 				memset(dl, 0, sizeof(*dl));
622 				dl->Owner = Owner;
623 				return dl;
624 			}
625 		}
626 	}
627 
628 	// then look for anything else
629 	dl = DLights;
630 	for (i = 0; i < MAX_DLIGHTS; i++, dl++)
631 	{
632 		if (dl->die < Level->Time)
633 		{
634 			memset(dl, 0, sizeof(*dl));
635 			dl->Owner = Owner;
636 			return dl;
637 		}
638 	}
639 
640 	int bestnum = 0;
641 	float bestdist = 0.0;
642 	for (i = 0; i < MAX_DLIGHTS; i++, dl++)
643 	{
644 		float dist = Length(dl->origin - cl->ViewOrg);
645 		if (dist > bestdist)
646 		{
647 			bestnum = i;
648 			bestdist = dist;
649 		}
650 	}
651 	dl = &DLights[bestnum];
652 	memset(dl, 0, sizeof(*dl));
653 	dl->Owner = Owner;
654 	return dl;
655 	unguard;
656 }
657 
658 //==========================================================================
659 //
660 //	VRenderLevelShared::DecayLights
661 //
662 //==========================================================================
663 
DecayLights(float time)664 void VRenderLevelShared::DecayLights(float time)
665 {
666 	guard(VRenderLevelShared::DecayLights);
667 	dlight_t* dl = DLights;
668 	for (int i = 0; i < MAX_DLIGHTS; i++, dl++)
669 	{
670 		if (dl->die < Level->Time || !dl->radius)
671 			continue;
672 
673 		dl->radius -= time * dl->decay;
674 		if (dl->radius < 0)
675 			dl->radius = 0;
676 	}
677 	unguard;
678 }
679 
680 //==========================================================================
681 //
682 //	VRenderLevel::MarkLights
683 //
684 //==========================================================================
685 
MarkLights(dlight_t * light,int bit,int bspnum)686 void VRenderLevel::MarkLights(dlight_t *light, int bit, int bspnum)
687 {
688 	guard(VRenderLevel::MarkLights);
689 	int leafnum;
690 
691     if (bspnum & NF_SUBSECTOR)
692     {
693 		int num;
694 
695 		if (bspnum == -1)
696 		    num = 0;
697 		else
698 		    num = bspnum & (~NF_SUBSECTOR);
699 		subsector_t *ss = &Level->Subsectors[num];
700 
701 		if (r_dynamic_clip)
702 		{
703 			vuint8* dyn_facevis = Level->LeafPVS(ss);
704 			leafnum = Level->PointInSubsector(light->origin) -
705 				Level->Subsectors;
706 
707 			// Check potential visibility
708 			if (!(dyn_facevis[leafnum >> 3] & (1 << (leafnum & 7))))
709 				return;
710 		}
711 
712 		if (ss->dlightframe != r_dlightframecount)
713 		{
714 			ss->dlightbits = 0;
715 			ss->dlightframe = r_dlightframecount;
716 		}
717 		ss->dlightbits |= bit;
718 	}
719 	else
720 	{
721 		node_t* node = &Level->Nodes[bspnum];
722 		float dist = DotProduct(light->origin, node->normal) - node->dist;
723 
724 		if (dist > -light->radius + light->minlight)
725 		{
726 			MarkLights(light, bit, node->children[0]);
727 		}
728 		if (dist < light->radius - light->minlight)
729 		{
730 			MarkLights(light, bit, node->children[1]);
731 		}
732 	}
733 	unguard;
734 }
735 
736 //==========================================================================
737 //
738 //	VRenderLevel::PushDlights
739 //
740 //==========================================================================
741 
PushDlights()742 void VRenderLevel::PushDlights()
743 {
744 	guard(VRenderLevel::PushDlights);
745 	if (GGameInfo->IsPaused())
746 	{
747 		return;
748 	}
749 	r_dlightframecount++;
750 
751 	if (!r_dynamic)
752 	{
753 		return;
754 	}
755 
756 	dlight_t* l = DLights;
757 	for (int i = 0; i < MAX_DLIGHTS; i++, l++)
758 	{
759 		if (l->die < Level->Time || !l->radius)
760 			continue;
761 		MarkLights(l, 1 << i, Level->NumNodes - 1);
762 	}
763 	unguard;
764 }
765 
766 //==========================================================================
767 //
768 //	VRenderLevel::LightPoint
769 //
770 //==========================================================================
771 
LightPoint(const TVec & p)772 vuint32 VRenderLevel::LightPoint(const TVec &p)
773 {
774 	guard(VRenderLevel::LightPoint);
775 	subsector_t		*sub;
776 	subregion_t		*reg;
777 	float			l, lr, lg, lb, d, add;
778 	int				i, s, t, ds, dt;
779 	surface_t		*surf;
780 	int				ltmp;
781 	rgb_t			*rgbtmp;
782 	int				leafnum;
783 
784 
785 	if (FixedLight)
786 	{
787 		return FixedLight | (FixedLight << 8) | (FixedLight << 16) | (FixedLight << 24);
788 	}
789 
790 	sub = Level->PointInSubsector(p);
791 	reg = sub->regions;
792 	while (reg->next)
793 	{
794 		d = DotProduct(p, reg->floor->secplane->normal) - reg->floor->secplane->dist;
795 
796 		if (d >= 0.0)
797 		{
798 			break;
799 		}
800 
801 		reg = reg->next;
802 	}
803 
804 	//	Region's base light
805 	l = reg->secregion->params->lightlevel + ExtraLight;
806 	if (r_darken)
807 	{
808 		l = light_remap[MIN(255, (int)l)];
809 	}
810 	l = MIN(255, l);
811 	int SecLightColour = reg->secregion->params->LightColour;
812 	lr = ((SecLightColour >> 16) & 255) * l / 255.0;
813 	lg = ((SecLightColour >> 8) & 255) * l / 255.0;
814 	lb = (SecLightColour & 255) * l / 255.0;
815 
816 	//	Light from floor's lightmap
817 	s = (int)(DotProduct(p, reg->floor->texinfo.saxis) + reg->floor->texinfo.soffs);
818 	t = (int)(DotProduct(p, reg->floor->texinfo.taxis) + reg->floor->texinfo.toffs);
819 	for (surf = reg->floor->surfs; surf; surf = surf->next)
820 	{
821 		if (!surf->lightmap)
822 		{
823 			continue;
824 		}
825 		if (s < surf->texturemins[0] ||	t < surf->texturemins[1])
826 		{
827 			continue;
828 		}
829 
830 		ds = s - surf->texturemins[0];
831 		dt = t - surf->texturemins[1];
832 
833 		if (ds > surf->extents[0] || dt > surf->extents[1])
834 		{
835 			continue;
836 		}
837 
838 		if (surf->lightmap_rgb)
839 		{
840 			l += surf->lightmap[(ds >> 4) + (dt >> 4) * ((surf->extents[0] >> 4) + 1)];
841 			rgbtmp = &surf->lightmap_rgb[(ds >> 4) + (dt >> 4) * ((surf->extents[0] >> 4) + 1)];
842 			lr += rgbtmp->r;
843 			lg += rgbtmp->g;
844 			lb += rgbtmp->b;
845 		}
846 		else
847 		{
848 			ltmp = surf->lightmap[(ds >> 4) + (dt >> 4) * ((surf->extents[0] >> 4) + 1)];
849 			l += ltmp;
850 			lr += ltmp;
851 			lg += ltmp;
852 			lb += ltmp;
853 		}
854 		break;
855 	}
856 
857 	//	Add dynamic lights
858 	if (sub->dlightframe == r_dlightframecount)
859 	{
860 		for (i = 0; i < MAX_DLIGHTS; i++)
861 		{
862 			if (!(sub->dlightbits & (1 << i)))
863 				continue;
864 			if (r_dynamic_clip)
865 			{
866 				vuint8* dyn_facevis = Level->LeafPVS(sub);
867 				leafnum = Level->PointInSubsector(DLights[i].origin) -
868 					Level->Subsectors;
869 
870 				// Check potential visibility
871 				if (!(dyn_facevis[leafnum >> 3] & (1 << (leafnum & 7))))
872 						continue;
873 			}
874 
875 			add = (DLights[i].radius - DLights[i].minlight) - Length(p - DLights[i].origin);
876 
877 			if (add > 0)
878 			{
879 				l += add;
880 				lr += add * ((DLights[i].colour >> 16) & 0xff) / 255.0;
881 				lg += add * ((DLights[i].colour >> 8) & 0xff) / 255.0;
882 				lb += add * (DLights[i].colour & 0xff) / 255.0;
883 			}
884 		}
885 	}
886 
887 	if (l > 255)
888 		l = 255;
889 	if (lr > 255)
890 		lr = 255;
891 	if (lg > 255)
892 		lg = 255;
893 	if (lb > 255)
894 		lb = 255;
895 
896 	return ((int)l << 24) | ((int)lr << 16) | ((int)lg << 8) | ((int)lb);
897 	unguard;
898 }
899 
900 //==========================================================================
901 //
902 //	VRenderLevel::AddDynamicLights
903 //
904 //==========================================================================
905 
AddDynamicLights(surface_t * surf)906 void VRenderLevel::AddDynamicLights(surface_t *surf)
907 {
908 	guard(VRenderLevel::AddDynamicLights);
909 	int			lnum;
910 	int			sd, td;
911 	float		dist, rad, minlight, rmul, gmul, bmul;
912 	TVec		impact, local;
913 	int			s, t, i;
914 	int			smax, tmax;
915 	texinfo_t	*tex;
916 	subsector_t *sub;
917 	int			leafnum;
918 
919 
920 	smax = (surf->extents[0] >> 4) + 1;
921 	tmax = (surf->extents[1] >> 4) + 1;
922 	tex = surf->texinfo;
923 
924 	for (lnum = 0; lnum < MAX_DLIGHTS; lnum++)
925 	{
926 		if (!(surf->dlightbits & (1<<lnum)))
927 			continue;		// not lit by this light
928 
929 		rad = DLights[lnum].radius;
930 		dist = DotProduct(DLights[lnum].origin, surf->plane->normal) -
931 				surf->plane->dist;
932 		if (r_dynamic_clip)
933 		{
934 			if (dist <= -0.1)
935 				continue;
936 		}
937 
938 		rad -= fabs(dist);
939 		minlight = DLights[lnum].minlight;
940 		if (rad < minlight)
941 			continue;
942 		minlight = rad - minlight;
943 
944 		impact = DLights[lnum].origin - surf->plane->normal * dist;
945 
946 		if (r_dynamic_clip)
947 		{
948 			sub = Level->PointInSubsector(*surf->verts);
949 			vuint8* dyn_facevis = Level->LeafPVS(sub);
950 			leafnum = Level->PointInSubsector(DLights[lnum].origin) -
951 				Level->Subsectors;
952 
953 			// Check potential visibility
954 			if (!(dyn_facevis[leafnum >> 3] & (1 << (leafnum & 7))))
955 				continue;
956 		}
957 
958 		rmul = (DLights[lnum].colour >> 16) & 0xff;
959 		gmul = (DLights[lnum].colour >> 8) & 0xff;
960 		bmul = DLights[lnum].colour & 0xff;
961 
962 		local.x = DotProduct(impact, tex->saxis) + tex->soffs;
963 		local.y = DotProduct(impact, tex->taxis) + tex->toffs;
964 
965 		local.x -= surf->texturemins[0];
966 		local.y -= surf->texturemins[1];
967 
968 		for (t = 0; t < tmax; t++)
969 		{
970 			td = (int)local.y - t * 16;
971 			if (td < 0)
972 				td = -td;
973 			for (s = 0; s < smax; s++)
974 			{
975 				sd = (int)local.x - s * 16;
976 				if (sd < 0)
977 					sd = -sd;
978 				if (sd > td)
979 					dist = sd + (td >> 1);
980 				else
981 					dist = td + (sd >> 1);
982 
983 				if (dist < minlight)
984 				{
985 					i = t * smax + s;
986 					blocklights[i] += (vuint32)((rad - dist) * 256);
987 					blocklightsr[i] += (vuint32)((rad - dist) * rmul);
988 					blocklightsg[i] += (vuint32)((rad - dist) * gmul);
989 					blocklightsb[i] += (vuint32)((rad - dist) * bmul);
990 					if (DLights[lnum].colour != 0xffffffff)
991 						is_coloured = true;
992 				}
993 			}
994 		}
995 	}
996 	unguard;
997 }
998 
999 //==========================================================================
1000 //
1001 //	VRenderLevel::BuildLightMap
1002 //
1003 //	Combine and scale multiple lightmaps into the 8.8 format in blocklights
1004 //
1005 //==========================================================================
1006 
BuildLightMap(surface_t * surf,int shift)1007 bool VRenderLevel::BuildLightMap(surface_t *surf, int shift)
1008 {
1009 	guard(VRenderLevel::BuildLightMap);
1010 	int			smax, tmax;
1011 	int			t;
1012 	int			i, size;
1013 	byte		*lightmap;
1014 	rgb_t		*lightmap_rgb;
1015 
1016 	is_coloured = false;
1017 	r_light_add = false;
1018 	smax = (surf->extents[0] >> 4) + 1;
1019 	tmax = (surf->extents[1] >> 4) + 1;
1020 	size = smax*tmax;
1021 	lightmap = surf->lightmap;
1022 	lightmap_rgb = surf->lightmap_rgb;
1023 
1024 	// clear to ambient
1025 	t = surf->Light >> 24;
1026 	t =	MAX(t, r_ambient);
1027  	t <<= 8;
1028 	int tR = ((surf->Light >> 16) & 255) * t / 255;
1029 	int tG = ((surf->Light >> 8) & 255) * t / 255;
1030 	int tB = (surf->Light & 255) * t / 255;
1031 	if (tR != tG || tR != tB)
1032 		is_coloured = true;
1033 	for (i = 0; i < size; i++)
1034 	{
1035 		blocklights[i] = t;
1036 		blocklightsr[i] = tR;
1037 		blocklightsg[i] = tG;
1038 		blocklightsb[i] = tB;
1039 		blockaddlightsr[i] = 0;
1040 		blockaddlightsg[i] = 0;
1041 		blockaddlightsb[i] = 0;
1042 	}
1043 
1044 	// add lightmap
1045 	if (lightmap_rgb)
1046 	{
1047 		if (!lightmap)
1048 		{
1049 			Sys_Error("RGB lightmap without uncoloured lightmap");
1050 		}
1051 		is_coloured = true;
1052 		for (i = 0; i < size; i++)
1053 		{
1054 			blocklights[i] += lightmap[i] << 8;
1055 			blocklightsr[i] += lightmap_rgb[i].r << 8;
1056 			blocklightsg[i] += lightmap_rgb[i].g << 8;
1057 			blocklightsb[i] += lightmap_rgb[i].b << 8;
1058 			if (!r_static_add)
1059 			{
1060 				if (blocklightsr[i] > 0xffff)
1061 					blocklightsr[i] = 0xffff;
1062 				if (blocklightsg[i] > 0xffff)
1063 					blocklightsg[i] = 0xffff;
1064 				if (blocklightsb[i] > 0xffff)
1065 					blocklightsb[i] = 0xffff;
1066 			}
1067 		}
1068 	}
1069 	else if (lightmap)
1070 	{
1071 		for (i = 0; i < size; i++)
1072 		{
1073 			t = lightmap[i] << 8;
1074 			blocklights[i] += t;
1075 			blocklightsr[i] += t;
1076 			blocklightsg[i] += t;
1077 			blocklightsb[i] += t;
1078 			if (!r_static_add)
1079 			{
1080 				if (blocklightsr[i] > 0xffff)
1081 					blocklightsr[i] = 0xffff;
1082 				if (blocklightsg[i] > 0xffff)
1083 					blocklightsg[i] = 0xffff;
1084 				if (blocklightsb[i] > 0xffff)
1085 					blocklightsb[i] = 0xffff;
1086 			}
1087 		}
1088 	}
1089 
1090 	// add all the dynamic lights
1091 	if (surf->dlightframe == r_dlightframecount)
1092 		AddDynamicLights(surf);
1093 
1094 	//  Calc additive light. This must be done before lightmap procesing
1095 	// because it will clamp all lights
1096 	if (!shift)
1097 	{
1098 		for (i = 0; i < size; i++)
1099 		{
1100 			t = blocklightsr[i] - 0x10000;
1101 			if (t > 0)
1102 			{
1103 				t = int(r_specular * t);
1104 				if (t > 0xffff)
1105 					t = 0xffff;
1106 				blockaddlightsr[i] = t;
1107 				r_light_add = true;
1108 			}
1109 
1110 			t = blocklightsg[i] - 0x10000;
1111 			if (t > 0)
1112 			{
1113 				t = int(r_specular * t);
1114 				if (t > 0xffff)
1115 					t = 0xffff;
1116 				blockaddlightsg[i] = t;
1117 				r_light_add = true;
1118 			}
1119 
1120 			t = blocklightsb[i] - 0x10000;
1121 			if (t > 0)
1122 			{
1123 				t = int(r_specular * t);
1124 				if (t > 0xffff)
1125 					t = 0xffff;
1126 				blockaddlightsb[i] = t;
1127 				r_light_add = true;
1128 			}
1129 		}
1130 	}
1131 
1132 	// bound, invert, and shift
1133 	int minlight = 1 << (8 - shift);
1134 	for (i = 0; i < size; i++)
1135 	{
1136 		t = (255 * 256 - (int)blocklights[i]) >> shift;
1137 		if (t < minlight)
1138 			t = minlight;
1139 		blocklights[i] = t;
1140 
1141 		t = (255 * 256 - (int)blocklightsr[i]) >> shift;
1142 		if (t < minlight)
1143 			t = minlight;
1144 		blocklightsr[i] = t;
1145 
1146 		t = (255 * 256 - (int)blocklightsg[i]) >> shift;
1147 		if (t < minlight)
1148 			t = minlight;
1149 		blocklightsg[i] = t;
1150 
1151 		t = (255 * 256 - (int)blocklightsb[i]) >> shift;
1152 		if (t < minlight)
1153 			t = minlight;
1154 		blocklightsb[i] = t;
1155 	}
1156 
1157 	return is_coloured;
1158 	unguard;
1159 }
1160 
1161 //==========================================================================
1162 //
1163 //	VRenderLevel::FlushCaches
1164 //
1165 //==========================================================================
1166 
FlushCaches()1167 void VRenderLevel::FlushCaches()
1168 {
1169 	guard(VRenderLevel::FlushCaches);
1170 	memset(blockbuf, 0, sizeof(blockbuf));
1171 	freeblocks = NULL;
1172 	for (int i = 0; i < NUM_CACHE_BLOCKS; i++)
1173 	{
1174 		blockbuf[i].chain = freeblocks;
1175 		freeblocks = &blockbuf[i];
1176 	}
1177 	for (int i = 0; i < NUM_BLOCK_SURFS; i++)
1178 	{
1179 		cacheblocks[i] = freeblocks;
1180 		freeblocks = freeblocks->chain;
1181 		cacheblocks[i]->width = BLOCK_WIDTH;
1182 		cacheblocks[i]->height = BLOCK_HEIGHT;
1183 		cacheblocks[i]->blocknum = i;
1184 	}
1185 	unguard;
1186 }
1187 
1188 //==========================================================================
1189 //
1190 //	VRenderLevel::FlushOldCaches
1191 //
1192 //==========================================================================
1193 
FlushOldCaches()1194 void VRenderLevel::FlushOldCaches()
1195 {
1196 	guard(VRenderLevel::FlushOldCaches);
1197 	int				i;
1198 	surfcache_t		*blines;
1199 	surfcache_t		*block;
1200 
1201 	for (i = 0; i < NUM_BLOCK_SURFS; i++)
1202 	{
1203 		for (blines = cacheblocks[i]; blines; blines = blines->bnext)
1204 		{
1205 			for (block = blines; block; block = block->lnext)
1206 			{
1207 				if (block->owner && cacheframecount != block->lastframe)
1208 				{
1209 					block = FreeBlock(block, false);
1210 				}
1211 			}
1212 			if (!blines->owner && !blines->lprev && !blines->lnext)
1213 			{
1214 				blines = FreeBlock(blines, true);
1215 			}
1216 		}
1217 	}
1218 	if (!freeblocks)
1219 	{
1220 		Sys_Error("No more free blocks");
1221 	}
1222 	unguard;
1223 }
1224 
1225 //==========================================================================
1226 //
1227 //	VRenderLevel::AllocBlock
1228 //
1229 //==========================================================================
1230 
AllocBlock(int width,int height)1231 surfcache_t* VRenderLevel::AllocBlock(int width, int height)
1232 {
1233 	guard(VRenderLevel::AllocBlock);
1234 	int				i;
1235 	surfcache_t*	blines;
1236 	surfcache_t*	block;
1237 	surfcache_t*	other;
1238 
1239 	for (i = 0; i < NUM_BLOCK_SURFS; i++)
1240 	{
1241 		for (blines = cacheblocks[i]; blines; blines = blines->bnext)
1242 		{
1243 			if (blines->height != height)
1244 			{
1245 				continue;
1246 			}
1247 			for (block = blines; block; block = block->lnext)
1248 			{
1249 				if (block->owner)
1250 				{
1251 					continue;
1252 				}
1253 				if (block->width < width)
1254 				{
1255 					continue;
1256 				}
1257 				if (block->width > width)
1258 				{
1259 					if (!freeblocks)
1260 					{
1261 						FlushOldCaches();
1262 					}
1263 					other = freeblocks;
1264 					freeblocks = other->chain;
1265 					other->s = block->s + width;
1266 					other->t = block->t;
1267 					other->width = block->width - width;
1268 					other->height = block->height;
1269 					other->lnext = block->lnext;
1270 					if (other->lnext)
1271 					{
1272 						other->lnext->lprev = other;
1273 					}
1274 					block->lnext = other;
1275 					other->lprev = block;
1276 					block->width = width;
1277 					other->owner = NULL;
1278 					other->blocknum = i;
1279 				}
1280 				return block;
1281 			}
1282 		}
1283 	}
1284 
1285 	for (i = 0; i < NUM_BLOCK_SURFS; i++)
1286 	{
1287 		for (blines = cacheblocks[i]; blines; blines = blines->bnext)
1288 		{
1289 			if (blines->height < height)
1290 			{
1291 				continue;
1292 			}
1293 			if (blines->lnext)
1294 			{
1295 				continue;
1296 			}
1297 
1298 			block = blines;
1299 			if (block->height > height)
1300 			{
1301 				if (!freeblocks)
1302 				{
1303 					FlushOldCaches();
1304 				}
1305 				other = freeblocks;
1306 				freeblocks = other->chain;
1307 				other->s = 0;
1308 				other->t = block->t + height;
1309 				other->width = block->width;
1310 				other->height = block->height - height;
1311 				other->lnext = NULL;
1312 				other->lprev = NULL;
1313 				other->bnext = block->bnext;
1314 				if (other->bnext)
1315 				{
1316 					other->bnext->bprev = other;
1317 				}
1318 				block->bnext = other;
1319 				other->bprev = block;
1320 				block->height = height;
1321 				other->owner = NULL;
1322 				other->blocknum = i;
1323 			}
1324 
1325 			if (!freeblocks)
1326 			{
1327 				FlushOldCaches();
1328 			}
1329 			other = freeblocks;
1330 			freeblocks = other->chain;
1331 			other->s = block->s + width;
1332 			other->t = block->t;
1333 			other->width = block->width - width;
1334 			other->height = block->height;
1335 			other->lnext = NULL;
1336 			block->lnext = other;
1337 			other->lprev = block;
1338 			block->width = width;
1339 			other->owner = NULL;
1340 			other->blocknum = i;
1341 
1342 			return block;
1343 		}
1344 	}
1345 
1346 	Sys_Error("overflow");
1347 	return NULL;
1348 	unguard;
1349 }
1350 
1351 //==========================================================================
1352 //
1353 //	VRenderLevel::FreeBlock
1354 //
1355 //==========================================================================
1356 
FreeBlock(surfcache_t * block,bool check_lines)1357 surfcache_t* VRenderLevel::FreeBlock(surfcache_t *block, bool check_lines)
1358 {
1359 	guard(VRenderLevel::FreeBlock);
1360 	surfcache_t		*other;
1361 
1362 	if (block->owner)
1363 	{
1364 		*block->owner = NULL;
1365 		block->owner = NULL;
1366 	}
1367 	if (block->lnext && !block->lnext->owner)
1368 	{
1369 		other = block->lnext;
1370 		block->width += other->width;
1371 		block->lnext = other->lnext;
1372 		if (block->lnext)
1373 		{
1374 			block->lnext->lprev = block;
1375 		}
1376 		other->chain = freeblocks;
1377 		freeblocks = other;
1378 	}
1379 	if (block->lprev && !block->lprev->owner)
1380 	{
1381 		other = block;
1382 		block = block->lprev;
1383 		block->width += other->width;
1384 		block->lnext = other->lnext;
1385 		if (block->lnext)
1386 		{
1387 			block->lnext->lprev = block;
1388 		}
1389 		other->chain = freeblocks;
1390 		freeblocks = other;
1391 	}
1392 
1393 	if (block->lprev || block->lnext || !check_lines)
1394 	{
1395 		return block;
1396 	}
1397 
1398 	if (block->bnext && !block->bnext->lnext)
1399 	{
1400 		other = block->bnext;
1401 		block->height += other->height;
1402 		block->bnext = other->bnext;
1403 		if (block->bnext)
1404 		{
1405 			block->bnext->bprev = block;
1406 		}
1407 		other->chain = freeblocks;
1408 		freeblocks = other;
1409 	}
1410 	if (block->bprev && !block->bprev->lnext)
1411 	{
1412 		other = block;
1413 		block = block->bprev;
1414 		block->height += other->height;
1415 		block->bnext = other->bnext;
1416 		if (block->bnext)
1417 		{
1418 			block->bnext->bprev = block;
1419 		}
1420 		other->chain = freeblocks;
1421 		freeblocks = other;
1422 	}
1423 	return block;
1424 	unguard;
1425 }
1426 
1427 //==========================================================================
1428 //
1429 //	VRenderLevel::FreeSurfCache
1430 //
1431 //==========================================================================
1432 
FreeSurfCache(surfcache_t * block)1433 void VRenderLevel::FreeSurfCache(surfcache_t *block)
1434 {
1435 	guard(VRenderLevel::FreeSurfCache);
1436 	FreeBlock(block, true);
1437 	unguard;
1438 }
1439 
1440 //==========================================================================
1441 //
1442 //	VRenderLevelShared::FreeSurfCache
1443 //
1444 //==========================================================================
1445 
FreeSurfCache(surfcache_t *)1446 void VRenderLevelShared::FreeSurfCache(surfcache_t*)
1447 {
1448 }
1449 
1450 //==========================================================================
1451 //
1452 //	VRenderLevel::CacheSurface
1453 //
1454 //==========================================================================
1455 
CacheSurface(surface_t * surface)1456 void VRenderLevel::CacheSurface(surface_t *surface)
1457 {
1458 	guard(VRenderLevel::CacheSurface);
1459 	surfcache_t     *cache;
1460 	int				smax, tmax;
1461 	int				i, j, bnum;
1462 
1463 	//
1464 	// see if the cache holds appropriate data
1465 	//
1466 	cache = surface->CacheSurf;
1467 
1468 	if (cache && !cache->dlight && surface->dlightframe != r_dlightframecount
1469 			&& cache->Light == surface->Light)
1470 	{
1471 		bnum = cache->blocknum;
1472 		cache->chain = light_chain[bnum];
1473 		light_chain[bnum] = cache;
1474 		cache->lastframe = cacheframecount;
1475 		return;
1476 	}
1477 
1478 	//
1479 	// determine shape of surface
1480 	//
1481 	smax = (surface->extents[0] >> 4) + 1;
1482 	tmax = (surface->extents[1] >> 4) + 1;
1483 
1484 	//
1485 	// allocate memory if needed
1486 	//
1487 	if (!cache)     // if a texture just animated, don't reallocate it
1488 	{
1489 		cache = AllocBlock(smax, tmax);
1490 		surface->CacheSurf = cache;
1491 		cache->owner = &surface->CacheSurf;
1492 		cache->surf = surface;
1493 	}
1494 
1495 	if (surface->dlightframe == r_dlightframecount)
1496 		cache->dlight = 1;
1497 	else
1498 		cache->dlight = 0;
1499 	cache->Light = surface->Light;
1500 
1501 	// calculate the lightings
1502 	BuildLightMap(surface, 0);
1503 	bnum = cache->blocknum;
1504 	block_changed[bnum] = true;
1505 
1506 	for (j = 0; j < tmax; j++)
1507 	{
1508 		for (i = 0; i < smax; i++)
1509 		{
1510 			rgba_t &lb = light_block[bnum][(j + cache->t) * BLOCK_WIDTH +
1511 				i + cache->s];
1512 			lb.r = 255 - byte(blocklightsr[j * smax + i] >> 8);
1513 			lb.g = 255 - byte(blocklightsg[j * smax + i] >> 8);
1514 			lb.b = 255 - byte(blocklightsb[j * smax + i] >> 8);
1515 			lb.a = 255;
1516 		}
1517 	}
1518 	cache->chain = light_chain[bnum];
1519 	light_chain[bnum] = cache;
1520 	cache->lastframe = cacheframecount;
1521 
1522 	// specular highlights
1523 	for (j = 0; j < tmax; j++)
1524 	{
1525 		for (i = 0; i < smax; i++)
1526 		{
1527 			rgba_t &lb = add_block[bnum][(j + cache->t) * BLOCK_WIDTH +
1528 				i + cache->s];
1529 			lb.r = byte(blockaddlightsr[j * smax + i] >> 8);
1530 			lb.g = byte(blockaddlightsg[j * smax + i] >> 8);
1531 			lb.b = byte(blockaddlightsb[j * smax + i] >> 8);
1532 			lb.a = 255;
1533 		}
1534 	}
1535 	if (r_light_add)
1536 	{
1537 		cache->addchain = add_chain[bnum];
1538 		add_chain[bnum] = cache;
1539 		add_changed[bnum] = true;
1540 	}
1541 	unguard;
1542 }
1543