1 // Emacs style mode select	 -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // DESCRIPTION:
18 //		BSP traversal, handling of LineSegs for rendering.
19 //
20 // This file contains some code from the Build Engine.
21 //
22 // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
23 // Ken Silverman's official web site: "http://www.advsys.net/ken"
24 // See the included license file "BUILDLIC.TXT" for license info.
25 //
26 //-----------------------------------------------------------------------------
27 
28 
29 #include <stdlib.h>
30 
31 #include "templates.h"
32 
33 #include "doomdef.h"
34 
35 #include "m_bbox.h"
36 
37 #include "i_system.h"
38 #include "p_lnspec.h"
39 #include "p_setup.h"
40 
41 #include "r_main.h"
42 #include "r_plane.h"
43 #include "r_draw.h"
44 #include "r_things.h"
45 #include "r_3dfloors.h"
46 #include "a_sharedglobal.h"
47 #include "g_level.h"
48 #include "p_effect.h"
49 
50 // State.
51 #include "doomstat.h"
52 #include "r_state.h"
53 #include "r_bsp.h"
54 #include "r_segs.h"
55 #include "v_palette.h"
56 #include "r_sky.h"
57 #include "po_man.h"
58 #include "r_data/colormaps.h"
59 
60 seg_t*			curline;
61 side_t* 		sidedef;
62 line_t* 		linedef;
63 sector_t*		frontsector;
64 sector_t*		backsector;
65 
66 // killough 4/7/98: indicates doors closed wrt automap bugfix:
67 int				doorclosed;
68 
69 bool			r_fakingunderwater;
70 
71 extern bool		rw_prepped;
72 extern bool		rw_havehigh, rw_havelow;
73 extern int		rw_floorstat, rw_ceilstat;
74 extern bool		rw_mustmarkfloor, rw_mustmarkceiling;
75 extern short	walltop[MAXWIDTH];	// [RH] record max extents of wall
76 extern short	wallbottom[MAXWIDTH];
77 extern short	wallupper[MAXWIDTH];
78 extern short	walllower[MAXWIDTH];
79 
80 fixed_t			rw_backcz1, rw_backcz2;
81 fixed_t			rw_backfz1, rw_backfz2;
82 fixed_t			rw_frontcz1, rw_frontcz2;
83 fixed_t			rw_frontfz1, rw_frontfz2;
84 
85 
86 size_t			MaxDrawSegs;
87 drawseg_t		*drawsegs;
88 drawseg_t*		firstdrawseg;
89 drawseg_t*		ds_p;
90 
91 size_t			FirstInterestingDrawseg;
92 TArray<size_t>	InterestingDrawsegs;
93 
94 FWallCoords		WallC;
95 FWallTmapVals	WallT;
96 
97 static BYTE		FakeSide;
98 
99 int WindowLeft, WindowRight;
100 WORD MirrorFlags;
101 seg_t *ActiveWallMirror;
102 TArray<size_t> WallMirrors;
103 
104 static subsector_t *InSubsector;
105 
106 CVAR (Bool, r_drawflat, false, 0)		// [RH] Don't texture segs?
107 
108 
109 void R_StoreWallRange (int start, int stop);
110 
111 //
112 // R_ClearDrawSegs
113 //
R_ClearDrawSegs(void)114 void R_ClearDrawSegs (void)
115 {
116 	if (drawsegs == NULL)
117 	{
118 		MaxDrawSegs = 256;		// [RH] Default. Increased as needed.
119 		firstdrawseg = drawsegs = (drawseg_t *)M_Malloc (MaxDrawSegs * sizeof(drawseg_t));
120 	}
121 	FirstInterestingDrawseg = 0;
122 	InterestingDrawsegs.Clear ();
123 	ds_p = drawsegs;
124 }
125 
126 
127 
128 //
129 // ClipWallSegment
130 // Clips the given range of columns
131 // and includes it in the new clip list.
132 //
133 //
134 // 1/11/98 killough: Since a type "short" is sufficient, we
135 // should use it, since smaller arrays fit better in cache.
136 //
137 
138 struct cliprange_t
139 {
140 	short first, last;		// killough
141 };
142 
143 
144 // newend is one past the last valid seg
145 static cliprange_t     *newend;
146 static cliprange_t		solidsegs[MAXWIDTH/2+2];
147 
148 
149 
150 //==========================================================================
151 //
152 // R_ClipWallSegment
153 //
154 // Clips the given range of columns, possibly including it in the clip list.
155 // Handles both windows (e.g. LineDefs with upper and lower textures) and
156 // solid walls (e.g. single sided LineDefs [middle texture]) that entirely
157 // block the view.
158 //
159 //==========================================================================
160 
R_ClipWallSegment(int first,int last,bool solid)161 bool R_ClipWallSegment (int first, int last, bool solid)
162 {
163 	cliprange_t *next, *start;
164 	int i, j;
165 	bool res = false;
166 
167 	// Find the first range that touches the range
168 	// (adjacent pixels are touching).
169 	start = solidsegs;
170 	while (start->last < first)
171 		start++;
172 
173 	if (first < start->first)
174 	{
175 		res = true;
176 		if (last <= start->first)
177 		{
178 			// Post is entirely visible (above start).
179 			R_StoreWallRange (first, last);
180 			if (fake3D & FAKE3D_FAKEMASK)
181 			{
182 				return true;
183 			}
184 
185 			// Insert a new clippost for solid walls.
186 			if (solid)
187 			{
188 				if (last == start->first)
189 				{
190 					start->first = first;
191 				}
192 				else
193 				{
194 					next = newend;
195 					newend++;
196 					while (next != start)
197 					{
198 						*next = *(next-1);
199 						next--;
200 					}
201 					next->first = first;
202 					next->last = last;
203 				}
204 			}
205 			return true;
206 		}
207 
208 		// There is a fragment above *start.
209 		R_StoreWallRange (first, start->first);
210 
211 		// Adjust the clip size for solid walls
212 		if (solid && !(fake3D & FAKE3D_FAKEMASK))
213 		{
214 			start->first = first;
215 		}
216 	}
217 
218 	// Bottom contained in start?
219 	if (last <= start->last)
220 		return res;
221 
222 	next = start;
223 	while (last >= (next+1)->first)
224 	{
225 		// There is a fragment between two posts.
226 		R_StoreWallRange (next->last, (next+1)->first);
227 		next++;
228 
229 		if (last <= next->last)
230 		{
231 			// Bottom is contained in next.
232 			last = next->last;
233 			goto crunch;
234 		}
235 	}
236 
237 	// There is a fragment after *next.
238 	R_StoreWallRange (next->last, last);
239 
240 crunch:
241 	if (fake3D & FAKE3D_FAKEMASK)
242 	{
243 		return true;
244 	}
245 	if (solid)
246 	{
247 		// Adjust the clip size.
248 		start->last = last;
249 
250 		if (next != start)
251 		{
252 			// Remove start+1 to next from the clip list,
253 			// because start now covers their area.
254 			for (i = 1, j = (int)(newend - next); j > 0; i++, j--)
255 			{
256 				start[i] = next[i];
257 			}
258 			newend = start+i;
259 		}
260 	}
261 	return true;
262 }
263 
R_CheckClipWallSegment(int first,int last)264 bool R_CheckClipWallSegment (int first, int last)
265 {
266 	cliprange_t *start;
267 
268 	// Find the first range that touches the range
269 	// (adjacent pixels are touching).
270 	start = solidsegs;
271 	while (start->last < first)
272 		start++;
273 
274 	if (first < start->first)
275 	{
276 		return true;
277 	}
278 
279 	// Bottom contained in start?
280 	if (last > start->last)
281 	{
282 		return true;
283 	}
284 
285 	return false;
286 }
287 
288 
289 
290 //
291 // R_ClearClipSegs
292 //
R_ClearClipSegs(short left,short right)293 void R_ClearClipSegs (short left, short right)
294 {
295 	solidsegs[0].first = -0x7fff;	// new short limit --  killough
296 	solidsegs[0].last = left;
297 	solidsegs[1].first = right;
298 	solidsegs[1].last = 0x7fff;		// new short limit --  killough
299 	newend = solidsegs+2;
300 }
301 
302 
303 //
304 // killough 3/7/98: Hack floor/ceiling heights for deep water etc.
305 //
306 // If player's view height is underneath fake floor, lower the
307 // drawn ceiling to be just under the floor height, and replace
308 // the drawn floor and ceiling textures, and light level, with
309 // the control sector's.
310 //
311 // Similar for ceiling, only reflected.
312 //
313 // killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter
314 //
315 
R_FakeFlat(sector_t * sec,sector_t * tempsec,int * floorlightlevel,int * ceilinglightlevel,bool back)316 sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec,
317 					 int *floorlightlevel, int *ceilinglightlevel,
318 					 bool back)
319 {
320 	// [RH] allow per-plane lighting
321 	if (floorlightlevel != NULL)
322 	{
323 		*floorlightlevel = sec->GetFloorLight ();
324 	}
325 
326 	if (ceilinglightlevel != NULL)
327 	{
328 		*ceilinglightlevel = sec->GetCeilingLight ();
329 	}
330 
331 	FakeSide = FAKED_Center;
332 
333 	const sector_t *s = sec->GetHeightSec();
334 	if (s != NULL)
335 	{
336 		sector_t *heightsec = viewsector->heightsec;
337 		bool underwater = r_fakingunderwater ||
338 			(heightsec && heightsec->floorplane.PointOnSide(viewx, viewy, viewz) <= 0);
339 		bool doorunderwater = false;
340 		int diffTex = (s->MoreFlags & SECF_CLIPFAKEPLANES);
341 
342 		// Replace sector being drawn with a copy to be hacked
343 		*tempsec = *sec;
344 
345 		// Replace floor and ceiling height with control sector's heights.
346 		if (diffTex)
347 		{
348 			if (s->floorplane.CopyPlaneIfValid (&tempsec->floorplane, &sec->ceilingplane))
349 			{
350 				tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false);
351 			}
352 			else if (s->MoreFlags & SECF_FAKEFLOORONLY)
353 			{
354 				if (underwater)
355 				{
356 					tempsec->ColorMap = s->ColorMap;
357 					if (!(s->MoreFlags & SECF_NOFAKELIGHT))
358 					{
359 						tempsec->lightlevel = s->lightlevel;
360 
361 						if (floorlightlevel != NULL)
362 						{
363 							*floorlightlevel = s->GetFloorLight ();
364 						}
365 
366 						if (ceilinglightlevel != NULL)
367 						{
368 							*ceilinglightlevel = s->GetCeilingLight ();
369 						}
370 					}
371 					FakeSide = FAKED_BelowFloor;
372 					return tempsec;
373 				}
374 				return sec;
375 			}
376 		}
377 		else
378 		{
379 			tempsec->floorplane = s->floorplane;
380 		}
381 
382 		if (!(s->MoreFlags & SECF_FAKEFLOORONLY))
383 		{
384 			if (diffTex)
385 			{
386 				if (s->ceilingplane.CopyPlaneIfValid (&tempsec->ceilingplane, &sec->floorplane))
387 				{
388 					tempsec->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false);
389 				}
390 			}
391 			else
392 			{
393 				tempsec->ceilingplane  = s->ceilingplane;
394 			}
395 		}
396 
397 		fixed_t refceilz = s->ceilingplane.ZatPoint (viewx, viewy);
398 		fixed_t orgceilz = sec->ceilingplane.ZatPoint (viewx, viewy);
399 
400 #if 1
401 		// [RH] Allow viewing underwater areas through doors/windows that
402 		// are underwater but not in a water sector themselves.
403 		// Only works if you cannot see the top surface of any deep water
404 		// sectors at the same time.
405 		if (back && !r_fakingunderwater && curline->frontsector->heightsec == NULL)
406 		{
407 			if (rw_frontcz1 <= s->floorplane.ZatPoint (curline->v1->x, curline->v1->y) &&
408 				rw_frontcz2 <= s->floorplane.ZatPoint (curline->v2->x, curline->v2->y))
409 			{
410 				// Check that the window is actually visible
411 				for (int z = WallC.sx1; z < WallC.sx2; ++z)
412 				{
413 					if (floorclip[z] > ceilingclip[z])
414 					{
415 						doorunderwater = true;
416 						r_fakingunderwater = true;
417 						break;
418 					}
419 				}
420 			}
421 		}
422 #endif
423 
424 		if (underwater || doorunderwater)
425 		{
426 			tempsec->floorplane = sec->floorplane;
427 			tempsec->ceilingplane = s->floorplane;
428 			tempsec->ceilingplane.FlipVert ();
429 			tempsec->ceilingplane.ChangeHeight (-1);
430 			tempsec->ColorMap = s->ColorMap;
431 		}
432 
433 		// killough 11/98: prevent sudden light changes from non-water sectors:
434 		if ((underwater && !back) || doorunderwater)
435 		{					// head-below-floor hack
436 			tempsec->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false);
437 			tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform;
438 
439 			tempsec->ceilingplane		= s->floorplane;
440 			tempsec->ceilingplane.FlipVert ();
441 			tempsec->ceilingplane.ChangeHeight (-1);
442 			if (s->GetTexture(sector_t::ceiling) == skyflatnum)
443 			{
444 				tempsec->floorplane			= tempsec->ceilingplane;
445 				tempsec->floorplane.FlipVert ();
446 				tempsec->floorplane.ChangeHeight (+1);
447 				tempsec->SetTexture(sector_t::ceiling, tempsec->GetTexture(sector_t::floor), false);
448 				tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform;
449 			}
450 			else
451 			{
452 				tempsec->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false);
453 				tempsec->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform;
454 			}
455 
456 			if (!(s->MoreFlags & SECF_NOFAKELIGHT))
457 			{
458 				tempsec->lightlevel = s->lightlevel;
459 
460 				if (floorlightlevel != NULL)
461 				{
462 					*floorlightlevel = s->GetFloorLight ();
463 				}
464 
465 				if (ceilinglightlevel != NULL)
466 				{
467 					*ceilinglightlevel = s->GetCeilingLight ();
468 				}
469 			}
470 			FakeSide = FAKED_BelowFloor;
471 		}
472 		else if (heightsec && heightsec->ceilingplane.PointOnSide(viewx, viewy, viewz) <= 0 &&
473 				 orgceilz > refceilz && !(s->MoreFlags & SECF_FAKEFLOORONLY))
474 		{	// Above-ceiling hack
475 			tempsec->ceilingplane		= s->ceilingplane;
476 			tempsec->floorplane			= s->ceilingplane;
477 			tempsec->floorplane.FlipVert ();
478 			tempsec->floorplane.ChangeHeight (+1);
479 			tempsec->ColorMap			= s->ColorMap;
480 			tempsec->ColorMap			= s->ColorMap;
481 
482 			tempsec->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false);
483 			tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false);
484 			tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform;
485 
486 			if (s->GetTexture(sector_t::floor) != skyflatnum)
487 			{
488 				tempsec->ceilingplane	= sec->ceilingplane;
489 				tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false);
490 				tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform;
491 			}
492 
493 			if (!(s->MoreFlags & SECF_NOFAKELIGHT))
494 			{
495 				tempsec->lightlevel  = s->lightlevel;
496 
497 				if (floorlightlevel != NULL)
498 				{
499 					*floorlightlevel = s->GetFloorLight ();
500 				}
501 
502 				if (ceilinglightlevel != NULL)
503 				{
504 					*ceilinglightlevel = s->GetCeilingLight ();
505 				}
506 			}
507 			FakeSide = FAKED_AboveCeiling;
508 		}
509 		sec = tempsec;					// Use other sector
510 	}
511 	return sec;
512 }
513 
514 
515 //
516 // R_AddLine
517 // Clips the given segment
518 // and adds any visible pieces to the line list.
519 //
520 
R_AddLine(seg_t * line)521 void R_AddLine (seg_t *line)
522 {
523 	static sector_t tempsec;	// killough 3/8/98: ceiling/water hack
524 	bool			solid;
525 	fixed_t			tx1, tx2, ty1, ty2;
526 
527 	curline = line;
528 
529 	// [RH] Color if not texturing line
530 	dc_color = (((int)(line - segs) * 8) + 4) & 255;
531 
532 	tx1 = line->v1->x - viewx;
533 	tx2 = line->v2->x - viewx;
534 	ty1 = line->v1->y - viewy;
535 	ty2 = line->v2->y - viewy;
536 
537 	// Reject lines not facing viewer
538 	if (DMulScale32 (ty1, tx1-tx2, tx1, ty2-ty1) >= 0)
539 		return;
540 
541 	if (WallC.Init(tx1, ty1, tx2, ty2, 32))
542 		return;
543 
544 	if (WallC.sx1 >= WindowRight || WallC.sx2 <= WindowLeft)
545 		return;
546 
547 	if (line->linedef == NULL)
548 	{
549 		if (R_CheckClipWallSegment (WallC.sx1, WallC.sx2))
550 		{
551 			InSubsector->flags |= SSECF_DRAWN;
552 		}
553 		return;
554 	}
555 
556 	vertex_t *v1, *v2;
557 
558 	v1 = line->linedef->v1;
559 	v2 = line->linedef->v2;
560 
561 	if ((v1 == line->v1 && v2 == line->v2) || (v2 == line->v1 && v1 == line->v2))
562 	{ // The seg is the entire wall.
563 		WallT.InitFromWallCoords(&WallC);
564 	}
565 	else
566 	{ // The seg is only part of the wall.
567 		if (line->linedef->sidedef[0] != line->sidedef)
568 		{
569 			swapvalues (v1, v2);
570 		}
571 		WallT.InitFromLine(v1->x - viewx, v1->y - viewy, v2->x - viewx, v2->y - viewy);
572 	}
573 
574 	if (!(fake3D & FAKE3D_FAKEBACK))
575 	{
576 		backsector = line->backsector;
577 	}
578 	rw_frontcz1 = frontsector->ceilingplane.ZatPoint (line->v1->x, line->v1->y);
579 	rw_frontfz1 = frontsector->floorplane.ZatPoint (line->v1->x, line->v1->y);
580 	rw_frontcz2 = frontsector->ceilingplane.ZatPoint (line->v2->x, line->v2->y);
581 	rw_frontfz2 = frontsector->floorplane.ZatPoint (line->v2->x, line->v2->y);
582 
583 	rw_mustmarkfloor = rw_mustmarkceiling = false;
584 	rw_havehigh = rw_havelow = false;
585 
586 	// Single sided line?
587 	if (backsector == NULL)
588 	{
589 		solid = true;
590 	}
591 	else
592 	{
593 		// kg3D - its fake, no transfer_heights
594 		if (!(fake3D & FAKE3D_FAKEBACK))
595 		{ // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water
596 			backsector = R_FakeFlat (backsector, &tempsec, NULL, NULL, true);
597 		}
598 		doorclosed = 0;		// killough 4/16/98
599 
600 		rw_backcz1 = backsector->ceilingplane.ZatPoint (line->v1->x, line->v1->y);
601 		rw_backfz1 = backsector->floorplane.ZatPoint (line->v1->x, line->v1->y);
602 		rw_backcz2 = backsector->ceilingplane.ZatPoint (line->v2->x, line->v2->y);
603 		rw_backfz2 = backsector->floorplane.ZatPoint (line->v2->x, line->v2->y);
604 
605 		// Cannot make these walls solid, because it can result in
606 		// sprite clipping problems for sprites near the wall
607 		if (rw_frontcz1 > rw_backcz1 || rw_frontcz2 > rw_backcz2)
608 		{
609 			rw_havehigh = true;
610 			WallMost (wallupper, backsector->ceilingplane, &WallC);
611 		}
612 		if (rw_frontfz1 < rw_backfz1 || rw_frontfz2 < rw_backfz2)
613 		{
614 			rw_havelow = true;
615 			WallMost (walllower, backsector->floorplane, &WallC);
616 		}
617 
618 		// Closed door.
619 		if ((rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2) ||
620 			(rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2))
621 		{
622 			solid = true;
623 		}
624 		else if (
625 		// properly render skies (consider door "open" if both ceilings are sky):
626 		(backsector->GetTexture(sector_t::ceiling) != skyflatnum || frontsector->GetTexture(sector_t::ceiling) != skyflatnum)
627 
628 		// if door is closed because back is shut:
629 		&& rw_backcz1 <= rw_backfz1 && rw_backcz2 <= rw_backfz2
630 
631 		// preserve a kind of transparent door/lift special effect:
632 		&& ((rw_backcz1 >= rw_frontcz1 && rw_backcz2 >= rw_frontcz2) || line->sidedef->GetTexture(side_t::top).isValid())
633 		&& ((rw_backfz1 <= rw_frontfz1 && rw_backfz2 <= rw_frontfz2) || line->sidedef->GetTexture(side_t::bottom).isValid()))
634 		{
635 		// killough 1/18/98 -- This function is used to fix the automap bug which
636 		// showed lines behind closed doors simply because the door had a dropoff.
637 		//
638 		// It assumes that Doom has already ruled out a door being closed because
639 		// of front-back closure (e.g. front floor is taller than back ceiling).
640 
641 		// This fixes the automap floor height bug -- killough 1/18/98:
642 		// killough 4/7/98: optimize: save result in doorclosed for use in r_segs.c
643 			doorclosed = true;
644 			solid = true;
645 		}
646 		else if (frontsector->ceilingplane != backsector->ceilingplane ||
647 			frontsector->floorplane != backsector->floorplane)
648 		{
649 		// Window.
650 			solid = false;
651 		}
652 		else if (backsector->lightlevel != frontsector->lightlevel
653 			|| backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor)
654 			|| backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling)
655 			|| curline->sidedef->GetTexture(side_t::mid).isValid()
656 
657 			// killough 3/7/98: Take flats offsets into account:
658 			|| backsector->GetXOffset(sector_t::floor) != frontsector->GetXOffset(sector_t::floor)
659 			|| backsector->GetYOffset(sector_t::floor) != frontsector->GetYOffset(sector_t::floor)
660 			|| backsector->GetXOffset(sector_t::ceiling) != frontsector->GetXOffset(sector_t::ceiling)
661 			|| backsector->GetYOffset(sector_t::ceiling) != frontsector->GetYOffset(sector_t::ceiling)
662 
663 			|| backsector->GetPlaneLight(sector_t::floor) != frontsector->GetPlaneLight(sector_t::floor)
664 			|| backsector->GetPlaneLight(sector_t::ceiling) != frontsector->GetPlaneLight(sector_t::ceiling)
665 			|| backsector->GetFlags(sector_t::floor) != frontsector->GetFlags(sector_t::floor)
666 			|| backsector->GetFlags(sector_t::ceiling) != frontsector->GetFlags(sector_t::ceiling)
667 
668 			// [RH] Also consider colormaps
669 			|| backsector->ColorMap != frontsector->ColorMap
670 
671 			// [RH] and scaling
672 			|| backsector->GetXScale(sector_t::floor) != frontsector->GetXScale(sector_t::floor)
673 			|| backsector->GetYScale(sector_t::floor) != frontsector->GetYScale(sector_t::floor)
674 			|| backsector->GetXScale(sector_t::ceiling) != frontsector->GetXScale(sector_t::ceiling)
675 			|| backsector->GetYScale(sector_t::ceiling) != frontsector->GetYScale(sector_t::ceiling)
676 
677 			// [RH] and rotation
678 			|| backsector->GetAngle(sector_t::floor) != frontsector->GetAngle(sector_t::floor)
679 			|| backsector->GetAngle(sector_t::ceiling) != frontsector->GetAngle(sector_t::ceiling)
680 
681 			// kg3D - and fake lights
682 			|| (frontsector->e && frontsector->e->XFloor.lightlist.Size())
683 			|| (backsector->e && backsector->e->XFloor.lightlist.Size())
684 			)
685 		{
686 			solid = false;
687 		}
688 		else
689 		{
690 			// Reject empty lines used for triggers and special events.
691 			// Identical floor and ceiling on both sides, identical light levels
692 			// on both sides, and no middle texture.
693 
694 			// When using GL nodes, do a clipping test for these lines so we can
695 			// mark their subsectors as visible for automap texturing.
696 			if (hasglnodes && !(InSubsector->flags & SSECF_DRAWN))
697 			{
698 				if (R_CheckClipWallSegment(WallC.sx1, WallC.sx2))
699 				{
700 					InSubsector->flags |= SSECF_DRAWN;
701 				}
702 			}
703 			return;
704 		}
705 	}
706 
707 	rw_prepped = false;
708 
709 	if (line->linedef->special == Line_Horizon)
710 	{
711 		// Be aware: Line_Horizon does not work properly with sloped planes
712 		clearbufshort (walltop+WallC.sx1, WallC.sx2 - WallC.sx1, centery);
713 		clearbufshort (wallbottom+WallC.sx1, WallC.sx2 - WallC.sx1, centery);
714 	}
715 	else
716 	{
717 		rw_ceilstat = WallMost (walltop, frontsector->ceilingplane, &WallC);
718 		rw_floorstat = WallMost (wallbottom, frontsector->floorplane, &WallC);
719 
720 		// [RH] treat off-screen walls as solid
721 #if 0	// Maybe later...
722 		if (!solid)
723 		{
724 			if (rw_ceilstat == 12 && line->sidedef->GetTexture(side_t::top) != 0)
725 			{
726 				rw_mustmarkceiling = true;
727 				solid = true;
728 			}
729 			if (rw_floorstat == 3 && line->sidedef->GetTexture(side_t::bottom) != 0)
730 			{
731 				rw_mustmarkfloor = true;
732 				solid = true;
733 			}
734 		}
735 #endif
736 	}
737 
738 	if (R_ClipWallSegment (WallC.sx1, WallC.sx2, solid))
739 	{
740 		InSubsector->flags |= SSECF_DRAWN;
741 	}
742 }
743 
744 //
745 // FWallCoords :: Init
746 //
747 // Transform and clip coordinates. Returns true if it was clipped away
748 //
Init(int x1,int y1,int x2,int y2,int too_close)749 bool FWallCoords::Init(int x1, int y1, int x2, int y2, int too_close)
750 {
751 	tx1 = DMulScale20(x1, viewsin, -y1, viewcos);
752 	tx2 = DMulScale20(x2, viewsin, -y2, viewcos);
753 
754 	ty1 = DMulScale20(x1, viewtancos, y1, viewtansin);
755 	ty2 = DMulScale20(x2, viewtancos, y2, viewtansin);
756 
757 	if (MirrorFlags & RF_XFLIP)
758 	{
759 		int t = 256 - tx1;
760 		tx1 = 256 - tx2;
761 		tx2 = t;
762 		swapvalues(ty1, ty2);
763 	}
764 
765 	if (tx1 >= -ty1)
766 	{
767 		if (tx1 > ty1) return true;	// left edge is off the right side
768 		if (ty1 == 0) return true;
769 		sx1 = (centerxfrac + Scale(tx1, centerxfrac, ty1)) >> FRACBITS;
770 		if (tx1 >= 0) sx1 = MIN(viewwidth, sx1+1); // fix for signed divide
771 		sz1 = ty1;
772 	}
773 	else
774 	{
775 		if (tx2 < -ty2) return true;	// wall is off the left side
776 		fixed_t den = tx1 - tx2 - ty2 + ty1;
777 		if (den == 0) return true;
778 		sx1 = 0;
779 		sz1 = ty1 + Scale(ty2 - ty1, tx1 + ty1, den);
780 	}
781 
782 	if (sz1 < too_close)
783 		return true;
784 
785 	if (tx2 <= ty2)
786 	{
787 		if (tx2 < -ty2) return true;	// right edge is off the left side
788 		if (ty2 == 0) return true;
789 		sx2 = (centerxfrac + Scale(tx2, centerxfrac, ty2)) >> FRACBITS;
790 		if (tx2 >= 0) sx2 = MIN(viewwidth, sx2+1);	// fix for signed divide
791 		sz2 = ty2;
792 	}
793 	else
794 	{
795 		if (tx1 > ty1) return true;	// wall is off the right side
796 		fixed_t den = ty2 - ty1 - tx2 + tx1;
797 		if (den == 0) return true;
798 		sx2 = viewwidth;
799 		sz2 = ty1 + Scale(ty2 - ty1, tx1 - ty1, den);
800 	}
801 
802 	if (sz2 < too_close || sx2 <= sx1)
803 		return true;
804 
805 	return false;
806 }
807 
InitFromWallCoords(const FWallCoords * wallc)808 void FWallTmapVals::InitFromWallCoords(const FWallCoords *wallc)
809 {
810 	if (MirrorFlags & RF_XFLIP)
811 	{
812 		UoverZorg = (float)wallc->tx2 * centerx;
813 		UoverZstep = (float)(-wallc->ty2);
814 		InvZorg = (float)(wallc->tx2 - wallc->tx1) * centerx;
815 		InvZstep = (float)(wallc->ty1 - wallc->ty2);
816 	}
817 	else
818 	{
819 		UoverZorg = (float)wallc->tx1 * centerx;
820 		UoverZstep = (float)(-wallc->ty1);
821 		InvZorg = (float)(wallc->tx1 - wallc->tx2) * centerx;
822 		InvZstep = (float)(wallc->ty2 - wallc->ty1);
823 	}
824 }
825 
InitFromLine(int tx1,int ty1,int tx2,int ty2)826 void FWallTmapVals::InitFromLine(int tx1, int ty1, int tx2, int ty2)
827 { // Coordinates should have already had viewx,viewy subtracted
828 	fixed_t fullx1 = DMulScale20 (tx1, viewsin, -ty1, viewcos);
829 	fixed_t fullx2 = DMulScale20 (tx2, viewsin, -ty2, viewcos);
830 	fixed_t fully1 = DMulScale20 (tx1, viewtancos, ty1, viewtansin);
831 	fixed_t fully2 = DMulScale20 (tx2, viewtancos, ty2, viewtansin);
832 
833 	if (MirrorFlags & RF_XFLIP)
834 	{
835 		fullx1 = -fullx1;
836 		fullx2 = -fullx2;
837 	}
838 
839 	UoverZorg = (float)fullx1 * centerx;
840 	UoverZstep = (float)(-fully1);
841 	InvZorg = (float)(fullx1 - fullx2) * centerx;
842 	InvZstep = (float)(fully2 - fully1);
843 }
844 
845 //
846 // R_CheckBBox
847 // Checks BSP node/subtree bounding box.
848 // Returns true if some part of the bbox might be visible.
849 //
850 extern "C" const int checkcoord[12][4] =
851 {
852 	{3,0,2,1},
853 	{3,0,2,0},
854 	{3,1,2,0},
855 	{0},
856 	{2,0,2,1},
857 	{0,0,0,0},
858 	{3,1,3,0},
859 	{0},
860 	{2,0,3,1},
861 	{2,1,3,1},
862 	{2,1,3,0}
863 };
864 
865 
R_CheckBBox(fixed_t * bspcoord)866 static bool R_CheckBBox (fixed_t *bspcoord)	// killough 1/28/98: static
867 {
868 	int 				boxx;
869 	int 				boxy;
870 	int 				boxpos;
871 
872 	fixed_t 			x1, y1, x2, y2;
873 	fixed_t				rx1, ry1, rx2, ry2;
874 	int					sx1, sx2;
875 
876 	cliprange_t*		start;
877 
878 	// Find the corners of the box
879 	// that define the edges from current viewpoint.
880 	if (viewx <= bspcoord[BOXLEFT])
881 		boxx = 0;
882 	else if (viewx < bspcoord[BOXRIGHT])
883 		boxx = 1;
884 	else
885 		boxx = 2;
886 
887 	if (viewy >= bspcoord[BOXTOP])
888 		boxy = 0;
889 	else if (viewy > bspcoord[BOXBOTTOM])
890 		boxy = 1;
891 	else
892 		boxy = 2;
893 
894 	boxpos = (boxy<<2)+boxx;
895 	if (boxpos == 5)
896 		return true;
897 
898 	x1 = bspcoord[checkcoord[boxpos][0]] - viewx;
899 	y1 = bspcoord[checkcoord[boxpos][1]] - viewy;
900 	x2 = bspcoord[checkcoord[boxpos][2]] - viewx;
901 	y2 = bspcoord[checkcoord[boxpos][3]] - viewy;
902 
903 	// check clip list for an open space
904 
905 	// Sitting on a line?
906 	if (DMulScale32 (y1, x1-x2, x1, y2-y1) >= 0)
907 		return true;
908 
909 	rx1 = DMulScale20 (x1, viewsin, -y1, viewcos);
910 	rx2 = DMulScale20 (x2, viewsin, -y2, viewcos);
911 	ry1 = DMulScale20 (x1, viewtancos, y1, viewtansin);
912 	ry2 = DMulScale20 (x2, viewtancos, y2, viewtansin);
913 
914 	if (MirrorFlags & RF_XFLIP)
915 	{
916 		int t = 256-rx1;
917 		rx1 = 256-rx2;
918 		rx2 = t;
919 		swapvalues (ry1, ry2);
920 	}
921 
922 	if (rx1 >= -ry1)
923 	{
924 		if (rx1 > ry1) return false;	// left edge is off the right side
925 		if (ry1 == 0) return false;
926 		sx1 = (centerxfrac + Scale (rx1, centerxfrac, ry1)) >> FRACBITS;
927 		if (rx1 >= 0) sx1 = MIN<int> (viewwidth, sx1+1);	// fix for signed divide
928 	}
929 	else
930 	{
931 		if (rx2 < -ry2) return false;	// wall is off the left side
932 		if (rx1 - rx2 - ry2 + ry1 == 0) return false;	// wall does not intersect view volume
933 		sx1 = 0;
934 	}
935 
936 	if (rx2 <= ry2)
937 	{
938 		if (rx2 < -ry2) return false;	// right edge is off the left side
939 		if (ry2 == 0) return false;
940 		sx2 = (centerxfrac + Scale (rx2, centerxfrac, ry2)) >> FRACBITS;
941 		if (rx2 >= 0) sx2 = MIN<int> (viewwidth, sx2+1);	// fix for signed divide
942 	}
943 	else
944 	{
945 		if (rx1 > ry1) return false;	// wall is off the right side
946 		if (ry2 - ry1 - rx2 + rx1 == 0) return false;	// wall does not intersect view volume
947 		sx2 = viewwidth;
948 	}
949 
950 	// Find the first clippost that touches the source post
951 	//	(adjacent pixels are touching).
952 
953 	// Does not cross a pixel.
954 	if (sx2 <= sx1)
955 		return false;
956 
957 	start = solidsegs;
958 	while (start->last < sx2)
959 		start++;
960 
961 	if (sx1 >= start->first && sx2 <= start->last)
962 	{
963 		// The clippost contains the new span.
964 		return false;
965 	}
966 
967 	return true;
968 }
969 
970 
971 void R_Subsector (subsector_t *sub);
R_AddPolyobjs(subsector_t * sub)972 static void R_AddPolyobjs(subsector_t *sub)
973 {
974 	if (sub->BSP == NULL || sub->BSP->bDirty)
975 	{
976 		sub->BuildPolyBSP();
977 	}
978 	if (sub->BSP->Nodes.Size() == 0)
979 	{
980 		R_Subsector(&sub->BSP->Subsectors[0]);
981 	}
982 	else
983 	{
984 		R_RenderBSPNode(&sub->BSP->Nodes.Last());
985 	}
986 }
987 
988 // kg3D - add fake segs, never rendered
R_FakeDrawLoop(subsector_t * sub)989 void R_FakeDrawLoop(subsector_t *sub)
990 {
991 	int 		 count;
992 	seg_t*		 line;
993 
994 	count = sub->numlines;
995 	line = sub->firstline;
996 
997 	while (count--)
998 	{
999 		if ((line->sidedef) && !(line->sidedef->Flags & WALLF_POLYOBJ))
1000 		{
1001 			R_AddLine (line);
1002 		}
1003 		line++;
1004 	}
1005 }
1006 
1007 //
1008 // R_Subsector
1009 // Determine floor/ceiling planes.
1010 // Add sprites of things in sector.
1011 // Draw one or more line segments.
1012 //
R_Subsector(subsector_t * sub)1013 void R_Subsector (subsector_t *sub)
1014 {
1015 	int 		 count;
1016 	seg_t*		 line;
1017 	sector_t     tempsec;				// killough 3/7/98: deep water hack
1018 	int          floorlightlevel;		// killough 3/16/98: set floor lightlevel
1019 	int          ceilinglightlevel;		// killough 4/11/98
1020 	bool		 outersubsector;
1021 	int	fll, cll, position;
1022 	ASkyViewpoint *skybox;
1023 
1024 	// kg3D - fake floor stuff
1025 	visplane_t *backupfp;
1026 	visplane_t *backupcp;
1027 	//secplane_t templane;
1028 	lightlist_t *light;
1029 
1030 	if (InSubsector != NULL)
1031 	{ // InSubsector is not NULL. This means we are rendering from a mini-BSP.
1032 		outersubsector = false;
1033 	}
1034 	else
1035 	{
1036 		outersubsector = true;
1037 		InSubsector = sub;
1038 	}
1039 
1040 #ifdef RANGECHECK
1041 	if (outersubsector && sub - subsectors >= (ptrdiff_t)numsubsectors)
1042 		I_Error ("R_Subsector: ss %ti with numss = %i", sub - subsectors, numsubsectors);
1043 #endif
1044 
1045 	assert(sub->sector != NULL);
1046 
1047 	if (sub->polys)
1048 	{ // Render the polyobjs in the subsector first
1049 		R_AddPolyobjs(sub);
1050 		if (outersubsector)
1051 		{
1052 			InSubsector = NULL;
1053 		}
1054 		return;
1055 	}
1056 
1057 	frontsector = sub->sector;
1058 	frontsector->MoreFlags |= SECF_DRAWN;
1059 	count = sub->numlines;
1060 	line = sub->firstline;
1061 
1062 	// killough 3/8/98, 4/4/98: Deep water / fake ceiling effect
1063 	frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel,
1064 						   &ceilinglightlevel, false);	// killough 4/11/98
1065 
1066 	fll = floorlightlevel;
1067 	cll = ceilinglightlevel;
1068 
1069 	// [RH] set foggy flag
1070 	foggy = level.fadeto || frontsector->ColorMap->Fade || (level.flags & LEVEL_HASFADETABLE);
1071 	r_actualextralight = foggy ? 0 : extralight << 4;
1072 
1073 	// kg3D - fake lights
1074 	if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size())
1075 	{
1076 		light = P_GetPlaneLight(frontsector, &frontsector->ceilingplane, false);
1077 		basecolormap = light->extra_colormap;
1078 		// If this is the real ceiling, don't discard plane lighting R_FakeFlat()
1079 		// accounted for.
1080 		if (light->p_lightlevel != &frontsector->lightlevel)
1081 		{
1082 			ceilinglightlevel = *light->p_lightlevel;
1083 		}
1084 	}
1085 	else
1086 	{
1087 		basecolormap = frontsector->ColorMap;
1088 	}
1089 
1090 	skybox = frontsector->GetSkyBox(sector_t::ceiling);
1091 
1092 	ceilingplane = frontsector->ceilingplane.PointOnSide(viewx, viewy, viewz) > 0 ||
1093 		frontsector->GetTexture(sector_t::ceiling) == skyflatnum ||
1094 		(skybox != NULL && skybox->bAlways) ||
1095 		(frontsector->heightsec &&
1096 		 !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) &&
1097 		 frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ?
1098 		R_FindPlane(frontsector->ceilingplane,		// killough 3/8/98
1099 					frontsector->GetTexture(sector_t::ceiling),
1100 					ceilinglightlevel + r_actualextralight,				// killough 4/11/98
1101 					frontsector->GetAlpha(sector_t::ceiling),
1102 					!!(frontsector->GetFlags(sector_t::ceiling) & PLANEF_ADDITIVE),
1103 					frontsector->GetXOffset(sector_t::ceiling),		// killough 3/7/98
1104 					frontsector->GetYOffset(sector_t::ceiling),		// killough 3/7/98
1105 					frontsector->GetXScale(sector_t::ceiling),
1106 					frontsector->GetYScale(sector_t::ceiling),
1107 					frontsector->GetAngle(sector_t::ceiling),
1108 					frontsector->sky,
1109 					skybox
1110 					) : NULL;
1111 
1112 	if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size())
1113 	{
1114 		light = P_GetPlaneLight(frontsector, &frontsector->floorplane, false);
1115 		basecolormap = light->extra_colormap;
1116 		// If this is the real floor, don't discard plane lighting R_FakeFlat()
1117 		// accounted for.
1118 		if (light->p_lightlevel != &frontsector->lightlevel)
1119 		{
1120 			floorlightlevel = *light->p_lightlevel;
1121 		}
1122 	}
1123 	else
1124 	{
1125 		basecolormap = frontsector->ColorMap;
1126 	}
1127 
1128 	// killough 3/7/98: Add (x,y) offsets to flats, add deep water check
1129 	// killough 3/16/98: add floorlightlevel
1130 	// killough 10/98: add support for skies transferred from sidedefs
1131 	skybox = frontsector->GetSkyBox(sector_t::floor);
1132 	floorplane = frontsector->floorplane.PointOnSide(viewx, viewy, viewz) > 0 || // killough 3/7/98
1133 		frontsector->GetTexture(sector_t::floor) == skyflatnum ||
1134 		(skybox != NULL && skybox->bAlways) ||
1135 		(frontsector->heightsec &&
1136 		 !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) &&
1137 		 frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ?
1138 		R_FindPlane(frontsector->floorplane,
1139 					frontsector->GetTexture(sector_t::floor),
1140 					floorlightlevel + r_actualextralight,				// killough 3/16/98
1141 					frontsector->GetAlpha(sector_t::floor),
1142 					!!(frontsector->GetFlags(sector_t::floor) & PLANEF_ADDITIVE),
1143 					frontsector->GetXOffset(sector_t::floor),		// killough 3/7/98
1144 					frontsector->GetYOffset(sector_t::floor),		// killough 3/7/98
1145 					frontsector->GetXScale(sector_t::floor),
1146 					frontsector->GetYScale(sector_t::floor),
1147 					frontsector->GetAngle(sector_t::floor),
1148 					frontsector->sky,
1149 					skybox
1150 					) : NULL;
1151 
1152 	// kg3D - fake planes rendering
1153 	if (r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size())
1154 	{
1155 		backupfp = floorplane;
1156 		backupcp = ceilingplane;
1157 		// first check all floors
1158 		for (int i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++)
1159 		{
1160 			fakeFloor = frontsector->e->XFloor.ffloors[i];
1161 			if (!(fakeFloor->flags & FF_EXISTS)) continue;
1162 			if (!fakeFloor->model) continue;
1163 			if (fakeFloor->bottom.plane->a || fakeFloor->bottom.plane->b) continue;
1164 			if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES)))
1165 			{
1166 				R_3D_AddHeight(fakeFloor->top.plane, frontsector);
1167 			}
1168 			if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
1169 			if (fakeFloor->alpha == 0) continue;
1170 			if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue;
1171 			fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE);
1172 			if (fakeFloor->validcount != validcount)
1173 			{
1174 				fakeFloor->validcount = validcount;
1175 				R_3D_NewClip();
1176 			}
1177 			fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->soundorg[0], frontsector->soundorg[0]);
1178 			if (fakeHeight < viewz &&
1179 				fakeHeight > frontsector->floorplane.ZatPoint(frontsector->soundorg[0], frontsector->soundorg[1]))
1180 			{
1181 				fake3D = FAKE3D_FAKEFLOOR;
1182 				tempsec = *fakeFloor->model;
1183 				tempsec.floorplane = *fakeFloor->top.plane;
1184 				tempsec.ceilingplane = *fakeFloor->bottom.plane;
1185 				if (!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR))
1186 				{
1187 					tempsec.SetTexture(sector_t::floor, tempsec.GetTexture(sector_t::ceiling));
1188 					position = sector_t::ceiling;
1189 				} else position = sector_t::floor;
1190 				frontsector = &tempsec;
1191 
1192 				if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size())
1193 				{
1194 					light = P_GetPlaneLight(sub->sector, &frontsector->floorplane, false);
1195 					basecolormap = light->extra_colormap;
1196 					floorlightlevel = *light->p_lightlevel;
1197 				}
1198 
1199 				ceilingplane = NULL;
1200 				floorplane = R_FindPlane(frontsector->floorplane,
1201 					frontsector->GetTexture(sector_t::floor),
1202 					floorlightlevel + r_actualextralight,				// killough 3/16/98
1203 					frontsector->GetAlpha(sector_t::floor),
1204 					!!(fakeFloor->flags & FF_ADDITIVETRANS),
1205 					frontsector->GetXOffset(position),		// killough 3/7/98
1206 					frontsector->GetYOffset(position),		// killough 3/7/98
1207 					frontsector->GetXScale(position),
1208 					frontsector->GetYScale(position),
1209 					frontsector->GetAngle(position),
1210 					frontsector->sky,
1211 					NULL);
1212 
1213 				R_FakeDrawLoop(sub);
1214 				fake3D = 0;
1215 				frontsector = sub->sector;
1216 			}
1217 		}
1218 		// and now ceilings
1219 		for (unsigned int i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++)
1220 		{
1221 			fakeFloor = frontsector->e->XFloor.ffloors[i];
1222 			if (!(fakeFloor->flags & FF_EXISTS)) continue;
1223 			if (!fakeFloor->model) continue;
1224 			if (fakeFloor->top.plane->a || fakeFloor->top.plane->b) continue;
1225 			if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES)))
1226 			{
1227 				R_3D_AddHeight(fakeFloor->bottom.plane, frontsector);
1228 			}
1229 			if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
1230 			if (fakeFloor->alpha == 0) continue;
1231 			if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE|FF_INVERTSECTOR)) == (FF_SWIMMABLE|FF_INVERTSECTOR)) continue;
1232 			fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE);
1233 
1234 			if (fakeFloor->validcount != validcount)
1235 			{
1236 				fakeFloor->validcount = validcount;
1237 				R_3D_NewClip();
1238 			}
1239 			fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->soundorg[0], frontsector->soundorg[1]);
1240 			if (fakeHeight > viewz &&
1241 				fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->soundorg[0], frontsector->soundorg[1]))
1242 			{
1243 				fake3D = FAKE3D_FAKECEILING;
1244 				tempsec = *fakeFloor->model;
1245 				tempsec.floorplane = *fakeFloor->top.plane;
1246 				tempsec.ceilingplane = *fakeFloor->bottom.plane;
1247 				if ((!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) ||
1248 					(fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR))
1249 				{
1250 					tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor));
1251 					position = sector_t::floor;
1252 				} else position = sector_t::ceiling;
1253 				frontsector = &tempsec;
1254 
1255 				tempsec.ceilingplane.ChangeHeight(-1);
1256 				if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size())
1257 				{
1258 					light = P_GetPlaneLight(sub->sector, &frontsector->ceilingplane, false);
1259 					basecolormap = light->extra_colormap;
1260 					ceilinglightlevel = *light->p_lightlevel;
1261 				}
1262 				tempsec.ceilingplane.ChangeHeight(1);
1263 
1264 				floorplane = NULL;
1265 				ceilingplane = R_FindPlane(frontsector->ceilingplane,		// killough 3/8/98
1266 					frontsector->GetTexture(sector_t::ceiling),
1267 					ceilinglightlevel + r_actualextralight,				// killough 4/11/98
1268 					frontsector->GetAlpha(sector_t::ceiling),
1269 					!!(fakeFloor->flags & FF_ADDITIVETRANS),
1270 					frontsector->GetXOffset(position),		// killough 3/7/98
1271 					frontsector->GetYOffset(position),		// killough 3/7/98
1272 					frontsector->GetXScale(position),
1273 					frontsector->GetYScale(position),
1274 					frontsector->GetAngle(position),
1275 					frontsector->sky,
1276 					NULL);
1277 
1278 				R_FakeDrawLoop(sub);
1279 				fake3D = 0;
1280 				frontsector = sub->sector;
1281 			}
1282 		}
1283 		fakeFloor = NULL;
1284 		floorplane = backupfp;
1285 		ceilingplane = backupcp;
1286 	}
1287 
1288 	basecolormap = frontsector->ColorMap;
1289 	floorlightlevel = fll;
1290 	ceilinglightlevel = cll;
1291 
1292 	// killough 9/18/98: Fix underwater slowdown, by passing real sector
1293 	// instead of fake one. Improve sprite lighting by basing sprite
1294 	// lightlevels on floor & ceiling lightlevels in the surrounding area.
1295 	// [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by
1296 	// it, otherwise they are lit by the floor.
1297 	R_AddSprites (sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ?
1298 		ceilinglightlevel : floorlightlevel, FakeSide);
1299 
1300 	// [RH] Add particles
1301 	if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors)
1302 	{ // Only do it for the main BSP.
1303 		int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight);
1304 		for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext)
1305 		{
1306 			R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide);
1307 		}
1308 	}
1309 
1310 	count = sub->numlines;
1311 	line = sub->firstline;
1312 
1313 	while (count--)
1314 	{
1315 		if (!outersubsector || line->sidedef == NULL || !(line->sidedef->Flags & WALLF_POLYOBJ))
1316 		{
1317 			// kg3D - fake planes bounding calculation
1318 			if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size())
1319 			{
1320 				backupfp = floorplane;
1321 				backupcp = ceilingplane;
1322 				floorplane = NULL;
1323 				ceilingplane = NULL;
1324 				for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++)
1325 				{
1326 					fakeFloor = line->backsector->e->XFloor.ffloors[i];
1327 					if (!(fakeFloor->flags & FF_EXISTS)) continue;
1328 					if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
1329 					if (!fakeFloor->model) continue;
1330 					fake3D = FAKE3D_FAKEBACK;
1331 					tempsec = *fakeFloor->model;
1332 					tempsec.floorplane = *fakeFloor->top.plane;
1333 					tempsec.ceilingplane = *fakeFloor->bottom.plane;
1334 					backsector = &tempsec;
1335 					if (fakeFloor->validcount != validcount)
1336 					{
1337 						fakeFloor->validcount = validcount;
1338 						R_3D_NewClip();
1339 					}
1340 					if (frontsector->CenterFloor() >= backsector->CenterFloor())
1341 					{
1342 						fake3D |= FAKE3D_CLIPBOTFRONT;
1343 					}
1344 					if (frontsector->CenterCeiling() <= backsector->CenterCeiling())
1345 					{
1346 						fake3D |= FAKE3D_CLIPTOPFRONT;
1347 					}
1348 					R_AddLine(line); // fake
1349 				}
1350 				fakeFloor = NULL;
1351 				fake3D = 0;
1352 				floorplane = backupfp;
1353 				ceilingplane = backupcp;
1354 			}
1355 			R_AddLine (line); // now real
1356 		}
1357 		line++;
1358 	}
1359 	if (outersubsector)
1360 	{
1361 		InSubsector = NULL;
1362 	}
1363 }
1364 
1365 //
1366 // RenderBSPNode
1367 // Renders all subsectors below a given node, traversing subtree recursively.
1368 // Just call with BSP root and -1.
1369 // killough 5/2/98: reformatted, removed tail recursion
1370 
R_RenderBSPNode(void * node)1371 void R_RenderBSPNode (void *node)
1372 {
1373 	if (numnodes == 0)
1374 	{
1375 		R_Subsector (subsectors);
1376 		return;
1377 	}
1378 	while (!((size_t)node & 1))  // Keep going until found a subsector
1379 	{
1380 		node_t *bsp = (node_t *)node;
1381 
1382 		// Decide which side the view point is on.
1383 		int side = R_PointOnSide (viewx, viewy, bsp);
1384 
1385 		// Recursively divide front space (toward the viewer).
1386 		R_RenderBSPNode (bsp->children[side]);
1387 
1388 		// Possibly divide back space (away from the viewer).
1389 		side ^= 1;
1390 		if (!R_CheckBBox (bsp->bbox[side]))
1391 			return;
1392 
1393 		node = bsp->children[side];
1394 	}
1395 	R_Subsector ((subsector_t *)((BYTE *)node - 1));
1396 }
1397