1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 // Rendering main loop and setup functions,
21 // utility functions (BSP, geometry, trigonometry).
22 // See tables.c, too.
23 //
24 //-----------------------------------------------------------------------------
25
26 // HEADER FILES ------------------------------------------------------------
27
28 #include <stdlib.h>
29 #include <math.h>
30
31 #include "templates.h"
32 #include "doomdef.h"
33 #include "d_net.h"
34 #include "doomstat.h"
35 #include "m_random.h"
36 #include "m_bbox.h"
37 #include "p_local.h"
38 #include "r_sky.h"
39 #include "st_stuff.h"
40 #include "c_cvars.h"
41 #include "c_dispatch.h"
42 #include "v_video.h"
43 #include "stats.h"
44 #include "i_video.h"
45 #include "i_system.h"
46 #include "a_sharedglobal.h"
47 #include "r_data/r_translate.h"
48 #include "p_3dmidtex.h"
49 #include "r_data/r_interpolate.h"
50 #include "v_palette.h"
51 #include "po_man.h"
52 #include "p_effect.h"
53 #include "st_start.h"
54 #include "v_font.h"
55 #include "r_renderer.h"
56 #include "r_data/colormaps.h"
57 #include "farchive.h"
58
59
60 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
61
62 extern bool DrawFSHUD; // [RH] Defined in d_main.cpp
63 EXTERN_CVAR (Bool, cl_capfps)
64
65 // TYPES -------------------------------------------------------------------
66
67 struct InterpolationViewer
68 {
69 AActor *ViewActor;
70 int otic;
71 fixed_t oviewx, oviewy, oviewz;
72 fixed_t nviewx, nviewy, nviewz;
73 int oviewpitch, nviewpitch;
74 angle_t oviewangle, nviewangle;
75 };
76
77 // PRIVATE DATA DECLARATIONS -----------------------------------------------
78 static TArray<InterpolationViewer> PastViewers;
79 static FRandom pr_torchflicker ("TorchFlicker");
80 static FRandom pr_hom;
81 static bool NoInterpolateView;
82
83 // PUBLIC DATA DEFINITIONS -------------------------------------------------
84
CVAR(Bool,r_deathcamera,false,CVAR_ARCHIVE)85 CVAR (Bool, r_deathcamera, false, CVAR_ARCHIVE)
86 CVAR (Int, r_clearbuffer, 0, 0)
87 CVAR (Bool, r_drawvoxels, true, 0)
88 CVAR (Bool, r_drawplayersprites, true, 0) // [RH] Draw player sprites?
89 CUSTOM_CVAR(Float, r_quakeintensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
90 {
91 if (self < 0.f) self = 0.f;
92 else if (self > 1.f) self = 1.f;
93 }
94
95 DCanvas *RenderTarget; // [RH] canvas to render to
96
97 int viewwindowx;
98 int viewwindowy;
99
100 fixed_t viewx;
101 fixed_t viewy;
102 fixed_t viewz;
103 int viewpitch;
104
105 extern "C"
106 {
107 int viewwidth;
108 int viewheight;
109 int centerx;
110 int centery;
111 int centerxwide;
112 }
113
114 int otic;
115
116 angle_t viewangle;
117 sector_t *viewsector;
118
119 fixed_t viewcos, viewtancos;
120 fixed_t viewsin, viewtansin;
121
122 AActor *camera; // [RH] camera to draw from. doesn't have to be a player
123
124 fixed_t r_TicFrac; // [RH] Fractional tic to render
125 DWORD r_FrameTime; // [RH] Time this frame started drawing (in ms)
126 bool r_NoInterpolate;
127 bool r_showviewer;
128
129 angle_t LocalViewAngle;
130 int LocalViewPitch;
131 bool LocalKeyboardTurner;
132
133 float LastFOV;
134 int WidescreenRatio;
135 int setblocks;
136 int extralight;
137 bool setsizeneeded;
138 fixed_t FocalTangent;
139
140 unsigned int R_OldBlend = ~0;
141 int validcount = 1; // increment every time a check is made
142 int FieldOfView = 2048; // Fineangles in the SCREENWIDTH wide window
143
144 FCanvasTextureInfo *FCanvasTextureInfo::List;
145
146
147 // CODE --------------------------------------------------------------------
148 static void R_Shutdown ();
149
150 //==========================================================================
151 //
152 // SlopeDiv
153 //
154 // Utility function, called by R_PointToAngle.
155 //
156 //==========================================================================
157
SlopeDiv(unsigned int num,unsigned den)158 angle_t SlopeDiv (unsigned int num, unsigned den)
159 {
160 unsigned int ans;
161
162 if (den < 512)
163 return (ANG45 - 1); //tantoangle[SLOPERANGE]
164
165 ans = (num << 3) / (den >> 8);
166
167 return ans <= SLOPERANGE ? tantoangle[ans] : (ANG45 - 1);
168 }
169
170
171 //==========================================================================
172 //
173 // R_PointToAngle
174 //
175 // To get a global angle from cartesian coordinates, the coordinates are
176 // flipped until they are in the first octant of the coordinate system,
177 // then the y (<=x) is scaled and divided by x to get a tangent (slope)
178 // value which is looked up in the tantoangle[] table.
179 //
180 //==========================================================================
181
R_PointToAngle2(fixed_t x1,fixed_t y1,fixed_t x,fixed_t y)182 angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x, fixed_t y)
183 {
184 x -= x1;
185 y -= y1;
186
187 if ((x | y) == 0)
188 {
189 return 0;
190 }
191
192 // We need to be aware of overflows here. If the values get larger than INT_MAX/4
193 // this code won't work anymore.
194
195 if (x < INT_MAX/4 && x > -INT_MAX/4 && y < INT_MAX/4 && y > -INT_MAX/4)
196 {
197 if (x >= 0)
198 {
199 if (y >= 0)
200 {
201 if (x > y)
202 { // octant 0
203 return SlopeDiv(y, x);
204 }
205 else
206 { // octant 1
207 return ANG90 - 1 - SlopeDiv(x, y);
208 }
209 }
210 else // y < 0
211 {
212 y = -y;
213 if (x > y)
214 { // octant 8
215 return 0 - SlopeDiv(y, x);
216 }
217 else
218 { // octant 7
219 return ANG270 + SlopeDiv(x, y);
220 }
221 }
222 }
223 else // x < 0
224 {
225 x = -x;
226 if (y >= 0)
227 {
228 if (x > y)
229 { // octant 3
230 return ANG180 - 1 - SlopeDiv(y, x);
231 }
232 else
233 { // octant 2
234 return ANG90 + SlopeDiv(x, y);
235 }
236 }
237 else // y < 0
238 {
239 y = -y;
240 if (x > y)
241 { // octant 4
242 return ANG180 + SlopeDiv(y, x);
243 }
244 else
245 { // octant 5
246 return ANG270 - 1 - SlopeDiv(x, y);
247 }
248 }
249 }
250 }
251 else
252 {
253 // we have to use the slower but more precise floating point atan2 function here.
254 return xs_RoundToUInt(atan2(double(y), double(x)) * (ANGLE_180/M_PI));
255 }
256 }
257
258 //==========================================================================
259 //
260 // R_InitPointToAngle
261 //
262 //==========================================================================
263
R_InitPointToAngle(void)264 void R_InitPointToAngle (void)
265 {
266 double f;
267 int i;
268 //
269 // slope (tangent) to angle lookup
270 //
271 for (i = 0; i <= SLOPERANGE; i++)
272 {
273 f = atan2 ((double)i, (double)SLOPERANGE) / (6.28318530718 /* 2*pi */);
274 tantoangle[i] = (angle_t)(0xffffffff*f);
275 }
276 }
277
278 //==========================================================================
279 //
280 // R_PointToDist2
281 //
282 // Returns the distance from (0,0) to some other point. In a
283 // floating point environment, we'd probably be better off using the
284 // Pythagorean Theorem to determine the result.
285 //
286 // killough 5/2/98: simplified
287 // [RH] Simplified further [sin (t + 90 deg) == cos (t)]
288 // Not used. Should it go away?
289 //
290 //==========================================================================
291
R_PointToDist2(fixed_t dx,fixed_t dy)292 fixed_t R_PointToDist2 (fixed_t dx, fixed_t dy)
293 {
294 dx = abs (dx);
295 dy = abs (dy);
296
297 if ((dx | dy) == 0)
298 {
299 return 0;
300 }
301
302 if (dy > dx)
303 {
304 swapvalues (dx, dy);
305 }
306
307 return FixedDiv (dx, finecosine[tantoangle[FixedDiv (dy, dx) >> DBITS] >> ANGLETOFINESHIFT]);
308 }
309
310 //==========================================================================
311 //
312 // R_InitTables
313 //
314 //==========================================================================
315
R_InitTables(void)316 void R_InitTables (void)
317 {
318 int i;
319 const double pimul = PI*2/FINEANGLES;
320
321 // viewangle tangent table
322 finetangent[0] = (fixed_t)(FRACUNIT*tan ((0.5-FINEANGLES/4)*pimul)+0.5);
323 for (i = 1; i < FINEANGLES/2; i++)
324 {
325 finetangent[i] = (fixed_t)(FRACUNIT*tan ((i-FINEANGLES/4)*pimul)+0.5);
326 }
327
328 // finesine table
329 for (i = 0; i < FINEANGLES/4; i++)
330 {
331 finesine[i] = (fixed_t)(FRACUNIT * sin (i*pimul));
332 }
333 for (i = 0; i < FINEANGLES/4; i++)
334 {
335 finesine[i+FINEANGLES/4] = finesine[FINEANGLES/4-1-i];
336 }
337 for (i = 0; i < FINEANGLES/2; i++)
338 {
339 finesine[i+FINEANGLES/2] = -finesine[i];
340 }
341 finesine[FINEANGLES/4] = FRACUNIT;
342 finesine[FINEANGLES*3/4] = -FRACUNIT;
343 memcpy (&finesine[FINEANGLES], &finesine[0], sizeof(angle_t)*FINEANGLES/4);
344 }
345
346 //==========================================================================
347 //
348 // R_SetFOV
349 //
350 // Changes the field of view in degrees
351 //
352 //==========================================================================
353
R_SetFOV(float fov)354 void R_SetFOV (float fov)
355 {
356 if (fov < 5.f)
357 fov = 5.f;
358 else if (fov > 170.f)
359 fov = 170.f;
360 if (fov != LastFOV)
361 {
362 LastFOV = fov;
363 FieldOfView = (int)(fov * (float)FINEANGLES / 360.f);
364 setsizeneeded = true;
365 }
366 }
367
368 //==========================================================================
369 //
370 // R_GetFOV
371 //
372 // Returns the current field of view in degrees
373 //
374 //==========================================================================
375
R_GetFOV()376 float R_GetFOV ()
377 {
378 return LastFOV;
379 }
380
381 //==========================================================================
382 //
383 // R_SetViewSize
384 //
385 // Do not really change anything here, because it might be in the middle
386 // of a refresh. The change will take effect next refresh.
387 //
388 //==========================================================================
389
R_SetViewSize(int blocks)390 void R_SetViewSize (int blocks)
391 {
392 setsizeneeded = true;
393 setblocks = blocks;
394 }
395
396 //==========================================================================
397 //
398 // R_SetWindow
399 //
400 //==========================================================================
401
R_SetWindow(int windowSize,int fullWidth,int fullHeight,int stHeight)402 void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight)
403 {
404 int trueratio;
405
406 if (windowSize >= 11)
407 {
408 viewwidth = fullWidth;
409 freelookviewheight = viewheight = fullHeight;
410 }
411 else if (windowSize == 10)
412 {
413 viewwidth = fullWidth;
414 viewheight = stHeight;
415 freelookviewheight = fullHeight;
416 }
417 else
418 {
419 viewwidth = ((setblocks*fullWidth)/10) & (~15);
420 viewheight = ((setblocks*stHeight)/10)&~7;
421 freelookviewheight = ((setblocks*fullHeight)/10)&~7;
422 }
423
424 // If the screen is approximately 16:9 or 16:10, consider it widescreen.
425 WidescreenRatio = CheckRatio (fullWidth, fullHeight, &trueratio);
426
427 DrawFSHUD = (windowSize == 11);
428
429 // [RH] Sky height fix for screens not 200 (or 240) pixels tall
430 R_InitSkyMap ();
431
432 centery = viewheight/2;
433 centerx = viewwidth/2;
434 if (WidescreenRatio & 4)
435 {
436 centerxwide = centerx;
437 }
438 else
439 {
440 centerxwide = centerx * BaseRatioSizes[WidescreenRatio][3] / 48;
441 }
442
443
444 int fov = FieldOfView;
445
446 // For widescreen displays, increase the FOV so that the middle part of the
447 // screen that would be visible on a 4:3 display has the requested FOV.
448 if (centerxwide != centerx)
449 { // centerxwide is what centerx would be if the display was not widescreen
450 fov = int(atan(double(centerx)*tan(double(fov)*M_PI/(FINEANGLES))/double(centerxwide))*(FINEANGLES)/M_PI);
451 if (fov > 170*FINEANGLES/360)
452 fov = 170*FINEANGLES/360;
453 }
454
455 FocalTangent = finetangent[FINEANGLES/4+fov/2];
456 Renderer->SetWindow(windowSize, fullWidth, fullHeight, stHeight, trueratio);
457 }
458
459 //==========================================================================
460 //
461 // R_ExecuteSetViewSize
462 //
463 //==========================================================================
464
R_ExecuteSetViewSize()465 void R_ExecuteSetViewSize ()
466 {
467 setsizeneeded = false;
468 V_SetBorderNeedRefresh();
469
470 R_SetWindow (setblocks, SCREENWIDTH, SCREENHEIGHT, ST_Y);
471
472 // Handle resize, e.g. smaller view windows with border and/or status bar.
473 viewwindowx = (screen->GetWidth() - viewwidth) >> 1;
474
475 // Same with base row offset.
476 viewwindowy = (viewwidth == screen->GetWidth()) ? 0 : (ST_Y - viewheight) >> 1;
477 }
478
479 //==========================================================================
480 //
481 // CVAR screenblocks
482 //
483 // Selects the size of the visible window
484 //
485 //==========================================================================
486
487 CUSTOM_CVAR (Int, screenblocks, 10, CVAR_ARCHIVE)
488 {
489 if (self > 12)
490 self = 12;
491 else if (self < 3)
492 self = 3;
493 else
494 R_SetViewSize (self);
495 }
496
497 //==========================================================================
498 //
499 // R_PointInSubsector
500 //
501 //==========================================================================
502
R_PointInSubsector(fixed_t x,fixed_t y)503 subsector_t *R_PointInSubsector (fixed_t x, fixed_t y)
504 {
505 node_t *node;
506 int side;
507
508 // single subsector is a special case
509 if (numnodes == 0)
510 return subsectors;
511
512 node = nodes + numnodes - 1;
513
514 do
515 {
516 side = R_PointOnSide (x, y, node);
517 node = (node_t *)node->children[side];
518 }
519 while (!((size_t)node & 1));
520
521 return (subsector_t *)((BYTE *)node - 1);
522 }
523
524 //==========================================================================
525 //
526 // R_Init
527 //
528 //==========================================================================
529
R_Init()530 void R_Init ()
531 {
532 atterm (R_Shutdown);
533
534 StartScreen->Progress();
535 V_InitFonts();
536 StartScreen->Progress();
537 // Colormap init moved back to InitPalette()
538 //R_InitColormaps ();
539 //StartScreen->Progress();
540
541 R_InitPointToAngle ();
542 R_InitTables ();
543 R_InitTranslationTables ();
544 R_SetViewSize (screenblocks);
545 Renderer->Init();
546 }
547
548 //==========================================================================
549 //
550 // R_Shutdown
551 //
552 //==========================================================================
553
R_Shutdown()554 static void R_Shutdown ()
555 {
556 R_DeinitTranslationTables();
557 R_DeinitColormaps ();
558 FCanvasTextureInfo::EmptyList();
559 }
560
561 //==========================================================================
562 //
563 // R_InterpolateView
564 //
565 //==========================================================================
566
567 //CVAR (Int, tf, 0, 0)
EXTERN_CVAR(Bool,cl_noprediction)568 EXTERN_CVAR (Bool, cl_noprediction)
569
570 void R_InterpolateView (player_t *player, fixed_t frac, InterpolationViewer *iview)
571 {
572 // frac = tf;
573 if (NoInterpolateView)
574 {
575 NoInterpolateView = false;
576 iview->oviewx = iview->nviewx;
577 iview->oviewy = iview->nviewy;
578 iview->oviewz = iview->nviewz;
579 iview->oviewpitch = iview->nviewpitch;
580 iview->oviewangle = iview->nviewangle;
581 }
582 viewx = iview->oviewx + FixedMul (frac, iview->nviewx - iview->oviewx);
583 viewy = iview->oviewy + FixedMul (frac, iview->nviewy - iview->oviewy);
584 viewz = iview->oviewz + FixedMul (frac, iview->nviewz - iview->oviewz);
585 if (player != NULL &&
586 !(player->cheats & CF_INTERPVIEW) &&
587 player - players == consoleplayer &&
588 camera == player->mo &&
589 !demoplayback &&
590 iview->nviewx == camera->X() &&
591 iview->nviewy == camera->Y() &&
592 !(player->cheats & (CF_TOTALLYFROZEN|CF_FROZEN)) &&
593 player->playerstate == PST_LIVE &&
594 player->mo->reactiontime == 0 &&
595 !NoInterpolateView &&
596 !paused &&
597 (!netgame || !cl_noprediction) &&
598 !LocalKeyboardTurner)
599 {
600 viewangle = iview->nviewangle + (LocalViewAngle & 0xFFFF0000);
601
602 fixed_t delta = player->centering ? 0 : -(signed)(LocalViewPitch & 0xFFFF0000);
603
604 viewpitch = iview->nviewpitch;
605 if (delta > 0)
606 {
607 // Avoid overflowing viewpitch (can happen when a netgame is stalled)
608 if (viewpitch > INT_MAX - delta)
609 {
610 viewpitch = player->MaxPitch;
611 }
612 else
613 {
614 viewpitch = MIN(viewpitch + delta, player->MaxPitch);
615 }
616 }
617 else if (delta < 0)
618 {
619 // Avoid overflowing viewpitch (can happen when a netgame is stalled)
620 if (viewpitch < INT_MIN - delta)
621 {
622 viewpitch = player->MinPitch;
623 }
624 else
625 {
626 viewpitch = MAX(viewpitch + delta, player->MinPitch);
627 }
628 }
629 }
630 else
631 {
632 viewpitch = iview->oviewpitch + FixedMul (frac, iview->nviewpitch - iview->oviewpitch);
633 viewangle = iview->oviewangle + FixedMul (frac, iview->nviewangle - iview->oviewangle);
634 }
635
636 // Due to interpolation this is not necessarily the same as the sector the camera is in.
637 viewsector = R_PointInSubsector(viewx, viewy)->sector;
638 }
639
640 //==========================================================================
641 //
642 // R_ResetViewInterpolation
643 //
644 //==========================================================================
645
R_ResetViewInterpolation()646 void R_ResetViewInterpolation ()
647 {
648 NoInterpolateView = true;
649 }
650
651 //==========================================================================
652 //
653 // R_SetViewAngle
654 //
655 //==========================================================================
656
R_SetViewAngle()657 void R_SetViewAngle ()
658 {
659 angle_t ang = viewangle >> ANGLETOFINESHIFT;
660
661 viewsin = finesine[ang];
662 viewcos = finecosine[ang];
663
664 viewtansin = FixedMul (FocalTangent, viewsin);
665 viewtancos = FixedMul (FocalTangent, viewcos);
666 }
667
668 //==========================================================================
669 //
670 // FindPastViewer
671 //
672 //==========================================================================
673
FindPastViewer(AActor * actor)674 static InterpolationViewer *FindPastViewer (AActor *actor)
675 {
676 for (unsigned int i = 0; i < PastViewers.Size(); ++i)
677 {
678 if (PastViewers[i].ViewActor == actor)
679 {
680 return &PastViewers[i];
681 }
682 }
683
684 // Not found, so make a new one
685 InterpolationViewer iview = { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
686 iview.ViewActor = actor;
687 iview.otic = -1;
688 return &PastViewers[PastViewers.Push (iview)];
689 }
690
691 //==========================================================================
692 //
693 // R_FreePastViewers
694 //
695 //==========================================================================
696
R_FreePastViewers()697 void R_FreePastViewers ()
698 {
699 PastViewers.Clear ();
700 }
701
702 //==========================================================================
703 //
704 // R_ClearPastViewer
705 //
706 // If the actor changed in a non-interpolatable way, remove it.
707 //
708 //==========================================================================
709
R_ClearPastViewer(AActor * actor)710 void R_ClearPastViewer (AActor *actor)
711 {
712 for (unsigned int i = 0; i < PastViewers.Size(); ++i)
713 {
714 if (PastViewers[i].ViewActor == actor)
715 {
716 // Found it, so remove it.
717 if (i == PastViewers.Size())
718 {
719 PastViewers.Delete (i);
720 }
721 else
722 {
723 PastViewers.Pop (PastViewers[i]);
724 }
725 }
726 }
727 }
728
729 //==========================================================================
730 //
731 // R_RebuildViewInterpolation
732 //
733 //==========================================================================
734
R_RebuildViewInterpolation(player_t * player)735 void R_RebuildViewInterpolation(player_t *player)
736 {
737 if (player == NULL || player->camera == NULL)
738 return;
739
740 if (!NoInterpolateView)
741 return;
742 NoInterpolateView = false;
743
744 InterpolationViewer *iview = FindPastViewer(player->camera);
745
746 iview->oviewx = iview->nviewx;
747 iview->oviewy = iview->nviewy;
748 iview->oviewz = iview->nviewz;
749 iview->oviewpitch = iview->nviewpitch;
750 iview->oviewangle = iview->nviewangle;
751 }
752
753 //==========================================================================
754 //
755 // R_GetViewInterpolationStatus
756 //
757 //==========================================================================
758
R_GetViewInterpolationStatus()759 bool R_GetViewInterpolationStatus()
760 {
761 return NoInterpolateView;
762 }
763
764 //==========================================================================
765 //
766 // QuakePower
767 //
768 //==========================================================================
769
QuakePower(fixed_t factor,fixed_t intensity,fixed_t offset)770 static fixed_t QuakePower(fixed_t factor, fixed_t intensity, fixed_t offset)
771 {
772 fixed_t randumb;
773
774 if (intensity == 0)
775 {
776 randumb = 0;
777 }
778 else
779 {
780 randumb = pr_torchflicker(intensity * 2) - intensity;
781 }
782 return FixedMul(factor, randumb + offset);
783 }
784
785 //==========================================================================
786 //
787 // R_SetupFrame
788 //
789 //==========================================================================
790
R_SetupFrame(AActor * actor)791 void R_SetupFrame (AActor *actor)
792 {
793 if (actor == NULL)
794 {
795 I_Error ("Tried to render from a NULL actor.");
796 }
797
798 player_t *player = actor->player;
799 unsigned int newblend;
800 InterpolationViewer *iview;
801
802 if (player != NULL && player->mo == actor)
803 { // [RH] Use camera instead of viewplayer
804 camera = player->camera;
805 if (camera == NULL)
806 {
807 camera = player->camera = player->mo;
808 }
809 }
810 else
811 {
812 camera = actor;
813 }
814
815 if (camera == NULL)
816 {
817 I_Error ("You lost your body. Bad dehacked work is likely to blame.");
818 }
819
820 iview = FindPastViewer (camera);
821
822 int nowtic = I_GetTime (false);
823 if (iview->otic != -1 && nowtic > iview->otic)
824 {
825 iview->otic = nowtic;
826 iview->oviewx = iview->nviewx;
827 iview->oviewy = iview->nviewy;
828 iview->oviewz = iview->nviewz;
829 iview->oviewpitch = iview->nviewpitch;
830 iview->oviewangle = iview->nviewangle;
831 }
832
833 if (player != NULL && gamestate != GS_TITLELEVEL &&
834 ((player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)))
835 {
836 // [RH] Use chasecam view
837 P_AimCamera (camera, iview->nviewx, iview->nviewy, iview->nviewz, viewsector);
838 r_showviewer = true;
839 }
840 else
841 {
842 iview->nviewx = camera->X();
843 iview->nviewy = camera->Y();
844 iview->nviewz = camera->player ? camera->player->viewz : camera->Z() + camera->GetClass()->Meta.GetMetaFixed(AMETA_CameraHeight);
845 viewsector = camera->Sector;
846 r_showviewer = false;
847 }
848 iview->nviewpitch = camera->pitch;
849 if (camera->player != 0)
850 {
851 player = camera->player;
852 }
853
854 iview->nviewangle = camera->angle;
855 if (iview->otic == -1 || r_NoInterpolate)
856 {
857 R_ResetViewInterpolation ();
858 iview->otic = nowtic;
859 }
860
861 r_TicFrac = I_GetTimeFrac (&r_FrameTime);
862 if (cl_capfps || r_NoInterpolate)
863 {
864 r_TicFrac = FRACUNIT;
865 }
866
867 R_InterpolateView (player, r_TicFrac, iview);
868
869 #ifdef TEST_X
870 viewx = TEST_X;
871 viewy = TEST_Y;
872 viewz = TEST_Z;
873 viewangle = TEST_ANGLE;
874 #endif
875
876 R_SetViewAngle ();
877
878 interpolator.DoInterpolations (r_TicFrac);
879
880 // Keep the view within the sector's floor and ceiling
881 fixed_t theZ = viewsector->ceilingplane.ZatPoint (viewx, viewy) - 4*FRACUNIT;
882 if (viewz > theZ)
883 {
884 viewz = theZ;
885 }
886
887 theZ = viewsector->floorplane.ZatPoint (viewx, viewy) + 4*FRACUNIT;
888 if (viewz < theZ)
889 {
890 viewz = theZ;
891 }
892
893 if (!paused)
894 {
895 FQuakeJiggers jiggers = { 0, };
896
897 if (DEarthquake::StaticGetQuakeIntensities(camera, jiggers) > 0)
898 {
899 fixed_t quakefactor = FLOAT2FIXED(r_quakeintensity);
900
901 if ((jiggers.RelIntensityX | jiggers.RelOffsetX) != 0)
902 {
903 int ang = (camera->angle) >> ANGLETOFINESHIFT;
904 fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityX, jiggers.RelOffsetX);
905 viewx += FixedMul(finecosine[ang], power);
906 viewy += FixedMul(finesine[ang], power);
907 }
908 if ((jiggers.RelIntensityY | jiggers.RelOffsetY) != 0)
909 {
910 int ang = (camera->angle + ANG90) >> ANGLETOFINESHIFT;
911 fixed_t power = QuakePower(quakefactor, jiggers.RelIntensityY, jiggers.RelOffsetY);
912 viewx += FixedMul(finecosine[ang], power);
913 viewy += FixedMul(finesine[ang], power);
914 }
915 // FIXME: Relative Z is not relative
916 // [MC]On it! Will be introducing pitch after QF_WAVE.
917 if ((jiggers.RelIntensityZ | jiggers.RelOffsetZ) != 0)
918 {
919 viewz += QuakePower(quakefactor, jiggers.RelIntensityZ, jiggers.RelOffsetZ);
920 }
921 if ((jiggers.IntensityX | jiggers.OffsetX) != 0)
922 {
923 viewx += QuakePower(quakefactor, jiggers.IntensityX, jiggers.OffsetX);
924 }
925 if ((jiggers.IntensityY | jiggers.OffsetY) != 0)
926 {
927 viewy += QuakePower(quakefactor, jiggers.IntensityY, jiggers.OffsetY);
928 }
929 if ((jiggers.IntensityZ | jiggers.OffsetZ) != 0)
930 {
931 viewz += QuakePower(quakefactor, jiggers.IntensityZ, jiggers.OffsetZ);
932 }
933 }
934 }
935
936 extralight = camera->player ? camera->player->extralight : 0;
937
938 // killough 3/20/98, 4/4/98: select colormap based on player status
939 // [RH] Can also select a blend
940 newblend = 0;
941
942 TArray<lightlist_t> &lightlist = viewsector->e->XFloor.lightlist;
943 if (lightlist.Size() > 0)
944 {
945 for(unsigned int i = 0; i < lightlist.Size(); i++)
946 {
947 secplane_t *plane;
948 int viewside;
949 plane = (i < lightlist.Size()-1) ? &lightlist[i+1].plane : &viewsector->floorplane;
950 viewside = plane->PointOnSide(viewx, viewy, viewz);
951 // Reverse the direction of the test if the plane was downward facing.
952 // We want to know if the view is above it, whatever its orientation may be.
953 if (plane->c < 0)
954 viewside = -viewside;
955 if (viewside > 0)
956 {
957 // 3d floor 'fog' is rendered as a blending value
958 PalEntry blendv = lightlist[i].blend;
959
960 // If no alpha is set, use 50%
961 if (blendv.a==0 && blendv!=0) blendv.a=128;
962 newblend = blendv.d;
963 break;
964 }
965 }
966 }
967 else
968 {
969 const sector_t *s = viewsector->GetHeightSec();
970 if (s != NULL)
971 {
972 newblend = s->floorplane.PointOnSide(viewx, viewy, viewz) < 0
973 ? s->bottommap
974 : s->ceilingplane.PointOnSide(viewx, viewy, viewz) < 0
975 ? s->topmap
976 : s->midmap;
977 if (APART(newblend) == 0 && newblend >= numfakecmaps)
978 newblend = 0;
979 }
980 }
981
982 // [RH] Don't override testblend unless entering a sector with a
983 // blend different from the previous sector's. Same goes with
984 // NormalLight's maps pointer.
985 if (R_OldBlend != newblend)
986 {
987 R_OldBlend = newblend;
988 if (APART(newblend))
989 {
990 BaseBlendR = RPART(newblend);
991 BaseBlendG = GPART(newblend);
992 BaseBlendB = BPART(newblend);
993 BaseBlendA = APART(newblend) / 255.f;
994 NormalLight.Maps = realcolormaps;
995 }
996 else
997 {
998 NormalLight.Maps = realcolormaps + NUMCOLORMAPS*256*newblend;
999 BaseBlendR = BaseBlendG = BaseBlendB = 0;
1000 BaseBlendA = 0.f;
1001 }
1002 }
1003
1004 Renderer->CopyStackedViewParameters();
1005 Renderer->SetupFrame(player);
1006
1007 validcount++;
1008
1009 if (RenderTarget == screen && r_clearbuffer != 0)
1010 {
1011 int color;
1012 int hom = r_clearbuffer;
1013
1014 if (hom == 3)
1015 {
1016 hom = ((I_FPSTime() / 128) & 1) + 1;
1017 }
1018 if (hom == 1)
1019 {
1020 color = GPalette.BlackIndex;
1021 }
1022 else if (hom == 2)
1023 {
1024 color = GPalette.WhiteIndex;
1025 }
1026 else if (hom == 4)
1027 {
1028 color = (I_FPSTime() / 32) & 255;
1029 }
1030 else
1031 {
1032 color = pr_hom();
1033 }
1034 Renderer->ClearBuffer(color);
1035 }
1036 }
1037
1038
1039 //==========================================================================
1040 //
1041 // FCanvasTextureInfo :: Add
1042 //
1043 // Assigns a camera to a canvas texture.
1044 //
1045 //==========================================================================
1046
Add(AActor * viewpoint,FTextureID picnum,int fov)1047 void FCanvasTextureInfo::Add (AActor *viewpoint, FTextureID picnum, int fov)
1048 {
1049 FCanvasTextureInfo *probe;
1050 FCanvasTexture *texture;
1051
1052 if (!picnum.isValid())
1053 {
1054 return;
1055 }
1056 texture = static_cast<FCanvasTexture *>(TexMan[picnum]);
1057 if (!texture->bHasCanvas)
1058 {
1059 Printf ("%s is not a valid target for a camera\n", texture->Name.GetChars());
1060 return;
1061 }
1062
1063 // Is this texture already assigned to a camera?
1064 for (probe = List; probe != NULL; probe = probe->Next)
1065 {
1066 if (probe->Texture == texture)
1067 {
1068 // Yes, change its assignment to this new camera
1069 if (probe->Viewpoint != viewpoint || probe->FOV != fov)
1070 {
1071 texture->bFirstUpdate = true;
1072 }
1073 probe->Viewpoint = viewpoint;
1074 probe->FOV = fov;
1075 return;
1076 }
1077 }
1078 // No, create a new assignment
1079 probe = new FCanvasTextureInfo;
1080 probe->Viewpoint = viewpoint;
1081 probe->Texture = texture;
1082 probe->PicNum = picnum;
1083 probe->FOV = fov;
1084 probe->Next = List;
1085 texture->bFirstUpdate = true;
1086 List = probe;
1087 }
1088
1089 //==========================================================================
1090 //
1091 // FCanvasTextureInfo :: UpdateAll
1092 //
1093 // Updates all canvas textures that were visible in the last frame.
1094 //
1095 //==========================================================================
1096
UpdateAll()1097 void FCanvasTextureInfo::UpdateAll ()
1098 {
1099 FCanvasTextureInfo *probe;
1100
1101 for (probe = List; probe != NULL; probe = probe->Next)
1102 {
1103 if (probe->Viewpoint != NULL && probe->Texture->bNeedsUpdate)
1104 {
1105 Renderer->RenderTextureView(probe->Texture, probe->Viewpoint, probe->FOV);
1106 }
1107 }
1108 }
1109
1110 //==========================================================================
1111 //
1112 // FCanvasTextureInfo :: EmptyList
1113 //
1114 // Removes all camera->texture assignments.
1115 //
1116 //==========================================================================
1117
EmptyList()1118 void FCanvasTextureInfo::EmptyList ()
1119 {
1120 FCanvasTextureInfo *probe, *next;
1121
1122 for (probe = List; probe != NULL; probe = next)
1123 {
1124 next = probe->Next;
1125 probe->Texture->Unload();
1126 delete probe;
1127 }
1128 List = NULL;
1129 }
1130
1131 //==========================================================================
1132 //
1133 // FCanvasTextureInfo :: Serialize
1134 //
1135 // Reads or writes the current set of mappings in an archive.
1136 //
1137 //==========================================================================
1138
Serialize(FArchive & arc)1139 void FCanvasTextureInfo::Serialize (FArchive &arc)
1140 {
1141 if (arc.IsStoring ())
1142 {
1143 FCanvasTextureInfo *probe;
1144
1145 for (probe = List; probe != NULL; probe = probe->Next)
1146 {
1147 if (probe->Texture != NULL && probe->Viewpoint != NULL)
1148 {
1149 arc << probe->Viewpoint << probe->FOV << probe->PicNum;
1150 }
1151 }
1152 AActor *nullactor = NULL;
1153 arc << nullactor;
1154 }
1155 else
1156 {
1157 AActor *viewpoint;
1158 int fov;
1159 FTextureID picnum;
1160
1161 EmptyList ();
1162 while (arc << viewpoint, viewpoint != NULL)
1163 {
1164 arc << fov << picnum;
1165 Add (viewpoint, picnum, fov);
1166 }
1167 }
1168 }
1169
1170 //==========================================================================
1171 //
1172 // FCanvasTextureInfo :: Mark
1173 //
1174 // Marks all viewpoints in the list for the collector.
1175 //
1176 //==========================================================================
1177
Mark()1178 void FCanvasTextureInfo::Mark()
1179 {
1180 for (FCanvasTextureInfo *probe = List; probe != NULL; probe = probe->Next)
1181 {
1182 GC::Mark(probe->Viewpoint);
1183 }
1184 }
1185
1186