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