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