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