mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 21:11:39 +00:00
f5421491ec
The idea is, when status bars are moved to ZScript that only this small wrapper class needs to be dealt with and the implementation can be left alone. SBARINFO is far too complex to be scriptified, but having it inherit directly from DBaseStatusBar and access its member variables severely limits the options of dealing with the status bar code. This way, it only accesses some globally visible functions in DBaseStatusBar and no variables. - renamed the global ST_X and ST_Y variables because it is far too confusing and error-prone to have the same names inside and outside DBaseStatusBar.
1135 lines
28 KiB
C++
1135 lines
28 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id:$
|
|
//
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
//
|
|
// This source is available for distribution and/or modification
|
|
// only under the terms of the DOOM Source Code License as
|
|
// published by id Software. All rights reserved.
|
|
//
|
|
// The source is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
// for more details.
|
|
//
|
|
// $Log:$
|
|
//
|
|
// DESCRIPTION:
|
|
// Rendering main loop and setup functions,
|
|
// utility functions (BSP, geometry, trigonometry).
|
|
// See tables.c, too.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
#include "templates.h"
|
|
#include "doomdef.h"
|
|
#include "d_net.h"
|
|
#include "doomstat.h"
|
|
#include "m_random.h"
|
|
#include "m_bbox.h"
|
|
#include "r_sky.h"
|
|
#include "st_stuff.h"
|
|
#include "c_cvars.h"
|
|
#include "c_dispatch.h"
|
|
#include "v_video.h"
|
|
#include "stats.h"
|
|
#include "i_video.h"
|
|
#include "i_system.h"
|
|
#include "a_sharedglobal.h"
|
|
#include "r_data/r_translate.h"
|
|
#include "p_3dmidtex.h"
|
|
#include "r_data/r_interpolate.h"
|
|
#include "v_palette.h"
|
|
#include "po_man.h"
|
|
#include "p_effect.h"
|
|
#include "st_start.h"
|
|
#include "v_font.h"
|
|
#include "r_renderer.h"
|
|
#include "r_data/colormaps.h"
|
|
#include "serializer.h"
|
|
#include "r_utility.h"
|
|
#include "d_player.h"
|
|
#include "p_local.h"
|
|
#include "g_levellocals.h"
|
|
#include "p_maputl.h"
|
|
#include "math/cmath.h"
|
|
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
extern bool DrawFSHUD; // [RH] Defined in d_main.cpp
|
|
EXTERN_CVAR (Bool, cl_capfps)
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
struct InterpolationViewer
|
|
{
|
|
struct instance
|
|
{
|
|
DVector3 Pos;
|
|
DRotator Angles;
|
|
};
|
|
|
|
AActor *ViewActor;
|
|
int otic;
|
|
instance Old, New;
|
|
};
|
|
|
|
// PRIVATE DATA DECLARATIONS -----------------------------------------------
|
|
static TArray<InterpolationViewer> PastViewers;
|
|
static FRandom pr_torchflicker ("TorchFlicker");
|
|
static FRandom pr_hom;
|
|
bool NoInterpolateView; // GL needs access to this.
|
|
static TArray<DVector3a> InterpolationPath;
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
CVAR (Bool, r_deathcamera, false, CVAR_ARCHIVE)
|
|
CVAR (Int, r_clearbuffer, 0, 0)
|
|
CVAR (Bool, r_drawvoxels, true, 0)
|
|
CVAR (Bool, r_drawplayersprites, true, 0) // [RH] Draw player sprites?
|
|
CUSTOM_CVAR(Float, r_quakeintensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
|
{
|
|
if (self < 0.f) self = 0.f;
|
|
else if (self > 1.f) self = 1.f;
|
|
}
|
|
|
|
DCanvas *RenderTarget; // [RH] canvas to render to
|
|
|
|
int viewwindowx;
|
|
int viewwindowy;
|
|
|
|
DVector3 ViewPos;
|
|
DVector3 ViewActorPos; // the actual position of the viewing actor, without interpolation and quake offsets.
|
|
DAngle ViewAngle;
|
|
DAngle ViewPitch;
|
|
DAngle ViewRoll;
|
|
DVector3 ViewPath[2];
|
|
|
|
extern "C"
|
|
{
|
|
int viewwidth;
|
|
int viewheight;
|
|
int centerx;
|
|
int centery;
|
|
int centerxwide;
|
|
}
|
|
|
|
int otic;
|
|
|
|
sector_t *viewsector;
|
|
|
|
double ViewCos, ViewTanCos;
|
|
double ViewSin, ViewTanSin;
|
|
|
|
AActor *camera; // [RH] camera to draw from. doesn't have to be a player
|
|
|
|
double r_TicFracF; // same as floating point
|
|
DWORD r_FrameTime; // [RH] Time this frame started drawing (in ms)
|
|
bool r_NoInterpolate;
|
|
bool r_showviewer;
|
|
|
|
angle_t LocalViewAngle;
|
|
int LocalViewPitch;
|
|
bool LocalKeyboardTurner;
|
|
|
|
float WidescreenRatio;
|
|
int setblocks;
|
|
int extralight;
|
|
bool setsizeneeded;
|
|
double FocalTangent;
|
|
|
|
unsigned int R_OldBlend = ~0;
|
|
int validcount = 1; // increment every time a check is made
|
|
DAngle FieldOfView = 90.; // Angles in the SCREENWIDTH wide window
|
|
|
|
FCanvasTextureInfo *FCanvasTextureInfo::List;
|
|
|
|
DVector3a view;
|
|
DAngle viewpitch;
|
|
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
static void R_Shutdown ();
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_SetFOV
|
|
//
|
|
// Changes the field of view in degrees
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_SetFOV (DAngle fov)
|
|
{
|
|
|
|
if (fov < 5.) fov = 5.;
|
|
else if (fov > 170.) fov = 170.;
|
|
if (fov != FieldOfView)
|
|
{
|
|
FieldOfView = fov;
|
|
setsizeneeded = true;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_SetViewSize
|
|
//
|
|
// Do not really change anything here, because it might be in the middle
|
|
// of a refresh. The change will take effect next refresh.
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_SetViewSize (int blocks)
|
|
{
|
|
setsizeneeded = true;
|
|
setblocks = blocks;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_SetWindow
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas)
|
|
{
|
|
float trueratio;
|
|
|
|
if (windowSize >= 11)
|
|
{
|
|
viewwidth = fullWidth;
|
|
freelookviewheight = viewheight = fullHeight;
|
|
}
|
|
else if (windowSize == 10)
|
|
{
|
|
viewwidth = fullWidth;
|
|
viewheight = stHeight;
|
|
freelookviewheight = fullHeight;
|
|
}
|
|
else
|
|
{
|
|
viewwidth = ((setblocks*fullWidth)/10) & (~15);
|
|
viewheight = ((setblocks*stHeight)/10)&~7;
|
|
freelookviewheight = ((setblocks*fullHeight)/10)&~7;
|
|
}
|
|
|
|
if (renderingToCanvas)
|
|
{
|
|
WidescreenRatio = fullWidth / (float)fullHeight;
|
|
trueratio = WidescreenRatio;
|
|
}
|
|
else
|
|
{
|
|
WidescreenRatio = ActiveRatio(fullWidth, fullHeight, &trueratio);
|
|
}
|
|
|
|
DrawFSHUD = (windowSize == 11);
|
|
|
|
// [RH] Sky height fix for screens not 200 (or 240) pixels tall
|
|
R_InitSkyMap ();
|
|
|
|
centery = viewheight/2;
|
|
centerx = viewwidth/2;
|
|
if (AspectTallerThanWide(WidescreenRatio))
|
|
{
|
|
centerxwide = centerx;
|
|
}
|
|
else
|
|
{
|
|
centerxwide = centerx * AspectMultiplier(WidescreenRatio) / 48;
|
|
}
|
|
|
|
|
|
DAngle fov = FieldOfView;
|
|
|
|
// For widescreen displays, increase the FOV so that the middle part of the
|
|
// screen that would be visible on a 4:3 display has the requested FOV.
|
|
if (centerxwide != centerx)
|
|
{ // centerxwide is what centerx would be if the display was not widescreen
|
|
fov = DAngle::ToDegrees(2 * atan(centerx * tan(fov.Radians()/2) / double(centerxwide)));
|
|
if (fov > 170.) fov = 170.;
|
|
}
|
|
FocalTangent = tan(fov.Radians() / 2);
|
|
Renderer->SetWindow(windowSize, fullWidth, fullHeight, stHeight, trueratio);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_ExecuteSetViewSize
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_ExecuteSetViewSize ()
|
|
{
|
|
setsizeneeded = false;
|
|
V_SetBorderNeedRefresh();
|
|
|
|
R_SetWindow (setblocks, SCREENWIDTH, SCREENHEIGHT, gST_Y);
|
|
|
|
// Handle resize, e.g. smaller view windows with border and/or status bar.
|
|
viewwindowx = (screen->GetWidth() - viewwidth) >> 1;
|
|
|
|
// Same with base row offset.
|
|
viewwindowy = (viewwidth == screen->GetWidth()) ? 0 : (gST_Y - viewheight) >> 1;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CVAR screenblocks
|
|
//
|
|
// Selects the size of the visible window
|
|
//
|
|
//==========================================================================
|
|
|
|
CUSTOM_CVAR (Int, screenblocks, 10, CVAR_ARCHIVE)
|
|
{
|
|
if (self > 12)
|
|
self = 12;
|
|
else if (self < 3)
|
|
self = 3;
|
|
else
|
|
R_SetViewSize (self);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_PointInSubsector
|
|
//
|
|
//==========================================================================
|
|
|
|
subsector_t *R_PointInSubsector (fixed_t x, fixed_t y)
|
|
{
|
|
node_t *node;
|
|
int side;
|
|
|
|
// single subsector is a special case
|
|
if (numnodes == 0)
|
|
return subsectors;
|
|
|
|
node = nodes + numnodes - 1;
|
|
|
|
do
|
|
{
|
|
side = R_PointOnSide (x, y, node);
|
|
node = (node_t *)node->children[side];
|
|
}
|
|
while (!((size_t)node & 1));
|
|
|
|
return (subsector_t *)((BYTE *)node - 1);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_Init
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_Init ()
|
|
{
|
|
atterm (R_Shutdown);
|
|
|
|
StartScreen->Progress();
|
|
V_InitFonts();
|
|
StartScreen->Progress();
|
|
// Colormap init moved back to InitPalette()
|
|
//R_InitColormaps ();
|
|
//StartScreen->Progress();
|
|
|
|
R_InitTranslationTables ();
|
|
R_SetViewSize (screenblocks);
|
|
Renderer->Init();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_Shutdown
|
|
//
|
|
//==========================================================================
|
|
|
|
static void R_Shutdown ()
|
|
{
|
|
R_DeinitTranslationTables();
|
|
R_DeinitColormaps ();
|
|
FCanvasTextureInfo::EmptyList();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_InterpolateView
|
|
//
|
|
//==========================================================================
|
|
|
|
//CVAR (Int, tf, 0, 0)
|
|
EXTERN_CVAR (Bool, cl_noprediction)
|
|
|
|
void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *iview)
|
|
{
|
|
if (NoInterpolateView)
|
|
{
|
|
InterpolationPath.Clear();
|
|
NoInterpolateView = false;
|
|
iview->Old = iview->New;
|
|
}
|
|
int oldgroup = R_PointInSubsector(iview->Old.Pos)->sector->PortalGroup;
|
|
int newgroup = R_PointInSubsector(iview->New.Pos)->sector->PortalGroup;
|
|
|
|
DAngle oviewangle = iview->Old.Angles.Yaw;
|
|
DAngle nviewangle = iview->New.Angles.Yaw;
|
|
if (!cl_capfps)
|
|
{
|
|
if ((iview->Old.Pos.X != iview->New.Pos.X || iview->Old.Pos.Y != iview->New.Pos.Y) && InterpolationPath.Size() > 0)
|
|
{
|
|
DVector3 view = iview->New.Pos;
|
|
|
|
// Interpolating through line portals is a messy affair.
|
|
// What needs be done is to store the portal transitions of the camera actor as waypoints
|
|
// and then find out on which part of the path the current view lies.
|
|
// Needless to say, this doesn't work for chasecam mode.
|
|
if (!r_showviewer)
|
|
{
|
|
double pathlen = 0;
|
|
double zdiff = 0;
|
|
double totalzdiff = 0;
|
|
DAngle adiff = 0.;
|
|
DAngle totaladiff = 0.;
|
|
double oviewz = iview->Old.Pos.Z;
|
|
double nviewz = iview->New.Pos.Z;
|
|
DVector3a oldpos = { { iview->Old.Pos.X, iview->Old.Pos.Y, 0 }, 0. };
|
|
DVector3a newpos = { { iview->New.Pos.X, iview->New.Pos.Y, 0 }, 0. };
|
|
InterpolationPath.Push(newpos); // add this to the array to simplify the loops below
|
|
|
|
for (unsigned i = 0; i < InterpolationPath.Size(); i += 2)
|
|
{
|
|
DVector3a &start = i == 0 ? oldpos : InterpolationPath[i - 1];
|
|
DVector3a &end = InterpolationPath[i];
|
|
pathlen += (end.pos - start.pos).Length();
|
|
totalzdiff += start.pos.Z;
|
|
totaladiff += start.angle;
|
|
}
|
|
double interpolatedlen = Frac * pathlen;
|
|
|
|
for (unsigned i = 0; i < InterpolationPath.Size(); i += 2)
|
|
{
|
|
DVector3a &start = i == 0 ? oldpos : InterpolationPath[i - 1];
|
|
DVector3a &end = InterpolationPath[i];
|
|
double fraglen = (end.pos - start.pos).Length();
|
|
zdiff += start.pos.Z;
|
|
adiff += start.angle;
|
|
if (fraglen <= interpolatedlen)
|
|
{
|
|
interpolatedlen -= fraglen;
|
|
}
|
|
else
|
|
{
|
|
double fragfrac = interpolatedlen / fraglen;
|
|
oviewz += zdiff;
|
|
nviewz -= totalzdiff - zdiff;
|
|
oviewangle += adiff;
|
|
nviewangle -= totaladiff - adiff;
|
|
DVector2 viewpos = start.pos + (fragfrac * (end.pos - start.pos));
|
|
ViewPos = { viewpos, oviewz + Frac * (nviewz - oviewz) };
|
|
break;
|
|
}
|
|
}
|
|
InterpolationPath.Pop();
|
|
ViewPath[0] = iview->Old.Pos;
|
|
ViewPath[1] = ViewPath[0] + (InterpolationPath[0].pos - ViewPath[0]).XY().MakeResize(pathlen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DVector2 disp = Displacements.getOffset(oldgroup, newgroup);
|
|
ViewPos = iview->Old.Pos + (iview->New.Pos - iview->Old.Pos - disp) * Frac;
|
|
ViewPath[0] = ViewPath[1] = iview->New.Pos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ViewPos = iview->New.Pos;
|
|
ViewPath[0] = ViewPath[1] = iview->New.Pos;
|
|
}
|
|
if (player != NULL &&
|
|
!(player->cheats & CF_INTERPVIEW) &&
|
|
player - players == consoleplayer &&
|
|
camera == player->mo &&
|
|
!demoplayback &&
|
|
iview->New.Pos.X == camera->X() &&
|
|
iview->New.Pos.Y == camera->Y() &&
|
|
!(player->cheats & (CF_TOTALLYFROZEN|CF_FROZEN)) &&
|
|
player->playerstate == PST_LIVE &&
|
|
player->mo->reactiontime == 0 &&
|
|
!NoInterpolateView &&
|
|
!paused &&
|
|
(!netgame || !cl_noprediction) &&
|
|
!LocalKeyboardTurner)
|
|
{
|
|
ViewAngle = (nviewangle + AngleToFloat(LocalViewAngle & 0xFFFF0000)).Normalized180();
|
|
DAngle delta = player->centering ? DAngle(0.) : AngleToFloat(int(LocalViewPitch & 0xFFFF0000));
|
|
ViewPitch = clamp<DAngle>((iview->New.Angles.Pitch - delta).Normalized180(), player->MinPitch, player->MaxPitch);
|
|
ViewRoll = iview->New.Angles.Roll.Normalized180();
|
|
}
|
|
else
|
|
{
|
|
ViewPitch = (iview->Old.Angles.Pitch + deltaangle(iview->Old.Angles.Pitch, iview->New.Angles.Pitch) * Frac).Normalized180();
|
|
ViewAngle = (oviewangle + deltaangle(oviewangle, nviewangle) * Frac).Normalized180();
|
|
ViewRoll = (iview->Old.Angles.Roll + deltaangle(iview->Old.Angles.Roll, iview->New.Angles.Roll) * Frac).Normalized180();
|
|
}
|
|
|
|
// Due to interpolation this is not necessarily the same as the sector the camera is in.
|
|
viewsector = R_PointInSubsector(ViewPos)->sector;
|
|
bool moved = false;
|
|
while (!viewsector->PortalBlocksMovement(sector_t::ceiling))
|
|
{
|
|
if (ViewPos.Z > viewsector->GetPortalPlaneZ(sector_t::ceiling))
|
|
{
|
|
ViewPos += viewsector->GetPortalDisplacement(sector_t::ceiling);
|
|
ViewActorPos += viewsector->GetPortalDisplacement(sector_t::ceiling);
|
|
viewsector = R_PointInSubsector(ViewPos)->sector;
|
|
moved = true;
|
|
}
|
|
else break;
|
|
}
|
|
if (!moved)
|
|
{
|
|
while (!viewsector->PortalBlocksMovement(sector_t::floor))
|
|
{
|
|
if (ViewPos.Z < viewsector->GetPortalPlaneZ(sector_t::floor))
|
|
{
|
|
ViewPos += viewsector->GetPortalDisplacement(sector_t::floor);
|
|
ViewActorPos += viewsector->GetPortalDisplacement(sector_t::floor);
|
|
viewsector = R_PointInSubsector(ViewPos)->sector;
|
|
moved = true;
|
|
}
|
|
else break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_ResetViewInterpolation
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_ResetViewInterpolation ()
|
|
{
|
|
InterpolationPath.Clear();
|
|
NoInterpolateView = true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_SetViewAngle
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_SetViewAngle ()
|
|
{
|
|
ViewSin = ViewAngle.Sin();
|
|
ViewCos = ViewAngle.Cos();
|
|
|
|
ViewTanSin = FocalTangent * ViewSin;
|
|
ViewTanCos = FocalTangent * ViewCos;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FindPastViewer
|
|
//
|
|
//==========================================================================
|
|
|
|
static InterpolationViewer *FindPastViewer (AActor *actor)
|
|
{
|
|
for (unsigned int i = 0; i < PastViewers.Size(); ++i)
|
|
{
|
|
if (PastViewers[i].ViewActor == actor)
|
|
{
|
|
return &PastViewers[i];
|
|
}
|
|
}
|
|
|
|
// Not found, so make a new one
|
|
InterpolationViewer iview;
|
|
memset(&iview, 0, sizeof(iview));
|
|
iview.ViewActor = actor;
|
|
iview.otic = -1;
|
|
InterpolationPath.Clear();
|
|
return &PastViewers[PastViewers.Push (iview)];
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_FreePastViewers
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_FreePastViewers ()
|
|
{
|
|
InterpolationPath.Clear();
|
|
PastViewers.Clear ();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_ClearPastViewer
|
|
//
|
|
// If the actor changed in a non-interpolatable way, remove it.
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_ClearPastViewer (AActor *actor)
|
|
{
|
|
InterpolationPath.Clear();
|
|
for (unsigned int i = 0; i < PastViewers.Size(); ++i)
|
|
{
|
|
if (PastViewers[i].ViewActor == actor)
|
|
{
|
|
// Found it, so remove it.
|
|
if (i == PastViewers.Size())
|
|
{
|
|
PastViewers.Delete (i);
|
|
}
|
|
else
|
|
{
|
|
PastViewers.Pop (PastViewers[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_RebuildViewInterpolation
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_RebuildViewInterpolation(player_t *player)
|
|
{
|
|
if (player == NULL || player->camera == NULL)
|
|
return;
|
|
|
|
if (!NoInterpolateView)
|
|
return;
|
|
NoInterpolateView = false;
|
|
|
|
InterpolationViewer *iview = FindPastViewer(player->camera);
|
|
|
|
iview->Old = iview->New;
|
|
InterpolationPath.Clear();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_GetViewInterpolationStatus
|
|
//
|
|
//==========================================================================
|
|
|
|
bool R_GetViewInterpolationStatus()
|
|
{
|
|
return NoInterpolateView;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_ClearInterpolationPath
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_ClearInterpolationPath()
|
|
{
|
|
InterpolationPath.Clear();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_AddInterpolationPoint
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_AddInterpolationPoint(const DVector3a &vec)
|
|
{
|
|
InterpolationPath.Push(vec);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// QuakePower
|
|
//
|
|
//==========================================================================
|
|
|
|
static double QuakePower(double factor, double intensity, double offset)
|
|
{
|
|
double randumb;
|
|
if (intensity == 0)
|
|
{
|
|
randumb = 0;
|
|
}
|
|
else
|
|
{
|
|
randumb = pr_torchflicker.GenRand_Real2() * (intensity * 2) - intensity;
|
|
}
|
|
return factor * (offset + randumb);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_SetupFrame
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_SetupFrame (AActor *actor)
|
|
{
|
|
if (actor == NULL)
|
|
{
|
|
I_Error ("Tried to render from a NULL actor.");
|
|
}
|
|
|
|
player_t *player = actor->player;
|
|
unsigned int newblend;
|
|
InterpolationViewer *iview;
|
|
bool unlinked = false;
|
|
|
|
if (player != NULL && player->mo == actor)
|
|
{ // [RH] Use camera instead of viewplayer
|
|
camera = player->camera;
|
|
if (camera == NULL)
|
|
{
|
|
camera = player->camera = player->mo;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
camera = actor;
|
|
}
|
|
|
|
if (camera == NULL)
|
|
{
|
|
I_Error ("You lost your body. Bad dehacked work is likely to blame.");
|
|
}
|
|
|
|
iview = FindPastViewer (camera);
|
|
|
|
int nowtic = I_GetTime (false);
|
|
if (iview->otic != -1 && nowtic > iview->otic)
|
|
{
|
|
iview->otic = nowtic;
|
|
iview->Old = iview->New;
|
|
}
|
|
|
|
if (player != NULL && gamestate != GS_TITLELEVEL &&
|
|
((player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)))
|
|
{
|
|
sector_t *oldsector = R_PointInSubsector(iview->Old.Pos)->sector;
|
|
// [RH] Use chasecam view
|
|
DVector3 campos;
|
|
DAngle camangle;
|
|
P_AimCamera (camera, campos, camangle, viewsector, unlinked); // fixme: This needs to translate the angle, too.
|
|
iview->New.Pos = campos;
|
|
iview->New.Angles.Yaw = camangle;
|
|
|
|
r_showviewer = true;
|
|
// Interpolating this is a very complicated thing because nothing keeps track of the aim camera's movement, so whenever we detect a portal transition
|
|
// it's probably best to just reset the interpolation for this move.
|
|
// Note that this can still cause problems with unusually linked portals
|
|
if (viewsector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256*256))
|
|
{
|
|
iview->otic = nowtic;
|
|
iview->Old = iview->New;
|
|
r_NoInterpolate = true;
|
|
}
|
|
ViewActorPos = campos;
|
|
}
|
|
else
|
|
{
|
|
ViewActorPos = iview->New.Pos = { camera->Pos().XY(), camera->player ? camera->player->viewz : camera->Z() + camera->GetCameraHeight() };
|
|
viewsector = camera->Sector;
|
|
r_showviewer = false;
|
|
}
|
|
iview->New.Angles = camera->Angles;
|
|
if (camera->player != 0)
|
|
{
|
|
player = camera->player;
|
|
}
|
|
|
|
if (iview->otic == -1 || r_NoInterpolate)
|
|
{
|
|
R_ResetViewInterpolation ();
|
|
iview->otic = nowtic;
|
|
}
|
|
|
|
r_TicFracF = I_GetTimeFrac (&r_FrameTime);
|
|
if (cl_capfps || r_NoInterpolate)
|
|
{
|
|
r_TicFracF = 1.;
|
|
}
|
|
R_InterpolateView (player, r_TicFracF, iview);
|
|
|
|
R_SetViewAngle ();
|
|
|
|
interpolator.DoInterpolations (r_TicFracF);
|
|
|
|
// Keep the view within the sector's floor and ceiling
|
|
if (viewsector->PortalBlocksMovement(sector_t::ceiling))
|
|
{
|
|
double theZ = viewsector->ceilingplane.ZatPoint(ViewPos) - 4;
|
|
if (ViewPos.Z > theZ)
|
|
{
|
|
ViewPos.Z = theZ;
|
|
}
|
|
}
|
|
|
|
if (viewsector->PortalBlocksMovement(sector_t::floor))
|
|
{
|
|
double theZ = viewsector->floorplane.ZatPoint(ViewPos) + 4;
|
|
if (ViewPos.Z < theZ)
|
|
{
|
|
ViewPos.Z = theZ;
|
|
}
|
|
}
|
|
|
|
if (!paused)
|
|
{
|
|
FQuakeJiggers jiggers;
|
|
|
|
memset(&jiggers, 0, sizeof(jiggers));
|
|
if (DEarthquake::StaticGetQuakeIntensities(camera, jiggers) > 0)
|
|
{
|
|
double quakefactor = r_quakeintensity;
|
|
DAngle an;
|
|
|
|
if (jiggers.RollIntensity != 0 || jiggers.RollWave != 0)
|
|
{
|
|
ViewRoll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave);
|
|
}
|
|
if (jiggers.RelIntensity.X != 0 || jiggers.RelOffset.X != 0)
|
|
{
|
|
an = camera->Angles.Yaw;
|
|
double power = QuakePower(quakefactor, jiggers.RelIntensity.X, jiggers.RelOffset.X);
|
|
ViewPos += an.ToVector(power);
|
|
}
|
|
if (jiggers.RelIntensity.Y != 0 || jiggers.RelOffset.Y != 0)
|
|
{
|
|
an = camera->Angles.Yaw + 90;
|
|
double power = QuakePower(quakefactor, jiggers.RelIntensity.Y, jiggers.RelOffset.Y);
|
|
ViewPos += an.ToVector(power);
|
|
}
|
|
// FIXME: Relative Z is not relative
|
|
if (jiggers.RelIntensity.Z != 0 || jiggers.RelOffset.Z != 0)
|
|
{
|
|
ViewPos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z);
|
|
}
|
|
if (jiggers.Intensity.X != 0 || jiggers.Offset.X != 0)
|
|
{
|
|
ViewPos.X += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X);
|
|
}
|
|
if (jiggers.Intensity.Y != 0 || jiggers.Offset.Y != 0)
|
|
{
|
|
ViewPos.Y += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y);
|
|
}
|
|
if (jiggers.Intensity.Z != 0 || jiggers.Offset.Z != 0)
|
|
{
|
|
ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z);
|
|
}
|
|
}
|
|
}
|
|
|
|
extralight = camera->player ? camera->player->extralight : 0;
|
|
|
|
// killough 3/20/98, 4/4/98: select colormap based on player status
|
|
// [RH] Can also select a blend
|
|
newblend = 0;
|
|
|
|
TArray<lightlist_t> &lightlist = viewsector->e->XFloor.lightlist;
|
|
if (lightlist.Size() > 0)
|
|
{
|
|
for(unsigned int i = 0; i < lightlist.Size(); i++)
|
|
{
|
|
secplane_t *plane;
|
|
int viewside;
|
|
plane = (i < lightlist.Size()-1) ? &lightlist[i+1].plane : &viewsector->floorplane;
|
|
viewside = plane->PointOnSide(ViewPos);
|
|
// Reverse the direction of the test if the plane was downward facing.
|
|
// We want to know if the view is above it, whatever its orientation may be.
|
|
if (plane->fC() < 0)
|
|
viewside = -viewside;
|
|
if (viewside > 0)
|
|
{
|
|
// 3d floor 'fog' is rendered as a blending value
|
|
PalEntry blendv = lightlist[i].blend;
|
|
|
|
// If no alpha is set, use 50%
|
|
if (blendv.a==0 && blendv!=0) blendv.a=128;
|
|
newblend = blendv.d;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const sector_t *s = viewsector->GetHeightSec();
|
|
if (s != NULL)
|
|
{
|
|
newblend = s->floorplane.PointOnSide(ViewPos) < 0
|
|
? s->bottommap
|
|
: s->ceilingplane.PointOnSide(ViewPos) < 0
|
|
? s->topmap
|
|
: s->midmap;
|
|
if (APART(newblend) == 0 && newblend >= numfakecmaps)
|
|
newblend = 0;
|
|
}
|
|
}
|
|
|
|
// [RH] Don't override testblend unless entering a sector with a
|
|
// blend different from the previous sector's. Same goes with
|
|
// NormalLight's maps pointer.
|
|
if (R_OldBlend != newblend)
|
|
{
|
|
R_OldBlend = newblend;
|
|
if (APART(newblend))
|
|
{
|
|
BaseBlendR = RPART(newblend);
|
|
BaseBlendG = GPART(newblend);
|
|
BaseBlendB = BPART(newblend);
|
|
BaseBlendA = APART(newblend) / 255.f;
|
|
NormalLight.Maps = realcolormaps;
|
|
}
|
|
else
|
|
{
|
|
NormalLight.Maps = realcolormaps + NUMCOLORMAPS*256*newblend;
|
|
BaseBlendR = BaseBlendG = BaseBlendB = 0;
|
|
BaseBlendA = 0.f;
|
|
}
|
|
}
|
|
|
|
Renderer->CopyStackedViewParameters();
|
|
Renderer->SetupFrame(player);
|
|
|
|
validcount++;
|
|
|
|
if (RenderTarget == screen && r_clearbuffer != 0)
|
|
{
|
|
int color;
|
|
int hom = r_clearbuffer;
|
|
|
|
if (hom == 3)
|
|
{
|
|
hom = ((I_FPSTime() / 128) & 1) + 1;
|
|
}
|
|
if (hom == 1)
|
|
{
|
|
color = GPalette.BlackIndex;
|
|
}
|
|
else if (hom == 2)
|
|
{
|
|
color = GPalette.WhiteIndex;
|
|
}
|
|
else if (hom == 4)
|
|
{
|
|
color = (I_FPSTime() / 32) & 255;
|
|
}
|
|
else
|
|
{
|
|
color = pr_hom();
|
|
}
|
|
Renderer->ClearBuffer(color);
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// FCanvasTextureInfo :: Add
|
|
//
|
|
// Assigns a camera to a canvas texture.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FCanvasTextureInfo::Add (AActor *viewpoint, FTextureID picnum, int fov)
|
|
{
|
|
FCanvasTextureInfo *probe;
|
|
FCanvasTexture *texture;
|
|
|
|
if (!picnum.isValid())
|
|
{
|
|
return;
|
|
}
|
|
texture = static_cast<FCanvasTexture *>(TexMan[picnum]);
|
|
if (!texture->bHasCanvas)
|
|
{
|
|
Printf ("%s is not a valid target for a camera\n", texture->Name.GetChars());
|
|
return;
|
|
}
|
|
|
|
// Is this texture already assigned to a camera?
|
|
for (probe = List; probe != NULL; probe = probe->Next)
|
|
{
|
|
if (probe->Texture == texture)
|
|
{
|
|
// Yes, change its assignment to this new camera
|
|
if (probe->Viewpoint != viewpoint || probe->FOV != fov)
|
|
{
|
|
texture->bFirstUpdate = true;
|
|
}
|
|
probe->Viewpoint = viewpoint;
|
|
probe->FOV = fov;
|
|
return;
|
|
}
|
|
}
|
|
// No, create a new assignment
|
|
probe = new FCanvasTextureInfo;
|
|
probe->Viewpoint = viewpoint;
|
|
probe->Texture = texture;
|
|
probe->PicNum = picnum;
|
|
probe->FOV = fov;
|
|
probe->Next = List;
|
|
texture->bFirstUpdate = true;
|
|
List = probe;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FCanvasTextureInfo :: UpdateAll
|
|
//
|
|
// Updates all canvas textures that were visible in the last frame.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FCanvasTextureInfo::UpdateAll ()
|
|
{
|
|
FCanvasTextureInfo *probe;
|
|
|
|
for (probe = List; probe != NULL; probe = probe->Next)
|
|
{
|
|
if (probe->Viewpoint != NULL && probe->Texture->bNeedsUpdate)
|
|
{
|
|
Renderer->RenderTextureView(probe->Texture, probe->Viewpoint, probe->FOV);
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FCanvasTextureInfo :: EmptyList
|
|
//
|
|
// Removes all camera->texture assignments.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FCanvasTextureInfo::EmptyList ()
|
|
{
|
|
FCanvasTextureInfo *probe, *next;
|
|
|
|
for (probe = List; probe != NULL; probe = next)
|
|
{
|
|
next = probe->Next;
|
|
probe->Texture->Unload();
|
|
delete probe;
|
|
}
|
|
List = NULL;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FCanvasTextureInfo :: Serialize
|
|
//
|
|
// Reads or writes the current set of mappings in an archive.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FCanvasTextureInfo::Serialize(FSerializer &arc)
|
|
{
|
|
if (arc.isWriting())
|
|
{
|
|
if (List != nullptr)
|
|
{
|
|
if (arc.BeginArray("canvastextures"))
|
|
{
|
|
FCanvasTextureInfo *probe;
|
|
|
|
for (probe = List; probe != nullptr; probe = probe->Next)
|
|
{
|
|
if (probe->Texture != nullptr && probe->Viewpoint != nullptr)
|
|
{
|
|
if (arc.BeginObject(nullptr))
|
|
{
|
|
arc("viewpoint", probe->Viewpoint)
|
|
("fov", probe->FOV)
|
|
("texture", probe->PicNum)
|
|
.EndObject();
|
|
}
|
|
}
|
|
}
|
|
arc.EndArray();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (arc.BeginArray("canvastextures"))
|
|
{
|
|
AActor *viewpoint;
|
|
int fov;
|
|
FTextureID picnum;
|
|
while (arc.BeginObject(nullptr))
|
|
{
|
|
arc("viewpoint", viewpoint)
|
|
("fov", fov)
|
|
("texture", picnum)
|
|
.EndObject();
|
|
Add(viewpoint, picnum, fov);
|
|
}
|
|
arc.EndArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FCanvasTextureInfo :: Mark
|
|
//
|
|
// Marks all viewpoints in the list for the collector.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FCanvasTextureInfo::Mark()
|
|
{
|
|
for (FCanvasTextureInfo *probe = List; probe != NULL; probe = probe->Next)
|
|
{
|
|
GC::Mark(probe->Viewpoint);
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// CVAR transsouls
|
|
//
|
|
// How translucent things drawn with STYLE_SoulTrans are. Normally, only
|
|
// Lost Souls have this render style.
|
|
// Values less than 0.25 will automatically be set to
|
|
// 0.25 to ensure some degree of visibility. Likewise, values above 1.0 will
|
|
// be set to 1.0, because anything higher doesn't make sense.
|
|
//
|
|
//==========================================================================
|
|
|
|
CUSTOM_CVAR(Float, transsouls, 0.75f, CVAR_ARCHIVE)
|
|
{
|
|
if (self < 0.25f)
|
|
{
|
|
self = 0.25f;
|
|
}
|
|
else if (self > 1.f)
|
|
{
|
|
self = 1.f;
|
|
}
|
|
}
|