1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 1993-2008 Raven Software
4 // Copyright(C) 2005-2014 Simon Howard
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16
17
18 #include "h2def.h"
19 #include "i_system.h"
20 #include "r_local.h"
21
22 // OPTIMIZE: closed two sided lines as single sided
23
24 boolean segtextured; // true if any of the segs textures might be vis
25 boolean markfloor; // false if the back side is the same plane
26 boolean markceiling;
27 boolean maskedtexture;
28 int toptexture, bottomtexture, midtexture;
29
30
31 angle_t rw_normalangle;
32 int rw_angle1; // angle to line origin
33
34 //
35 // wall
36 //
37 int rw_x;
38 int rw_stopx;
39 angle_t rw_centerangle;
40 fixed_t rw_offset;
41 fixed_t rw_distance;
42 fixed_t rw_scale;
43 fixed_t rw_scalestep;
44 fixed_t rw_midtexturemid;
45 fixed_t rw_toptexturemid;
46 fixed_t rw_bottomtexturemid;
47
48 int worldtop, worldbottom, worldhigh, worldlow;
49
50 fixed_t pixhigh, pixlow;
51 fixed_t pixhighstep, pixlowstep;
52 fixed_t topfrac, topstep;
53 fixed_t bottomfrac, bottomstep;
54
55
56 lighttable_t **walllights;
57
58 short *maskedtexturecol;
59
60 /*
61 ================
62 =
63 = R_RenderMaskedSegRange
64 =
65 ================
66 */
67
R_RenderMaskedSegRange(drawseg_t * ds,int x1,int x2)68 void R_RenderMaskedSegRange(drawseg_t * ds, int x1, int x2)
69 {
70 unsigned index;
71 column_t *col;
72 int lightnum;
73 int texnum;
74
75 //
76 // calculate light table
77 // use different light tables for horizontal / vertical / diagonal
78 // OPTIMIZE: get rid of LIGHTSEGSHIFT globally
79 curline = ds->curline;
80 frontsector = curline->frontsector;
81 backsector = curline->backsector;
82 texnum = texturetranslation[curline->sidedef->midtexture];
83
84 lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight;
85 //if (curline->v1->y == curline->v2->y)
86 // lightnum--;
87 //else if (curline->v1->x == curline->v2->x)
88 // lightnum++;
89 //if (lightnum < 0)
90 // walllights = scalelight[0];
91 if (lightnum >= LIGHTLEVELS)
92 walllights = scalelight[LIGHTLEVELS - 1];
93 else
94 walllights = scalelight[lightnum];
95
96 maskedtexturecol = ds->maskedtexturecol;
97
98 rw_scalestep = ds->scalestep;
99 spryscale = ds->scale1 + (x1 - ds->x1) * rw_scalestep;
100 mfloorclip = ds->sprbottomclip;
101 mceilingclip = ds->sprtopclip;
102
103 //
104 // find positioning
105 //
106 if (curline->linedef->flags & ML_DONTPEGBOTTOM)
107 {
108 dc_texturemid = frontsector->floorheight > backsector->floorheight
109 ? frontsector->floorheight : backsector->floorheight;
110 dc_texturemid = dc_texturemid + textureheight[texnum] - viewz;
111 }
112 else
113 {
114 dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight
115 ? frontsector->ceilingheight : backsector->ceilingheight;
116 dc_texturemid = dc_texturemid - viewz;
117 }
118 dc_texturemid += curline->sidedef->rowoffset;
119
120 if (fixedcolormap)
121 dc_colormap = fixedcolormap;
122 //
123 // draw the columns
124 //
125 for (dc_x = x1; dc_x <= x2; dc_x++)
126 {
127 // calculate lighting
128 if (maskedtexturecol[dc_x] != SHRT_MAX)
129 {
130 if (!fixedcolormap)
131 {
132 index = spryscale >> (LIGHTSCALESHIFT + crispy->hires);
133 if (index >= MAXLIGHTSCALE)
134 index = MAXLIGHTSCALE - 1;
135 dc_colormap = walllights[index];
136 }
137
138 sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
139 dc_iscale = 0xffffffffu / (unsigned) spryscale;
140
141 //
142 // draw the texture
143 //
144 col = (column_t *) ((byte *)
145 R_GetColumn(texnum,
146 maskedtexturecol[dc_x]) - 3);
147
148 R_DrawMaskedColumn(col, -1);
149 maskedtexturecol[dc_x] = SHRT_MAX;
150 }
151 spryscale += rw_scalestep;
152 }
153
154 }
155
156 /*
157 ================
158 =
159 = R_RenderSegLoop
160 =
161 = Draws zero, one, or two textures (and possibly a masked texture) for walls
162 = Can draw or mark the starting pixel of floor and ceiling textures
163 =
164 = CALLED: CORE LOOPING ROUTINE
165 ================
166 */
167
168 #define HEIGHTBITS 12
169 #define HEIGHTUNIT (1<<HEIGHTBITS)
170
R_RenderSegLoop(void)171 void R_RenderSegLoop(void)
172 {
173 angle_t angle;
174 unsigned index;
175 int yl, yh, mid;
176 fixed_t texturecolumn;
177 int top, bottom;
178
179 texturecolumn = 0; // shut up compiler warning
180
181 for (; rw_x < rw_stopx; rw_x++)
182 {
183 //
184 // mark floor / ceiling areas
185 //
186 yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS;
187 if (yl < ceilingclip[rw_x] + 1)
188 yl = ceilingclip[rw_x] + 1; // no space above wall
189 if (markceiling)
190 {
191 top = ceilingclip[rw_x] + 1;
192 bottom = yl - 1;
193 if (bottom >= floorclip[rw_x])
194 bottom = floorclip[rw_x] - 1;
195 if (top <= bottom)
196 {
197 ceilingplane->top[rw_x] = top;
198 ceilingplane->bottom[rw_x] = bottom;
199 }
200 }
201
202 yh = bottomfrac >> HEIGHTBITS;
203 if (yh >= floorclip[rw_x])
204 yh = floorclip[rw_x] - 1;
205 if (markfloor)
206 {
207 top = yh + 1;
208 bottom = floorclip[rw_x] - 1;
209 if (top <= ceilingclip[rw_x])
210 top = ceilingclip[rw_x] + 1;
211 if (top <= bottom)
212 {
213 floorplane->top[rw_x] = top;
214 floorplane->bottom[rw_x] = bottom;
215 }
216 }
217
218 //
219 // texturecolumn and lighting are independent of wall tiers
220 //
221 if (segtextured)
222 {
223 // calculate texture offset
224 angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT;
225 texturecolumn =
226 rw_offset - FixedMul(finetangent[angle], rw_distance);
227 texturecolumn >>= FRACBITS;
228 // calculate lighting
229 index = rw_scale >> (LIGHTSCALESHIFT + crispy->hires);
230 if (index >= MAXLIGHTSCALE)
231 index = MAXLIGHTSCALE - 1;
232 dc_colormap = walllights[index];
233 dc_x = rw_x;
234 dc_iscale = 0xffffffffu / (unsigned) rw_scale;
235 }
236
237 //
238 // draw the wall tiers
239 //
240 if (midtexture)
241 { // single sided line
242 dc_yl = yl;
243 dc_yh = yh;
244 dc_texturemid = rw_midtexturemid;
245 dc_source = R_GetColumn(midtexture, texturecolumn);
246 colfunc();
247 ceilingclip[rw_x] = viewheight;
248 floorclip[rw_x] = -1;
249 }
250 else
251 { // two sided line
252 if (toptexture)
253 { // top wall
254 mid = pixhigh >> HEIGHTBITS;
255 pixhigh += pixhighstep;
256 if (mid >= floorclip[rw_x])
257 mid = floorclip[rw_x] - 1;
258 if (mid >= yl)
259 {
260 dc_yl = yl;
261 dc_yh = mid;
262 dc_texturemid = rw_toptexturemid;
263 dc_source = R_GetColumn(toptexture, texturecolumn);
264 colfunc();
265 ceilingclip[rw_x] = mid;
266 }
267 else
268 ceilingclip[rw_x] = yl - 1;
269 }
270 else
271 { // no top wall
272 if (markceiling)
273 ceilingclip[rw_x] = yl - 1;
274 }
275
276 if (bottomtexture)
277 { // bottom wall
278 mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS;
279 pixlow += pixlowstep;
280 if (mid <= ceilingclip[rw_x])
281 mid = ceilingclip[rw_x] + 1; // no space above wall
282 if (mid <= yh)
283 {
284 dc_yl = mid;
285 dc_yh = yh;
286 dc_texturemid = rw_bottomtexturemid;
287 dc_source = R_GetColumn(bottomtexture, texturecolumn);
288 colfunc();
289 floorclip[rw_x] = mid;
290 }
291 else
292 floorclip[rw_x] = yh + 1;
293 }
294 else
295 { // no bottom wall
296 if (markfloor)
297 floorclip[rw_x] = yh + 1;
298 }
299
300 if (maskedtexture)
301 { // save texturecol for backdrawing of masked mid texture
302 maskedtexturecol[rw_x] = texturecolumn;
303 }
304 }
305
306 rw_scale += rw_scalestep;
307 topfrac += topstep;
308 bottomfrac += bottomstep;
309 }
310
311 }
312
313
314
315 /*
316 =====================
317 =
318 = R_StoreWallRange
319 =
320 = A wall segment will be drawn between start and stop pixels (inclusive)
321 =
322 ======================
323 */
324
R_StoreWallRange(int start,int stop)325 void R_StoreWallRange(int start, int stop)
326 {
327 fixed_t hyp;
328 fixed_t sineval;
329 angle_t distangle, offsetangle;
330 fixed_t vtop;
331 int lightnum;
332
333 if (ds_p == &drawsegs[MAXDRAWSEGS])
334 return; // don't overflow and crash
335
336 #ifdef RANGECHECK
337 if (start >= viewwidth || start > stop)
338 I_Error("Bad R_RenderWallRange: %i to %i", start, stop);
339 #endif
340
341 sidedef = curline->sidedef;
342 linedef = curline->linedef;
343
344 // mark the segment as visible for auto map
345 linedef->flags |= ML_MAPPED;
346
347 //
348 // calculate rw_distance for scale calculation
349 //
350 rw_normalangle = curline->angle + ANG90;
351 offsetangle = abs((int) rw_normalangle - (int) rw_angle1);
352 if (offsetangle > ANG90)
353 offsetangle = ANG90;
354 distangle = ANG90 - offsetangle;
355 hyp = R_PointToDist(curline->v1->x, curline->v1->y);
356 sineval = finesine[distangle >> ANGLETOFINESHIFT];
357 rw_distance = FixedMul(hyp, sineval);
358
359
360 ds_p->x1 = rw_x = start;
361 ds_p->x2 = stop;
362 ds_p->curline = curline;
363 rw_stopx = stop + 1;
364
365 //
366 // calculate scale at both ends and step
367 //
368 ds_p->scale1 = rw_scale =
369 R_ScaleFromGlobalAngle(viewangle + xtoviewangle[start]);
370 if (stop > start)
371 {
372 ds_p->scale2 = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[stop]);
373 ds_p->scalestep = rw_scalestep =
374 (ds_p->scale2 - rw_scale) / (stop - start);
375 }
376 else
377 {
378 //
379 // try to fix the stretched line bug
380 //
381 #if 0
382 if (rw_distance < FRACUNIT / 2)
383 {
384 fixed_t trx, try;
385 fixed_t gxt, gyt;
386
387 trx = curline->v1->x - viewx;
388 try = curline->v1->y - viewy;
389
390 gxt = FixedMul(trx, viewcos);
391 gyt = -FixedMul(try, viewsin);
392 ds_p->scale1 = FixedDiv(projection, gxt - gyt);
393 }
394 #endif
395 ds_p->scale2 = ds_p->scale1;
396 }
397
398
399 //
400 // calculate texture boundaries and decide if floor / ceiling marks
401 // are needed
402 //
403 worldtop = frontsector->ceilingheight - viewz;
404 worldbottom = frontsector->floorheight - viewz;
405
406 midtexture = toptexture = bottomtexture = maskedtexture = 0;
407 ds_p->maskedtexturecol = NULL;
408
409 if (!backsector)
410 {
411 //
412 // single sided line
413 //
414 midtexture = texturetranslation[sidedef->midtexture];
415 // a single sided line is terminal, so it must mark ends
416 markfloor = markceiling = true;
417 if (linedef->flags & ML_DONTPEGBOTTOM)
418 {
419 vtop = frontsector->floorheight +
420 textureheight[sidedef->midtexture];
421 rw_midtexturemid = vtop - viewz; // bottom of texture at bottom
422 }
423 else
424 rw_midtexturemid = worldtop; // top of texture at top
425 rw_midtexturemid += sidedef->rowoffset;
426 ds_p->silhouette = SIL_BOTH;
427 ds_p->sprtopclip = screenheightarray;
428 ds_p->sprbottomclip = negonearray;
429 ds_p->bsilheight = INT_MAX;
430 ds_p->tsilheight = INT_MIN;
431 }
432 else
433 {
434 //
435 // two sided line
436 //
437 ds_p->sprtopclip = ds_p->sprbottomclip = NULL;
438 ds_p->silhouette = 0;
439 if (frontsector->floorheight > backsector->floorheight)
440 {
441 ds_p->silhouette = SIL_BOTTOM;
442 ds_p->bsilheight = frontsector->floorheight;
443 }
444 else if (backsector->floorheight > viewz)
445 {
446 ds_p->silhouette = SIL_BOTTOM;
447 ds_p->bsilheight = INT_MAX;
448 // ds_p->sprbottomclip = negonearray;
449 }
450 if (frontsector->ceilingheight < backsector->ceilingheight)
451 {
452 ds_p->silhouette |= SIL_TOP;
453 ds_p->tsilheight = frontsector->ceilingheight;
454 }
455 else if (backsector->ceilingheight < viewz)
456 {
457 ds_p->silhouette |= SIL_TOP;
458 ds_p->tsilheight = INT_MIN;
459 // ds_p->sprtopclip = screenheightarray;
460 }
461
462 if (backsector->ceilingheight <= frontsector->floorheight)
463 {
464 ds_p->sprbottomclip = negonearray;
465 ds_p->bsilheight = INT_MAX;
466 ds_p->silhouette |= SIL_BOTTOM;
467 }
468 if (backsector->floorheight >= frontsector->ceilingheight)
469 {
470 ds_p->sprtopclip = screenheightarray;
471 ds_p->tsilheight = INT_MIN;
472 ds_p->silhouette |= SIL_TOP;
473 }
474 worldhigh = backsector->ceilingheight - viewz;
475 worldlow = backsector->floorheight - viewz;
476
477 // hack to allow height changes in outdoor areas
478 if (frontsector->ceilingpic == skyflatnum
479 && backsector->ceilingpic == skyflatnum)
480 worldtop = worldhigh;
481
482 if (worldlow != worldbottom
483 || backsector->floorpic != frontsector->floorpic
484 || backsector->lightlevel != frontsector->lightlevel
485 || backsector->special != frontsector->special)
486 markfloor = true;
487 else
488 markfloor = false; // same plane on both sides
489
490 if (worldhigh != worldtop
491 || backsector->ceilingpic != frontsector->ceilingpic
492 || backsector->lightlevel != frontsector->lightlevel)
493 markceiling = true;
494 else
495 markceiling = false; // same plane on both sides
496
497 if (backsector->ceilingheight <= frontsector->floorheight
498 || backsector->floorheight >= frontsector->ceilingheight)
499 markceiling = markfloor = true; // closed door
500
501 if (worldhigh < worldtop)
502 { // top texture
503 toptexture = texturetranslation[sidedef->toptexture];
504 if (linedef->flags & ML_DONTPEGTOP)
505 rw_toptexturemid = worldtop; // top of texture at top
506 else
507 {
508 vtop = backsector->ceilingheight +
509 textureheight[sidedef->toptexture];
510 rw_toptexturemid = vtop - viewz; // bottom of texture
511 }
512 }
513 if (worldlow > worldbottom)
514 { // bottom texture
515 bottomtexture = texturetranslation[sidedef->bottomtexture];
516 if (linedef->flags & ML_DONTPEGBOTTOM)
517 { // bottom of texture at bottom
518 rw_bottomtexturemid = worldtop; // top of texture at top
519 }
520 else // top of texture at top
521 rw_bottomtexturemid = worldlow;
522 }
523 rw_toptexturemid += sidedef->rowoffset;
524 rw_bottomtexturemid += sidedef->rowoffset;
525
526 //
527 // allocate space for masked texture tables
528 //
529 if (sidedef->midtexture)
530 { // masked midtexture
531 maskedtexture = true;
532 ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x;
533 lastopening += rw_stopx - rw_x;
534 }
535 }
536
537 //
538 // calculate rw_offset (only needed for textured lines)
539 //
540 segtextured = midtexture | toptexture | bottomtexture | maskedtexture;
541
542 if (segtextured)
543 {
544 offsetangle = rw_normalangle - rw_angle1;
545 if (offsetangle > ANG180)
546 offsetangle = -offsetangle;
547 if (offsetangle > ANG90)
548 offsetangle = ANG90;
549 sineval = finesine[offsetangle >> ANGLETOFINESHIFT];
550 rw_offset = FixedMul(hyp, sineval);
551 if (rw_normalangle - rw_angle1 < ANG180)
552 rw_offset = -rw_offset;
553 rw_offset += sidedef->textureoffset + curline->offset;
554 rw_centerangle = ANG90 + viewangle - rw_normalangle;
555
556 //
557 // calculate light table
558 // use different light tables for horizontal / vertical / diagonal
559 // OPTIMIZE: get rid of LIGHTSEGSHIFT globally
560 if (!fixedcolormap)
561 {
562 lightnum =
563 (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight;
564 //if (curline->v1->y == curline->v2->y)
565 // lightnum--;
566 //else if (curline->v1->x == curline->v2->x)
567 // lightnum++;
568 //if (lightnum < 0)
569 // walllights = scalelight[0];
570 if (lightnum >= LIGHTLEVELS)
571 walllights = scalelight[LIGHTLEVELS - 1];
572 else
573 walllights = scalelight[lightnum];
574 }
575 }
576
577
578 //
579 // if a floor / ceiling plane is on the wrong side of the view plane
580 // it is definately invisible and doesn't need to be marked
581 //
582 if (frontsector->floorheight >= viewz)
583 markfloor = false; // above view plane
584 if (frontsector->ceilingheight <= viewz
585 && frontsector->ceilingpic != skyflatnum)
586 markceiling = false; // below view plane
587
588 //
589 // calculate incremental stepping values for texture edges
590 //
591 worldtop >>= 4;
592 worldbottom >>= 4;
593
594 topstep = -FixedMul(rw_scalestep, worldtop);
595 topfrac = (centeryfrac >> 4) - FixedMul(worldtop, rw_scale);
596
597 bottomstep = -FixedMul(rw_scalestep, worldbottom);
598 bottomfrac = (centeryfrac >> 4) - FixedMul(worldbottom, rw_scale);
599
600 if (backsector)
601 {
602 worldhigh >>= 4;
603 worldlow >>= 4;
604
605 if (worldhigh < worldtop)
606 {
607 pixhigh = (centeryfrac >> 4) - FixedMul(worldhigh, rw_scale);
608 pixhighstep = -FixedMul(rw_scalestep, worldhigh);
609 }
610 if (worldlow > worldbottom)
611 {
612 pixlow = (centeryfrac >> 4) - FixedMul(worldlow, rw_scale);
613 pixlowstep = -FixedMul(rw_scalestep, worldlow);
614 }
615 }
616
617 //
618 // render it
619 //
620 if (markceiling)
621 ceilingplane = R_CheckPlane(ceilingplane, rw_x, rw_stopx - 1);
622 if (markfloor)
623 floorplane = R_CheckPlane(floorplane, rw_x, rw_stopx - 1);
624
625 R_RenderSegLoop();
626
627 //
628 // save sprite clipping info
629 //
630 if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip)
631 {
632 memcpy(lastopening, ceilingclip + start, 2 * (rw_stopx - start));
633 ds_p->sprtopclip = lastopening - start;
634 lastopening += rw_stopx - start;
635 }
636 if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture)
637 && !ds_p->sprbottomclip)
638 {
639 memcpy(lastopening, floorclip + start, 2 * (rw_stopx - start));
640 ds_p->sprbottomclip = lastopening - start;
641 lastopening += rw_stopx - start;
642 }
643 if (maskedtexture && !(ds_p->silhouette & SIL_TOP))
644 {
645 ds_p->silhouette |= SIL_TOP;
646 ds_p->tsilheight = INT_MIN;
647 }
648 if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM))
649 {
650 ds_p->silhouette |= SIL_BOTTOM;
651 ds_p->bsilheight = INT_MAX;
652 }
653 ds_p++;
654 }
655