aboutsummaryrefslogtreecommitdiff
path: root/src/p_sight.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/p_sight.c')
-rw-r--r--src/p_sight.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/src/p_sight.c b/src/p_sight.c
new file mode 100644
index 0000000..d5d5756
--- /dev/null
+++ b/src/p_sight.c
@@ -0,0 +1,338 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
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
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 * LineOfSight/Visibility checks, uses REJECT Lookup Table.
31 *
32 *-----------------------------------------------------------------------------*/
33
34#include "doomstat.h"
35#include "r_main.h"
36#include "p_map.h"
37#include "p_maputl.h"
38#include "p_setup.h"
39#include "m_bbox.h"
40#include "lprintf.h"
41
42//
43// P_CheckSight
44//
45// killough 4/19/98:
46// Convert LOS info to struct for reentrancy and efficiency of data locality
47
48typedef struct {
49 fixed_t sightzstart, t2x, t2y; // eye z of looker
50 divline_t strace; // from t1 to t2
51 fixed_t topslope, bottomslope; // slopes to top and bottom of target
52 fixed_t bbox[4];
53 fixed_t maxz,minz; // cph - z optimisations for 2sided lines
54} los_t;
55
56static los_t los; // cph - made static
57
58//
59// P_DivlineSide
60// Returns side 0 (front), 1 (back), or 2 (on).
61//
62// killough 4/19/98: made static, cleaned up
63
64inline static int P_DivlineSide(fixed_t x, fixed_t y, const divline_t *node)
65{
66 fixed_t left, right;
67 return
68 !node->dx ? x == node->x ? 2 : x <= node->x ? node->dy > 0 : node->dy < 0 :
69 !node->dy ? ( compatibility_level < prboom_4_compatibility ? x : y) == node->y ? 2 : y <= node->y ? node->dx < 0 : node->dx > 0 :
70 (right = ((y - node->y) >> FRACBITS) * (node->dx >> FRACBITS)) <
71 (left = ((x - node->x) >> FRACBITS) * (node->dy >> FRACBITS)) ? 0 :
72 right == left ? 2 : 1;
73}
74
75//
76// P_CrossSubsector
77// Returns true
78// if strace crosses the given subsector successfully.
79//
80// killough 4/19/98: made static and cleaned up
81
82static boolean P_CrossSubsector(int num)
83{
84 seg_t *seg = segs + subsectors[num].firstline;
85 int count;
86 fixed_t opentop = 0, openbottom = 0;
87 const sector_t *front = NULL, *back = NULL;
88
89#ifdef RANGECHECK
90 if (num >= numsubsectors)
91 I_Error("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors);
92#endif
93
94 for (count = subsectors[num].numlines; --count >= 0; seg++) { // check lines
95 line_t *line = seg->linedef;
96 divline_t divl;
97
98 if(!line) // figgi -- skip minisegs
99 continue;
100
101 // allready checked other side?
102 if (line->validcount == validcount)
103 continue;
104
105 line->validcount = validcount;
106
107 /* OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test
108 * cph - this is causing demo desyncs on original Doom demos.
109 * Who knows why. Exclude test for those.
110 */
111 if (!demo_compatibility)
112 if (line->bbox[BOXLEFT ] > los.bbox[BOXRIGHT ] ||
113 line->bbox[BOXRIGHT ] < los.bbox[BOXLEFT ] ||
114 line->bbox[BOXBOTTOM] > los.bbox[BOXTOP ] ||
115 line->bbox[BOXTOP] < los.bbox[BOXBOTTOM])
116 continue;
117
118 // cph - do what we can before forced to check intersection
119 if (line->flags & ML_TWOSIDED) {
120
121 // no wall to block sight with?
122 if ((front = seg->frontsector)->floorheight ==
123 (back = seg->backsector)->floorheight &&
124 front->ceilingheight == back->ceilingheight)
125 continue;
126
127 // possible occluder
128 // because of ceiling height differences
129 opentop = front->ceilingheight < back->ceilingheight ?
130 front->ceilingheight : back->ceilingheight ;
131
132 // because of floor height differences
133 openbottom = front->floorheight > back->floorheight ?
134 front->floorheight : back->floorheight ;
135
136 // cph - reject if does not intrude in the z-space of the possible LOS
137 if ((opentop >= los.maxz) && (openbottom <= los.minz))
138 continue;
139 }
140
141 { // Forget this line if it doesn't cross the line of sight
142 const vertex_t *v1,*v2;
143
144 v1 = line->v1;
145 v2 = line->v2;
146
147 if (P_DivlineSide(v1->x, v1->y, &los.strace) ==
148 P_DivlineSide(v2->x, v2->y, &los.strace))
149 continue;
150
151 divl.dx = v2->x - (divl.x = v1->x);
152 divl.dy = v2->y - (divl.y = v1->y);
153
154 // line isn't crossed?
155 if (P_DivlineSide(los.strace.x, los.strace.y, &divl) ==
156 P_DivlineSide(los.t2x, los.t2y, &divl))
157 continue;
158 }
159
160 // cph - if bottom >= top or top < minz or bottom > maxz then it must be
161 // solid wrt this LOS
162 if (!(line->flags & ML_TWOSIDED) || (openbottom >= opentop) ||
163 (opentop < los.minz) || (openbottom > los.maxz))
164 return false;
165
166 { // crosses a two sided line
167 /* cph 2006/07/15 - oops, we missed this in 2.4.0 & .1;
168 * use P_InterceptVector2 for those compat levels only. */
169 fixed_t frac = (compatibility_level == prboom_5_compatibility || compatibility_level == prboom_6_compatibility) ?
170 P_InterceptVector2(&los.strace, &divl) :
171 P_InterceptVector(&los.strace, &divl);
172
173 if (front->floorheight != back->floorheight) {
174 fixed_t slope = FixedDiv(openbottom - los.sightzstart , frac);
175 if (slope > los.bottomslope)
176 los.bottomslope = slope;
177 }
178
179 if (front->ceilingheight != back->ceilingheight)
180 {
181 fixed_t slope = FixedDiv(opentop - los.sightzstart , frac);
182 if (slope < los.topslope)
183 los.topslope = slope;
184 }
185
186 if (los.topslope <= los.bottomslope)
187 return false; // stop
188 }
189 }
190 // passed the subsector ok
191 return true;
192}
193
194//
195// P_CrossBSPNode
196// Returns true
197// if strace crosses the given node successfully.
198//
199// killough 4/20/98: rewritten to remove tail recursion, clean up, and optimize
200// cph - Made to use R_PointOnSide instead of P_DivlineSide, since the latter
201// could return 2 which was ambigous, and the former is
202// better optimised; also removes two casts :-)
203
204static boolean P_CrossBSPNode_LxDoom(int bspnum)
205{
206 while (!(bspnum & NF_SUBSECTOR))
207 {
208 register const node_t *bsp = nodes + bspnum;
209 int side,side2;
210 side = R_PointOnSide(los.strace.x, los.strace.y, bsp);
211 side2 = R_PointOnSide(los.t2x, los.t2y, bsp);
212 if (side == side2)
213 bspnum = bsp->children[side]; // doesn't touch the other side
214 else // the partition plane is crossed here
215 if (!P_CrossBSPNode_LxDoom(bsp->children[side]))
216 return 0; // cross the starting side
217 else
218 bspnum = bsp->children[side^1]; // cross the ending side
219 }
220 return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
221}
222
223static boolean P_CrossBSPNode_PrBoom(int bspnum)
224{
225 while (!(bspnum & NF_SUBSECTOR))
226 {
227 register const node_t *bsp = nodes + bspnum;
228 int side,side2;
229 side = P_DivlineSide(los.strace.x,los.strace.y,(const divline_t *)bsp)&1;
230 side2= P_DivlineSide(los.t2x, los.t2y, (const divline_t *) bsp);
231 if (side == side2)
232 bspnum = bsp->children[side]; // doesn't touch the other side
233 else // the partition plane is crossed here
234 if (!P_CrossBSPNode_PrBoom(bsp->children[side]))
235 return 0; // cross the starting side
236 else
237 bspnum = bsp->children[side^1]; // cross the ending side
238 }
239 return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
240}
241
242/* proff - Moved the compatibility check outside the functions
243 * this gives a slight speedup
244 */
245static boolean P_CrossBSPNode(int bspnum)
246{
247 /* cph - LxDoom used some R_* funcs here */
248 if (compatibility_level == lxdoom_1_compatibility)
249 return P_CrossBSPNode_LxDoom(bspnum);
250 else
251 return P_CrossBSPNode_PrBoom(bspnum);
252}
253
254//
255// P_CheckSight
256// Returns true
257// if a straight line between t1 and t2 is unobstructed.
258// Uses REJECT.
259//
260// killough 4/20/98: cleaned up, made to use new LOS struct
261
262boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
263{
264 const sector_t *s1 = t1->subsector->sector;
265 const sector_t *s2 = t2->subsector->sector;
266 int pnum = (s1-sectors)*numsectors + (s2-sectors);
267
268 // First check for trivial rejection.
269 // Determine subsector entries in REJECT table.
270 //
271 // Check in REJECT table.
272
273 if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
274 return false;
275
276 // killough 4/19/98: make fake floors and ceilings block monster view
277
278 if ((s1->heightsec != -1 &&
279 ((t1->z + t1->height <= sectors[s1->heightsec].floorheight &&
280 t2->z >= sectors[s1->heightsec].floorheight) ||
281 (t1->z >= sectors[s1->heightsec].ceilingheight &&
282 t2->z + t1->height <= sectors[s1->heightsec].ceilingheight)))
283 ||
284 (s2->heightsec != -1 &&
285 ((t2->z + t2->height <= sectors[s2->heightsec].floorheight &&
286 t1->z >= sectors[s2->heightsec].floorheight) ||
287 (t2->z >= sectors[s2->heightsec].ceilingheight &&
288 t1->z + t2->height <= sectors[s2->heightsec].ceilingheight))))
289 return false;
290
291 /* killough 11/98: shortcut for melee situations
292 * same subsector? obviously visible
293 * cph - compatibility optioned for demo sync, cf HR06-UV.LMP */
294 if ((t1->subsector == t2->subsector) &&
295 (compatibility_level >= mbf_compatibility))
296 return true;
297
298 // An unobstructed LOS is possible.
299 // Now look from eyes of t1 to any part of t2.
300
301 validcount++;
302
303 los.topslope = (los.bottomslope = t2->z - (los.sightzstart =
304 t1->z + t1->height -
305 (t1->height>>2))) + t2->height;
306 los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x);
307 los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y);
308
309 if (t1->x > t2->x)
310 los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x;
311 else
312 los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x;
313
314 if (t1->y > t2->y)
315 los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y;
316 else
317 los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
318
319 /* cph - calculate min and max z of the potential line of sight
320 * For old demos, we disable this optimisation by setting them to
321 * the extremes */
322 switch (compatibility_level) {
323 case lxdoom_1_compatibility:
324 if (los.sightzstart < t2->z) {
325 los.maxz = t2->z + t2->height; los.minz = los.sightzstart;
326 } else if (los.sightzstart > t2->z + t2->height) {
327 los.maxz = los.sightzstart; los.minz = t2->z;
328 } else {
329 los.maxz = t2->z + t2->height; los.minz = t2->z;
330 }
331 break;
332 default:
333 los.maxz = INT_MAX; los.minz = INT_MIN;
334 }
335
336 // the head node is the last node output
337 return P_CrossBSPNode(numnodes-1);
338}