Rebase to SVN, and do a bit of cleanup as well as minor fixes.
[kugel-rb.git] / apps / plugins / doom / r_bsp.c
bloba06cb96196ef94057ffaa865dbddc12c58dc0279
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
25 * 02111-1307, USA.
27 * DESCRIPTION:
28 * BSP traversal, handling of LineSegs for rendering.
30 *-----------------------------------------------------------------------------*/
32 #include "doomstat.h"
33 #include "m_bbox.h"
34 #include "i_system.h"
35 #include "r_main.h"
36 #include "r_segs.h"
37 #include "r_plane.h"
38 #include "r_things.h"
39 #include "r_bsp.h" // cph - sanity checking
41 seg_t *curline;
42 side_t *sidedef;
43 line_t *linedef;
44 sector_t *frontsector;
45 sector_t *backsector;
46 drawseg_t *ds_p;
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
52 drawseg_t *drawsegs;
53 unsigned maxdrawsegs;
54 // drawseg_t drawsegs[MAXDRAWSEGS]; // old code -- killough
57 // R_ClearDrawSegs
60 void R_ClearDrawSegs(void)
62 ds_p = drawsegs;
65 // CPhipps -
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;
71 // CPhipps -
72 // R_ClipWallSegment
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)
79 byte *p;
80 while (first < last)
82 if (solidcol[first])
84 if (!(p = memchr(solidcol+first, 0, last-first)))
85 return; // All solid
86 first = p - solidcol;
88 else
90 int to;
91 if (!(p = memchr(solidcol+first, 1, last-first)))
92 to = last;
93 else
94 to = p - solidcol;
95 R_StoreWallRange(first, to-1);
96 if (solid)
98 memset(solidcol+first,1,to-first);
100 first = to;
106 // R_ClearClipSegs
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
128 || (
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;
145 else
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;
164 return;
166 else
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)
172 return;
174 /* Now decide on texture tiling */
175 if (linedef->flags & ML_TWOSIDED)
177 int c;
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;
189 else
191 int c;
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,
214 boolean back)
216 if (floorlightlevel)
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 = &sectors[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
231 *tempsec = *sec;
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;
245 if (underwater)
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;
254 else
256 tempsec->ceilingpic = s->ceilingpic;
257 tempsec->ceiling_xoffs = s->ceiling_xoffs;
258 tempsec->ceiling_yoffs = s->ceiling_yoffs;
262 tempsec->lightlevel = s->lightlevel;
264 if (floorlightlevel)
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
272 else
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;
293 if (floorlightlevel)
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
303 return sec;
307 // R_AddLine
308 // Clips the given segment
309 // and adds any visible pieces to the line list.
312 static void R_AddLine (seg_t *line)
314 int x1;
315 int x2;
316 angle_t angle1;
317 angle_t angle2;
318 angle_t span;
319 angle_t tspan;
320 static sector_t tempsec; // killough 3/8/98: ceiling/water hack
321 // boolean solid = true;
323 curline = line;
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
332 if (span >= ANG180)
333 return;
335 // Global angle needed by segcalc.
336 rw_angle1 = angle1;
337 angle1 -= viewangle;
338 angle2 -= viewangle;
340 tspan = angle1 + clipangle;
341 if (tspan > 2*clipangle)
343 tspan -= 2*clipangle;
345 // Totally off the left edge?
346 if (tspan >= span)
347 return;
349 angle1 = clipangle;
352 tspan = clipangle - angle2;
353 if (tspan > 2*clipangle)
355 tspan -= 2*clipangle;
357 // Totally off the left edge?
358 if (tspan >= span)
359 return;
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
375 return;
377 backsector = line->backsector;
379 // Single sided line?
380 if (backsector)
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)
386 R_RecalcLineFlags();
388 if (linedef->r_flags & RF_IGNORE)
390 return;
392 else
393 R_ClipWallSegment (x1, x2, linedef->r_flags & RF_CLOSED);
397 // R_CheckBBox
398 // Checks BSP node/subtree bounding box.
399 // Returns true
400 // if some part of the bbox might be visible.
403 static const int checkcoord[12][4] = // killough -- static const
406 3,0,2,1},
407 {3,0,2,0},
408 {3,1,2,0},
409 {0},
410 {2,0,2,1},
411 {0,0,0,0},
412 {3,1,3,0},
413 {0},
414 {2,0,3,1},
415 {2,1,3,1},
416 {2,1,3,0}
419 // killough 1/28/98: static // CPhipps - const parameter, reformatted
420 static boolean R_CheckBBox(const fixed_t *bspcoord)
422 angle_t angle1, angle2;
425 int boxpos;
426 const int* check;
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);
433 if (boxpos == 5)
434 return true;
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 */
450 else
451 angle2 = INT_MIN;
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.
474 if (sx1 == sx2)
475 return false;
477 if (!memchr(solidcol+sx1, 0, sx2-sx1))
478 return false;
479 // All columns it covers are already solidly covered
482 return true;
486 // R_Subsector
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)
497 int count;
498 seg_t *line;
499 subsector_t *sub;
501 int floorlightlevel; // killough 3/16/98: set floor lightlevel
502 int ceilinglightlevel; // killough 4/11/98
504 #ifdef RANGECHECK
506 if (num>=numsubsectors)
507 I_Error ("R_Subsector: ss %i with numss = %i", num, numsubsectors);
508 #endif
510 sub = &subsectors[num];
511 frontsector = sub->sector;
512 count = sub->numlines;
513 line = &segs[sub->firstline];
514 // sscount++;
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
534 ) : NULL;
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
547 ) : NULL;
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.
553 // 10/98 killough:
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);
564 while (count--)
566 if (line->miniseg == false)
567 R_AddLine (line);
568 line++;
574 // RenderBSPNode
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]))
595 return;
597 bspnum = bsp->children[side^1];
599 R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);