1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  r_bsp.c
12 /// \brief BSP traversal, handling of LineSegs for rendering
13 
14 #include "doomdef.h"
15 #include "g_game.h"
16 #include "r_local.h"
17 #include "r_state.h"
18 #include "r_portal.h" // Add seg portals
19 
20 #include "r_splats.h"
21 #include "p_local.h" // camera
22 #include "p_slopes.h"
23 #include "z_zone.h" // Check R_Prep3DFloors
24 #include "taglist.h"
25 
26 seg_t *curline;
27 side_t *sidedef;
28 line_t *linedef;
29 sector_t *frontsector;
30 sector_t *backsector;
31 
32 // very ugly realloc() of drawsegs at run-time, I upped it to 512
33 // instead of 256.. and someone managed to send me a level with
34 // 896 drawsegs! So too bad here's a limit removal a-la-Boom
35 drawseg_t *curdrawsegs = NULL; /**< This is used to handle multiple lists for masked drawsegs. */
36 drawseg_t *drawsegs = NULL;
37 drawseg_t *ds_p = NULL;
38 
39 // indicates doors closed wrt automap bugfix:
40 INT32 doorclosed;
41 
42 //
43 // R_ClearDrawSegs
44 //
R_ClearDrawSegs(void)45 void R_ClearDrawSegs(void)
46 {
47 	ds_p = drawsegs;
48 }
49 
50 // Fix from boom.
51 #define MAXSEGS (MAXVIDWIDTH/2+1)
52 
53 // newend is one past the last valid seg
54 static cliprange_t *newend;
55 static cliprange_t solidsegs[MAXSEGS];
56 
57 //
58 // R_ClipSolidWallSegment
59 // Does handle solid walls,
60 //  e.g. single sided LineDefs (middle texture)
61 //  that entirely block the view.
62 //
R_ClipSolidWallSegment(INT32 first,INT32 last)63 static void R_ClipSolidWallSegment(INT32 first, INT32 last)
64 {
65 	cliprange_t *next;
66 	cliprange_t *start;
67 
68 	// Find the first range that touches the range (adjacent pixels are touching).
69 	start = solidsegs;
70 	while (start->last < first - 1)
71 		start++;
72 
73 	if (first < start->first)
74 	{
75 		if (last < start->first - 1)
76 		{
77 			// Post is entirely visible (above start), so insert a new clippost.
78 			R_StoreWallRange(first, last);
79 			next = newend;
80 			newend++;
81 			// NO MORE CRASHING!
82 			if (newend - solidsegs > MAXSEGS)
83 				I_Error("R_ClipSolidWallSegment: Solid Segs overflow!\n");
84 
85 			while (next != start)
86 			{
87 				*next = *(next-1);
88 				next--;
89 			}
90 			next->first = first;
91 			next->last = last;
92 			return;
93 		}
94 
95 		// There is a fragment above *start.
96 		R_StoreWallRange(first, start->first - 1);
97 		// Now adjust the clip size.
98 		start->first = first;
99 	}
100 
101 	// Bottom contained in start?
102 	if (last <= start->last)
103 		return;
104 
105 	next = start;
106 	while (last >= (next+1)->first - 1)
107 	{
108 		// There is a fragment between two posts.
109 		R_StoreWallRange(next->last + 1, (next+1)->first - 1);
110 		next++;
111 
112 		if (last <= next->last)
113 		{
114 			// Bottom is contained in next.
115 			// Adjust the clip size.
116 			start->last = next->last;
117 			goto crunch;
118 		}
119 	}
120 
121 	// There is a fragment after *next.
122 	R_StoreWallRange(next->last + 1, last);
123 	// Adjust the clip size.
124 	start->last = last;
125 
126 	// Remove start+1 to next from the clip list, because start now covers their area.
127 crunch:
128 	if (next == start)
129 		return; // Post just extended past the bottom of one post.
130 
131 	while (next++ != newend)
132 		*++start = *next; // Remove a post.
133 
134 	newend = start + 1;
135 
136 	// NO MORE CRASHING!
137 	if (newend - solidsegs > MAXSEGS)
138 		I_Error("R_ClipSolidWallSegment: Solid Segs overflow!\n");
139 }
140 
141 //
142 // R_ClipPassWallSegment
143 // Clips the given range of columns, but does not include it in the clip list.
144 // Does handle windows, e.g. LineDefs with upper and lower texture.
145 //
R_ClipPassWallSegment(INT32 first,INT32 last)146 static inline void R_ClipPassWallSegment(INT32 first, INT32 last)
147 {
148 	cliprange_t *start;
149 
150 	// Find the first range that touches the range
151 	//  (adjacent pixels are touching).
152 	start = solidsegs;
153 	while (start->last < first - 1)
154 		start++;
155 
156 	if (first < start->first)
157 	{
158 		if (last < start->first - 1)
159 		{
160 			// Post is entirely visible (above start).
161 			R_StoreWallRange(first, last);
162 			return;
163 		}
164 
165 		// There is a fragment above *start.
166 		R_StoreWallRange(first, start->first - 1);
167 	}
168 
169 	// Bottom contained in start?
170 	if (last <= start->last)
171 		return;
172 
173 	while (last >= (start+1)->first - 1)
174 	{
175 		// There is a fragment between two posts.
176 		R_StoreWallRange(start->last + 1, (start+1)->first - 1);
177 		start++;
178 
179 		if (last <= start->last)
180 			return;
181 	}
182 
183 	// There is a fragment after *next.
184 	R_StoreWallRange(start->last + 1, last);
185 }
186 
187 //
188 // R_ClearClipSegs
189 //
R_ClearClipSegs(void)190 void R_ClearClipSegs(void)
191 {
192 	solidsegs[0].first = -0x7fffffff;
193 	solidsegs[0].last = -1;
194 	solidsegs[1].first = viewwidth;
195 	solidsegs[1].last = 0x7fffffff;
196 	newend = solidsegs + 2;
197 }
R_PortalClearClipSegs(INT32 start,INT32 end)198 void R_PortalClearClipSegs(INT32 start, INT32 end)
199 {
200 	solidsegs[0].first = -0x7fffffff;
201 	solidsegs[0].last = start-1;
202 	solidsegs[1].first = end;
203 	solidsegs[1].last = 0x7fffffff;
204 	newend = solidsegs + 2;
205 }
206 
207 
208 // R_DoorClosed
209 //
210 // This function is used to fix the automap bug which
211 // showed lines behind closed doors simply because the door had a dropoff.
212 //
213 // It assumes that Doom has already ruled out a door being closed because
214 // of front-back closure (e.g. front floor is taller than back ceiling).
R_DoorClosed(void)215 static INT32 R_DoorClosed(void)
216 {
217 	return
218 
219 	// if door is closed because back is shut:
220 	backsector->ceilingheight <= backsector->floorheight
221 
222 	// preserve a kind of transparent door/lift special effect:
223 	&& (backsector->ceilingheight >= frontsector->ceilingheight || curline->sidedef->toptexture)
224 
225 	&& (backsector->floorheight <= frontsector->floorheight || curline->sidedef->bottomtexture);
226 }
227 
228 //
229 // If player's view height is underneath fake floor, lower the
230 // drawn ceiling to be just under the floor height, and replace
231 // the drawn floor and ceiling textures, and light level, with
232 // the control sector's.
233 //
234 // Similar for ceiling, only reflected.
235 //
R_FakeFlat(sector_t * sec,sector_t * tempsec,INT32 * floorlightlevel,INT32 * ceilinglightlevel,boolean back)236 sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
237 	INT32 *ceilinglightlevel, boolean back)
238 {
239 	if (floorlightlevel)
240 		*floorlightlevel = sec->floorlightsec == -1 ?
241 			sec->lightlevel : sectors[sec->floorlightsec].lightlevel;
242 
243 	if (ceilinglightlevel)
244 		*ceilinglightlevel = sec->ceilinglightsec == -1 ?
245 			sec->lightlevel : sectors[sec->ceilinglightsec].lightlevel;
246 
247 	// if (sec->midmap != -1)
248 	//	mapnum = sec->midmap;
249 	// In original colormap code, this block did not run if sec->midmap was set
250 	if (!sec->extra_colormap && sec->heightsec != -1)
251 	{
252 		const sector_t *s = &sectors[sec->heightsec];
253 		mobj_t *viewmobj = viewplayer->mo;
254 		INT32 heightsec;
255 		boolean underwater;
256 
257 		if (splitscreen && viewplayer == &players[secondarydisplayplayer] && camera2.chase)
258 			heightsec = R_PointInSubsector(camera2.x, camera2.y)->sector->heightsec;
259 		else if (camera.chase && viewplayer == &players[displayplayer])
260 			heightsec = R_PointInSubsector(camera.x, camera.y)->sector->heightsec;
261 		else if (viewmobj)
262 			heightsec = R_PointInSubsector(viewmobj->x, viewmobj->y)->sector->heightsec;
263 		else
264 			return sec;
265 		underwater = heightsec != -1 && viewz <= sectors[heightsec].floorheight;
266 
267 		// Replace sector being drawn, with a copy to be hacked
268 		*tempsec = *sec;
269 
270 		// Replace floor and ceiling height with other sector's heights.
271 		tempsec->floorheight = s->floorheight;
272 		tempsec->ceilingheight = s->ceilingheight;
273 
274 		if ((underwater && (tempsec->  floorheight = sec->floorheight,
275 			tempsec->ceilingheight = s->floorheight - 1, !back)) || viewz <= s->floorheight)
276 		{ // head-below-floor hack
277 			tempsec->floorpic = s->floorpic;
278 			tempsec->floor_xoffs = s->floor_xoffs;
279 			tempsec->floor_yoffs = s->floor_yoffs;
280 			tempsec->floorpic_angle = s->floorpic_angle;
281 
282 			if (underwater)
283 			{
284 				if (s->ceilingpic == skyflatnum)
285 				{
286 					tempsec->floorheight = tempsec->ceilingheight+1;
287 					tempsec->ceilingpic = tempsec->floorpic;
288 					tempsec->ceiling_xoffs = tempsec->floor_xoffs;
289 					tempsec->ceiling_yoffs = tempsec->floor_yoffs;
290 					tempsec->ceilingpic_angle = tempsec->floorpic_angle;
291 				}
292 				else
293 				{
294 					tempsec->ceilingpic = s->ceilingpic;
295 					tempsec->ceiling_xoffs = s->ceiling_xoffs;
296 					tempsec->ceiling_yoffs = s->ceiling_yoffs;
297 					tempsec->ceilingpic_angle = s->ceilingpic_angle;
298 				}
299 			}
300 
301 			tempsec->lightlevel = s->lightlevel;
302 
303 			if (floorlightlevel)
304 				*floorlightlevel = s->floorlightsec == -1 ? s->lightlevel
305 					: sectors[s->floorlightsec].lightlevel;
306 
307 			if (ceilinglightlevel)
308 				*ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel
309 					: sectors[s->ceilinglightsec].lightlevel;
310 		}
311 		else if (heightsec != -1 && viewz >= sectors[heightsec].ceilingheight
312 			&& sec->ceilingheight > s->ceilingheight)
313 		{ // Above-ceiling hack
314 			tempsec->ceilingheight = s->ceilingheight;
315 			tempsec->floorheight = s->ceilingheight + 1;
316 
317 			tempsec->floorpic = tempsec->ceilingpic = s->ceilingpic;
318 			tempsec->floor_xoffs = tempsec->ceiling_xoffs = s->ceiling_xoffs;
319 			tempsec->floor_yoffs = tempsec->ceiling_yoffs = s->ceiling_yoffs;
320 			tempsec->floorpic_angle = tempsec->ceilingpic_angle = s->ceilingpic_angle;
321 
322 			if (s->floorpic == skyflatnum) // SKYFIX?
323 			{
324 				tempsec->ceilingheight = tempsec->floorheight-1;
325 				tempsec->floorpic = tempsec->ceilingpic;
326 				tempsec->floor_xoffs = tempsec->ceiling_xoffs;
327 				tempsec->floor_yoffs = tempsec->ceiling_yoffs;
328 				tempsec->floorpic_angle = tempsec->ceilingpic_angle;
329 			}
330 			else
331 			{
332 				tempsec->ceilingheight = sec->ceilingheight;
333 				tempsec->floorpic = s->floorpic;
334 				tempsec->floor_xoffs = s->floor_xoffs;
335 				tempsec->floor_yoffs = s->floor_yoffs;
336 				tempsec->floorpic_angle = s->floorpic_angle;
337 			}
338 
339 			tempsec->lightlevel = s->lightlevel;
340 
341 			if (floorlightlevel)
342 				*floorlightlevel = s->floorlightsec == -1 ? s->lightlevel :
343 			sectors[s->floorlightsec].lightlevel;
344 
345 			if (ceilinglightlevel)
346 				*ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel :
347 			sectors[s->ceilinglightsec].lightlevel;
348 		}
349 		sec = tempsec;
350 	}
351 
352 	return sec;
353 }
354 
R_IsEmptyLine(seg_t * line,sector_t * front,sector_t * back)355 boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back)
356 {
357 	return (
358 		!line->polyseg &&
359 		back->ceilingpic == front->ceilingpic
360 		&& back->floorpic == front->floorpic
361 		&& back->f_slope == front->f_slope
362 		&& back->c_slope == front->c_slope
363 		&& back->lightlevel == front->lightlevel
364 		&& !line->sidedef->midtexture
365 		// Check offsets too!
366 		&& back->floor_xoffs == front->floor_xoffs
367 		&& back->floor_yoffs == front->floor_yoffs
368 		&& back->floorpic_angle == front->floorpic_angle
369 		&& back->ceiling_xoffs == front->ceiling_xoffs
370 		&& back->ceiling_yoffs == front->ceiling_yoffs
371 		&& back->ceilingpic_angle == front->ceilingpic_angle
372 		// Consider altered lighting.
373 		&& back->floorlightsec == front->floorlightsec
374 		&& back->ceilinglightsec == front->ceilinglightsec
375 		// Consider colormaps
376 		&& back->extra_colormap == front->extra_colormap
377 		&& ((!front->ffloors && !back->ffloors)
378 		|| Tag_Compare(&front->tags, &back->tags)));
379 }
380 
381 //
382 // R_AddLine
383 // Clips the given segment and adds any visible pieces to the line list.
384 //
R_AddLine(seg_t * line)385 static void R_AddLine(seg_t *line)
386 {
387 	INT32 x1, x2;
388 	angle_t angle1, angle2, span, tspan;
389 	static sector_t tempsec;
390 	boolean bothceilingssky = false, bothfloorssky = false;
391 
392 	portalline = false;
393 
394 	if (line->polyseg && !(line->polyseg->flags & POF_RENDERSIDES))
395 		return;
396 
397 	// big room fix
398 	angle1 = R_PointToAngleEx(viewx, viewy, line->v1->x, line->v1->y);
399 	angle2 = R_PointToAngleEx(viewx, viewy, line->v2->x, line->v2->y);
400 	curline = line;
401 
402 	// Clip to view edges.
403 	span = angle1 - angle2;
404 
405 	// Back side? i.e. backface culling?
406 	if (span >= ANGLE_180)
407 		return;
408 
409 	// Global angle needed by segcalc.
410 	rw_angle1 = angle1;
411 	angle1 -= viewangle;
412 	angle2 -= viewangle;
413 
414 	tspan = angle1 + clipangle;
415 	if (tspan > doubleclipangle)
416 	{
417 		tspan -= doubleclipangle;
418 
419 		// Totally off the left edge?
420 		if (tspan >= span)
421 			return;
422 
423 		angle1 = clipangle;
424 	}
425 	tspan = clipangle - angle2;
426 	if (tspan > doubleclipangle)
427 	{
428 		tspan -= doubleclipangle;
429 
430 		// Totally off the left edge?
431 		if (tspan >= span)
432 			return;
433 
434 		angle2 = -(signed)clipangle;
435 	}
436 
437 	// The seg is in the view range, but not necessarily visible.
438 	angle1 = (angle1+ANGLE_90)>>ANGLETOFINESHIFT;
439 	angle2 = (angle2+ANGLE_90)>>ANGLETOFINESHIFT;
440 	x1 = viewangletox[angle1];
441 	x2 = viewangletox[angle2];
442 
443 	// Does not cross a pixel?
444 	if (x1 >= x2)       // killough 1/31/98 -- change == to >= for robustness
445 		return;
446 
447 	backsector = line->backsector;
448 
449 	// Portal line
450 	if (line->linedef->special == 40 && line->side == 0)
451 	{
452 		// Render portal if recursiveness limit hasn't been reached.
453 		// Otherwise, render the wall normally.
454 		if (portalrender < cv_maxportals.value)
455 		{
456 			size_t p;
457 			mtag_t tag = Tag_FGet(&line->linedef->tags);
458 			INT32 li1 = line->linedef-lines;
459 			INT32 li2;
460 
461 			for (p = 0; (li2 = Tag_Iterate_Lines(tag, p)) >= 0; p++)
462 			{
463 				// Skip invalid lines.
464 				if ((tag != Tag_FGet(&lines[li2].tags)) || (lines[li1].special != lines[li2].special) || (li1 == li2))
465 					continue;
466 
467 				Portal_Add2Lines(li1, li2, x1, x2);
468 				goto clipsolid;
469 			}
470 		}
471 	}
472 
473 	// Single sided line?
474 	if (!backsector)
475 		goto clipsolid;
476 
477 	backsector = R_FakeFlat(backsector, &tempsec, NULL, NULL, true);
478 
479 	doorclosed = 0;
480 
481 	if (backsector->ceilingpic == skyflatnum && frontsector->ceilingpic == skyflatnum)
482 		bothceilingssky = true;
483 	if (backsector->floorpic == skyflatnum && frontsector->floorpic == skyflatnum)
484 		bothfloorssky = true;
485 
486 	if (bothceilingssky && bothfloorssky) // everything's sky? let's save us a bit of time then
487 	{
488 		if (!line->polyseg &&
489 			!line->sidedef->midtexture
490 			&& ((!frontsector->ffloors && !backsector->ffloors)
491 				|| Tag_Compare(&frontsector->tags, &backsector->tags)))
492 			return; // line is empty, don't even bother
493 
494 		goto clippass; // treat like wide open window instead
495 	}
496 
497 	// Closed door.
498 	if (frontsector->f_slope || frontsector->c_slope || backsector->f_slope || backsector->c_slope)
499 	{
500 		fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends
501 		fixed_t backf1, backf2, backc1, backc2; // back floor ceiling ends
502 #define SLOPEPARAMS(slope, end1, end2, normalheight) \
503 		end1 = P_GetZAt(slope, line->v1->x, line->v1->y, normalheight); \
504 		end2 = P_GetZAt(slope, line->v2->x, line->v2->y, normalheight);
505 
506 		SLOPEPARAMS(frontsector->f_slope, frontf1, frontf2, frontsector->  floorheight)
507 		SLOPEPARAMS(frontsector->c_slope, frontc1, frontc2, frontsector->ceilingheight)
508 		SLOPEPARAMS( backsector->f_slope,  backf1,  backf2,  backsector->  floorheight)
509 		SLOPEPARAMS( backsector->c_slope,  backc1,  backc2,  backsector->ceilingheight)
510 #undef SLOPEPARAMS
511 		// if both ceilings are skies, consider it always "open"
512 		// same for floors
513 		if (!bothceilingssky && !bothfloorssky)
514 		{
515 			if ((backc1 <= frontf1 && backc2 <= frontf2)
516 				|| (backf1 >= frontc1 && backf2 >= frontc2))
517 			{
518 				goto clipsolid;
519 			}
520 
521 			// Check for automap fix. Store in doorclosed for r_segs.c
522 			doorclosed = (backc1 <= backf1 && backc2 <= backf2
523 			&& ((backc1 >= frontc1 && backc2 >= frontc2) || curline->sidedef->toptexture)
524 			&& ((backf1 <= frontf1 && backf2 >= frontf2) || curline->sidedef->bottomtexture));
525 
526 			if (doorclosed)
527 				goto clipsolid;
528 		}
529 
530 		// Window.
531 		if (!bothceilingssky) // ceilings are always the "same" when sky
532 			if (backc1 != frontc1 || backc2 != frontc2)
533 				goto clippass;
534 		if (!bothfloorssky)	// floors are always the "same" when sky
535 			if (backf1 != frontf1 || backf2 != frontf2)
536 				goto clippass;
537 	}
538 	else
539 	{
540 		// if both ceilings are skies, consider it always "open"
541 		// same for floors
542 		if (!bothceilingssky && !bothfloorssky)
543 		{
544 			if (backsector->ceilingheight <= frontsector->floorheight
545 				|| backsector->floorheight >= frontsector->ceilingheight)
546 			{
547 				goto clipsolid;
548 			}
549 
550 			// Check for automap fix. Store in doorclosed for r_segs.c
551 			doorclosed = R_DoorClosed();
552 			if (doorclosed)
553 				goto clipsolid;
554 		}
555 
556 		// Window.
557 		if (!bothceilingssky) // ceilings are always the "same" when sky
558 			if (backsector->ceilingheight != frontsector->ceilingheight)
559 				goto clippass;
560 		if (!bothfloorssky)	// floors are always the "same" when sky
561 			if (backsector->floorheight != frontsector->floorheight)
562 				goto clippass;
563 	}
564 
565 	// Reject empty lines used for triggers and special events.
566 	// Identical floor and ceiling on both sides, identical light levels on both sides,
567 	// and no middle texture.
568 
569 	if (R_IsEmptyLine(line, frontsector, backsector))
570 		return;
571 
572 clippass:
573 	R_ClipPassWallSegment(x1, x2 - 1);
574 	return;
575 
576 clipsolid:
577 	R_ClipSolidWallSegment(x1, x2 - 1);
578 }
579 
580 //
581 // R_CheckBBox
582 // Checks BSP node/subtree bounding box.
583 // Returns true if some part of the bbox might be visible.
584 //
585 //   | 0 | 1 | 2
586 // --+---+---+---
587 // 0 | 0 | 1 | 2
588 // 1 | 4 | 5 | 6
589 // 2 | 8 | 9 | A
590 INT32 checkcoord[12][4] =
591 {
592 	{3, 0, 2, 1},
593 	{3, 0, 2, 0},
594 	{3, 1, 2, 0},
595 	{0}, // UNUSED
596 	{2, 0, 2, 1},
597 	{0}, // UNUSED
598 	{3, 1, 3, 0},
599 	{0}, // UNUSED
600 	{2, 0, 3, 1},
601 	{2, 1, 3, 1},
602 	{2, 1, 3, 0}
603 };
604 
R_CheckBBox(const fixed_t * bspcoord)605 static boolean R_CheckBBox(const fixed_t *bspcoord)
606 {
607 	angle_t angle1, angle2;
608 	INT32 sx1, sx2, boxpos;
609 	const INT32* check;
610 	cliprange_t *start;
611 
612 	// Find the corners of the box that define the edges from current viewpoint.
613 	if ((boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT] ? 1 : 2) + (viewy >= bspcoord[BOXTOP] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8)) == 5)
614 		return true;
615 
616 	check = checkcoord[boxpos];
617 
618 	// big room fix
619 	angle1 = R_PointToAngleEx(viewx, viewy, bspcoord[check[0]], bspcoord[check[1]]) - viewangle;
620 	angle2 = R_PointToAngleEx(viewx, viewy, bspcoord[check[2]], bspcoord[check[3]]) - viewangle;
621 
622 	if ((signed)angle1 < (signed)angle2)
623 	{
624 		if ((angle1 >= ANGLE_180) && (angle1 < ANGLE_270))
625 			angle1 = ANGLE_180-1;
626 		else
627 			angle2 = ANGLE_180;
628 	}
629 
630 	if ((signed)angle2 >= (signed)clipangle) return false;
631 	if ((signed)angle1 <= -(signed)clipangle) return false;
632 	if ((signed)angle1 >= (signed)clipangle) angle1 = clipangle;
633 	if ((signed)angle2 <= -(signed)clipangle) angle2 = 0-clipangle;
634 
635 	// Find the first clippost that touches the source post (adjacent pixels are touching).
636 	angle1 = (angle1+ANGLE_90)>>ANGLETOFINESHIFT;
637 	angle2 = (angle2+ANGLE_90)>>ANGLETOFINESHIFT;
638 	sx1 = viewangletox[angle1];
639 	sx2 = viewangletox[angle2];
640 
641 	// Does not cross a pixel.
642 	if (sx1 >= sx2) return false;
643 
644 	start = solidsegs;
645 	while (start->last < sx2)
646 		start++;
647 
648 	if (sx1 >= start->first && sx2 <= start->last)
649 		return false; // The clippost contains the new span.
650 
651 	return true;
652 }
653 
654 size_t numpolys;        // number of polyobjects in current subsector
655 size_t num_po_ptrs;     // number of polyobject pointers allocated
656 polyobj_t **po_ptrs; // temp ptr array to sort polyobject pointers
657 
658 //
659 // R_PolyobjCompare
660 //
661 // Callback for qsort that compares the z distance of two polyobjects.
662 // Returns the difference such that the closer polyobject will be
663 // sorted first.
664 //
R_PolyobjCompare(const void * p1,const void * p2)665 static int R_PolyobjCompare(const void *p1, const void *p2)
666 {
667 	const polyobj_t *po1 = *(const polyobj_t * const *)p1;
668 	const polyobj_t *po2 = *(const polyobj_t * const *)p2;
669 
670 	return po1->zdist - po2->zdist;
671 }
672 
673 //
674 // R_SortPolyObjects
675 //
676 // haleyjd 03/03/06: Here's the REAL meat of Eternity's polyobject system.
677 // Hexen just figured this was impossible, but as mentioned in polyobj.c,
678 // it is perfectly doable within the confines of the BSP tree. Polyobjects
679 // must be sorted to draw in DOOM's front-to-back order within individual
680 // subsectors. This is a modified version of R_SortVisSprites.
681 //
R_SortPolyObjects(subsector_t * sub)682 void R_SortPolyObjects(subsector_t *sub)
683 {
684 	if (numpolys)
685 	{
686 		polyobj_t *po;
687 		INT32 i = 0;
688 
689 		// allocate twice the number needed to minimize allocations
690 		if (num_po_ptrs < numpolys*2)
691 		{
692 			// use free instead realloc since faster (thanks Lee ^_^)
693 			free(po_ptrs);
694 			po_ptrs = malloc((num_po_ptrs = numpolys*2)
695 				* sizeof(*po_ptrs));
696 		}
697 
698 		po = sub->polyList;
699 
700 		while (po)
701 		{
702 			po->zdist = R_PointToDist2(viewx, viewy,
703 				po->centerPt.x, po->centerPt.y);
704 			po_ptrs[i++] = po;
705 			po = (polyobj_t *)(po->link.next);
706 		}
707 
708 		// the polyobjects are NOT in any particular order, so use qsort
709 		// 03/10/06: only bother if there are actually polys to sort
710 		if (numpolys >= 2)
711 		{
712 			qsort(po_ptrs, numpolys, sizeof(polyobj_t *),
713 				R_PolyobjCompare);
714 		}
715 	}
716 }
717 
718 //
719 // R_PolysegCompare
720 //
721 // Callback for qsort to sort the segs of a polyobject. Returns such that the
722 // closer one is sorted first. I sure hope this doesn't break anything. -Red
723 //
R_PolysegCompare(const void * p1,const void * p2)724 static int R_PolysegCompare(const void *p1, const void *p2)
725 {
726 	const seg_t *seg1 = *(const seg_t * const *)p1;
727 	const seg_t *seg2 = *(const seg_t * const *)p2;
728 	fixed_t dist1v1, dist1v2, dist2v1, dist2v2;
729 
730 	// TODO might be a better way to get distance?
731 #define pdist(x, y) (FixedMul(R_PointToDist(x, y), FINECOSINE((R_PointToAngle(x, y)-viewangle)>>ANGLETOFINESHIFT))+0xFFFFFFF)
732 #define vxdist(v) pdist(v->x, v->y)
733 
734 	dist1v1 = vxdist(seg1->v1);
735 	dist1v2 = vxdist(seg1->v2);
736 	dist2v1 = vxdist(seg2->v1);
737 	dist2v2 = vxdist(seg2->v2);
738 
739 	if (min(dist1v1, dist1v2) != min(dist2v1, dist2v2))
740 		return min(dist1v1, dist1v2) - min(dist2v1, dist2v2);
741 
742 	{ // That didn't work, so now let's try this.......
743 		fixed_t delta1, delta2, x1, y1, x2, y2;
744 		vertex_t *near1, *near2, *far1, *far2; // wherever you are~
745 
746 		delta1 = R_PointToDist2(seg1->v1->x, seg1->v1->y, seg1->v2->x, seg1->v2->y);
747 		delta2 = R_PointToDist2(seg2->v1->x, seg2->v1->y, seg2->v2->x, seg2->v2->y);
748 
749 		delta1 = FixedDiv(128<<FRACBITS, delta1);
750 		delta2 = FixedDiv(128<<FRACBITS, delta2);
751 
752 		if (dist1v1 < dist1v2)
753 		{
754 			near1 = seg1->v1;
755 			far1 = seg1->v2;
756 		}
757 		else
758 		{
759 			near1 = seg1->v2;
760 			far1 = seg1->v1;
761 		}
762 
763 		if (dist2v1 < dist2v2)
764 		{
765 			near2 = seg2->v1;
766 			far2 = seg2->v2;
767 		}
768 		else
769 		{
770 			near2 = seg2->v2;
771 			far2 = seg2->v1;
772 		}
773 
774 		x1 = near1->x + FixedMul(far1->x-near1->x, delta1);
775 		y1 = near1->y + FixedMul(far1->y-near1->y, delta1);
776 
777 		x2 = near2->x + FixedMul(far2->x-near2->x, delta2);
778 		y2 = near2->y + FixedMul(far2->y-near2->y, delta2);
779 
780 		return pdist(x1, y1)-pdist(x2, y2);
781 	}
782 #undef vxdist
783 #undef pdist
784 }
785 
786 //
787 // R_AddPolyObjects
788 //
789 // haleyjd 02/19/06
790 // Adds all segs in all polyobjects in the given subsector.
791 //
R_AddPolyObjects(subsector_t * sub)792 static void R_AddPolyObjects(subsector_t *sub)
793 {
794 	polyobj_t *po = sub->polyList;
795 	size_t i, j;
796 
797 	numpolys = 0;
798 
799 	// count polyobjects
800 	while (po)
801 	{
802 		++numpolys;
803 		po = (polyobj_t *)(po->link.next);
804 	}
805 
806 	// for render stats
807 	ps_numpolyobjects += numpolys;
808 
809 	// sort polyobjects
810 	R_SortPolyObjects(sub);
811 
812 	// render polyobjects
813 	for (i = 0; i < numpolys; ++i)
814 	{
815 		qsort(po_ptrs[i]->segs, po_ptrs[i]->segCount, sizeof(seg_t *), R_PolysegCompare);
816 		for (j = 0; j < po_ptrs[i]->segCount; ++j)
817 			R_AddLine(po_ptrs[i]->segs[j]);
818 	}
819 }
820 
821 //
822 // R_Subsector
823 // Determine floor/ceiling planes.
824 // Add sprites of things in sector.
825 // Draw one or more line segments.
826 //
827 
828 drawseg_t *firstseg;
829 
R_Subsector(size_t num)830 static void R_Subsector(size_t num)
831 {
832 	INT32 count, floorlightlevel, ceilinglightlevel, light;
833 	seg_t *line;
834 	subsector_t *sub;
835 	static sector_t tempsec; // Deep water hack
836 	extracolormap_t *floorcolormap;
837 	extracolormap_t *ceilingcolormap;
838 	fixed_t floorcenterz, ceilingcenterz;
839 
840 #ifdef RANGECHECK
841 	if (num >= numsubsectors)
842 		I_Error("R_Subsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors));
843 #endif
844 
845 	// subsectors added at run-time
846 	if (num >= numsubsectors)
847 		return;
848 
849 	sub = &subsectors[num];
850 	frontsector = sub->sector;
851 	count = sub->numlines;
852 	line = &segs[sub->firstline];
853 
854 	// Deep water/fake ceiling effect.
855 	frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, false);
856 
857 	floorcolormap = ceilingcolormap = frontsector->extra_colormap;
858 
859 	floorcenterz   = P_GetSectorFloorZAt  (frontsector, frontsector->soundorg.x, frontsector->soundorg.y);
860 	ceilingcenterz = P_GetSectorCeilingZAt(frontsector, frontsector->soundorg.x, frontsector->soundorg.y);
861 
862 	// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
863 	if (frontsector->ffloors)
864 	{
865 		if (frontsector->moved)
866 		{
867 			frontsector->numlights = sub->sector->numlights = 0;
868 			R_Prep3DFloors(frontsector);
869 			sub->sector->lightlist = frontsector->lightlist;
870 			sub->sector->numlights = frontsector->numlights;
871 			sub->sector->moved = frontsector->moved = false;
872 		}
873 
874 		light = R_GetPlaneLight(frontsector, floorcenterz, false);
875 		if (frontsector->floorlightsec == -1)
876 			floorlightlevel = *frontsector->lightlist[light].lightlevel;
877 		floorcolormap = *frontsector->lightlist[light].extra_colormap;
878 		light = R_GetPlaneLight(frontsector, ceilingcenterz, false);
879 		if (frontsector->ceilinglightsec == -1)
880 			ceilinglightlevel = *frontsector->lightlist[light].lightlevel;
881 		ceilingcolormap = *frontsector->lightlist[light].extra_colormap;
882 	}
883 
884 	sub->sector->extra_colormap = frontsector->extra_colormap;
885 
886 	if (P_GetSectorFloorZAt(frontsector, viewx, viewy) < viewz
887 		|| frontsector->floorpic == skyflatnum
888 		|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum))
889 	{
890 		floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel,
891 			frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL, NULL, frontsector->f_slope);
892 	}
893 	else
894 		floorplane = NULL;
895 
896 	if (P_GetSectorCeilingZAt(frontsector, viewx, viewy) > viewz
897 		|| frontsector->ceilingpic == skyflatnum
898 		|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum))
899 	{
900 		ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic,
901 			ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle,
902 			ceilingcolormap, NULL, NULL, frontsector->c_slope);
903 	}
904 	else
905 		ceilingplane = NULL;
906 
907 	numffloors = 0;
908 	ffloor[numffloors].slope = NULL;
909 	ffloor[numffloors].plane = NULL;
910 	ffloor[numffloors].polyobj = NULL;
911 	if (frontsector->ffloors)
912 	{
913 		ffloor_t *rover;
914 		fixed_t heightcheck, planecenterz;
915 
916 		for (rover = frontsector->ffloors; rover && numffloors < MAXFFLOORS; rover = rover->next)
917 		{
918 			if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES))
919 				continue;
920 
921 			if (frontsector->cullheight)
922 			{
923 				if (R_DoCulling(frontsector->cullheight, viewsector->cullheight, viewz, *rover->bottomheight, *rover->topheight))
924 				{
925 					rover->norender = leveltime;
926 					continue;
927 				}
928 			}
929 
930 			ffloor[numffloors].plane = NULL;
931 			ffloor[numffloors].polyobj = NULL;
932 
933 			heightcheck = P_GetFFloorBottomZAt(rover, viewx, viewy);
934 
935 			planecenterz = P_GetFFloorBottomZAt(rover, frontsector->soundorg.x, frontsector->soundorg.y);
936 			if (planecenterz <= ceilingcenterz
937 				&& planecenterz >= floorcenterz
938 				&& ((viewz < heightcheck && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES)))
939 				|| (viewz > heightcheck && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES))))
940 			{
941 				light = R_GetPlaneLight(frontsector, planecenterz,
942 					viewz < heightcheck);
943 
944 				ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic,
945 					*frontsector->lightlist[light].lightlevel, *rover->bottomxoffs,
946 					*rover->bottomyoffs, *rover->bottomangle, *frontsector->lightlist[light].extra_colormap, rover, NULL, *rover->b_slope);
947 
948 				ffloor[numffloors].slope = *rover->b_slope;
949 
950 				// Tell the renderer this sector has slopes in it.
951 				if (ffloor[numffloors].slope)
952 					frontsector->hasslope = true;
953 
954 				ffloor[numffloors].height = heightcheck;
955 				ffloor[numffloors].ffloor = rover;
956 				numffloors++;
957 			}
958 			if (numffloors >= MAXFFLOORS)
959 				break;
960 			ffloor[numffloors].plane = NULL;
961 			ffloor[numffloors].polyobj = NULL;
962 
963 			heightcheck = P_GetFFloorTopZAt(rover, viewx, viewy);
964 
965 			planecenterz = P_GetFFloorTopZAt(rover, frontsector->soundorg.x, frontsector->soundorg.y);
966 			if (planecenterz >= floorcenterz
967 				&& planecenterz <= ceilingcenterz
968 				&& ((viewz > heightcheck && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES)))
969 				|| (viewz < heightcheck && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES))))
970 			{
971 				light = R_GetPlaneLight(frontsector, planecenterz, viewz < heightcheck);
972 
973 				ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic,
974 					*frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle,
975 					*frontsector->lightlist[light].extra_colormap, rover, NULL, *rover->t_slope);
976 
977 				ffloor[numffloors].slope = *rover->t_slope;
978 
979 				// Tell the renderer this sector has slopes in it.
980 				if (ffloor[numffloors].slope)
981 					frontsector->hasslope = true;
982 
983 				ffloor[numffloors].height = heightcheck;
984 				ffloor[numffloors].ffloor = rover;
985 				numffloors++;
986 			}
987 		}
988 	}
989 
990 	// Polyobjects have planes, too!
991 	if (sub->polyList)
992 	{
993 		polyobj_t *po = sub->polyList;
994 		sector_t *polysec;
995 
996 		while (po)
997 		{
998 			if (numffloors >= MAXFFLOORS)
999 				break;
1000 
1001 			if (!(po->flags & POF_RENDERPLANES)) // Don't draw planes
1002 			{
1003 				po = (polyobj_t *)(po->link.next);
1004 				continue;
1005 			}
1006 
1007 			polysec = po->lines[0]->backsector;
1008 			ffloor[numffloors].plane = NULL;
1009 
1010 			if (polysec->floorheight <= ceilingcenterz
1011 				&& polysec->floorheight >= floorcenterz
1012 				&& (viewz < polysec->floorheight))
1013 			{
1014 				light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight);
1015 				ffloor[numffloors].plane = R_FindPlane(polysec->floorheight, polysec->floorpic,
1016 					(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->floor_xoffs, polysec->floor_yoffs,
1017 					polysec->floorpic_angle-po->angle,
1018 					(light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po,
1019 					NULL); // will ffloors be slopable eventually?
1020 
1021 				ffloor[numffloors].height = polysec->floorheight;
1022 				ffloor[numffloors].polyobj = po;
1023 				ffloor[numffloors].slope = NULL;
1024 				//ffloor[numffloors].ffloor = rover;
1025 				po->visplane = ffloor[numffloors].plane;
1026 				numffloors++;
1027 			}
1028 
1029 			if (numffloors >= MAXFFLOORS)
1030 				break;
1031 
1032 			ffloor[numffloors].plane = NULL;
1033 
1034 			if (polysec->ceilingheight >= floorcenterz
1035 				&& polysec->ceilingheight <= ceilingcenterz
1036 				&& (viewz > polysec->ceilingheight))
1037 			{
1038 				light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight);
1039 				ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic,
1040 					(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->ceiling_xoffs, polysec->ceiling_yoffs, polysec->ceilingpic_angle-po->angle,
1041 					(light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po,
1042 					NULL); // will ffloors be slopable eventually?
1043 
1044 				ffloor[numffloors].polyobj = po;
1045 				ffloor[numffloors].height = polysec->ceilingheight;
1046 				ffloor[numffloors].slope = NULL;
1047 				//ffloor[numffloors].ffloor = rover;
1048 				po->visplane = ffloor[numffloors].plane;
1049 				numffloors++;
1050 			}
1051 
1052 			po = (polyobj_t *)(po->link.next);
1053 		}
1054 	}
1055 
1056    // killough 9/18/98: Fix underwater slowdown, by passing real sector
1057    // instead of fake one. Improve sprite lighting by basing sprite
1058    // lightlevels on floor & ceiling lightlevels in the surrounding area.
1059    //
1060    // 10/98 killough:
1061    //
1062    // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!!
1063    // That is part of the 242 effect!!!  If you simply pass sub->sector to
1064    // the old code you will not get correct lighting for underwater sprites!!!
1065    // Either you must pass the fake sector and handle validcount here, on the
1066    // real sector, or you must account for the lighting in some other way,
1067    // like passing it as an argument.
1068 	R_AddSprites(sub->sector, (floorlightlevel+ceilinglightlevel)/2);
1069 
1070 	firstseg = NULL;
1071 
1072 	// haleyjd 02/19/06: draw polyobjects before static lines
1073 	if (sub->polyList)
1074 		R_AddPolyObjects(sub);
1075 
1076 	while (count--)
1077 	{
1078 //		CONS_Debug(DBG_GAMELOGIC, "Adding normal line %d...(%d)\n", line->linedef-lines, leveltime);
1079 		if (!line->glseg && !line->polyseg) // ignore segs that belong to polyobjects
1080 			R_AddLine(line);
1081 		line++;
1082 		curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so stuff doesn't try using it for other things */
1083 	}
1084 }
1085 
1086 //
1087 // R_Prep3DFloors
1088 //
1089 // This function creates the lightlists that the given sector uses to light
1090 // floors/ceilings/walls according to the 3D floors.
R_Prep3DFloors(sector_t * sector)1091 void R_Prep3DFloors(sector_t *sector)
1092 {
1093 	ffloor_t *rover;
1094 	ffloor_t *best;
1095 	fixed_t bestheight, maxheight;
1096 	INT32 count, i;
1097 	sector_t *sec;
1098 	pslope_t *bestslope = NULL;
1099 	fixed_t heighttest; // I think it's better to check the Z height at the sector's center
1100 	                    // than assume unsloped heights are accurate indicators of order in sloped sectors. -Red
1101 
1102 	count = 1;
1103 	for (rover = sector->ffloors; rover; rover = rover->next)
1104 	{
1105 		if ((rover->flags & FF_EXISTS) && (!(rover->flags & FF_NOSHADE)
1106 			|| (rover->flags & FF_CUTLEVEL) || (rover->flags & FF_CUTSPRITES)))
1107 		{
1108 			count++;
1109 			if (rover->flags & FF_DOUBLESHADOW)
1110 				count++;
1111 		}
1112 	}
1113 
1114 	if (count != sector->numlights)
1115 	{
1116 		Z_Free(sector->lightlist);
1117 		sector->lightlist = Z_Calloc(sizeof (*sector->lightlist) * count, PU_LEVEL, NULL);
1118 		sector->numlights = count;
1119 	}
1120 	else
1121 		memset(sector->lightlist, 0, sizeof (lightlist_t) * count);
1122 
1123 	heighttest = P_GetSectorCeilingZAt(sector, sector->soundorg.x, sector->soundorg.y);
1124 
1125 	sector->lightlist[0].height = heighttest + 1;
1126 	sector->lightlist[0].slope = sector->c_slope;
1127 	sector->lightlist[0].lightlevel = &sector->lightlevel;
1128 	sector->lightlist[0].caster = NULL;
1129 	sector->lightlist[0].extra_colormap = &sector->extra_colormap;
1130 	sector->lightlist[0].flags = 0;
1131 
1132 	maxheight = INT32_MAX;
1133 	for (i = 1; i < count; i++)
1134 	{
1135 		bestheight = INT32_MAX * -1;
1136 		best = NULL;
1137 		for (rover = sector->ffloors; rover; rover = rover->next)
1138 		{
1139 			rover->lastlight = 0;
1140 			if (!(rover->flags & FF_EXISTS) || (rover->flags & FF_NOSHADE
1141 				&& !(rover->flags & FF_CUTLEVEL) && !(rover->flags & FF_CUTSPRITES)))
1142 			continue;
1143 
1144 			heighttest = P_GetFFloorTopZAt(rover, sector->soundorg.x, sector->soundorg.y);
1145 
1146 			if (heighttest > bestheight && heighttest < maxheight)
1147 			{
1148 				best = rover;
1149 				bestheight = heighttest;
1150 				bestslope = *rover->t_slope;
1151 				continue;
1152 			}
1153 			if (rover->flags & FF_DOUBLESHADOW) {
1154 				heighttest = P_GetFFloorBottomZAt(rover, sector->soundorg.x, sector->soundorg.y);
1155 
1156 				if (heighttest > bestheight
1157 					&& heighttest < maxheight)
1158 				{
1159 					best = rover;
1160 					bestheight = heighttest;
1161 					bestslope = *rover->b_slope;
1162 					continue;
1163 				}
1164 			}
1165 		}
1166 		if (!best)
1167 		{
1168 			sector->numlights = i;
1169 			return;
1170 		}
1171 
1172 		sector->lightlist[i].height = maxheight = bestheight;
1173 		sector->lightlist[i].caster = best;
1174 		sector->lightlist[i].flags = best->flags;
1175 		sector->lightlist[i].slope = bestslope;
1176 		sec = &sectors[best->secnum];
1177 
1178 		if (best->flags & FF_NOSHADE)
1179 		{
1180 			sector->lightlist[i].lightlevel = sector->lightlist[i-1].lightlevel;
1181 			sector->lightlist[i].extra_colormap = sector->lightlist[i-1].extra_colormap;
1182 		}
1183 		else if (best->flags & FF_COLORMAPONLY)
1184 		{
1185 			sector->lightlist[i].lightlevel = sector->lightlist[i-1].lightlevel;
1186 			sector->lightlist[i].extra_colormap = &sec->extra_colormap;
1187 		}
1188 		else
1189 		{
1190 			sector->lightlist[i].lightlevel = best->toplightlevel;
1191 			sector->lightlist[i].extra_colormap = &sec->extra_colormap;
1192 		}
1193 
1194 		if (best->flags & FF_DOUBLESHADOW)
1195 		{
1196 			heighttest = P_GetFFloorBottomZAt(best, sector->soundorg.x, sector->soundorg.y);
1197 			if (bestheight == heighttest) ///TODO: do this in a more efficient way -Red
1198 			{
1199 				sector->lightlist[i].lightlevel = sector->lightlist[best->lastlight].lightlevel;
1200 				sector->lightlist[i].extra_colormap =
1201 					sector->lightlist[best->lastlight].extra_colormap;
1202 			}
1203 			else
1204 				best->lastlight = i - 1;
1205 		}
1206 	}
1207 }
1208 
R_GetPlaneLight(sector_t * sector,fixed_t planeheight,boolean underside)1209 INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside)
1210 {
1211 	INT32 i;
1212 
1213 	if (!underside)
1214 	{
1215 		for (i = 1; i < sector->numlights; i++)
1216 			if (sector->lightlist[i].height <= planeheight)
1217 				return i - 1;
1218 
1219 		return sector->numlights - 1;
1220 	}
1221 
1222 	for (i = 1; i < sector->numlights; i++)
1223 		if (sector->lightlist[i].height < planeheight)
1224 			return i - 1;
1225 
1226 	return sector->numlights - 1;
1227 }
1228 
1229 //
1230 // RenderBSPNode
1231 // Renders all subsectors below a given node,
1232 //  traversing subtree recursively.
1233 // Just call with BSP root.
1234 //
1235 // killough 5/2/98: reformatted, removed tail recursion
1236 
R_RenderBSPNode(INT32 bspnum)1237 void R_RenderBSPNode(INT32 bspnum)
1238 {
1239 	node_t *bsp;
1240 	INT32 side;
1241 
1242 	ps_numbspcalls++;
1243 
1244 	while (!(bspnum & NF_SUBSECTOR))  // Found a subsector?
1245 	{
1246 		bsp = &nodes[bspnum];
1247 
1248 		// Decide which side the view point is on.
1249 		side = R_PointOnSide(viewx, viewy, bsp);
1250 		// Recursively divide front space.
1251 		R_RenderBSPNode(bsp->children[side]);
1252 
1253 		// Possibly divide back space.
1254 
1255 		if (!R_CheckBBox(bsp->bbox[side^1]))
1256 			return;
1257 
1258 		bspnum = bsp->children[side^1];
1259 	}
1260 
1261 	// PORTAL CULLING
1262 	if (portalcullsector) {
1263 		sector_t *sect = subsectors[bspnum & ~NF_SUBSECTOR].sector;
1264 		if (sect != portalcullsector)
1265 			return;
1266 		portalcullsector = NULL;
1267 	}
1268 
1269 	R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
1270 }
1271