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