1 // WL_DRAW.C
2 
3 #include "wl_def.h"
4 #include "id_sd.h"
5 #include "id_in.h"
6 #include "id_vl.h"
7 #include "id_vh.h"
8 #include "id_us.h"
9 #include "textures/textures.h"
10 #include "c_cvars.h"
11 #include "r_sprites.h"
12 #include "r_data/colormaps.h"
13 
14 #include "wl_cloudsky.h"
15 #include "wl_atmos.h"
16 #include "wl_shade.h"
17 #include "actor.h"
18 #include "id_ca.h"
19 #include "gamemap.h"
20 #include "g_mapinfo.h"
21 #include "lumpremap.h"
22 #include "wl_agent.h"
23 #include "wl_draw.h"
24 #include "wl_game.h"
25 #include "wl_play.h"
26 #include "wl_state.h"
27 #include "a_inventory.h"
28 #include "thingdef/thingdef.h"
29 
30 /*
31 =============================================================================
32 
33 							LOCAL CONSTANTS
34 
35 =============================================================================
36 */
37 
38 #define MINDIST         (0x4000l)
39 
40 #define mapheight (map->GetHeader().height)
41 #define mapwidth (map->GetHeader().width)
42 #define maparea (mapheight*mapwidth)
43 
44 /*
45 =============================================================================
46 
47 							GLOBAL VARIABLES
48 
49 =============================================================================
50 */
51 
52 void DrawFloorAndCeiling(byte *vbuf, unsigned vbufPitch, int min_wallheight);
53 
54 const RatioInformation AspectCorrection[] =
55 {
56 	/* UNC */	{960,	600,	0x10000,	0,				48,			false},
57 	/* 16:9 */	{1280,	450,	0x15555,	0,				48*3/4,		true},
58 	/* 16:10 */	{1152,	500,	0x13333,	0,				48*5/6,		true},
59 	/* 17:10 */ {1224,	471,	0x14666,	0,				48*40/51,	true},
60 	/* 4:3 */	{960,	600,	0x10000,	0,				48,			false},
61 	/* 5:4 */	{960,	640,	0x10000,(fixed)6.5*FRACUNIT,	48*15/16,	false}
62 };
63 
64 /*static*/ byte *vbuf = NULL;
65 unsigned vbufPitch = 0;
66 
67 int32_t	lasttimecount;
68 int32_t	frameon;
69 bool	fpscounter;
70 int		r_extralight;
71 
72 int fps_frames=0, fps_time=0, fps=0;
73 
74 int *wallheight;
75 int min_wallheight;
76 
77 //
78 // math tables
79 //
80 short *pixelangle;
81 fixed finetangent[FINEANGLES/2 + ANG180];
82 fixed finesine[FINEANGLES+FINEANGLES/4];
83 fixed *finecosine = finesine+ANG90;
84 
85 //
86 // refresh variables
87 //
88 fixed   viewx,viewy;                    // the focal point
89 angle_t viewangle;
90 fixed   viewsin,viewcos;
91 int viewshift = 0;
92 fixed viewz = 32;
93 
94 fixed gLevelVisibility = VISIBILITY_DEFAULT;
95 fixed gLevelMaxLightVis = MAXLIGHTVIS_DEFAULT;
96 int gLevelLight = LIGHTLEVEL_DEFAULT;
97 
98 void    TransformActor (AActor *ob);
99 void    BuildTables (void);
100 void    ClearScreen (void);
101 int     CalcRotate (AActor *ob);
102 void    DrawScaleds (void);
103 void    CalcTics (void);
104 void    ThreeDRefresh (void);
105 
106 
107 
108 //
109 // wall optimization variables
110 //
111 int     lastside;               // true for vertical
112 int32_t    lastintercept;
113 MapSpot lasttilehit;
114 int     lasttexture;
115 
116 //
117 // ray tracing variables
118 //
119 short    focaltx,focalty,viewtx,viewty;
120 longword xpartialup,xpartialdown,ypartialup,ypartialdown;
121 
122 short   midangle;
123 short   angle;
124 
125 MapTile::Side hitdir;
126 MapSpot tilehit;
127 int     pixx;
128 
129 short   xtile,ytile;
130 short   xtilestep,ytilestep;
131 int32_t    xintercept,yintercept;
132 word    xstep,ystep;
133 int     texdelta;
134 int		texheight;
135 
136 #define TEXTUREBASE 0x4000000
137 fixed	texxscale = FRACUNIT;
138 fixed	texyscale = FRACUNIT;
139 
140 
141 /*
142 ============================================================================
143 
144 						3 - D  DEFINITIONS
145 
146 ============================================================================
147 */
148 
149 /*
150 ========================
151 =
152 = TransformActor
153 =
154 = Takes paramaters:
155 =   gx,gy               : globalx/globaly of point
156 =
157 = globals:
158 =   viewx,viewy         : point of view
159 =   viewcos,viewsin     : sin/cos of viewangle
160 =   scale               : conversion from global value to screen value
161 =
162 = sets:
163 =   screenx,transx,transy,screenheight: projected edge location and size
164 =
165 ========================
166 */
167 
168 
169 //
170 // transform actor
171 //
TransformActor(AActor * ob)172 void TransformActor (AActor *ob)
173 {
174 	fixed gx,gy,gxt,gyt,nx,ny;
175 
176 //
177 // translate point to view centered coordinates
178 //
179 	gx = ob->x-viewx;
180 	gy = ob->y-viewy;
181 
182 //
183 // calculate newx
184 //
185 	gxt = FixedMul(gx,viewcos);
186 	gyt = FixedMul(gy,viewsin);
187 	// Wolf4SDL used 0x2000 for statics and 0x4000 for moving actors, but since
188 	// we no longer tell the difference, use the smaller fudging value since
189 	// the larger one will just look ugly in general.
190 	nx = gxt-gyt-0x2000;
191 
192 //
193 // calculate newy
194 //
195 	gxt = FixedMul(gx,viewsin);
196 	gyt = FixedMul(gy,viewcos);
197 	ny = gyt+gxt;
198 
199 //
200 // calculate perspective ratio
201 //
202 	ob->transx = nx;
203 	ob->transy = ny;
204 
205 	if (nx<MINDIST)                 // too close, don't overflow the divide
206 	{
207 		ob->viewheight = 0;
208 		return;
209 	}
210 
211 	ob->viewx = (word)(centerx + ny*scale/nx);
212 
213 //
214 // calculate height (heightnumerator/(nx>>8))
215 //
216 	ob->viewheight = (word)(heightnumerator/(nx>>8));
217 }
218 
219 //==========================================================================
220 
221 /*
222 ====================
223 =
224 = CalcHeight
225 =
226 = Calculates the height of xintercept,yintercept from viewx,viewy
227 =
228 ====================
229 */
230 
CalcHeight()231 int CalcHeight()
232 {
233 	fixed z = FixedMul(xintercept - viewx, viewcos)
234 		- FixedMul(yintercept - viewy, viewsin);
235 	if(z < MINDIST) z = MINDIST;
236 	int height = heightnumerator / (z >> 8);
237 	if(height < min_wallheight) min_wallheight = height;
238 	return height;
239 }
240 
241 //==========================================================================
242 
243 /*
244 ===================
245 =
246 = ScalePost
247 =
248 ===================
249 */
250 
251 const byte *postsource;
252 int postx;
253 
ScalePost()254 void ScalePost()
255 {
256 	if(postsource == NULL)
257 		return;
258 
259 	int ywcount, yoffs, yw, yd, yendoffs;
260 	byte col;
261 
262 	const int shade = LIGHT2SHADE(gLevelLight + r_extralight);
263 	const int tz = FixedMul(r_depthvisibility<<8, wallheight[postx]);
264 	BYTE *curshades = &NormalLight.Maps[GETPALOOKUP(MAX(tz, MINZ), shade)<<8];
265 
266 	ywcount = yd = (wallheight[postx] >> 3);
267 	if(yd <= 0)
268 		yd = 100;
269 
270 	// Calculate starting and ending offsets
271 	{
272 		// ywcount can be large enough to cause an overflow if we don't reduce
273 		// fixed point precision here
274 		const int topoffset = ywcount*(viewz>>8)/(32<<(FRACBITS-8));
275 		const int botoffset = ywcount*((viewz - (64<<FRACBITS))>>8)/(32<<(FRACBITS-8));
276 
277 		yoffs = (viewheight / 2 - topoffset - viewshift) * vbufPitch;
278 		if(yoffs < 0) yoffs = 0;
279 		yoffs += postx;
280 
281 		yendoffs = viewheight / 2 - botoffset - 1 - viewshift;
282 		yw=texyscale-1;
283 	}
284 
285 	while(yendoffs >= viewheight)
286 	{
287 		ywcount -= texyscale/2;
288 		while(ywcount <= 0)
289 		{
290 			ywcount += yd;
291 			yw--;
292 		}
293 		yendoffs--;
294 	}
295 	if(yw < 0)
296 		return;
297 
298 	col = curshades[postsource[yw]];
299 	yendoffs = yendoffs * vbufPitch + postx;
300 	while(yoffs <= yendoffs)
301 	{
302 		vbuf[yendoffs] = col;
303 		ywcount -= texyscale/2;
304 		if(ywcount <= 0)
305 		{
306 			do
307 			{
308 				ywcount += yd;
309 				yw--;
310 			}
311 			while(ywcount <= 0);
312 			if(yw < 0) break;
313 			col = curshades[postsource[yw]];
314 		}
315 		yendoffs -= vbufPitch;
316 	}
317 }
318 
GlobalScalePost(byte * vidbuf,unsigned pitch)319 void GlobalScalePost(byte *vidbuf, unsigned pitch)
320 {
321 	vbuf = vidbuf;
322 	vbufPitch = pitch;
323 	ScalePost();
324 }
325 
DetermineHitDir(bool vertical)326 static void DetermineHitDir(bool vertical)
327 {
328 	if(vertical)
329 	{
330 		if(xtilestep==-1 && (xintercept>>16)<=xtile)
331 			hitdir = MapTile::East;
332 		else
333 			hitdir = MapTile::West;
334 	}
335 	else
336 	{
337 		if(ytilestep==-1 && (yintercept>>16)<=ytile)
338 			hitdir = MapTile::South;
339 		else
340 			hitdir = MapTile::North;
341 	}
342 }
343 
SlideTextureOffset(unsigned int style,int intercept,int amount)344 static int SlideTextureOffset(unsigned int style, int intercept, int amount)
345 {
346 	if(!amount)
347 		return 0;
348 
349 	switch(style)
350 	{
351 		default:
352 			return -amount;
353 		case SLIDE_Split:
354 			if(intercept < FRACUNIT/2)
355 				return amount/2;
356 			return -amount/2;
357 		case SLIDE_Invert:
358 			return amount;
359 	}
360 }
361 
362 /*
363 ====================
364 =
365 = HitVertWall
366 =
367 = tilehit bit 7 is 0, because it's not a door tile
368 = if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
369 =
370 ====================
371 */
372 
HitVertWall(void)373 void HitVertWall (void)
374 {
375 	if(!tilehit)
376 		return;
377 
378 	int texture;
379 
380 	DetermineHitDir(true);
381 
382 	tilehit->amFlags |= AM_Visible;
383 	texture = (yintercept+texdelta+SlideTextureOffset(tilehit->slideStyle, (word)yintercept, tilehit->slideAmount[hitdir]))&(FRACUNIT-1);
384 	if (xtilestep == -1 && !tilehit->tile->offsetVertical)
385 	{
386 		texture = (FRACUNIT - texture)&(FRACUNIT-1);
387 		xintercept += TILEGLOBAL;
388 	}
389 
390 	if(lastside==1 && lastintercept==xtile && lasttilehit==tilehit && !(lasttilehit->tile->offsetVertical))
391 	{
392 		texture -= texture%texxscale;
393 
394 		if((pixx&3) && texture == lasttexture)
395 		{
396 			ScalePost();
397 			postx = pixx;
398 			wallheight[pixx] = wallheight[pixx-1];
399 			return;
400 		}
401 		ScalePost();
402 		wallheight[pixx] = CalcHeight();
403 		if(postsource)
404 			postsource+=(texture-lasttexture)*texheight/texxscale;
405 		postx=pixx;
406 		lasttexture=texture;
407 		return;
408 	}
409 
410 	if(lastside!=-1) ScalePost();
411 
412 	lastside=1;
413 	lastintercept=xtile;
414 	lasttilehit=tilehit;
415 	wallheight[pixx] = CalcHeight();
416 	postx = pixx;
417 	FTexture *source = NULL;
418 
419 	MapSpot adj = tilehit->GetAdjacent(hitdir);
420 	if (adj && adj->tile && adj->tile->offsetHorizontal && !adj->tile->offsetVertical) // check for adjacent doors
421 		source = TexMan(adj->texture[hitdir]);
422 	else
423 		source = TexMan(tilehit->texture[hitdir]);
424 
425 	if(source)
426 	{
427 		texheight = source->GetHeight();
428 		texxscale = TEXTUREBASE/source->xScale;
429 		texyscale = (64*source->yScale)>>FRACBITS;
430 		texture -= texture%texxscale;
431 
432 		postsource = source->GetColumn(texture/texxscale, NULL);
433 	}
434 	else
435 		postsource = NULL;
436 
437 	lasttexture=texture;
438 }
439 
440 
441 /*
442 ====================
443 =
444 = HitHorizWall
445 =
446 = tilehit bit 7 is 0, because it's not a door tile
447 = if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
448 =
449 ====================
450 */
451 
HitHorizWall(void)452 void HitHorizWall (void)
453 {
454 	if(!tilehit)
455 		return;
456 
457 	int texture;
458 
459 	DetermineHitDir(false);
460 
461 	tilehit->amFlags |= AM_Visible;
462 	texture = (xintercept+texdelta+SlideTextureOffset(tilehit->slideStyle, (word)xintercept, tilehit->slideAmount[hitdir]))&(FRACUNIT-1);
463 	if(!tilehit->tile->offsetHorizontal)
464 	{
465 		if (ytilestep == -1)
466 			yintercept += TILEGLOBAL;
467 		else
468 			texture = (FRACUNIT - texture)&(FRACUNIT-1);
469 	}
470 
471 	if(lastside==0 && lastintercept==ytile && lasttilehit==tilehit && !(lasttilehit->tile->offsetHorizontal))
472 	{
473 		texture -= texture%texxscale;
474 
475 		if((pixx&3) && texture == lasttexture)
476 		{
477 			ScalePost();
478 			postx=pixx;
479 			wallheight[pixx] = wallheight[pixx-1];
480 			return;
481 		}
482 		ScalePost();
483 		wallheight[pixx] = CalcHeight();
484 		if(postsource)
485 			postsource+=(texture-lasttexture)*texheight/texxscale;
486 		postx=pixx;
487 		lasttexture=texture;
488 		return;
489 	}
490 
491 	if(lastside!=-1) ScalePost();
492 
493 	lastside=0;
494 	lastintercept=ytile;
495 	lasttilehit=tilehit;
496 	wallheight[pixx] = CalcHeight();
497 	postx = pixx;
498 	FTexture *source = NULL;
499 
500 	MapSpot adj = tilehit->GetAdjacent(hitdir);
501 	if (adj && adj->tile && adj->tile->offsetVertical && !adj->tile->offsetHorizontal) // check for adjacent doors
502 		source = TexMan(adj->texture[hitdir]);
503 	else
504 		source = TexMan(tilehit->texture[hitdir]);
505 
506 	if(source)
507 	{
508 		texheight = source->GetHeight();
509 		texxscale = TEXTUREBASE/source->xScale;
510 		texyscale = (64*source->yScale)>>FRACBITS;
511 		texture -= texture%texxscale;
512 
513 		postsource = source->GetColumn(texture/texxscale, NULL);
514 	}
515 	else
516 		postsource = NULL;
517 
518 	lasttexture=texture;
519 }
520 
521 //==========================================================================
522 
523 #define HitHorizBorder HitHorizWall
524 #define HitVertBorder HitVertWall
525 
526 //==========================================================================
527 
528 /*
529 =====================
530 =
531 = CalcRotate
532 =
533 =====================
534 */
535 
CalcRotate(AActor * ob)536 int CalcRotate (AActor *ob)
537 {
538 	angle_t angle, viewangle;
539 
540 	// this isn't exactly correct, as it should vary by a trig value,
541 	// but it is close enough with only eight rotations
542 
543 	viewangle = players[0].camera->angle + (centerx - ob->viewx)/8;
544 
545 	angle = viewangle - ob->angle;
546 
547 	angle+=ANGLE_45/2;
548 
549 	return angle/ANGLE_45;
550 }
551 
552 /*
553 =====================
554 =
555 = DrawScaleds
556 =
557 = Draws all objects that are visable
558 =
559 =====================
560 */
561 
562 #define MAXVISABLE 250
563 
564 typedef struct
565 {
566 	AActor *actor;
567 	short viewheight;
568 	//short      viewx,
569 	//		viewheight,
570 	//		shapenum;
571 	//short      flags;          // this must be changed to uint32_t, when you
572 							// you need more than 16-flags for drawing
573 #ifdef USE_DIR3DSPR
574 	statobj_t *transsprite;
575 #endif
576 } visobj_t;
577 
578 visobj_t vislist[MAXVISABLE];
579 visobj_t *visptr,*visstep,*farthest;
580 
DrawScaleds(void)581 void DrawScaleds (void)
582 {
583 	int      i,least,numvisable,height;
584 
585 	visptr = &vislist[0];
586 
587 //
588 // place active objects
589 //
590 	for(AActor::Iterator iter = AActor::GetIterator();iter.Next();)
591 	{
592 		AActor *obj = iter;
593 
594 		if (obj->sprite == SPR_NONE)
595 			continue;
596 
597 		MapSpot spot = map->GetSpot(obj->tilex, obj->tiley, 0);
598 		MapSpot spots[8];
599 		spots[0] = spot->GetAdjacent(MapTile::East);
600 		spots[1] = spots[0] ? spots[0]->GetAdjacent(MapTile::North) : NULL;
601 		spots[2] = spot->GetAdjacent(MapTile::North);
602 		spots[3] = spots[2] ? spots[2]->GetAdjacent(MapTile::West) : NULL;
603 		spots[4] = spot->GetAdjacent(MapTile::West);
604 		spots[5] = spots[4] ? spots[4]->GetAdjacent(MapTile::South) : NULL;
605 		spots[6] = spot->GetAdjacent(MapTile::South);
606 		spots[7] = spots[6] ? spots[6]->GetAdjacent(MapTile::East) : NULL;
607 
608 		//
609 		// could be in any of the nine surrounding tiles
610 		//
611 		if (spot->visible
612 			|| ( spots[0] && (spots[0]->visible && !spots[0]->tile) )
613 			|| ( spots[1] && (spots[1]->visible && !spots[1]->tile) )
614 			|| ( spots[2] && (spots[2]->visible && !spots[2]->tile) )
615 			|| ( spots[3] && (spots[3]->visible && !spots[3]->tile) )
616 			|| ( spots[4] && (spots[4]->visible && !spots[4]->tile) )
617 			|| ( spots[5] && (spots[5]->visible && !spots[5]->tile) )
618 			|| ( spots[6] && (spots[6]->visible && !spots[6]->tile) )
619 			|| ( spots[7] && (spots[7]->visible && !spots[7]->tile) ) )
620 		{
621 			TransformActor (obj);
622 			if (!obj->viewheight || (gamestate.victoryflag && obj == players[0].mo))
623 				continue;                                               // too close or far away
624 
625 			visptr->actor = obj;
626 			visptr->viewheight = obj->viewheight;
627 
628 			if (visptr < &vislist[MAXVISABLE-1])    // don't let it overflow
629 			{
630 #ifdef USE_DIR3DSPR
631 				visptr->transsprite = NULL;
632 #endif
633 				visptr++;
634 			}
635 			obj->flags |= FL_VISABLE;
636 		}
637 		else
638 			obj->flags &= ~FL_VISABLE;
639 	}
640 
641 //
642 // draw from back to front
643 //
644 	numvisable = (int) (visptr-&vislist[0]);
645 
646 	if (!numvisable)
647 		return;                                                                 // no visable objects
648 
649 	for (i = 0; i<numvisable; i++)
650 	{
651 		least = 32000;
652 		for (visstep=&vislist[0] ; visstep<visptr ; visstep++)
653 		{
654 			height = visstep->viewheight;
655 			if (height < least)
656 			{
657 				least = height;
658 				farthest = visstep;
659 			}
660 		}
661 		//
662 		// draw farthest
663 		//
664 #ifdef USE_DIR3DSPR
665 		if(farthest->transsprite)
666 			Scale3DShape(vbuf, vbufPitch, farthest->transsprite);
667 		else
668 #endif
669 			ScaleSprite(farthest->actor, farthest->actor->viewx, farthest->actor->state, farthest->viewheight);
670 
671 		farthest->viewheight = 32000;
672 	}
673 }
674 
675 //==========================================================================
676 
677 /*
678 ==============
679 =
680 = DrawPlayerWeapon
681 =
682 = Draw the player's hands
683 =
684 ==============
685 */
686 
DrawPlayerWeapon(void)687 void DrawPlayerWeapon (void)
688 {
689 	for(unsigned int i = 0;i < player_t::NUM_PSPRITES;++i)
690 	{
691 		if(!players[0].psprite[i].frame)
692 			return;
693 
694 		fixed xoffset, yoffset;
695 		players[0].BobWeapon(&xoffset, &yoffset);
696 
697 		R_DrawPlayerSprite(players[0].ReadyWeapon, players[0].psprite[i].frame, players[0].psprite[i].sx+xoffset, players[0].psprite[i].sy+yoffset);
698 	}
699 }
700 
701 
702 //==========================================================================
703 
704 
705 /*
706 =====================
707 =
708 = CalcTics
709 =
710 =====================
711 */
712 
CalcTics(void)713 void CalcTics (void)
714 {
715 //
716 // calculate tics since last refresh for adaptive timing
717 //
718 	if (lasttimecount > (int32_t) GetTimeCount())
719 		lasttimecount = GetTimeCount();    // if the game was paused a LONG time
720 
721 	uint32_t curtime = SDL_GetTicks();
722 	tics = (curtime * 7) / 100 - lasttimecount;
723 	if(!tics)
724 	{
725 		// wait until end of current tic
726 		SDL_Delay(((lasttimecount + 1) * 100) / 7 - curtime);
727 		tics = 1;
728 	}
729 	else if(noadaptive)
730 		tics = 1;
731 
732 	lasttimecount += tics;
733 
734 	if (tics>MAXTICS)
735 		tics = MAXTICS;
736 }
737 
738 
739 //==========================================================================
740 
AsmRefresh()741 void AsmRefresh()
742 {
743 	static word xspot[2],yspot[2];
744 	int32_t xstep=0,ystep=0;
745 	longword xpartial=0,ypartial=0;
746 	MapSpot focalspot = map->GetSpot(focaltx, focalty, 0);
747 	bool playerInPushwallBackTile = focalspot->pushAmount != 0;
748 
749 	for(pixx=0;pixx<viewwidth;pixx++)
750 	{
751 		short angl=midangle+pixelangle[pixx];
752 		if(angl<0) angl+=FINEANGLES;
753 		if(angl>=ANG360) angl-=FINEANGLES;
754 		if(angl<ANG90)
755 		{
756 			xtilestep=1;
757 			ytilestep=-1;
758 			xstep=finetangent[ANG90-1-angl];
759 			ystep=-finetangent[angl];
760 			xpartial=xpartialup;
761 			ypartial=ypartialdown;
762 		}
763 		else if(angl<ANG180)
764 		{
765 			xtilestep=-1;
766 			ytilestep=-1;
767 			xstep=-finetangent[angl-ANG90];
768 			ystep=-finetangent[ANG180-1-angl];
769 			xpartial=xpartialdown;
770 			ypartial=ypartialdown;
771 		}
772 		else if(angl<ANG270)
773 		{
774 			xtilestep=-1;
775 			ytilestep=1;
776 			xstep=-finetangent[ANG270-1-angl];
777 			ystep=finetangent[angl-ANG180];
778 			xpartial=xpartialdown;
779 			ypartial=ypartialup;
780 		}
781 		else if(angl<ANG360)
782 		{
783 			xtilestep=1;
784 			ytilestep=1;
785 			xstep=finetangent[angl-ANG270];
786 			ystep=finetangent[ANG360-1-angl];
787 			xpartial=xpartialup;
788 			ypartial=ypartialup;
789 		}
790 		yintercept=FixedMul(ystep,xpartial)+viewy;
791 		xtile=focaltx+xtilestep;
792 		xspot[0]=xtile;
793 		xspot[1]=yintercept>>16;
794 		xintercept=FixedMul(xstep,ypartial)+viewx;
795 		ytile=focalty+ytilestep;
796 		yspot[0]=xintercept>>16;
797 		yspot[1]=ytile;
798 		texdelta=0;
799 
800 		// Special treatment when player is in back tile of pushwall
801 		if(playerInPushwallBackTile)
802 		{
803 			if(focalspot->pushReceptor)
804 				focalspot = focalspot->pushReceptor;
805 
806 			if((focalspot->pushDirection == MapTile::East && xtilestep == 1) ||
807 				(focalspot->pushDirection == MapTile::West && xtilestep == -1))
808 			{
809 				int32_t yintbuf = yintercept - ytilestep*(abs(ystep * signed(64 - focalspot->pushAmount)) >> 6);
810 				if((yintbuf >> 16) == focalty)   // ray hits pushwall back?
811 				{
812 					if(focalspot->pushDirection == MapTile::East)
813 						xintercept = (focaltx << TILESHIFT) + (focalspot->pushAmount << 10);
814 					else
815 						xintercept = (focaltx << TILESHIFT) - TILEGLOBAL + ((64 - focalspot->pushAmount) << 10);
816 					yintercept = yintbuf;
817 					ytile = (short) (yintercept >> TILESHIFT);
818 					tilehit = focalspot;
819 					HitVertWall();
820 					continue;
821 				}
822 			}
823 			else if((focalspot->pushDirection == MapTile::South && ytilestep == 1) ||
824 				(focalspot->pushDirection == MapTile::North && ytilestep == -1))
825 			{
826 				int32_t xintbuf = xintercept - xtilestep*(abs(xstep * signed(64 - focalspot->pushAmount)) >> 6);
827 				if((xintbuf >> 16) == focaltx)   // ray hits pushwall back?
828 				{
829 					xintercept = xintbuf;
830 					if(focalspot->pushDirection == MapTile::South)
831 						yintercept = (focalty << TILESHIFT) + (focalspot->pushAmount << 10);
832 					else
833 						yintercept = (focalty << TILESHIFT) - TILEGLOBAL + ((64 - focalspot->pushAmount) << 10);
834 					xtile = (short) (xintercept >> TILESHIFT);
835 					tilehit = focalspot;
836 					HitHorizWall();
837 					continue;
838 				}
839 			}
840 		}
841 
842 		do
843 		{
844 			if(ytilestep==-1 && (yintercept>>16)<=ytile) goto horizentry;
845 			if(ytilestep==1 && (yintercept>>16)>=ytile) goto horizentry;
846 vertentry:
847 
848 			if((uint32_t)yintercept>mapheight*65536-1 || (word)xtile>=mapwidth)
849 			{
850 				if(xtile<0) xintercept=0, xtile=0;
851 				else if((unsigned)xtile>=mapwidth) xintercept=mapwidth<<TILESHIFT, xtile=mapwidth-1;
852 				else xtile=(short) (xintercept >> TILESHIFT);
853 				if(yintercept<0) yintercept=0, ytile=0;
854 				else if((unsigned)yintercept>=(mapheight<<TILESHIFT)) yintercept=mapheight<<TILESHIFT, ytile=mapheight-1;
855 				yspot[0]=0xffff;
856 				tilehit=0;
857 				HitHorizBorder();
858 				break;
859 			}
860 			if(xspot[0]>=mapwidth || xspot[1]>=mapheight) break;
861 			tilehit=map->GetSpot(xspot[0], xspot[1], 0);
862 			if(tilehit && tilehit->tile)
863 			{
864 				if(tilehit->tile->offsetVertical)
865 				{
866 					DetermineHitDir(true);
867 					int32_t yintbuf=yintercept+(ystep>>1);
868 					if((yintbuf>>16)!=(yintercept>>16))
869 						goto passvert;
870 					if(CheckSlidePass(tilehit->slideStyle, (word)yintbuf, tilehit->slideAmount[hitdir]))
871 						goto passvert;
872 					yintercept=yintbuf;
873 					xintercept=(xtile<<TILESHIFT)|0x8000;
874 					ytile = (short) (yintercept >> TILESHIFT);
875 					HitVertWall();
876 				}
877 				else
878 				{
879 					bool isPushwall = tilehit->pushAmount != 0 || tilehit->pushReceptor;
880 					if(tilehit->pushReceptor)
881 						tilehit = tilehit->pushReceptor;
882 
883 					if(isPushwall)
884 					{
885 						if(tilehit->pushDirection==MapTile::West || tilehit->pushDirection==MapTile::East)
886 						{
887 							int32_t yintbuf;
888 							int pwallposnorm;
889 							int pwallposinv;
890 							if(tilehit->pushDirection==MapTile::West)
891 							{
892 								pwallposnorm = 64-tilehit->pushAmount;
893 								pwallposinv = tilehit->pushAmount;
894 							}
895 							else
896 							{
897 								pwallposnorm = tilehit->pushAmount;
898 								pwallposinv = 64-tilehit->pushAmount;
899 							}
900 							if((tilehit->pushDirection==MapTile::East && xtile==(signed)tilehit->GetX() && ((uint32_t)yintercept>>16)==tilehit->GetY())
901 								|| (tilehit->pushDirection==MapTile::West && !(xtile==(signed)tilehit->GetX() && ((uint32_t)yintercept>>16)==tilehit->GetY())))
902 							{
903 								yintbuf=yintercept+((ystep*pwallposnorm)>>6);
904 								if((yintbuf>>16)!=(yintercept>>16))
905 									goto passvert;
906 
907 								xintercept=(xtile<<TILESHIFT)+TILEGLOBAL-(pwallposinv<<10);
908 								yintercept=yintbuf;
909 								ytile = (short) (yintercept >> TILESHIFT);
910 								HitVertWall();
911 							}
912 							else
913 							{
914 								yintbuf=yintercept+((ystep*pwallposinv)>>6);
915 								if((yintbuf>>16)!=(yintercept>>16))
916 									goto passvert;
917 
918 								xintercept=(xtile<<TILESHIFT)-(pwallposinv<<10);
919 								yintercept=yintbuf;
920 								ytile = (short) (yintercept >> TILESHIFT);
921 								HitVertWall();
922 							}
923 						}
924 						else
925 						{
926 							int pwallposi = tilehit->pushAmount;
927 							if(tilehit->pushDirection==MapTile::North) pwallposi = 64-tilehit->pushAmount;
928 							if((tilehit->pushDirection==MapTile::South && (word)yintercept<(pwallposi<<10))
929 								|| (tilehit->pushDirection==MapTile::North && (word)yintercept>(pwallposi<<10)))
930 							{
931 								if(((uint32_t)yintercept>>16)==tilehit->GetY() && xtile==(signed)tilehit->GetX())
932 								{
933 									if((tilehit->pushDirection==MapTile::South && (int32_t)((word)yintercept)+ystep<(pwallposi<<10))
934 										|| (tilehit->pushDirection==MapTile::North && (int32_t)((word)yintercept)+ystep>(pwallposi<<10)))
935 										goto passvert;
936 
937 									if(tilehit->pushDirection==MapTile::South)
938 									{
939 										yintercept=(yintercept&0xffff0000)+(pwallposi<<10);
940 										xintercept=xintercept-((xstep*(64-pwallposi))>>6);
941 									}
942 									else
943 									{
944 										yintercept=(yintercept&0xffff0000)-TILEGLOBAL+(pwallposi<<10);
945 										xintercept=xintercept-((xstep*pwallposi)>>6);
946 									}
947 									xtile = (short) (xintercept >> TILESHIFT);
948 									HitHorizWall();
949 								}
950 								else
951 								{
952 									texdelta = -(pwallposi<<10);
953 									xintercept=xtile<<TILESHIFT;
954 									ytile = (short) (yintercept >> TILESHIFT);
955 									HitVertWall();
956 								}
957 							}
958 							else
959 							{
960 								if(((uint32_t)yintercept>>16)==tilehit->GetY() && xtile==(signed)tilehit->GetX())
961 								{
962 									texdelta = -(pwallposi<<10);
963 									xintercept=xtile<<TILESHIFT;
964 									ytile = (short) (yintercept >> TILESHIFT);
965 									HitVertWall();
966 								}
967 								else
968 								{
969 									if((tilehit->pushDirection==MapTile::South && (int32_t)((word)yintercept)+ystep>(pwallposi<<10))
970 										|| (tilehit->pushDirection==MapTile::North && (int32_t)((word)yintercept)+ystep<(pwallposi<<10)))
971 										goto passvert;
972 
973 									if(tilehit->pushDirection==MapTile::South)
974 									{
975 										yintercept=(yintercept&0xffff0000)-TILEGLOBAL+(pwallposi<<10);
976 										xintercept=xintercept-((xstep*pwallposi)>>6);
977 									}
978 									else
979 									{
980 										yintercept=(yintercept&0xffff0000)+(pwallposi<<10);
981 										xintercept=xintercept-((xstep*(64-pwallposi))>>6);
982 									}
983 									xtile = (short) (xintercept >> TILESHIFT);
984 									HitHorizWall();
985 								}
986 							}
987 						}
988 					}
989 					else
990 					{
991 						xintercept=xtile<<TILESHIFT;
992 						ytile = (short) (yintercept >> TILESHIFT);
993 						HitVertWall();
994 					}
995 				}
996 				break;
997 			}
998 passvert:
999 			tilehit->visible=true;
1000 			tilehit->amFlags |= AM_Visible;
1001 			xtile+=xtilestep;
1002 			yintercept+=ystep;
1003 			xspot[0]=xtile;
1004 			xspot[1]=yintercept>>16;
1005 		}
1006 		while(1);
1007 		continue;
1008 
1009 		do
1010 		{
1011 			if(xtilestep==-1 && (xintercept>>16)<=xtile) goto vertentry;
1012 			if(xtilestep==1 && (xintercept>>16)>=xtile) goto vertentry;
1013 horizentry:
1014 
1015 			if((uint32_t)xintercept>mapwidth*65536-1 || (word)ytile>=mapheight)
1016 			{
1017 				if(ytile<0) yintercept=0, ytile=0;
1018 				else if((unsigned)ytile>=mapheight) yintercept=mapheight<<TILESHIFT, ytile=mapheight-1;
1019 				else ytile=(short) (yintercept >> TILESHIFT);
1020 				if(xintercept<0) xintercept=0, xtile=0;
1021 				else if((unsigned)xintercept>=(mapwidth<<TILESHIFT)) xintercept=mapwidth<<TILESHIFT, xtile=mapwidth-1;
1022 				xspot[0]=0xffff;
1023 				tilehit=0;
1024 				HitVertBorder();
1025 				break;
1026 			}
1027 			if(yspot[0]>=mapwidth || yspot[1]>=mapheight) break;
1028 			tilehit=map->GetSpot(yspot[0], yspot[1], 0);
1029 			if(tilehit && tilehit->tile)
1030 			{
1031 				if(tilehit->tile->offsetHorizontal)
1032 				{
1033 					DetermineHitDir(false);
1034 					int32_t xintbuf=xintercept+(xstep>>1);
1035 					if((xintbuf>>16)!=(xintercept>>16))
1036 						goto passhoriz;
1037 					if(CheckSlidePass(tilehit->slideStyle, (word)xintbuf, tilehit->slideAmount[hitdir]))
1038 						goto passhoriz;
1039 					xintercept=xintbuf;
1040 					yintercept=(ytile<<TILESHIFT)+0x8000;
1041 					xtile = (short) (xintercept >> TILESHIFT);
1042 					HitHorizWall();
1043 				}
1044 				else
1045 				{
1046 					bool isPushwall = tilehit->pushAmount != 0 || tilehit->pushReceptor;
1047 					if(tilehit->pushReceptor)
1048 						tilehit = tilehit->pushReceptor;
1049 
1050 					if(isPushwall)
1051 					{
1052 						if(tilehit->pushDirection==MapTile::North || tilehit->pushDirection==MapTile::South)
1053 						{
1054 							int32_t xintbuf;
1055 							int pwallposnorm;
1056 							int pwallposinv;
1057 							if(tilehit->pushDirection==MapTile::North)
1058 							{
1059 								pwallposnorm = 64-tilehit->pushAmount;
1060 								pwallposinv = tilehit->pushAmount;
1061 							}
1062 							else
1063 							{
1064 								pwallposnorm = tilehit->pushAmount;
1065 								pwallposinv = 64-tilehit->pushAmount;
1066 							}
1067 							if((tilehit->pushDirection == MapTile::South && ytile==(signed)tilehit->GetY() && ((uint32_t)xintercept>>16)==tilehit->GetX())
1068 								|| (tilehit->pushDirection == MapTile::North && !(ytile==(signed)tilehit->GetY() && ((uint32_t)xintercept>>16)==tilehit->GetX())))
1069 							{
1070 								xintbuf=xintercept+((xstep*pwallposnorm)>>6);
1071 								if((xintbuf>>16)!=(xintercept>>16))
1072 									goto passhoriz;
1073 
1074 								yintercept=(ytile<<TILESHIFT)+TILEGLOBAL-(pwallposinv<<10);
1075 								xintercept=xintbuf;
1076 								xtile = (short) (xintercept >> TILESHIFT);
1077 								HitHorizWall();
1078 							}
1079 							else
1080 							{
1081 								xintbuf=xintercept+((xstep*pwallposinv)>>6);
1082 								if((xintbuf>>16)!=(xintercept>>16))
1083 									goto passhoriz;
1084 
1085 								yintercept=(ytile<<TILESHIFT)-(pwallposinv<<10);
1086 								xintercept=xintbuf;
1087 								xtile = (short) (xintercept >> TILESHIFT);
1088 								HitHorizWall();
1089 							}
1090 						}
1091 						else
1092 						{
1093 							int pwallposi = tilehit->pushAmount;
1094 							if(tilehit->pushDirection==MapTile::West) pwallposi = 64-tilehit->pushAmount;
1095 							if((tilehit->pushDirection==MapTile::East && (word)xintercept<(pwallposi<<10))
1096 								|| (tilehit->pushDirection==MapTile::West && (word)xintercept>(pwallposi<<10)))
1097 							{
1098 								if(((uint32_t)xintercept>>16)==tilehit->GetX() && ytile==(signed)tilehit->GetY())
1099 								{
1100 									if((tilehit->pushDirection==MapTile::East && (int32_t)((word)xintercept)+xstep<(pwallposi<<10))
1101 										|| (tilehit->pushDirection==MapTile::West && (int32_t)((word)xintercept)+xstep>(pwallposi<<10)))
1102 										goto passhoriz;
1103 
1104 									if(tilehit->pushDirection==MapTile::East)
1105 									{
1106 										xintercept=(xintercept&0xffff0000)+(pwallposi<<10);
1107 										yintercept=yintercept-((ystep*(64-pwallposi))>>6);
1108 									}
1109 									else
1110 									{
1111 										xintercept=(xintercept&0xffff0000)-TILEGLOBAL+(pwallposi<<10);
1112 										yintercept=yintercept-((ystep*pwallposi)>>6);
1113 									}
1114 									ytile = (short) (yintercept >> TILESHIFT);
1115 									HitVertWall();
1116 								}
1117 								else
1118 								{
1119 									texdelta = -(pwallposi<<10);
1120 									yintercept=ytile<<TILESHIFT;
1121 									xtile = (short) (xintercept >> TILESHIFT);
1122 									HitHorizWall();
1123 								}
1124 							}
1125 							else
1126 							{
1127 								if(((uint32_t)xintercept>>16)==tilehit->GetX() && ytile==(signed)tilehit->GetY())
1128 								{
1129 									texdelta = -(pwallposi<<10);
1130 									yintercept=ytile<<TILESHIFT;
1131 									xtile = (short) (xintercept >> TILESHIFT);
1132 									HitHorizWall();
1133 								}
1134 								else
1135 								{
1136 									if((tilehit->pushDirection==MapTile::East && (int32_t)((word)xintercept)+xstep>(pwallposi<<10))
1137 										|| (tilehit->pushDirection==MapTile::West && (int32_t)((word)xintercept)+xstep<(pwallposi<<10)))
1138 										goto passhoriz;
1139 
1140 									if(tilehit->pushDirection==MapTile::East)
1141 									{
1142 										xintercept=(xintercept&0xffff0000)-TILEGLOBAL+(pwallposi<<10);
1143 										yintercept=yintercept-((ystep*pwallposi)>>6);
1144 									}
1145 									else
1146 									{
1147 										xintercept=(xintercept&0xffff0000)+(pwallposi<<10);
1148 										yintercept=yintercept-((ystep*(64-pwallposi))>>6);
1149 									}
1150 									ytile = (short) (yintercept >> TILESHIFT);
1151 									HitVertWall();
1152 								}
1153 							}
1154 						}
1155 					}
1156 					else
1157 					{
1158 						yintercept=ytile<<TILESHIFT;
1159 						xtile = (short) (xintercept >> TILESHIFT);
1160 						HitHorizWall();
1161 					}
1162 				}
1163 				break;
1164 			}
1165 passhoriz:
1166 			tilehit->visible=true;
1167 			tilehit->amFlags |= AM_Visible;
1168 			ytile+=ytilestep;
1169 			xintercept+=xstep;
1170 			yspot[0]=xintercept>>16;
1171 			yspot[1]=ytile;
1172 		}
1173 		while(1);
1174 	}
1175 }
1176 
1177 /*
1178 ====================
1179 =
1180 = WallRefresh
1181 =
1182 ====================
1183 */
1184 
WallRefresh(void)1185 void WallRefresh (void)
1186 {
1187 	xpartialdown = viewx&(TILEGLOBAL-1);
1188 	xpartialup = TILEGLOBAL-xpartialdown;
1189 	ypartialdown = viewy&(TILEGLOBAL-1);
1190 	ypartialup = TILEGLOBAL-ypartialdown;
1191 
1192 	min_wallheight = viewheight;
1193 	lastside = -1;                  // the first pixel is on a new wall
1194 	viewshift = FixedMul(focallengthy, finetangent[(ANGLE_180+players[0].camera->pitch)>>ANGLETOFINESHIFT]);
1195 
1196 
1197 	angle_t bobangle = ((gamestate.TimeCount<<13)/(20*TICRATE/35)) & FINEMASK;
1198 	const fixed playerMovebob = players[0].mo->GetClass()->Meta.GetMetaFixed(APMETA_MoveBob);
1199 	fixed curbob = gamestate.victoryflag ? 0 : FixedMul(FixedMul(players[0].bob, playerMovebob)>>1, finesine[bobangle]);
1200 
1201 	viewz = (64<<FRACBITS) - players[0].mo->viewheight + curbob;
1202 
1203 	AsmRefresh();
1204 	ScalePost ();                   // no more optimization on last post
1205 }
1206 
CalcViewVariables()1207 void CalcViewVariables()
1208 {
1209 	viewangle = players[0].camera->angle;
1210 	midangle = viewangle>>ANGLETOFINESHIFT;
1211 	viewsin = finesine[viewangle>>ANGLETOFINESHIFT];
1212 	viewcos = finecosine[viewangle>>ANGLETOFINESHIFT];
1213 	viewx = players[0].camera->x - FixedMul(focallength,viewcos);
1214 	viewy = players[0].camera->y + FixedMul(focallength,viewsin);
1215 
1216 	focaltx = (short)(viewx>>TILESHIFT);
1217 	focalty = (short)(viewy>>TILESHIFT);
1218 
1219 	viewtx = (short)(players[0].camera->x >> TILESHIFT);
1220 	viewty = (short)(players[0].camera->y >> TILESHIFT);
1221 
1222 	if(players[0].camera->player)
1223 		r_extralight = players[0].camera->player->extralight << 3;
1224 	else
1225 		r_extralight = 0;
1226 }
1227 
1228 //==========================================================================
1229 
R_RenderView()1230 void R_RenderView()
1231 {
1232 	CalcViewVariables();
1233 
1234 //
1235 // follow the walls from there to the right, drawing as we go
1236 //
1237 #if defined(USE_FEATUREFLAGS) && defined(USE_STARSKY)
1238 	if(GetFeatureFlags() & FF_STARSKY)
1239 		DrawStarSky(vbuf, vbufPitch);
1240 #endif
1241 
1242 	WallRefresh ();
1243 
1244 #if defined(USE_FEATUREFLAGS) && defined(USE_PARALLAX)
1245 	if(GetFeatureFlags() & FF_PARALLAXSKY)
1246 		DrawParallax(vbuf, vbufPitch);
1247 #endif
1248 #if defined(USE_FEATUREFLAGS) && defined(USE_CLOUDSKY)
1249 	if(GetFeatureFlags() & FF_CLOUDSKY)
1250 		DrawClouds(vbuf, vbufPitch, min_wallheight);
1251 #endif
1252 	DrawFloorAndCeiling(vbuf, vbufPitch, min_wallheight);
1253 
1254 //
1255 // draw all the scaled images
1256 //
1257 	DrawScaleds();                  // draw scaled stuff
1258 
1259 #if defined(USE_FEATUREFLAGS) && defined(USE_RAIN)
1260 	if(GetFeatureFlags() & FF_RAIN)
1261 		DrawRain(vbuf, vbufPitch);
1262 #endif
1263 #if defined(USE_FEATUREFLAGS) && defined(USE_SNOW)
1264 	if(GetFeatureFlags() & FF_SNOW)
1265 		DrawSnow(vbuf, vbufPitch);
1266 #endif
1267 
1268 	DrawPlayerWeapon ();    // draw players[0].mo's hands
1269 
1270 	if((buttonstate[bt_showstatusbar] || buttonheld[bt_showstatusbar]) && viewsize == 21)
1271 	{
1272 		ingame = false;
1273 		StatusBar->DrawStatusBar();
1274 		ingame = true;
1275 	}
1276 
1277 	// Always mark the current spot as visible in the automap
1278 	map->GetSpot(players[0].mo->tilex, players[0].mo->tiley, 0)->amFlags |= AM_Visible;
1279 }
1280 
1281 /*
1282 ========================
1283 =
1284 = ThreeDRefresh
1285 =
1286 ========================
1287 */
1288 
ThreeDRefresh(void)1289 void    ThreeDRefresh (void)
1290 {
1291 	// Ensure we have a valid camera
1292 	if(players[0].camera == NULL)
1293 		players[0].camera = players[0].mo;
1294 
1295 	if (fizzlein && gameinfo.DeathTransition == GameInfo::TRANSITION_Fizzle)
1296 		FizzleFadeStart();
1297 
1298 //
1299 // clear out the traced array
1300 //
1301 	map->ClearVisibility();
1302 
1303 	vbuf = VL_LockSurface();
1304 	if(vbuf == NULL) return;
1305 
1306 	vbuf += screenofs;
1307 	vbufPitch = SCREENPITCH;
1308 
1309 	R_RenderView();
1310 
1311 	VL_UnlockSurface();
1312 	vbuf = NULL;
1313 
1314 //
1315 // show screen and time last cycle
1316 //
1317 	if (fizzlein)
1318 	{
1319 		if(gameinfo.DeathTransition == GameInfo::TRANSITION_Fizzle)
1320 			FizzleFade(0, 0, screenWidth, screenHeight, 20, false);
1321 		else
1322 			VL_FadeIn(0, 255, 24);
1323 		fizzlein = false;
1324 
1325 		lasttimecount = GetTimeCount();          // don't make a big tic count
1326 	}
1327 	else if (fpscounter)
1328 	{
1329 		FString fpsDisplay;
1330 		fpsDisplay.Format("%2u fps", fps);
1331 
1332 		word x = 0;
1333 		word y = 0;
1334 		word width, height;
1335 		VW_MeasurePropString(ConFont, fpsDisplay, width, height);
1336 		MenuToRealCoords(x, y, width, height, MENU_TOP);
1337 		VWB_Clear(GPalette.BlackIndex, x, y, x+width+1, y+height+1);
1338 		px = 0;
1339 		py = 0;
1340 		pa = MENU_TOP;
1341 		VWB_DrawPropString(ConFont, fpsDisplay, CR_WHITE);
1342 		pa = MENU_CENTER;
1343 	}
1344 
1345 	if (fpscounter)
1346 	{
1347 		fps_frames++;
1348 		fps_time+=tics;
1349 
1350 		if(fps_time>35)
1351 		{
1352 			fps_time-=35;
1353 			fps=fps_frames<<1;
1354 			fps_frames=0;
1355 		}
1356 	}
1357 }
1358