gzdoom/src/r_main.cpp

1117 lines
28 KiB
C++
Raw Normal View History

2016-03-01 15:47:10 +00:00
// 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_local.h"
#include "r_plane.h"
#include "r_bsp.h"
#include "r_segs.h"
#include "r_3dfloors.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_data/colormaps.h"
#include "farchive.h"
// MACROS ------------------------------------------------------------------
#if 0
#define TEST_X 32343794
#define TEST_Y 111387517
#define TEST_Z 2164524
#define TEST_ANGLE 2468347904
#endif
2016-03-01 15:47:10 +00:00
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void R_SpanInitData ();
void R_DeinitSprites();
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void R_ShutdownRenderer();
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern short *openings;
extern bool r_fakingunderwater;
extern "C" int fuzzviewheight;
extern subsector_t *InSubsector;
extern bool r_showviewer;
2016-03-01 15:47:10 +00:00
// PRIVATE DATA DECLARATIONS -----------------------------------------------
static double CurrentVisibility = 8.f;
static double MaxVisForWall;
static double MaxVisForFloor;
2016-03-01 15:47:10 +00:00
bool r_dontmaplines;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CVAR (String, r_viewsize, "", CVAR_NOSET)
CVAR (Bool, r_shadercolormaps, true, CVAR_ARCHIVE)
double r_BaseVisibility;
double r_WallVisibility;
double r_FloorVisibility;
2016-03-01 15:47:10 +00:00
float r_TiltVisibility;
double r_SpriteVisibility;
double r_ParticleVisibility;
2016-03-01 15:47:10 +00:00
double GlobVis;
2016-03-01 15:47:10 +00:00
fixed_t viewingrangerecip;
2016-04-14 17:40:14 +00:00
double FocalLengthX;
double FocalLengthY;
2016-03-01 15:47:10 +00:00
FDynamicColormap*basecolormap; // [RH] colormap currently drawing with
int fixedlightlev;
lighttable_t *fixedcolormap;
FSpecialColormap *realfixedcolormap;
2016-04-14 17:40:14 +00:00
double WallTMapScale2;
2016-03-01 15:47:10 +00:00
bool bRenderingToCanvas; // [RH] True if rendering to a special canvas
2016-04-14 17:40:14 +00:00
double globaluclip, globaldclip;
double CenterX, CenterY;
double YaspectMul;
double BaseYaspectMul; // yaspectmul without a forced aspect ratio
double IYaspectMul;
double InvZtoScale;
2016-03-01 15:47:10 +00:00
// just for profiling purposes
int linecount;
int loopcount;
//
// precalculated math tables
//
// The xtoviewangleangle[] table maps a screen pixel
// to the lowest viewangle that maps back to x ranges
// from clipangle to -clipangle.
angle_t xtoviewangle[MAXWIDTH+1];
bool foggy; // [RH] ignore extralight and fullbright?
int r_actualextralight;
void (*colfunc) (void);
void (*basecolfunc) (void);
void (*fuzzcolfunc) (void);
void (*transcolfunc) (void);
void (*spanfunc) (void);
void (*hcolfunc_pre) (void);
void (*hcolfunc_post1) (int hx, int sx, int yl, int yh);
void (*hcolfunc_post2) (int hx, int sx, int yl, int yh);
void (*hcolfunc_post4) (int sx, int yl, int yh);
2016-03-01 15:47:10 +00:00
cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static int lastcenteryfrac;
// CODE --------------------------------------------------------------------
//==========================================================================
//
// viewangletox
//
// Used solely for construction the xtoviewangle table.
//
//==========================================================================
static inline int viewangletox(int i)
{
if (finetangent[i] > FRACUNIT*2)
{
return -1;
}
else if (finetangent[i] < -FRACUNIT*2)
{
return viewwidth+1;
}
else
{
2016-04-14 17:40:14 +00:00
double t = FIXED2DBL(finetangent[i]) * FocalLengthX;
return clamp(xs_CeilToInt(CenterX - t), -1, viewwidth+1);
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
// R_InitTextureMapping
//
//==========================================================================
void R_InitTextureMapping ()
{
int i, x;
// Calc focallength so FieldOfView fineangles covers viewwidth.
2016-04-14 17:40:14 +00:00
FocalLengthX = CenterX / FocalTangent;
FocalLengthY = FocalLengthX * YaspectMul;
2016-03-01 15:47:10 +00:00
// This is 1/FocalTangent before the widescreen extension of FOV.
viewingrangerecip = DivScale32(1, finetangent[FINEANGLES/4+(FieldOfView/2)]);
// [RH] Do not generate viewangletox, because texture mapping is no
// longer done with trig, so it's not needed.
// Now generate xtoviewangle for sky texture mapping.
// We do this with a hybrid approach: The center 90 degree span is
// constructed as per the original code:
// Scan xtoviewangle to find the smallest view angle that maps to x.
// (viewangletox is sorted in non-increasing order.)
// This reduces the chances of "doubling-up" of texture columns in
// the drawn sky texture.
// The remaining arcs are done with tantoangle instead.
2016-04-14 17:40:14 +00:00
const int t1 = MAX(int(CenterX - FocalLengthX), 0);
const int t2 = MIN(int(CenterX + FocalLengthX), viewwidth);
const fixed_t dfocus = FLOAT2FIXED(FocalLengthX) >> DBITS;
2016-03-01 15:47:10 +00:00
for (i = 0, x = t2; x >= t1; --x)
{
2016-04-14 17:40:14 +00:00
while (viewangletox(i) > x)
2016-03-01 15:47:10 +00:00
{
++i;
}
xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANGLE_90;
}
for (x = t2 + 1; x <= viewwidth; ++x)
{
xtoviewangle[x] = ANGLE_270 + tantoangle[dfocus / (x - centerx)];
}
for (x = 0; x < t1; ++x)
{
xtoviewangle[x] = (angle_t)(-(signed)xtoviewangle[viewwidth - x]);
}
}
//==========================================================================
//
// R_SetVisibility
//
// Changes how rapidly things get dark with distance
//
//==========================================================================
void R_SetVisibility(double vis)
2016-03-01 15:47:10 +00:00
{
// Allow negative visibilities, just for novelty's sake
vis = clamp(vis, -204.7, 204.7); // (205 and larger do not work in 5:4 aspect ratio)
2016-03-01 15:47:10 +00:00
CurrentVisibility = vis;
if (FocalTangent == 0 || FocalLengthY == 0)
{ // If r_visibility is called before the renderer is all set up, don't
// divide by zero. This will be called again later, and the proper
// values can be initialized then.
return;
}
r_BaseVisibility = vis;
2016-03-01 15:47:10 +00:00
// Prevent overflow on walls
if (r_BaseVisibility < 0 && r_BaseVisibility < -MaxVisForWall)
r_WallVisibility = -MaxVisForWall;
else if (r_BaseVisibility > 0 && r_BaseVisibility > MaxVisForWall)
r_WallVisibility = MaxVisForWall;
else
r_WallVisibility = r_BaseVisibility;
r_WallVisibility = (InvZtoScale * SCREENWIDTH*BaseRatioSizes[WidescreenRatio][1] /
(viewwidth*SCREENHEIGHT*3)) * (r_WallVisibility * FocalTangent);
2016-03-01 15:47:10 +00:00
// Prevent overflow on floors/ceilings. Note that the calculation of
// MaxVisForFloor means that planes less than two units from the player's
// view could still overflow, but there is no way to totally eliminate
// that while still using fixed point math.
if (r_BaseVisibility < 0 && r_BaseVisibility < -MaxVisForFloor)
r_FloorVisibility = -MaxVisForFloor;
else if (r_BaseVisibility > 0 && r_BaseVisibility > MaxVisForFloor)
r_FloorVisibility = MaxVisForFloor;
else
r_FloorVisibility = r_BaseVisibility;
r_FloorVisibility = 160.0 * r_FloorVisibility / FocalLengthY;
2016-03-01 15:47:10 +00:00
r_TiltVisibility = float(vis * FocalTangent * (16.f * 320.f) / viewwidth);
2016-03-01 15:47:10 +00:00
r_SpriteVisibility = r_WallVisibility;
}
//==========================================================================
//
// R_GetVisibility
//
//==========================================================================
double R_GetVisibility()
2016-03-01 15:47:10 +00:00
{
return CurrentVisibility;
}
//==========================================================================
//
// CCMD r_visibility
//
// Controls how quickly light ramps across a 1/z range. Set this, and it
// sets all the r_*Visibility variables (except r_SkyVisibilily, which is
// currently unused).
//
//==========================================================================
CCMD (r_visibility)
{
if (argv.argc() < 2)
{
Printf ("Visibility is %g\n", R_GetVisibility());
}
else if (!netgame)
{
R_SetVisibility(atof(argv[1]));
2016-03-01 15:47:10 +00:00
}
else
{
Printf ("Visibility cannot be changed in net games.\n");
}
}
//==========================================================================
//
// R_SetWindow
//
//==========================================================================
void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio)
{
int virtheight, virtwidth, virtwidth2, virtheight2;
if (!bRenderingToCanvas)
{ // Set r_viewsize cvar to reflect the current view size
UCVarValue value;
char temp[16];
mysnprintf (temp, countof(temp), "%d x %d", viewwidth, viewheight);
value.String = temp;
r_viewsize.ForceSet (value, CVAR_String);
}
fuzzviewheight = viewheight - 2; // Maximum row the fuzzer can draw to
halfviewwidth = (viewwidth >> 1) - 1;
lastcenteryfrac = 1<<30;
2016-04-14 17:40:14 +00:00
CenterX = centerx;
CenterY = centery;
2016-03-01 15:47:10 +00:00
virtwidth = virtwidth2 = fullWidth;
virtheight = virtheight2 = fullHeight;
if (Is54Aspect(trueratio))
2016-03-01 15:47:10 +00:00
{
virtheight2 = virtheight2 * BaseRatioSizes[trueratio][3] / 48;
}
else
{
virtwidth2 = virtwidth2 * BaseRatioSizes[trueratio][3] / 48;
}
if (Is54Aspect(WidescreenRatio))
2016-03-01 15:47:10 +00:00
{
virtheight = virtheight * BaseRatioSizes[WidescreenRatio][3] / 48;
}
else
{
virtwidth = virtwidth * BaseRatioSizes[WidescreenRatio][3] / 48;
}
2016-04-14 17:40:14 +00:00
BaseYaspectMul = 320.0 * virtheight2 / (r_Yaspect * virtwidth2);
YaspectMul = 320.0 * virtheight / (r_Yaspect * virtwidth);
IYaspectMul = (double)virtwidth * r_Yaspect / 320.0 / virtheight;
InvZtoScale = YaspectMul * CenterX;
2016-03-01 15:47:10 +00:00
2016-04-14 17:40:14 +00:00
WallTMapScale2 = IYaspectMul * (1 << 18) / CenterX;
2016-03-01 15:47:10 +00:00
// psprite scales
pspritexscale = centerxwide / 160.0;
pspriteyscale = pspritexscale * YaspectMul;
pspritexiscale = 1 / pspritexscale;
2016-03-01 15:47:10 +00:00
// thing clipping
clearbufshort (screenheightarray, viewwidth, (short)viewheight);
R_InitTextureMapping ();
MaxVisForWall = (InvZtoScale * (SCREENWIDTH*r_Yaspect) /
(viewwidth*SCREENHEIGHT * FocalTangent));
MaxVisForWall = 32767.0 / MaxVisForWall;
MaxVisForFloor = 32767.0 / (viewheight * FocalLengthY / 160);
2016-03-01 15:47:10 +00:00
// Reset r_*Visibility vars
R_SetVisibility(R_GetVisibility());
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// CVAR r_columnmethod
//
// Selects which version of the seg renderers to use.
//
//==========================================================================
CUSTOM_CVAR (Int, r_columnmethod, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (self != 0 && self != 1)
{
self = 1;
}
else
{ // Trigger the change
setsizeneeded = true;
}
}
//==========================================================================
//
// R_Init
//
//==========================================================================
void R_InitRenderer()
{
atterm(R_ShutdownRenderer);
// viewwidth / viewheight are set by the defaults
clearbufshort (zeroarray, MAXWIDTH, 0);
R_InitPlanes ();
R_InitShadeMaps();
R_InitColumnDrawers ();
colfunc = basecolfunc = R_DrawColumn;
fuzzcolfunc = R_DrawFuzzColumn;
transcolfunc = R_DrawTranslatedColumn;
spanfunc = R_DrawSpan;
// [RH] Horizontal column drawers
hcolfunc_pre = R_DrawColumnHoriz;
hcolfunc_post1 = rt_map1col;
hcolfunc_post4 = rt_map4cols;
}
//==========================================================================
//
// R_ShutdownRenderer
//
//==========================================================================
static void R_ShutdownRenderer()
{
R_DeinitSprites();
R_DeinitPlanes();
// Free openings
if (openings != NULL)
{
M_Free (openings);
openings = NULL;
}
// Free drawsegs
if (drawsegs != NULL)
{
M_Free (drawsegs);
drawsegs = NULL;
}
}
//==========================================================================
//
// R_CopyStackedViewParameters
//
//==========================================================================
void R_CopyStackedViewParameters()
{
2016-04-14 17:40:14 +00:00
stacked_viewpos = ViewPos;
stacked_angle = ViewAngle;
2016-03-01 15:47:10 +00:00
stacked_extralight = extralight;
stacked_visibility = R_GetVisibility();
}
//==========================================================================
//
// R_SetupColormap
//
// Sets up special fixed colormaps
//
//==========================================================================
void R_SetupColormap(player_t *player)
{
realfixedcolormap = NULL;
fixedcolormap = NULL;
fixedlightlev = -1;
if (player != NULL && camera == player->mo)
{
if (player->fixedcolormap >= 0 && player->fixedcolormap < (int)SpecialColormaps.Size())
{
realfixedcolormap = &SpecialColormaps[player->fixedcolormap];
if (RenderTarget == screen && (DFrameBuffer *)screen->Accel2D && r_shadercolormaps)
{
// Render everything fullbright. The copy to video memory will
// apply the special colormap, so it won't be restricted to the
// palette.
fixedcolormap = realcolormaps;
}
else
{
fixedcolormap = SpecialColormaps[player->fixedcolormap].Colormap;
}
}
else if (player->fixedlightlevel >= 0 && player->fixedlightlevel < NUMCOLORMAPS)
{
fixedlightlev = player->fixedlightlevel * 256;
}
}
// [RH] Inverse light for shooting the Sigil
if (fixedcolormap == NULL && extralight == INT_MIN)
{
fixedcolormap = SpecialColormaps[INVERSECOLORMAP].Colormap;
extralight = 0;
}
}
//==========================================================================
//
// R_SetupFreelook
//
// [RH] freelook stuff
//
//==========================================================================
void R_SetupFreelook()
{
{
2016-04-14 17:40:14 +00:00
double dy;
2016-03-01 15:47:10 +00:00
if (camera != NULL)
{
2016-04-14 17:40:14 +00:00
dy = FocalLengthY * (-ViewPitch).Tan();
2016-03-01 15:47:10 +00:00
}
else
{
dy = 0;
}
2016-04-14 17:40:14 +00:00
CenterY = (viewheight / 2.0) + dy;
centery = xs_ToInt(CenterY);
globaluclip = -CenterY / InvZtoScale;
globaldclip = (viewheight - CenterY) / InvZtoScale;
2016-03-01 15:47:10 +00:00
//centeryfrac &= 0xffff0000;
int e, i;
i = 0;
e = viewheight;
2016-04-14 17:40:14 +00:00
float focus = float(FocalLengthY);
float den;
float cy = float(CenterY);
2016-03-01 15:47:10 +00:00
if (i < centery)
{
2016-04-14 17:40:14 +00:00
den = cy - i - 0.5f;
2016-03-01 15:47:10 +00:00
if (e <= centery)
{
do {
2016-04-14 17:40:14 +00:00
yslope[i] = FLOAT2FIXED(focus / den);
den -= 1;
2016-03-01 15:47:10 +00:00
} while (++i < e);
}
else
{
do {
2016-04-14 17:40:14 +00:00
yslope[i] = FLOAT2FIXED(focus / den);
den -= 1;
2016-03-01 15:47:10 +00:00
} while (++i < centery);
2016-04-14 17:40:14 +00:00
den = i - cy + 0.5f;
2016-03-01 15:47:10 +00:00
do {
2016-04-14 17:40:14 +00:00
yslope[i] = FLOAT2FIXED(focus / den);
den += 1;
2016-03-01 15:47:10 +00:00
} while (++i < e);
}
}
else
{
2016-04-14 17:40:14 +00:00
den = i - cy + 0.5f;
2016-03-01 15:47:10 +00:00
do {
2016-04-14 17:40:14 +00:00
yslope[i] = FLOAT2FIXED(focus / den);
den += 1;
2016-03-01 15:47:10 +00:00
} while (++i < e);
}
}
}
//==========================================================================
//
// R_EnterPortal
//
// [RH] Draw the reflection inside a mirror
// [ZZ] Merged with portal code, originally called R_EnterMirror
//
//==========================================================================
CVAR(Int, r_portal_recursions, 4, CVAR_ARCHIVE)
CVAR(Bool, r_highlight_portals, false, CVAR_ARCHIVE)
void R_HighlightPortal (PortalDrawseg* pds)
{
// [ZZ] NO OVERFLOW CHECKS HERE
// I believe it won't break. if it does, blame me. :(
BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 255, 0, 0, 0, 255);
BYTE* pixels = RenderTarget->GetBuffer();
// top edge
for (int x = pds->x1; x < pds->x2; x++)
{
if (x < 0 || x >= RenderTarget->GetWidth())
continue;
int p = x - pds->x1;
int Ytop = pds->ceilingclip[p];
int Ybottom = pds->floorclip[p];
if (x == pds->x1 || x == pds->x2-1)
{
RenderTarget->DrawLine(x, Ytop, x, Ybottom+1, color, 0);
continue;
}
int YtopPrev = pds->ceilingclip[p-1];
int YbottomPrev = pds->floorclip[p-1];
if (abs(Ytop-YtopPrev) > 1)
RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0);
else *(pixels + Ytop * RenderTarget->GetPitch() + x) = color;
if (abs(Ybottom-YbottomPrev) > 1)
RenderTarget->DrawLine(x, YbottomPrev, x, Ybottom, color, 0);
else *(pixels + Ybottom * RenderTarget->GetPitch() + x) = color;
}
}
void R_EnterPortal (PortalDrawseg* pds, int depth)
{
// [ZZ] check depth. fill portal with black if it's exceeding the visual recursion limit, and continue like nothing happened.
if (depth >= r_portal_recursions)
{
BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 0, 0, 0, 0, 255);
int spacing = RenderTarget->GetPitch();
for (int x = pds->x1; x < pds->x2; x++)
{
if (x < 0 || x >= RenderTarget->GetWidth())
continue;
int Ytop = pds->ceilingclip[x-pds->x1];
int Ybottom = pds->floorclip[x-pds->x1];
BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing;
for (int y = Ytop; y <= Ybottom; y++)
{
*dest = color;
dest += spacing;
}
}
if (r_highlight_portals)
R_HighlightPortal(pds);
return;
}
2016-04-14 17:40:14 +00:00
DAngle startang = ViewAngle;
DVector3 startpos = ViewPos;
DVector3 savedpath[2] = { ViewPath[0], ViewPath[1] };
ActorRenderFlags savedvisibility = camera? camera->renderflags & RF_INVISIBLE : ActorRenderFlags::FromInt(0);
2016-03-01 15:47:10 +00:00
CurrentPortalUniq++;
unsigned int portalsAtStart = WallPortals.Size ();
if (pds->mirror)
{
//vertex_t *v1 = ds->curline->v1;
vertex_t *v1 = pds->src->v1;
// Reflect the current view behind the mirror.
if (pds->src->Delta().X == 0)
2016-03-01 15:47:10 +00:00
{ // vertical mirror
2016-04-14 17:40:14 +00:00
ViewPos.X = v1->fX() - startpos.X + v1->fX();
2016-03-01 15:47:10 +00:00
}
else if (pds->src->Delta().Y == 0)
2016-03-01 15:47:10 +00:00
{ // horizontal mirror
2016-04-14 17:40:14 +00:00
ViewPos.Y = v1->fY() - startpos.Y + v1->fY();
2016-03-01 15:47:10 +00:00
}
else
2016-04-14 17:40:14 +00:00
{ // any mirror
2016-03-01 15:47:10 +00:00
vertex_t *v2 = pds->src->v2;
double dx = v2->fX() - v1->fX();
double dy = v2->fY() - v1->fY();
double x1 = v1->fX();
double y1 = v1->fY();
2016-04-14 17:40:14 +00:00
double x = startpos.X;
double y = startpos.Y;
2016-03-01 15:47:10 +00:00
// the above two cases catch len == 0
double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy);
2016-04-14 17:40:14 +00:00
ViewPos.X = (x1 + r * dx)*2 - x;
ViewPos.Y = (y1 + r * dy)*2 - y;
2016-03-01 15:47:10 +00:00
}
2016-04-14 17:40:14 +00:00
ViewAngle = pds->src->Delta().Angle() - startang;
2016-03-01 15:47:10 +00:00
}
else
{
2016-04-14 17:40:14 +00:00
DVector3 view(ViewPos);
DAngle va = ViewAngle;
P_TranslatePortalXY(pds->src, view.X, view.Y);
P_TranslatePortalZ(pds->src, view.Z);
P_TranslatePortalAngle(pds->src, va);
P_TranslatePortalXY(pds->src, ViewPath[0].X, ViewPath[0].Y);
P_TranslatePortalXY(pds->src, ViewPath[1].X, ViewPath[1].Y);
if (!r_showviewer && camera)
{
double distp = (ViewPath[0] - ViewPath[1]).Length();
if (distp > EQUAL_EPSILON)
{
double dist1 = (view - ViewPath[0]).Length();
double dist2 = (view - ViewPath[1]).Length();
if (dist1 + dist2 < distp + 1)
{
camera->renderflags |= RF_INVISIBLE;
}
}
}
2016-04-14 17:40:14 +00:00
ViewPos = view;
ViewAngle = va;
2016-03-01 15:47:10 +00:00
}
2016-04-14 17:40:14 +00:00
ViewSin = ViewAngle.Sin();
ViewCos = ViewAngle.Cos();
2016-03-01 15:47:10 +00:00
2016-04-14 17:40:14 +00:00
ViewTanSin = FocalTangent * ViewSin;
ViewTanCos = FocalTangent * ViewCos;
2016-03-01 15:47:10 +00:00
R_CopyStackedViewParameters();
validcount++;
PortalDrawseg* prevpds = CurrentPortal;
CurrentPortal = pds;
R_ClearPlanes (false);
R_ClearClipSegs (pds->x1, pds->x2);
WindowLeft = pds->x1;
WindowRight = pds->x2;
// RF_XFLIP should be removed before calling the root function
int prevmf = MirrorFlags;
if (pds->mirror)
{
if (MirrorFlags & RF_XFLIP)
MirrorFlags &= ~RF_XFLIP;
else MirrorFlags |= RF_XFLIP;
}
// some portals have height differences, account for this here
R_3D_EnterSkybox(); // push 3D floor height map
CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes.
// first pass, set clipping
memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len*sizeof(*ceilingclip));
memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->len*sizeof(*floorclip));
InSubsector = NULL;
2016-03-01 15:47:10 +00:00
R_RenderBSPNode (nodes + numnodes - 1);
R_3D_ResetClip(); // reset clips (floor/ceiling)
if (!savedvisibility && camera) camera->renderflags &= ~RF_INVISIBLE;
2016-03-01 15:47:10 +00:00
PlaneCycles.Clock();
R_DrawPlanes ();
R_DrawPortals ();
2016-03-01 15:47:10 +00:00
PlaneCycles.Unclock();
2016-04-14 17:40:14 +00:00
double vzp = ViewPos.Z;
2016-03-01 15:47:10 +00:00
int prevuniq = CurrentPortalUniq;
// depth check is in another place right now
unsigned int portalsAtEnd = WallPortals.Size ();
for (; portalsAtStart < portalsAtEnd; portalsAtStart++)
{
R_EnterPortal (&WallPortals[portalsAtStart], depth + 1);
}
int prevuniq2 = CurrentPortalUniq;
CurrentPortalUniq = prevuniq;
NetUpdate();
MaskedCycles.Clock(); // [ZZ] count sprites in portals/mirrors along with normal ones.
R_DrawMasked (); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal.
MaskedCycles.Unclock();
NetUpdate();
R_3D_LeaveSkybox(); // pop 3D floor height map
CurrentPortalUniq = prevuniq2;
// draw a red line around a portal if it's being highlighted
if (r_highlight_portals)
R_HighlightPortal(pds);
CurrentPortal = prevpds;
MirrorFlags = prevmf;
2016-04-14 17:40:14 +00:00
ViewAngle = startang;
ViewPos = startpos;
ViewPath[0] = savedpath[0];
ViewPath[1] = savedpath[1];
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// R_SetupBuffer
//
// Precalculate all row offsets and fuzz table.
//
//==========================================================================
void R_SetupBuffer ()
{
static BYTE *lastbuff = NULL;
int pitch = RenderTarget->GetPitch();
BYTE *lineptr = RenderTarget->GetBuffer() + viewwindowy*pitch + viewwindowx;
if (dc_pitch != pitch || lineptr != lastbuff)
{
if (dc_pitch != pitch)
{
dc_pitch = pitch;
R_InitFuzzTable (pitch);
#if defined(X86_ASM) || defined(X64_ASM)
ASM_PatchPitch ();
#endif
}
dc_destorg = lineptr;
for (int i = 0; i < RenderTarget->GetHeight(); i++)
{
ylookup[i] = i * pitch;
}
}
}
//==========================================================================
//
// R_RenderActorView
//
//==========================================================================
void R_RenderActorView (AActor *actor, bool dontmaplines)
{
WallCycles.Reset();
PlaneCycles.Reset();
MaskedCycles.Reset();
WallScanCycles.Reset();
fakeActive = 0; // kg3D - reset fake floor indicator
R_3D_ResetClip(); // reset clips (floor/ceiling)
R_SetupBuffer ();
R_SetupFrame (actor);
// Clear buffers.
R_ClearClipSegs (0, viewwidth);
R_ClearDrawSegs ();
R_ClearPlanes (true);
R_ClearSprites ();
NetUpdate ();
// [RH] Show off segs if r_drawflat is 1
if (r_drawflat)
{
hcolfunc_pre = R_FillColumnHorizP;
hcolfunc_post1 = rt_copy1col;
hcolfunc_post4 = rt_copy4cols;
colfunc = R_FillColumnP;
spanfunc = R_FillSpan;
}
else
{
hcolfunc_pre = R_DrawColumnHoriz;
hcolfunc_post1 = rt_map1col;
hcolfunc_post4 = rt_map4cols;
colfunc = basecolfunc;
spanfunc = R_DrawSpan;
}
WindowLeft = 0;
WindowRight = viewwidth;
MirrorFlags = 0;
CurrentPortal = NULL;
CurrentPortalUniq = 0;
r_dontmaplines = dontmaplines;
// [RH] Hack to make windows into underwater areas possible
r_fakingunderwater = false;
// [RH] Setup particles for this frame
P_FindParticleSubsectors ();
WallCycles.Clock();
ActorRenderFlags savedflags = camera->renderflags;
// Never draw the player unless in chasecam mode
if (!r_showviewer)
{
camera->renderflags |= RF_INVISIBLE;
}
// Link the polyobjects right before drawing the scene to reduce the amounts of calls to this function
PO_LinkToSubsectors();
InSubsector = NULL;
2016-03-01 15:47:10 +00:00
R_RenderBSPNode (nodes + numnodes - 1); // The head node is the last node output.
R_3D_ResetClip(); // reset clips (floor/ceiling)
camera->renderflags = savedflags;
WallCycles.Unclock();
NetUpdate ();
if (viewactive)
{
PlaneCycles.Clock();
R_DrawPlanes ();
R_DrawPortals ();
2016-03-01 15:47:10 +00:00
PlaneCycles.Unclock();
// [RH] Walk through mirrors
// [ZZ] Merged with portals
size_t lastportal = WallPortals.Size();
for (unsigned int i = 0; i < lastportal; i++)
{
R_EnterPortal(&WallPortals[i], 0);
}
CurrentPortal = NULL;
CurrentPortalUniq = 0;
NetUpdate ();
MaskedCycles.Clock();
R_DrawMasked ();
MaskedCycles.Unclock();
NetUpdate ();
}
WallPortals.Clear ();
interpolator.RestoreInterpolations ();
R_SetupBuffer ();
// If we don't want shadered colormaps, NULL it now so that the
// copy to the screen does not use a special colormap shader.
if (!r_shadercolormaps)
{
realfixedcolormap = NULL;
}
}
//==========================================================================
//
// R_RenderViewToCanvas
//
// Pre: Canvas is already locked.
//
//==========================================================================
void R_RenderViewToCanvas (AActor *actor, DCanvas *canvas,
int x, int y, int width, int height, bool dontmaplines)
{
const bool savedviewactive = viewactive;
viewwidth = width;
RenderTarget = canvas;
bRenderingToCanvas = true;
R_SetWindow (12, width, height, height);
viewwindowx = x;
viewwindowy = y;
viewactive = true;
R_RenderActorView (actor, dontmaplines);
RenderTarget = screen;
bRenderingToCanvas = false;
R_ExecuteSetViewSize ();
screen->Lock (true);
R_SetupBuffer ();
screen->Unlock ();
viewactive = savedviewactive;
}
//==========================================================================
//
// R_MultiresInit
//
// Called from V_SetResolution()
//
//==========================================================================
void R_MultiresInit ()
{
R_PlaneInitData ();
}
//==========================================================================
//
// STAT fps
//
// Displays statistics about rendering times
//
//==========================================================================
extern cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles;
extern cycle_t FrameCycles;
ADD_STAT (fps)
{
FString out;
out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms",
FrameCycles.TimeMS(), WallCycles.TimeMS(), PlaneCycles.TimeMS(), MaskedCycles.TimeMS());
return out;
}
static double f_acc, w_acc,p_acc,m_acc;
static int acc_c;
ADD_STAT (fps_accumulated)
{
f_acc += FrameCycles.TimeMS();
w_acc += WallCycles.TimeMS();
p_acc += PlaneCycles.TimeMS();
m_acc += MaskedCycles.TimeMS();
acc_c++;
FString out;
out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms %d counts",
f_acc/acc_c, w_acc/acc_c, p_acc/acc_c, m_acc/acc_c, acc_c);
Printf(PRINT_LOG, "%s\n", out.GetChars());
return out;
}
//==========================================================================
//
// STAT wallcycles
//
// Displays the minimum number of cycles spent drawing walls
//
//==========================================================================
static double bestwallcycles = HUGE_VAL;
ADD_STAT (wallcycles)
{
FString out;
double cycles = WallCycles.Time();
if (cycles && cycles < bestwallcycles)
bestwallcycles = cycles;
out.Format ("%g", bestwallcycles);
return out;
}
//==========================================================================
//
// CCMD clearwallcycles
//
// Resets the count of minimum wall drawing cycles
//
//==========================================================================
CCMD (clearwallcycles)
{
bestwallcycles = HUGE_VAL;
}
#if 1
// To use these, also uncomment the clock/unclock in wallscan
static double bestscancycles = HUGE_VAL;
ADD_STAT (scancycles)
{
FString out;
double scancycles = WallScanCycles.Time();
if (scancycles && scancycles < bestscancycles)
bestscancycles = scancycles;
out.Format ("%g", bestscancycles);
return out;
}
CCMD (clearscancycles)
{
bestscancycles = HUGE_VAL;
}
#endif