1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 * BSP traversal, handling of LineSegs for rendering.
30 *-----------------------------------------------------------------------------*/
39 #include "r_bsp.h" // cph - sanity checking
44 sector_t
*frontsector
;
48 // killough 4/7/98: indicates doors closed wrt automap bugfix:
49 // cph - replaced by linedef rendering flags - int doorclosed;
51 // killough: New code which removes 2s linedef limit
54 // drawseg_t drawsegs[MAXDRAWSEGS]; // old code -- killough
60 void R_ClearDrawSegs(void)
66 // Instead of clipsegs, let's try using an array with one entry for each column,
67 // indicating whether it's blocked by a solid wall yet or not.
69 byte solidcol
[MAX_SCREENWIDTH
] IBSS_ATTR
;
74 // Replaces the old R_Clip*WallSegment functions. It draws bits of walls in those
75 // columns which aren't solid, and updates the solidcol[] array appropriately
77 void R_ClipWallSegment(int first
, int last
, boolean solid
)
84 if (!(p
= memchr(solidcol
+first
, 0, last
-first
)))
91 if (!(p
= memchr(solidcol
+first
, 1, last
-first
)))
95 R_StoreWallRange(first
, to
-1);
98 memset(solidcol
+first
,1,to
-first
);
109 void R_ClearClipSegs (void)
111 memset(solidcol
, 0, SCREENWIDTH
);
114 // killough 1/18/98 -- This function is used to fix the automap bug which
115 // showed lines behind closed doors simply because the door had a dropoff.
117 // cph - converted to R_RecalcLineFlags. This recalculates all the flags for
118 // a line, including closure and texture tiling.
120 static void R_RecalcLineFlags(void)
122 linedef
->r_validcount
= gametic
;
124 /* First decide if the line is closed, normal, or invisible */
125 if (!(linedef
->flags
& ML_TWOSIDED
)
126 || backsector
->ceilingheight
<= frontsector
->floorheight
127 || backsector
->floorheight
>= frontsector
->ceilingheight
129 // if door is closed because back is shut:
130 backsector
->ceilingheight
<= backsector
->floorheight
132 // preserve a kind of transparent door/lift special effect:
133 && (backsector
->ceilingheight
>= frontsector
->ceilingheight
||
134 curline
->sidedef
->toptexture
)
136 && (backsector
->floorheight
<= frontsector
->floorheight
||
137 curline
->sidedef
->bottomtexture
)
139 // properly render skies (consider door "open" if both ceilings are sky):
140 && (backsector
->ceilingpic
!=skyflatnum
||
141 frontsector
->ceilingpic
!=skyflatnum
)
144 linedef
->r_flags
= RF_CLOSED
;
147 // Reject empty lines used for triggers
148 // and special events.
149 // Identical floor and ceiling on both sides,
150 // identical light levels on both sides,
151 // and no middle texture.
152 // CPhipps - recode for speed, not certain if this is portable though
153 if (backsector
->ceilingheight
!= frontsector
->ceilingheight
154 || backsector
->floorheight
!= frontsector
->floorheight
155 || curline
->sidedef
->midtexture
156 || memcmp(&backsector
->floor_xoffs
, &frontsector
->floor_xoffs
,
157 sizeof(frontsector
->floor_xoffs
) + sizeof(frontsector
->floor_yoffs
) +
158 sizeof(frontsector
->ceiling_xoffs
) + sizeof(frontsector
->ceiling_yoffs
) +
159 sizeof(frontsector
->ceilingpic
) + sizeof(frontsector
->floorpic
) +
160 sizeof(frontsector
->lightlevel
) + sizeof(frontsector
->floorlightsec
) +
161 sizeof(frontsector
->ceilinglightsec
)))
163 linedef
->r_flags
= 0;
167 linedef
->r_flags
= RF_IGNORE
;
170 /* cph - I'm too lazy to try and work with offsets in this */
171 if (curline
->sidedef
->rowoffset
)
174 /* Now decide on texture tiling */
175 if (linedef
->flags
& ML_TWOSIDED
)
179 /* Does top texture need tiling */
180 if ((c
= frontsector
->ceilingheight
- backsector
->ceilingheight
) > 0 &&
181 (textureheight
[texturetranslation
[curline
->sidedef
->toptexture
]] > c
))
182 linedef
->r_flags
|= RF_TOP_TILE
;
184 /* Does bottom texture need tiling */
185 if ((c
= frontsector
->floorheight
- backsector
->floorheight
) > 0 &&
186 (textureheight
[texturetranslation
[curline
->sidedef
->bottomtexture
]] > c
))
187 linedef
->r_flags
|= RF_BOT_TILE
;
192 /* Does middle texture need tiling */
193 if ((c
= frontsector
->ceilingheight
- frontsector
->floorheight
) > 0 &&
194 (textureheight
[texturetranslation
[curline
->sidedef
->midtexture
]] > c
))
195 linedef
->r_flags
|= RF_MID_TILE
;
200 // killough 3/7/98: Hack floor/ceiling heights for deep water etc.
202 // If player's view height is underneath fake floor, lower the
203 // drawn ceiling to be just under the floor height, and replace
204 // the drawn floor and ceiling textures, and light level, with
205 // the control sector's.
207 // Similar for ceiling, only reflected.
209 // killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter
212 sector_t
*R_FakeFlat(sector_t
*sec
, sector_t
*tempsec
,
213 int *floorlightlevel
, int *ceilinglightlevel
,
217 *floorlightlevel
= sec
->floorlightsec
== -1 ?
218 sec
->lightlevel
: sectors
[sec
->floorlightsec
].lightlevel
;
220 if (ceilinglightlevel
)
221 *ceilinglightlevel
= sec
->ceilinglightsec
== -1 ? // killough 4/11/98
222 sec
->lightlevel
: sectors
[sec
->ceilinglightsec
].lightlevel
;
224 if (sec
->heightsec
!= -1)
226 const sector_t
*s
= §ors
[sec
->heightsec
];
227 int heightsec
= viewplayer
->mo
->subsector
->sector
->heightsec
;
228 int underwater
= heightsec
!=-1 && viewz
<=sectors
[heightsec
].floorheight
;
230 // Replace sector being drawn, with a copy to be hacked
233 // Replace floor and ceiling height with other sector's heights.
234 tempsec
->floorheight
= s
->floorheight
;
235 tempsec
->ceilingheight
= s
->ceilingheight
;
237 // killough 11/98: prevent sudden light changes from non-water sectors:
238 if (underwater
&& (tempsec
-> floorheight
= sec
->floorheight
,
239 tempsec
->ceilingheight
= s
->floorheight
-1, !back
))
240 { // head-below-floor hack
241 tempsec
->floorpic
= s
->floorpic
;
242 tempsec
->floor_xoffs
= s
->floor_xoffs
;
243 tempsec
->floor_yoffs
= s
->floor_yoffs
;
247 if (s
->ceilingpic
== skyflatnum
)
249 tempsec
->floorheight
= tempsec
->ceilingheight
+1;
250 tempsec
->ceilingpic
= tempsec
->floorpic
;
251 tempsec
->ceiling_xoffs
= tempsec
->floor_xoffs
;
252 tempsec
->ceiling_yoffs
= tempsec
->floor_yoffs
;
256 tempsec
->ceilingpic
= s
->ceilingpic
;
257 tempsec
->ceiling_xoffs
= s
->ceiling_xoffs
;
258 tempsec
->ceiling_yoffs
= s
->ceiling_yoffs
;
262 tempsec
->lightlevel
= s
->lightlevel
;
265 *floorlightlevel
= s
->floorlightsec
== -1 ? s
->lightlevel
:
266 sectors
[s
->floorlightsec
].lightlevel
; // killough 3/16/98
268 if (ceilinglightlevel
)
269 *ceilinglightlevel
= s
->ceilinglightsec
== -1 ? s
->lightlevel
:
270 sectors
[s
->ceilinglightsec
].lightlevel
; // killough 4/11/98
273 if (heightsec
!= -1 && viewz
>= sectors
[heightsec
].ceilingheight
&&
274 sec
->ceilingheight
> s
->ceilingheight
)
275 { // Above-ceiling hack
276 tempsec
->ceilingheight
= s
->ceilingheight
;
277 tempsec
->floorheight
= s
->ceilingheight
+ 1;
279 tempsec
->floorpic
= tempsec
->ceilingpic
= s
->ceilingpic
;
280 tempsec
->floor_xoffs
= tempsec
->ceiling_xoffs
= s
->ceiling_xoffs
;
281 tempsec
->floor_yoffs
= tempsec
->ceiling_yoffs
= s
->ceiling_yoffs
;
283 if (s
->floorpic
!= skyflatnum
)
285 tempsec
->ceilingheight
= sec
->ceilingheight
;
286 tempsec
->floorpic
= s
->floorpic
;
287 tempsec
->floor_xoffs
= s
->floor_xoffs
;
288 tempsec
->floor_yoffs
= s
->floor_yoffs
;
291 tempsec
->lightlevel
= s
->lightlevel
;
294 *floorlightlevel
= s
->floorlightsec
== -1 ? s
->lightlevel
:
295 sectors
[s
->floorlightsec
].lightlevel
; // killough 3/16/98
297 if (ceilinglightlevel
)
298 *ceilinglightlevel
= s
->ceilinglightsec
== -1 ? s
->lightlevel
:
299 sectors
[s
->ceilinglightsec
].lightlevel
; // killough 4/11/98
301 sec
= tempsec
; // Use other sector
308 // Clips the given segment
309 // and adds any visible pieces to the line list.
312 static void R_AddLine (seg_t
*line
)
320 static sector_t tempsec
; // killough 3/8/98: ceiling/water hack
321 // boolean solid = true;
325 angle1
= R_PointToAngle (line
->v1
->x
, line
->v1
->y
);
326 angle2
= R_PointToAngle (line
->v2
->x
, line
->v2
->y
);
328 // Clip to view edges.
329 span
= angle1
- angle2
;
331 // Back side, i.e. backface culling
335 // Global angle needed by segcalc.
340 tspan
= angle1
+ clipangle
;
341 if (tspan
> 2*clipangle
)
343 tspan
-= 2*clipangle
;
345 // Totally off the left edge?
352 tspan
= clipangle
- angle2
;
353 if (tspan
> 2*clipangle
)
355 tspan
-= 2*clipangle
;
357 // Totally off the left edge?
360 angle2
= 0-clipangle
;
363 // The seg is in the view range,
364 // but not necessarily visible.
366 angle1
= (angle1
+ANG90
)>>ANGLETOFINESHIFT
;
367 angle2
= (angle2
+ANG90
)>>ANGLETOFINESHIFT
;
369 // killough 1/31/98: Here is where "slime trails" can SOMETIMES occur:
370 x1
= viewangletox
[angle1
];
371 x2
= viewangletox
[angle2
];
373 // Does not cross a pixel?
374 if (x1
>= x2
) // killough 1/31/98 -- change == to >= for robustness
377 backsector
= line
->backsector
;
379 // Single sided line?
381 // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water
382 backsector
= R_FakeFlat(backsector
, &tempsec
, NULL
, NULL
, true);
384 /* cph - roll up linedef properties in flags */
385 if ((linedef
= curline
->linedef
)->r_validcount
!= gametic
)
388 if (linedef
->r_flags
& RF_IGNORE
)
393 R_ClipWallSegment (x1
, x2
, linedef
->r_flags
& RF_CLOSED
);
398 // Checks BSP node/subtree bounding box.
400 // if some part of the bbox might be visible.
403 static const int checkcoord
[12][4] = // killough -- static const
419 // killough 1/28/98: static // CPhipps - const parameter, reformatted
420 static boolean
R_CheckBBox(const fixed_t
*bspcoord
)
422 angle_t angle1
, angle2
;
428 // Find the corners of the box
429 // that define the edges from current viewpoint.
430 boxpos
= (viewx
<= bspcoord
[BOXLEFT
] ? 0 : viewx
< bspcoord
[BOXRIGHT
] ? 1 : 2) +
431 (viewy
>= bspcoord
[BOXTOP
] ? 0 : viewy
> bspcoord
[BOXBOTTOM
] ? 4 : 8);
436 check
= checkcoord
[boxpos
];
437 angle1
= R_PointToAngle (bspcoord
[check
[0]], bspcoord
[check
[1]]) - viewangle
;
438 angle2
= R_PointToAngle (bspcoord
[check
[2]], bspcoord
[check
[3]]) - viewangle
;
441 // cph - replaced old code, which was unclear and badly commented
442 // Much more efficient code now
443 if ((signed)angle1
< (signed)angle2
)
444 { /* it's "behind" us */
445 /* Either angle1 or angle2 is behind us, so it doesn't matter if we
446 * change it to the corect sign
448 if ((angle1
>= ANG180
) && (angle1
< ANG270
))
449 angle1
= INT_MAX
; /* which is ANG180-1 */
454 if ((signed)angle2
>= (signed)clipangle
)
455 return false; // Both off left edge
456 if ((signed)angle1
<= -(signed)clipangle
)
457 return false; // Both off right edge
458 if ((signed)angle1
>= (signed)clipangle
)
459 angle1
= clipangle
; // Clip at left edge
460 if ((signed)angle2
<= -(signed)clipangle
)
461 angle2
= 0-clipangle
; // Clip at right edge
463 // Find the first clippost
464 // that touches the source post
465 // (adjacent pixels are touching).
466 angle1
= (angle1
+ANG90
)>>ANGLETOFINESHIFT
;
467 angle2
= (angle2
+ANG90
)>>ANGLETOFINESHIFT
;
469 int sx1
= viewangletox
[angle1
];
470 int sx2
= viewangletox
[angle2
];
471 // const cliprange_t *start;
473 // Does not cross a pixel.
477 if (!memchr(solidcol
+sx1
, 0, sx2
-sx1
))
479 // All columns it covers are already solidly covered
487 // Determine floor/ceiling planes.
488 // Add sprites of things in sector.
489 // Draw one or more line segments.
491 // killough 1/31/98 -- made static, polished
493 // Had to move this out of the function - causes stack overflows in RockBox
494 sector_t tempsec IBSS_ATTR
; // killough 3/7/98: deep water hack
495 static void R_Subsector(int num
)
501 int floorlightlevel
; // killough 3/16/98: set floor lightlevel
502 int ceilinglightlevel
; // killough 4/11/98
506 if (num
>=numsubsectors
)
507 I_Error ("R_Subsector: ss %i with numss = %i", num
, numsubsectors
);
510 sub
= &subsectors
[num
];
511 frontsector
= sub
->sector
;
512 count
= sub
->numlines
;
513 line
= &segs
[sub
->firstline
];
516 // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect
517 frontsector
= R_FakeFlat(frontsector
, &tempsec
, &floorlightlevel
,
518 &ceilinglightlevel
, false); // killough 4/11/98
520 // killough 3/7/98: Add (x,y) offsets to flats, add deep water check
521 // killough 3/16/98: add floorlightlevel
522 // killough 10/98: add support for skies transferred from sidedefs
524 floorplane
= frontsector
->floorheight
< viewz
|| // killough 3/7/98
525 (frontsector
->heightsec
!= -1 &&
526 sectors
[frontsector
->heightsec
].ceilingpic
== skyflatnum
) ?
527 R_FindPlane(frontsector
->floorheight
,
528 frontsector
->floorpic
== skyflatnum
&& // kilough 10/98
529 frontsector
->sky
& PL_SKYFLAT
? frontsector
->sky
:
530 frontsector
->floorpic
,
531 floorlightlevel
, // killough 3/16/98
532 frontsector
->floor_xoffs
, // killough 3/7/98
533 frontsector
->floor_yoffs
536 ceilingplane
= frontsector
->ceilingheight
> viewz
||
537 frontsector
->ceilingpic
== skyflatnum
||
538 (frontsector
->heightsec
!= -1 &&
539 sectors
[frontsector
->heightsec
].floorpic
== skyflatnum
) ?
540 R_FindPlane(frontsector
->ceilingheight
, // killough 3/8/98
541 frontsector
->ceilingpic
== skyflatnum
&& // kilough 10/98
542 frontsector
->sky
& PL_SKYFLAT
? frontsector
->sky
:
543 frontsector
->ceilingpic
,
544 ceilinglightlevel
, // killough 4/11/98
545 frontsector
->ceiling_xoffs
, // killough 3/7/98
546 frontsector
->ceiling_yoffs
549 // killough 9/18/98: Fix underwater slowdown, by passing real sector
550 // instead of fake one. Improve sprite lighting by basing sprite
551 // lightlevels on floor & ceiling lightlevels in the surrounding area.
555 // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!!
556 // That is part of the 242 effect!!! If you simply pass sub->sector to
557 // the old code you will not get correct lighting for underwater sprites!!!
558 // Either you must pass the fake sector and handle validcount here, on the
559 // real sector, or you must account for the lighting in some other way,
560 // like passing it as an argument.
562 R_AddSprites(sub
, (floorlightlevel
+ceilinglightlevel
)/2);
566 if (line
->miniseg
== false)
575 // Renders all subsectors below a given node,
576 // traversing subtree recursively.
577 // Just call with BSP root.
579 // killough 5/2/98: reformatted, removed tail recursion
581 void R_RenderBSPNode(int bspnum
)
583 while (!(bspnum
& NF_SUBSECTOR
)) // Found a subsector?
585 const node_t
*bsp
= &nodes
[bspnum
];
587 // Decide which side the view point is on.
588 int side
= R_PointOnSide(viewx
, viewy
, bsp
);
589 // Recursively divide front space.
590 R_RenderBSPNode(bsp
->children
[side
]);
592 // Possibly divide back space.
594 if (!R_CheckBBox(bsp
->bbox
[side
^1]))
597 bspnum
= bsp
->children
[side
^1];
599 R_Subsector(bspnum
== -1 ? 0 : bspnum
& ~NF_SUBSECTOR
);