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