mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-05 20:41:02 +00:00
1101 lines
28 KiB
C++
1101 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_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
|
|
|
|
// 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;
|
|
|
|
|
|
// PRIVATE DATA DECLARATIONS -----------------------------------------------
|
|
|
|
static float CurrentVisibility = 8.f;
|
|
static fixed_t MaxVisForWall;
|
|
static fixed_t MaxVisForFloor;
|
|
bool r_dontmaplines;
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
CVAR (String, r_viewsize, "", CVAR_NOSET)
|
|
CVAR (Bool, r_shadercolormaps, true, CVAR_ARCHIVE)
|
|
|
|
fixed_t r_BaseVisibility;
|
|
fixed_t r_WallVisibility;
|
|
fixed_t r_FloorVisibility;
|
|
float r_TiltVisibility;
|
|
fixed_t r_SpriteVisibility;
|
|
fixed_t r_ParticleVisibility;
|
|
fixed_t r_SkyVisibility;
|
|
|
|
fixed_t GlobVis;
|
|
fixed_t viewingrangerecip;
|
|
fixed_t FocalLengthX;
|
|
fixed_t FocalLengthY;
|
|
float FocalLengthXfloat;
|
|
FDynamicColormap*basecolormap; // [RH] colormap currently drawing with
|
|
int fixedlightlev;
|
|
lighttable_t *fixedcolormap;
|
|
FSpecialColormap *realfixedcolormap;
|
|
float WallTMapScale2;
|
|
|
|
|
|
bool bRenderingToCanvas; // [RH] True if rendering to a special canvas
|
|
fixed_t globaluclip, globaldclip;
|
|
fixed_t centerxfrac;
|
|
fixed_t centeryfrac;
|
|
fixed_t yaspectmul;
|
|
fixed_t baseyaspectmul; // yaspectmul without a forced aspect ratio
|
|
float iyaspectmulfloat;
|
|
fixed_t InvZtoScale;
|
|
|
|
// 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 (STACK_ARGS *hcolfunc_post4) (int sx, int yl, int yh);
|
|
|
|
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
|
|
{
|
|
int t = FixedMul(finetangent[i], FocalLengthX);
|
|
t = (centerxfrac - t + FRACUNIT-1) >> FRACBITS;
|
|
return clamp(t, -1, viewwidth+1);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_InitTextureMapping
|
|
//
|
|
//==========================================================================
|
|
|
|
void R_InitTextureMapping ()
|
|
{
|
|
int i, x;
|
|
|
|
// Calc focallength so FieldOfView fineangles covers viewwidth.
|
|
FocalLengthX = FixedDiv (centerxfrac, FocalTangent);
|
|
FocalLengthY = Scale (centerxfrac, yaspectmul, FocalTangent);
|
|
FocalLengthXfloat = (float)FocalLengthX / 65536.f;
|
|
|
|
// 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.
|
|
|
|
const int t1 = MAX<int>(centerx - (FocalLengthX >> FRACBITS), 0);
|
|
const int t2 = MIN<int>(centerx + (FocalLengthX >> FRACBITS), viewwidth);
|
|
const fixed_t dfocus = FocalLengthX >> DBITS;
|
|
|
|
for (i = 0, x = t2; x >= t1; --x)
|
|
{
|
|
while(viewangletox(i) > x)
|
|
{
|
|
++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 (float vis)
|
|
{
|
|
// Allow negative visibilities, just for novelty's sake
|
|
vis = clamp (vis, -204.7f, 204.7f); // (205 and larger do not work in 5:4 aspect ratio)
|
|
|
|
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 = xs_RoundToInt(vis * 65536.f);
|
|
|
|
// 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 = FixedMul (Scale (InvZtoScale, SCREENWIDTH*BaseRatioSizes[WidescreenRatio][1],
|
|
viewwidth*SCREENHEIGHT*3), FixedMul (r_WallVisibility, FocalTangent));
|
|
|
|
// 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 = Scale (160*FRACUNIT, r_FloorVisibility, FocalLengthY);
|
|
|
|
r_TiltVisibility = vis * (float)FocalTangent * (16.f * 320.f) / (float)viewwidth;
|
|
r_SpriteVisibility = r_WallVisibility;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// R_GetVisibility
|
|
//
|
|
//==========================================================================
|
|
|
|
float R_GetVisibility ()
|
|
{
|
|
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 ((float)atof (argv[1]));
|
|
}
|
|
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;
|
|
centerxfrac = centerx<<FRACBITS;
|
|
centeryfrac = centery<<FRACBITS;
|
|
|
|
virtwidth = virtwidth2 = fullWidth;
|
|
virtheight = virtheight2 = fullHeight;
|
|
|
|
if (Is54Aspect(trueratio))
|
|
{
|
|
virtheight2 = virtheight2 * BaseRatioSizes[trueratio][3] / 48;
|
|
}
|
|
else
|
|
{
|
|
virtwidth2 = virtwidth2 * BaseRatioSizes[trueratio][3] / 48;
|
|
}
|
|
|
|
if (Is54Aspect(WidescreenRatio))
|
|
{
|
|
virtheight = virtheight * BaseRatioSizes[WidescreenRatio][3] / 48;
|
|
}
|
|
else
|
|
{
|
|
virtwidth = virtwidth * BaseRatioSizes[WidescreenRatio][3] / 48;
|
|
}
|
|
|
|
baseyaspectmul = Scale(320 << FRACBITS, virtheight2, r_Yaspect * virtwidth2);
|
|
yaspectmul = Scale ((320<<FRACBITS), virtheight, r_Yaspect * virtwidth);
|
|
iyaspectmulfloat = (float)virtwidth * r_Yaspect / 320.f / (float)virtheight;
|
|
InvZtoScale = yaspectmul * centerx;
|
|
|
|
WallTMapScale2 = iyaspectmulfloat * 64.f / (float)centerx;
|
|
|
|
// psprite scales
|
|
pspritexscale = (centerxwide << FRACBITS) / 160;
|
|
pspriteyscale = FixedMul (pspritexscale, yaspectmul);
|
|
pspritexiscale = FixedDiv (FRACUNIT, pspritexscale);
|
|
|
|
// thing clipping
|
|
clearbufshort (screenheightarray, viewwidth, (short)viewheight);
|
|
|
|
R_InitTextureMapping ();
|
|
|
|
MaxVisForWall = FixedMul (Scale (InvZtoScale, SCREENWIDTH*r_Yaspect,
|
|
viewwidth*SCREENHEIGHT), FocalTangent);
|
|
MaxVisForWall = FixedDiv (0x7fff0000, MaxVisForWall);
|
|
MaxVisForFloor = Scale (FixedDiv (0x7fff0000, viewheight<<(FRACBITS-2)), FocalLengthY, 160*FRACUNIT);
|
|
|
|
// Reset r_*Visibility vars
|
|
R_SetVisibility (R_GetVisibility ());
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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()
|
|
{
|
|
stacked_viewx = viewx;
|
|
stacked_viewy = viewy;
|
|
stacked_viewz = viewz;
|
|
stacked_angle = viewangle;
|
|
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()
|
|
{
|
|
{
|
|
fixed_t dy;
|
|
|
|
if (camera != NULL)
|
|
{
|
|
dy = FixedMul (FocalLengthY, finetangent[(ANGLE_90-viewpitch)>>ANGLETOFINESHIFT]);
|
|
}
|
|
else
|
|
{
|
|
dy = 0;
|
|
}
|
|
|
|
centeryfrac = (viewheight << (FRACBITS-1)) + dy;
|
|
centery = centeryfrac >> FRACBITS;
|
|
globaluclip = FixedDiv (-centeryfrac, InvZtoScale);
|
|
globaldclip = FixedDiv ((viewheight<<FRACBITS)-centeryfrac, InvZtoScale);
|
|
|
|
//centeryfrac &= 0xffff0000;
|
|
int e, i;
|
|
|
|
i = 0;
|
|
e = viewheight;
|
|
fixed_t focus = FocalLengthY;
|
|
fixed_t den;
|
|
if (i < centery)
|
|
{
|
|
den = centeryfrac - (i << FRACBITS) - FRACUNIT/2;
|
|
if (e <= centery)
|
|
{
|
|
do {
|
|
yslope[i] = FixedDiv (focus, den);
|
|
den -= FRACUNIT;
|
|
} while (++i < e);
|
|
}
|
|
else
|
|
{
|
|
do {
|
|
yslope[i] = FixedDiv (focus, den);
|
|
den -= FRACUNIT;
|
|
} while (++i < centery);
|
|
den = (i << FRACBITS) - centeryfrac + FRACUNIT/2;
|
|
do {
|
|
yslope[i] = FixedDiv (focus, den);
|
|
den += FRACUNIT;
|
|
} while (++i < e);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
den = (i << FRACBITS) - centeryfrac + FRACUNIT/2;
|
|
do {
|
|
yslope[i] = FixedDiv (focus, den);
|
|
den += FRACUNIT;
|
|
} 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;
|
|
}
|
|
|
|
angle_t startang = viewangle;
|
|
fixed_t startx = viewx;
|
|
fixed_t starty = viewy;
|
|
fixed_t startz = viewz;
|
|
|
|
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->dx == 0)
|
|
{ // vertical mirror
|
|
viewx = v1->x - startx + v1->x;
|
|
}
|
|
else if (pds->src->dy == 0)
|
|
{ // horizontal mirror
|
|
viewy = v1->y - starty + v1->y;
|
|
}
|
|
else
|
|
{ // any mirror--use floats to avoid integer overflow
|
|
vertex_t *v2 = pds->src->v2;
|
|
|
|
double dx = FIXED2DBL(v2->x - v1->x);
|
|
double dy = FIXED2DBL(v2->y - v1->y);
|
|
double x1 = FIXED2DBL(v1->x);
|
|
double y1 = FIXED2DBL(v1->y);
|
|
double x = FIXED2DBL(startx);
|
|
double y = FIXED2DBL(starty);
|
|
|
|
// the above two cases catch len == 0
|
|
double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy);
|
|
|
|
viewx = FLOAT2FIXED((x1 + r * dx)*2 - x);
|
|
viewy = FLOAT2FIXED((y1 + r * dy)*2 - y);
|
|
}
|
|
viewangle = 2*R_PointToAngle2 (pds->src->v1->x, pds->src->v1->y,
|
|
pds->src->v2->x, pds->src->v2->y) - startang;
|
|
|
|
}
|
|
else
|
|
{
|
|
P_TranslatePortalXY(pds->src, viewx, viewy);
|
|
P_TranslatePortalZ(pds->src, viewz);
|
|
P_TranslatePortalAngle(pds->src, viewangle);
|
|
}
|
|
|
|
viewsin = finesine[viewangle>>ANGLETOFINESHIFT];
|
|
viewcos = finecosine[viewangle>>ANGLETOFINESHIFT];
|
|
|
|
viewtansin = FixedMul (FocalTangent, viewsin);
|
|
viewtancos = FixedMul (FocalTangent, viewcos);
|
|
|
|
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;
|
|
R_RenderBSPNode (nodes + numnodes - 1);
|
|
R_3D_ResetClip(); // reset clips (floor/ceiling)
|
|
|
|
PlaneCycles.Clock();
|
|
R_DrawPlanes ();
|
|
R_DrawSkyBoxes ();
|
|
PlaneCycles.Unclock();
|
|
|
|
fixed_t vzp = viewz;
|
|
|
|
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;
|
|
viewangle = startang;
|
|
viewx = startx;
|
|
viewy = starty;
|
|
viewz = startz;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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;
|
|
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_DrawSkyBoxes ();
|
|
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
|