Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
298 | serge | 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 | // $Log:$ |
||
18 | // |
||
19 | // DESCRIPTION: |
||
20 | // BSP traversal, handling of LineSegs for rendering. |
||
21 | // |
||
22 | //----------------------------------------------------------------------------- |
||
23 | |||
24 | |||
25 | static const char |
||
26 | rcsid[] = "$Id: r_bsp.c,v 1.4 1997/02/03 22:45:12 b1 Exp $"; |
||
27 | |||
28 | |||
29 | #include "doomdef.h" |
||
30 | |||
31 | #include "m_bbox.h" |
||
32 | |||
33 | #include "i_system.h" |
||
34 | |||
35 | #include "r_main.h" |
||
36 | #include "r_plane.h" |
||
37 | #include "r_things.h" |
||
38 | |||
39 | // State. |
||
40 | #include "doomstat.h" |
||
41 | #include "r_state.h" |
||
42 | |||
43 | //#include "r_local.h" |
||
44 | |||
45 | |||
46 | |||
47 | seg_t* curline; |
||
48 | side_t* sidedef; |
||
49 | line_t* linedef; |
||
50 | sector_t* frontsector; |
||
51 | sector_t* backsector; |
||
52 | |||
53 | drawseg_t drawsegs[MAXDRAWSEGS]; |
||
54 | drawseg_t* ds_p; |
||
55 | |||
56 | |||
57 | void |
||
58 | R_StoreWallRange |
||
59 | ( int start, |
||
60 | int stop ); |
||
61 | |||
62 | |||
63 | |||
64 | |||
65 | // |
||
66 | // R_ClearDrawSegs |
||
67 | // |
||
68 | void R_ClearDrawSegs (void) |
||
69 | { |
||
70 | ds_p = drawsegs; |
||
71 | } |
||
72 | |||
73 | |||
74 | |||
75 | // |
||
76 | // ClipWallSegment |
||
77 | // Clips the given range of columns |
||
78 | // and includes it in the new clip list. |
||
79 | // |
||
80 | typedef struct |
||
81 | { |
||
82 | int first; |
||
83 | int last; |
||
84 | |||
85 | } cliprange_t; |
||
86 | |||
87 | |||
88 | #define MAXSEGS 32 |
||
89 | |||
90 | // newend is one past the last valid seg |
||
91 | cliprange_t* newend; |
||
92 | cliprange_t solidsegs[MAXSEGS]; |
||
93 | |||
94 | |||
95 | |||
96 | |||
97 | // |
||
98 | // R_ClipSolidWallSegment |
||
99 | // Does handle solid walls, |
||
100 | // e.g. single sided LineDefs (middle texture) |
||
101 | // that entirely block the view. |
||
102 | // |
||
103 | void |
||
104 | R_ClipSolidWallSegment |
||
105 | ( int first, |
||
106 | int last ) |
||
107 | { |
||
108 | cliprange_t* next; |
||
109 | cliprange_t* start; |
||
110 | |||
111 | // Find the first range that touches the range |
||
112 | // (adjacent pixels are touching). |
||
113 | start = solidsegs; |
||
114 | while (start->last < first-1) |
||
115 | start++; |
||
116 | |||
117 | if (first < start->first) |
||
118 | { |
||
119 | if (last < start->first-1) |
||
120 | { |
||
121 | // Post is entirely visible (above start), |
||
122 | // so insert a new clippost. |
||
123 | R_StoreWallRange (first, last); |
||
124 | next = newend; |
||
125 | newend++; |
||
126 | |||
127 | while (next != start) |
||
128 | { |
||
129 | *next = *(next-1); |
||
130 | next--; |
||
131 | } |
||
132 | next->first = first; |
||
133 | next->last = last; |
||
134 | return; |
||
135 | } |
||
136 | |||
137 | // There is a fragment above *start. |
||
138 | R_StoreWallRange (first, start->first - 1); |
||
139 | // Now adjust the clip size. |
||
140 | start->first = first; |
||
141 | } |
||
142 | |||
143 | // Bottom contained in start? |
||
144 | if (last <= start->last) |
||
145 | return; |
||
146 | |||
147 | next = start; |
||
148 | while (last >= (next+1)->first-1) |
||
149 | { |
||
150 | // There is a fragment between two posts. |
||
151 | R_StoreWallRange (next->last + 1, (next+1)->first - 1); |
||
152 | next++; |
||
153 | |||
154 | if (last <= next->last) |
||
155 | { |
||
156 | // Bottom is contained in next. |
||
157 | // Adjust the clip size. |
||
158 | start->last = next->last; |
||
159 | goto crunch; |
||
160 | } |
||
161 | } |
||
162 | |||
163 | // There is a fragment after *next. |
||
164 | R_StoreWallRange (next->last + 1, last); |
||
165 | // Adjust the clip size. |
||
166 | start->last = last; |
||
167 | |||
168 | // Remove start+1 to next from the clip list, |
||
169 | // because start now covers their area. |
||
170 | crunch: |
||
171 | if (next == start) |
||
172 | { |
||
173 | // Post just extended past the bottom of one post. |
||
174 | return; |
||
175 | } |
||
176 | |||
177 | |||
178 | while (next++ != newend) |
||
179 | { |
||
180 | // Remove a post. |
||
181 | *++start = *next; |
||
182 | } |
||
183 | |||
184 | newend = start+1; |
||
185 | } |
||
186 | |||
187 | |||
188 | |||
189 | // |
||
190 | // R_ClipPassWallSegment |
||
191 | // Clips the given range of columns, |
||
192 | // but does not includes it in the clip list. |
||
193 | // Does handle windows, |
||
194 | // e.g. LineDefs with upper and lower texture. |
||
195 | // |
||
196 | void |
||
197 | R_ClipPassWallSegment |
||
198 | ( int first, |
||
199 | int last ) |
||
200 | { |
||
201 | cliprange_t* start; |
||
202 | |||
203 | // Find the first range that touches the range |
||
204 | // (adjacent pixels are touching). |
||
205 | start = solidsegs; |
||
206 | while (start->last < first-1) |
||
207 | start++; |
||
208 | |||
209 | if (first < start->first) |
||
210 | { |
||
211 | if (last < start->first-1) |
||
212 | { |
||
213 | // Post is entirely visible (above start). |
||
214 | R_StoreWallRange (first, last); |
||
215 | return; |
||
216 | } |
||
217 | |||
218 | // There is a fragment above *start. |
||
219 | R_StoreWallRange (first, start->first - 1); |
||
220 | } |
||
221 | |||
222 | // Bottom contained in start? |
||
223 | if (last <= start->last) |
||
224 | return; |
||
225 | |||
226 | while (last >= (start+1)->first-1) |
||
227 | { |
||
228 | // There is a fragment between two posts. |
||
229 | R_StoreWallRange (start->last + 1, (start+1)->first - 1); |
||
230 | start++; |
||
231 | |||
232 | if (last <= start->last) |
||
233 | return; |
||
234 | } |
||
235 | |||
236 | // There is a fragment after *next. |
||
237 | R_StoreWallRange (start->last + 1, last); |
||
238 | } |
||
239 | |||
240 | |||
241 | |||
242 | // |
||
243 | // R_ClearClipSegs |
||
244 | // |
||
245 | void R_ClearClipSegs (void) |
||
246 | { |
||
247 | solidsegs[0].first = -0x7fffffff; |
||
248 | solidsegs[0].last = -1; |
||
249 | solidsegs[1].first = viewwidth; |
||
250 | solidsegs[1].last = 0x7fffffff; |
||
251 | newend = solidsegs+2; |
||
252 | } |
||
253 | |||
254 | // |
||
255 | // R_AddLine |
||
256 | // Clips the given segment |
||
257 | // and adds any visible pieces to the line list. |
||
258 | // |
||
259 | void R_AddLine (seg_t* line) |
||
260 | { |
||
261 | int x1; |
||
262 | int x2; |
||
263 | angle_t angle1; |
||
264 | angle_t angle2; |
||
265 | angle_t span; |
||
266 | angle_t tspan; |
||
267 | |||
268 | curline = line; |
||
269 | |||
270 | // OPTIMIZE: quickly reject orthogonal back sides. |
||
271 | angle1 = R_PointToAngle (line->v1->x, line->v1->y); |
||
272 | angle2 = R_PointToAngle (line->v2->x, line->v2->y); |
||
273 | |||
274 | // Clip to view edges. |
||
275 | // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). |
||
276 | span = angle1 - angle2; |
||
277 | |||
278 | // Back side? I.e. backface culling? |
||
279 | if (span >= ANG180) |
||
280 | return; |
||
281 | |||
282 | // Global angle needed by segcalc. |
||
283 | rw_angle1 = angle1; |
||
284 | angle1 -= viewangle; |
||
285 | angle2 -= viewangle; |
||
286 | |||
287 | tspan = angle1 + clipangle; |
||
288 | if (tspan > 2*clipangle) |
||
289 | { |
||
290 | tspan -= 2*clipangle; |
||
291 | |||
292 | // Totally off the left edge? |
||
293 | if (tspan >= span) |
||
294 | return; |
||
295 | |||
296 | angle1 = clipangle; |
||
297 | } |
||
298 | tspan = clipangle - angle2; |
||
299 | if (tspan > 2*clipangle) |
||
300 | { |
||
301 | tspan -= 2*clipangle; |
||
302 | |||
303 | // Totally off the left edge? |
||
304 | if (tspan >= span) |
||
305 | return; |
||
306 | angle2 = -clipangle; |
||
307 | } |
||
308 | |||
309 | // The seg is in the view range, |
||
310 | // but not necessarily visible. |
||
311 | angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; |
||
312 | angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; |
||
313 | x1 = viewangletox[angle1]; |
||
314 | x2 = viewangletox[angle2]; |
||
315 | |||
316 | // Does not cross a pixel? |
||
317 | if (x1 == x2) |
||
318 | return; |
||
319 | |||
320 | backsector = line->backsector; |
||
321 | |||
322 | // Single sided line? |
||
323 | if (!backsector) |
||
324 | goto clipsolid; |
||
325 | |||
326 | // Closed door. |
||
327 | if (backsector->ceilingheight <= frontsector->floorheight |
||
328 | || backsector->floorheight >= frontsector->ceilingheight) |
||
329 | goto clipsolid; |
||
330 | |||
331 | // Window. |
||
332 | if (backsector->ceilingheight != frontsector->ceilingheight |
||
333 | || backsector->floorheight != frontsector->floorheight) |
||
334 | goto clippass; |
||
335 | |||
336 | // Reject empty lines used for triggers |
||
337 | // and special events. |
||
338 | // Identical floor and ceiling on both sides, |
||
339 | // identical light levels on both sides, |
||
340 | // and no middle texture. |
||
341 | if (backsector->ceilingpic == frontsector->ceilingpic |
||
342 | && backsector->floorpic == frontsector->floorpic |
||
343 | && backsector->lightlevel == frontsector->lightlevel |
||
344 | && curline->sidedef->midtexture == 0) |
||
345 | { |
||
346 | return; |
||
347 | } |
||
348 | |||
349 | |||
350 | clippass: |
||
351 | R_ClipPassWallSegment (x1, x2-1); |
||
352 | return; |
||
353 | |||
354 | clipsolid: |
||
355 | R_ClipSolidWallSegment (x1, x2-1); |
||
356 | } |
||
357 | |||
358 | |||
359 | // |
||
360 | // R_CheckBBox |
||
361 | // Checks BSP node/subtree bounding box. |
||
362 | // Returns true |
||
363 | // if some part of the bbox might be visible. |
||
364 | // |
||
365 | int checkcoord[12][4] = |
||
366 | { |
||
367 | {3,0,2,1}, |
||
368 | {3,0,2,0}, |
||
369 | {3,1,2,0}, |
||
370 | {0}, |
||
371 | {2,0,2,1}, |
||
372 | {0,0,0,0}, |
||
373 | {3,1,3,0}, |
||
374 | {0}, |
||
375 | {2,0,3,1}, |
||
376 | {2,1,3,1}, |
||
377 | {2,1,3,0} |
||
378 | }; |
||
379 | |||
380 | |||
381 | boolean R_CheckBBox (fixed_t* bspcoord) |
||
382 | { |
||
383 | int boxx; |
||
384 | int boxy; |
||
385 | int boxpos; |
||
386 | |||
387 | fixed_t x1; |
||
388 | fixed_t y1; |
||
389 | fixed_t x2; |
||
390 | fixed_t y2; |
||
391 | |||
392 | angle_t angle1; |
||
393 | angle_t angle2; |
||
394 | angle_t span; |
||
395 | angle_t tspan; |
||
396 | |||
397 | cliprange_t* start; |
||
398 | |||
399 | int sx1; |
||
400 | int sx2; |
||
401 | |||
402 | // Find the corners of the box |
||
403 | // that define the edges from current viewpoint. |
||
404 | if (viewx <= bspcoord[BOXLEFT]) |
||
405 | boxx = 0; |
||
406 | else if (viewx < bspcoord[BOXRIGHT]) |
||
407 | boxx = 1; |
||
408 | else |
||
409 | boxx = 2; |
||
410 | |||
411 | if (viewy >= bspcoord[BOXTOP]) |
||
412 | boxy = 0; |
||
413 | else if (viewy > bspcoord[BOXBOTTOM]) |
||
414 | boxy = 1; |
||
415 | else |
||
416 | boxy = 2; |
||
417 | |||
418 | boxpos = (boxy<<2)+boxx; |
||
419 | if (boxpos == 5) |
||
420 | return true; |
||
421 | |||
422 | x1 = bspcoord[checkcoord[boxpos][0]]; |
||
423 | y1 = bspcoord[checkcoord[boxpos][1]]; |
||
424 | x2 = bspcoord[checkcoord[boxpos][2]]; |
||
425 | y2 = bspcoord[checkcoord[boxpos][3]]; |
||
426 | |||
427 | // check clip list for an open space |
||
428 | angle1 = R_PointToAngle (x1, y1) - viewangle; |
||
429 | angle2 = R_PointToAngle (x2, y2) - viewangle; |
||
430 | |||
431 | span = angle1 - angle2; |
||
432 | |||
433 | // Sitting on a line? |
||
434 | if (span >= ANG180) |
||
435 | return true; |
||
436 | |||
437 | tspan = angle1 + clipangle; |
||
438 | |||
439 | if (tspan > 2*clipangle) |
||
440 | { |
||
441 | tspan -= 2*clipangle; |
||
442 | |||
443 | // Totally off the left edge? |
||
444 | if (tspan >= span) |
||
445 | return false; |
||
446 | |||
447 | angle1 = clipangle; |
||
448 | } |
||
449 | tspan = clipangle - angle2; |
||
450 | if (tspan > 2*clipangle) |
||
451 | { |
||
452 | tspan -= 2*clipangle; |
||
453 | |||
454 | // Totally off the left edge? |
||
455 | if (tspan >= span) |
||
456 | return false; |
||
457 | |||
458 | angle2 = -clipangle; |
||
459 | } |
||
460 | |||
461 | |||
462 | // Find the first clippost |
||
463 | // that touches the source post |
||
464 | // (adjacent pixels are touching). |
||
465 | angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; |
||
466 | angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; |
||
467 | sx1 = viewangletox[angle1]; |
||
468 | sx2 = viewangletox[angle2]; |
||
469 | |||
470 | // Does not cross a pixel. |
||
471 | if (sx1 == sx2) |
||
472 | return false; |
||
473 | sx2--; |
||
474 | |||
475 | start = solidsegs; |
||
476 | while (start->last < sx2) |
||
477 | start++; |
||
478 | |||
479 | if (sx1 >= start->first |
||
480 | && sx2 <= start->last) |
||
481 | { |
||
482 | // The clippost contains the new span. |
||
483 | return false; |
||
484 | } |
||
485 | |||
486 | return true; |
||
487 | } |
||
488 | |||
489 | |||
490 | |||
491 | // |
||
492 | // R_Subsector |
||
493 | // Determine floor/ceiling planes. |
||
494 | // Add sprites of things in sector. |
||
495 | // Draw one or more line segments. |
||
496 | // |
||
497 | void R_Subsector (int num) |
||
498 | { |
||
499 | int count; |
||
500 | seg_t* line; |
||
501 | subsector_t* sub; |
||
502 | |||
503 | #ifdef RANGECHECK |
||
504 | if (num>=numsubsectors) |
||
505 | I_Error ("R_Subsector: ss %i with numss = %i", |
||
506 | num, |
||
507 | numsubsectors); |
||
508 | #endif |
||
509 | |||
510 | sscount++; |
||
511 | sub = &subsectors[num]; |
||
512 | frontsector = sub->sector; |
||
513 | count = sub->numlines; |
||
514 | line = &segs[sub->firstline]; |
||
515 | |||
516 | if (frontsector->floorheight < viewz) |
||
517 | { |
||
518 | floorplane = R_FindPlane (frontsector->floorheight, |
||
519 | frontsector->floorpic, |
||
520 | frontsector->lightlevel); |
||
521 | } |
||
522 | else |
||
523 | floorplane = NULL; |
||
524 | |||
525 | if (frontsector->ceilingheight > viewz |
||
526 | || frontsector->ceilingpic == skyflatnum) |
||
527 | { |
||
528 | ceilingplane = R_FindPlane (frontsector->ceilingheight, |
||
529 | frontsector->ceilingpic, |
||
530 | frontsector->lightlevel); |
||
531 | } |
||
532 | else |
||
533 | ceilingplane = NULL; |
||
534 | |||
535 | R_AddSprites (frontsector); |
||
536 | |||
537 | while (count--) |
||
538 | { |
||
539 | R_AddLine (line); |
||
540 | line++; |
||
541 | } |
||
542 | } |
||
543 | |||
544 | |||
545 | |||
546 | |||
547 | // |
||
548 | // RenderBSPNode |
||
549 | // Renders all subsectors below a given node, |
||
550 | // traversing subtree recursively. |
||
551 | // Just call with BSP root. |
||
552 | void R_RenderBSPNode (int bspnum) |
||
553 | { |
||
554 | node_t* bsp; |
||
555 | int side; |
||
556 | |||
557 | // Found a subsector? |
||
558 | if (bspnum & NF_SUBSECTOR) |
||
559 | { |
||
560 | if (bspnum == -1) |
||
561 | R_Subsector (0); |
||
562 | else |
||
563 | R_Subsector (bspnum&(~NF_SUBSECTOR)); |
||
564 | return; |
||
565 | } |
||
566 | |||
567 | bsp = &nodes[bspnum]; |
||
568 | |||
569 | // Decide which side the view point is on. |
||
570 | side = R_PointOnSide (viewx, viewy, bsp); |
||
571 | |||
572 | // Recursively divide front space. |
||
573 | R_RenderBSPNode (bsp->children[side]); |
||
574 | |||
575 | // Possibly divide back space. |
||
576 | if (R_CheckBBox (bsp->bbox[side^1])) |
||
577 | R_RenderBSPNode (bsp->children[side^1]); |
||
578 | }>=>>2)+boxx; |
||
579 | |||
580 |