1623 lines
42 KiB
C++
1623 lines
42 KiB
C++
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
// Copyright (C) 1999-2018 by Sonic Team Junior.
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file r_main.c
|
|
/// \brief Rendering main loop and setup functions,
|
|
/// utility functions (BSP, geometry, trigonometry).
|
|
/// See tables.c, too.
|
|
|
|
#include <algorithm>
|
|
|
|
#include "doomdef.h"
|
|
#include "g_game.h"
|
|
#include "g_input.h"
|
|
#include "g_state.h"
|
|
#include "r_local.h"
|
|
#include "r_splats.h" // faB(21jan): testing
|
|
#include "r_sky.h"
|
|
#include "st_stuff.h"
|
|
#include "p_local.h"
|
|
#include "keys.h"
|
|
#include "i_video.h"
|
|
#include "m_menu.h"
|
|
#include "am_map.h"
|
|
#include "d_main.h"
|
|
#include "v_video.h"
|
|
#include "p_spec.h" // skyboxmo
|
|
#include "p_setup.h"
|
|
#include "z_zone.h"
|
|
#include "m_random.h" // quake camera shake
|
|
#include "r_portal.h"
|
|
#include "doomstat.h" // MAXSPLITSCREENPLAYERS
|
|
#include "r_fps.h" // Frame interpolation/uncapped
|
|
#include "tables.h"
|
|
|
|
#ifdef HWRENDER
|
|
#include "hardware/hw_main.h"
|
|
#endif
|
|
|
|
// Fineangles in the SCREENWIDTH wide window.
|
|
#define FIELDOFVIEW 2048
|
|
|
|
// increment every time a check is made
|
|
size_t validcount = 1;
|
|
|
|
INT32 centerx = 0, centery = 0;
|
|
|
|
fixed_t centerxfrac = 0, centeryfrac = 0;
|
|
fixed_t projection = 0;
|
|
fixed_t projectiony = 0; // aspect ratio
|
|
fixed_t fovtan = 0; // field of view
|
|
|
|
// just for profiling purposes
|
|
size_t framecount = 0;
|
|
|
|
size_t loopcount = 0;
|
|
|
|
fixed_t viewx = 0, viewy = 0, viewz = 0;
|
|
angle_t viewangle = 0, aimingangle = 0, viewroll = 0;
|
|
UINT8 viewssnum = 0;
|
|
fixed_t viewcos = 0, viewsin = 0;
|
|
boolean skyVisible = 0;
|
|
boolean skyVisiblePerPlayer[MAXSPLITSCREENPLAYERS] = {}; // saved values of skyVisible for each splitscreen player
|
|
sector_t *viewsector = NULL;
|
|
player_t *viewplayer = NULL;
|
|
|
|
fixed_t renderdeltatics = 0;
|
|
boolean renderisnewtic = true;
|
|
|
|
//
|
|
// precalculated math tables
|
|
//
|
|
angle_t clipangle = 0;
|
|
angle_t doubleclipangle = 0;
|
|
|
|
// The viewangletox[viewangle + FINEANGLES/4] lookup
|
|
// maps the visible view angles to screen X coordinates,
|
|
// flattening the arc to a flat projection plane.
|
|
// There will be many angles mapped to the same X.
|
|
INT32 viewangletox[FINEANGLES/2] = {};
|
|
|
|
// The xtoviewangleangle[] table maps a screen pixel
|
|
// to the lowest viewangle that maps back to x ranges
|
|
// from clipangle to -clipangle.
|
|
angle_t *xtoviewangle = NULL;
|
|
|
|
lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE] = {};
|
|
lighttable_t *scalelightfixed[MAXLIGHTSCALE] = {};
|
|
lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ] = {};
|
|
|
|
// Hack to support extra boom colormaps.
|
|
size_t num_extra_colormaps = 0;
|
|
extracolormap_t extra_colormaps[MAXCOLORMAPS] = {};
|
|
|
|
// Performance stats
|
|
precise_t ps_prevframetime = 0;
|
|
ps_metric_t ps_rendercalltime = {};
|
|
ps_metric_t ps_otherrendertime = {};
|
|
ps_metric_t ps_uitime = {};
|
|
ps_metric_t ps_swaptime = {};
|
|
|
|
ps_metric_t ps_skyboxtime = {};
|
|
ps_metric_t ps_bsptime = {};
|
|
|
|
ps_metric_t ps_sw_spritecliptime = {};
|
|
ps_metric_t ps_sw_portaltime = {};
|
|
ps_metric_t ps_sw_planetime = {};
|
|
ps_metric_t ps_sw_maskedtime = {};
|
|
|
|
ps_metric_t ps_numbspcalls = {};
|
|
ps_metric_t ps_numsprites = {};
|
|
ps_metric_t ps_numdrawnodes = {};
|
|
ps_metric_t ps_numpolyobjects = {};
|
|
|
|
static CV_PossibleValue_t drawdist_cons_t[] = {
|
|
/*{256, "256"},*/ {512, "512"}, {768, "768"},
|
|
{1024, "1024"}, {1536, "1536"}, {2048, "2048"},
|
|
{3072, "3072"}, {4096, "4096"}, {6144, "6144"},
|
|
{8192, "8192"}, {12288, "12288"}, {20480, "20480"},
|
|
{24576, "24576"},{0, "Infinite"}, {0, NULL}};
|
|
|
|
#if __SIZEOF_POINTER__ < 8 // only for 64bit
|
|
static CV_PossibleValue_t drawdist_precip_cons_t[] = {
|
|
{256, "256"}, {512, "512"}, {768, "768"},
|
|
{1024, "1024"}, {1536, "1536"}, {2048, "2048"},
|
|
{0, "None"}, {0, NULL}};
|
|
|
|
#else
|
|
static CV_PossibleValue_t drawdist_precip_cons_t[] = {
|
|
{256, "256"}, {512, "512"}, {768, "768"},
|
|
{1024, "1024"}, {1536, "1536"}, {2048, "2048"},
|
|
{3072, "3072"}, {4096, "4096"}, {0, "None"}, {0, NULL}};
|
|
#endif
|
|
|
|
static CV_PossibleValue_t maxinterpdist_cons_t[] = {
|
|
/*{256, "256"},*/ {512, "512"}, {768, "768"},
|
|
{1024, "1024"}, {1536, "1536"}, {2048, "2048"},
|
|
{3072, "3072"}, {4096, "4096"}, {6144, "6144"},
|
|
{8192, "8192"}, {0, "Infinite"}, {0, NULL}};
|
|
|
|
static CV_PossibleValue_t fov_cons_t[] = {{MINFOV*FRACUNIT, "MIN"}, {MAXFOV*FRACUNIT, "MAX"}, {0, NULL}};
|
|
|
|
static CV_PossibleValue_t translucenthud_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}};
|
|
static CV_PossibleValue_t maxportals_cons_t[] = {{0, "MIN"}, {12, "MAX"}, {0, NULL}}; // lmao rendering 32 portals, you're a card
|
|
static CV_PossibleValue_t homremoval_cons_t[] = {{0, "No"}, {1, "Yes"}, {2, "Flash"}, {0, NULL}};
|
|
static CV_PossibleValue_t secbright_cons_t[] = {{0, "MIN"}, {255, "MAX"}, {0, NULL}};
|
|
|
|
static void R_SetFov(fixed_t playerfov);
|
|
|
|
static void Fov_OnChange(void);
|
|
static void FlipCam_OnChange(void);
|
|
static void FlipCam2_OnChange(void);
|
|
static void FlipCam3_OnChange(void);
|
|
static void FlipCam4_OnChange(void);
|
|
|
|
static void DirLight_OnChange(void);
|
|
|
|
static void Precipstuff_OnChange(void);
|
|
|
|
consvar_t cv_tailspickup = {"tailspickup", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
// if enabled, load all graphics at level load
|
|
consvar_t cv_precachetextures = {"precachetextures", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
consvar_t cv_chasecam[MAXSPLITSCREENPLAYERS] = {
|
|
{"chasecam", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL},
|
|
{"chasecam2", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL},
|
|
{"chasecam3", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL},
|
|
{"chasecam4", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}
|
|
};
|
|
|
|
consvar_t cv_flipcam[MAXSPLITSCREENPLAYERS] = {
|
|
{"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL},
|
|
{"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL},
|
|
{"flipcam3", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam3_OnChange, 0, NULL, NULL, 0, 0, NULL},
|
|
{"flipcam4", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}
|
|
};
|
|
|
|
static CV_PossibleValue_t flipcammode_cons_t[] = {{0, "Displayplayer"}, {1, "Local"}, {0, NULL}};
|
|
consvar_t cv_flipcammode = {"flipcammode", "Displayplayer", CV_SAVE, flipcammode_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_skydome = {"skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_ffloorclip = {"r_ffloorclip", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_spriteclip = {"r_spriteclip", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_softcyancut = {"softwarecyancut", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_translucency = {"translucency", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_drawdist = {"drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_drawdist_precip = {"drawdist_precip", "1024", CV_SAVE|CV_CALL|CV_NOINIT, drawdist_precip_cons_t, Precipstuff_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_lessprecip = {"lessweathereffects", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Precipstuff_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_maxinterpdist = {"maxinterpdist", "Infinite", CV_SAVE, maxinterpdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_playerfade = {"playerfade", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_ripplewater = {"waterripples", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
// Okay, whoever said homremoval causes a performance hit should be shot.
|
|
consvar_t cv_homremoval = {"homremoval", "Yes", CV_SAVE, homremoval_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
consvar_t cv_secbright = {"r_secbright", "0", CV_SAVE, secbright_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
consvar_t cv_maxportals = {"maxportals", "2", CV_SAVE, maxportals_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
// cap fov, fov too high tears software apart.
|
|
consvar_t cv_fov = {"fov", "90", CV_FLOAT|CV_CALL|CV_SAVE, fov_cons_t, Fov_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_fovchange = {"fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
// randomized directional lightning
|
|
consvar_t cv_randomdirlight = {"randomdirectionallight", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, DirLight_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
consvar_t cv_uncappedhud = {"uncappedhud", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_soniccd = {"soniccd", "Off", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
|
|
|
|
// tells to reset the maplight at next map change
|
|
static void DirLight_OnChange(void)
|
|
{
|
|
reinitmaplight = true;
|
|
}
|
|
|
|
void SplitScreen_OnChange(void)
|
|
{
|
|
UINT8 i;
|
|
|
|
// recompute screen size
|
|
R_ExecuteSetViewSize();
|
|
|
|
if (!demo.playback && !botingame)
|
|
{
|
|
for (i = 1; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
if (i > splitscreen)
|
|
CL_RemoveSplitscreenPlayer(displayplayers[i]);
|
|
else
|
|
CL_AddSplitscreenPlayer();
|
|
}
|
|
|
|
if (server && !netgame)
|
|
multiplayer = splitscreen;
|
|
}
|
|
else
|
|
{
|
|
for (i = 1; i < MAXSPLITSCREENPLAYERS; i++)
|
|
displayplayers[i] = consoleplayer;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (playeringame[i] && i != consoleplayer)
|
|
{
|
|
UINT8 j;
|
|
|
|
for (j = 1; j < MAXSPLITSCREENPLAYERS; j++)
|
|
{
|
|
if (displayplayers[j] == consoleplayer)
|
|
{
|
|
displayplayers[j] = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == MAXSPLITSCREENPLAYERS)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Fov_OnChange(void)
|
|
{
|
|
R_SetViewSize();
|
|
}
|
|
|
|
static void FlipCam_OnChange(void)
|
|
{
|
|
SendWeaponPref(0);
|
|
}
|
|
|
|
static void FlipCam2_OnChange(void)
|
|
{
|
|
SendWeaponPref(1);
|
|
}
|
|
|
|
static void FlipCam3_OnChange(void)
|
|
{
|
|
SendWeaponPref(2);
|
|
}
|
|
|
|
static void FlipCam4_OnChange(void)
|
|
{
|
|
SendWeaponPref(3);
|
|
}
|
|
|
|
static void Precipstuff_OnChange(void)
|
|
{
|
|
if (gamestate == GS_LEVEL)
|
|
{
|
|
P_PurgePrecipitation();
|
|
P_SpawnPrecipitation();
|
|
}
|
|
}
|
|
|
|
//
|
|
// R_PointToAngle
|
|
// To get a global angle from cartesian coordinates,
|
|
// the coordinates are flipped until they are in
|
|
// the first octant of the coordinate system, then
|
|
// the y (<=x) is scaled and divided by x to get a
|
|
// tangent (slope) value which is looked up in the
|
|
// tantoangle[] table. The +1 size of tantoangle[]
|
|
// is to handle the case when x==y without additional
|
|
// checking.
|
|
//
|
|
// killough 5/2/98: reformatted, cleaned up
|
|
|
|
angle_t R_PointToAngle(fixed_t x, fixed_t y)
|
|
{
|
|
return (y -= viewy, (x -= viewx) || y) ?
|
|
x >= 0 ?
|
|
y >= 0 ?
|
|
(x > y) ? tantoangle[SlopeDiv(y,x)] : // octant 0
|
|
ANGLE_90-tantoangle[SlopeDiv(x,y)] : // octant 1
|
|
x > (y = -y) ? 0-tantoangle[SlopeDiv(y,x)] : // octant 8
|
|
ANGLE_270+tantoangle[SlopeDiv(x,y)] : // octant 7
|
|
y >= 0 ? (x = -x) > y ? ANGLE_180-tantoangle[SlopeDiv(y,x)] : // octant 3
|
|
ANGLE_90 + tantoangle[SlopeDiv(x,y)] : // octant 2
|
|
(x = -x) > (y = -y) ? ANGLE_180+tantoangle[SlopeDiv(y,x)] : // octant 4
|
|
ANGLE_270-tantoangle[SlopeDiv(x,y)] : // octant 5
|
|
0;
|
|
}
|
|
|
|
// This version uses 64-bit variables to avoid overflows with large values.
|
|
angle_t R_PointToAngle64(INT64 x, INT64 y)
|
|
{
|
|
return (y -= viewy, (x -= viewx) || y) ?
|
|
x >= 0 ?
|
|
y >= 0 ?
|
|
(x > y) ? tantoangle[SlopeDivEx(y,x)] : // octant 0
|
|
ANGLE_90-tantoangle[SlopeDivEx(x,y)] : // octant 1
|
|
x > (y = -y) ? 0-tantoangle[SlopeDivEx(y,x)] : // octant 8
|
|
ANGLE_270+tantoangle[SlopeDivEx(x,y)] : // octant 7
|
|
y >= 0 ? (x = -x) > y ? ANGLE_180-tantoangle[SlopeDivEx(y,x)] : // octant 3
|
|
ANGLE_90 + tantoangle[SlopeDivEx(x,y)] : // octant 2
|
|
(x = -x) > (y = -y) ? ANGLE_180+tantoangle[SlopeDivEx(y,x)] : // octant 4
|
|
ANGLE_270-tantoangle[SlopeDivEx(x,y)] : // octant 5
|
|
0;
|
|
}
|
|
|
|
angle_t R_PointToAngle2(fixed_t pviewx, fixed_t pviewy, fixed_t x, fixed_t y)
|
|
{
|
|
return (y -= pviewy, (x -= pviewx) || y) ?
|
|
x >= 0 ?
|
|
y >= 0 ?
|
|
(x > y) ? tantoangle[SlopeDiv(y,x)] : // octant 0
|
|
ANGLE_90-tantoangle[SlopeDiv(x,y)] : // octant 1
|
|
x > (y = -y) ? 0-tantoangle[SlopeDiv(y,x)] : // octant 8
|
|
ANGLE_270+tantoangle[SlopeDiv(x,y)] : // octant 7
|
|
y >= 0 ? (x = -x) > y ? ANGLE_180-tantoangle[SlopeDiv(y,x)] : // octant 3
|
|
ANGLE_90 + tantoangle[SlopeDiv(x,y)] : // octant 2
|
|
(x = -x) > (y = -y) ? ANGLE_180+tantoangle[SlopeDiv(y,x)] : // octant 4
|
|
ANGLE_270-tantoangle[SlopeDiv(x,y)] : // octant 5
|
|
0;
|
|
}
|
|
|
|
angle_t R_PlayerSliptideAngle(player_t *player)
|
|
{
|
|
mobj_t *mo;
|
|
spritedef_t *sprdef;
|
|
spriteframe_t *sprframe;
|
|
angle_t ang = 0;
|
|
|
|
if (!cv_sliptideroll.value || !player || P_MobjWasRemoved(player->mo))
|
|
return 0;
|
|
|
|
mo = player->mo;
|
|
|
|
if (mo->player->sliproll == 0 || mo->player->kartstuff[k_aizdriftstrat] == 0)
|
|
return 0;
|
|
|
|
size_t rot = (mo->frame & FF_FRAMEMASK);
|
|
|
|
if (mo->skin && mo->sprite == SPR_PLAY)
|
|
{
|
|
sprdef = &((skin_t *)mo->skin)->spritedef;
|
|
|
|
if (rot >= sprdef->numframes)
|
|
sprdef = &sprites[mo->sprite];
|
|
}
|
|
else
|
|
{
|
|
sprdef = &sprites[mo->sprite];
|
|
}
|
|
|
|
if (rot >= sprdef->numframes)
|
|
{
|
|
sprdef = &sprites[states[S_UNKNOWN].sprite];
|
|
rot = (states[S_UNKNOWN].frame & FF_FRAMEMASK);
|
|
}
|
|
|
|
sprframe = &sprdef->spriteframes[rot];
|
|
|
|
// No sprite frame? I guess it is possible
|
|
if (!sprframe)
|
|
return 0;
|
|
|
|
if (sprframe->rotate != SRF_SINGLE || R_ThingIsPaperSprite(mo))
|
|
ang = R_PointToAngle(mo->x, mo->y) - mo->angle;
|
|
|
|
return FixedMul(FINECOSINE((ang) >> ANGLETOFINESHIFT), mo->player->sliproll * mo->player->kartstuff[k_aizdriftstrat]);
|
|
}
|
|
|
|
//
|
|
// R_ScaleFromGlobalAngle
|
|
// Returns the texture mapping scale for the current line (horizontal span)
|
|
// at the given angle.
|
|
// rw_distance must be calculated first.
|
|
//
|
|
// killough 5/2/98: reformatted, cleaned up
|
|
//
|
|
// note: THIS IS USED ONLY FOR WALLS!
|
|
fixed_t R_ScaleFromGlobalAngle(angle_t visangle)
|
|
{
|
|
angle_t anglea = ANGLE_90 + (visangle-viewangle);
|
|
angle_t angleb = ANGLE_90 + (visangle-rw_normalangle);
|
|
fixed_t den = FixedMul(rw_distance, FINESINE(anglea>>ANGLETOFINESHIFT));
|
|
// proff 11/06/98: Changed for high-res
|
|
fixed_t num = FixedMul(projectiony, FINESINE(angleb>>ANGLETOFINESHIFT));
|
|
|
|
if (den > num>>16)
|
|
{
|
|
num = FixedDiv(num, den);
|
|
num = CLAMP(num, 256, 64*FRACUNIT);
|
|
return num;
|
|
}
|
|
return 64*FRACUNIT;
|
|
}
|
|
|
|
line_t *R_GetFFloorLine(const line_t *line, const ffloor_t *pfloor, const sector_t *sector)
|
|
{
|
|
if (pfloor->master->flags & ML_TFERLINE)
|
|
{
|
|
size_t linenum = std::min<size_t>((size_t)(line - sector->lines[0]), pfloor->master->frontsector->linecount);
|
|
return pfloor->master->frontsector->lines[0] + linenum;
|
|
}
|
|
|
|
return pfloor->master;
|
|
}
|
|
|
|
side_t *R_GetFFloorSide(const line_t *line, const ffloor_t *pfloor, const sector_t *sector)
|
|
{
|
|
if (pfloor->master->flags & ML_TFERLINE)
|
|
{
|
|
line_t *newline = R_GetFFloorLine(line, pfloor, sector);
|
|
return &sides[newline->sidenum[0]];
|
|
}
|
|
|
|
return &sides[pfloor->master->sidenum[0]];
|
|
}
|
|
|
|
//
|
|
// R_DoCulling
|
|
// Checks viewz and top/bottom heights of an item against culling planes
|
|
// Returns true if the item is to be culled, i.e it shouldn't be drawn!
|
|
// if ML_NOCLIMB is set, the camera view is required to be in the same area for culling to occur
|
|
boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixed_t bottomh, fixed_t toph)
|
|
{
|
|
fixed_t cullplane;
|
|
|
|
if (!cullheight)
|
|
return false;
|
|
|
|
cullplane = cullheight->frontsector->floorheight;
|
|
|
|
if (cullheight->flags & ML_NOCLIMB) // Group culling
|
|
{
|
|
if (!viewcullheight)
|
|
return false;
|
|
|
|
// Make sure this is part of the same group
|
|
if (viewcullheight->frontsector == cullheight->frontsector)
|
|
{
|
|
// OK, we can cull
|
|
if (vz > cullplane && toph < cullplane) // Cull if below plane
|
|
return true;
|
|
|
|
if (bottomh > cullplane && vz <= cullplane) // Cull if above plane
|
|
return true;
|
|
}
|
|
}
|
|
else // Quick culling
|
|
{
|
|
if (vz > cullplane && toph < cullplane) // Cull if below plane
|
|
return true;
|
|
|
|
if (bottomh > cullplane && vz <= cullplane) // Cull if above plane
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Returns search dimensions within a blockmap, in the direction of viewangle and out to a certain distance.
|
|
void R_GetRenderBlockMapDimensions(fixed_t drawdist, INT32 *xl, INT32 *xh, INT32 *yl, INT32 *yh)
|
|
{
|
|
const angle_t left = viewangle - clipangle;
|
|
const angle_t right = viewangle + clipangle;
|
|
|
|
const fixed_t vxleft = viewx + FixedMul(drawdist, FCOS(left));
|
|
const fixed_t vyleft = viewy + FixedMul(drawdist, FSIN(left));
|
|
|
|
const fixed_t vxright = viewx + FixedMul(drawdist, FCOS(right));
|
|
const fixed_t vyright = viewy + FixedMul(drawdist, FSIN(right));
|
|
|
|
// Try to narrow the search to within only the field of view
|
|
*xl = (unsigned)(std::min(viewx, std::min(vxleft, vxright)) - bmaporgx)>>MAPBLOCKSHIFT;
|
|
*xh = (unsigned)(std::max(viewx, std::max(vxleft, vxright)) - bmaporgx)>>MAPBLOCKSHIFT;
|
|
*yl = (unsigned)(std::min(viewy, std::min(vyleft, vyright)) - bmaporgy)>>MAPBLOCKSHIFT;
|
|
*yh = (unsigned)(std::max(viewy, std::max(vyleft, vyright)) - bmaporgy)>>MAPBLOCKSHIFT;
|
|
|
|
if (*xh >= bmapwidth)
|
|
*xh = bmapwidth - 1;
|
|
|
|
if (*yh >= bmapheight)
|
|
*yh = bmapheight - 1;
|
|
|
|
BMBOUNDFIX(*xl, *xh, *yl, *yh);
|
|
}
|
|
|
|
//
|
|
// R_InitTextureMapping
|
|
//
|
|
static void R_InitTextureMapping(void)
|
|
{
|
|
INT32 i;
|
|
INT32 x;
|
|
INT32 t;
|
|
fixed_t focallength;
|
|
|
|
// Use tangent table to generate viewangletox:
|
|
// viewangletox will give the next greatest x
|
|
// after the view angle.
|
|
//
|
|
// Calc focallength
|
|
// so FIELDOFVIEW angles covers SCREENWIDTH.
|
|
focallength = FixedDiv(projection,
|
|
FINETANGENT(FINEANGLES/4+/*cv_fov.value*/ FIELDOFVIEW/2));
|
|
|
|
focallengthf = FIXED_TO_FLOAT(focallength);
|
|
|
|
for (i = 0; i < FINEANGLES/2; i++)
|
|
{
|
|
if (FINETANGENT(i) > fovtan*2)
|
|
t = -1;
|
|
else if (FINETANGENT(i) < -fovtan*2)
|
|
t = viewwidth+1;
|
|
else
|
|
{
|
|
t = FixedMul(FINETANGENT(i), focallength);
|
|
t = (centerxfrac - t+FRACUNIT-1)>>FRACBITS;
|
|
|
|
if (t < -1)
|
|
t = -1;
|
|
else if (t > viewwidth+1)
|
|
t = viewwidth+1;
|
|
}
|
|
|
|
viewangletox[i] = t;
|
|
}
|
|
|
|
// Scan viewangletox[] to generate xtoviewangle[]:
|
|
// xtoviewangle will give the smallest view angle
|
|
// that maps to x.
|
|
for (x = 0; x <= viewwidth;x++)
|
|
{
|
|
i = 0;
|
|
while (viewangletox[i] > x)
|
|
i++;
|
|
xtoviewangle[x] = (i<<ANGLETOFINESHIFT) - ANGLE_90;
|
|
}
|
|
|
|
// Take out the fencepost cases from viewangletox.
|
|
for (i = 0; i < FINEANGLES/2; i++)
|
|
{
|
|
if (viewangletox[i] == -1)
|
|
viewangletox[i] = 0;
|
|
else if (viewangletox[i] == viewwidth+1)
|
|
viewangletox[i] = viewwidth;
|
|
}
|
|
|
|
clipangle = xtoviewangle[0];
|
|
doubleclipangle = clipangle*2;
|
|
}
|
|
|
|
//
|
|
// R_InitLightTables
|
|
// Only inits the zlight table,
|
|
// because the scalelight table changes with view size.
|
|
//
|
|
#define DISTMAP 2
|
|
|
|
static inline void R_InitLightTables(void)
|
|
{
|
|
INT32 i;
|
|
INT32 j;
|
|
INT32 level;
|
|
INT32 startmapl;
|
|
INT32 scale;
|
|
|
|
// Calculate the light levels to use
|
|
// for each level / distance combination.
|
|
for (i = 0; i < LIGHTLEVELS; i++)
|
|
{
|
|
startmapl = ((LIGHTLEVELS-1-i)*2)*NUMCOLORMAPS/LIGHTLEVELS;
|
|
for (j = 0; j < MAXLIGHTZ; j++)
|
|
{
|
|
//added : 02-02-98 : use BASEVIDWIDTH, vid.width is not set already,
|
|
// and it seems it needs to be calculated only once.
|
|
scale = FixedDiv((BASEVIDWIDTH/2*FRACUNIT), (j+1)<<LIGHTZSHIFT);
|
|
scale >>= LIGHTSCALESHIFT;
|
|
level = startmapl - scale/DISTMAP;
|
|
|
|
if (level < 0)
|
|
level = 0;
|
|
|
|
if (level >= NUMCOLORMAPS)
|
|
level = NUMCOLORMAPS-1;
|
|
|
|
zlight[i][j] = colormaps + level*256;
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct {
|
|
angle_t rollangle; // pre-shifted by fineshift
|
|
|
|
fixed_t zoomneeded;
|
|
INT32 *scrmap;
|
|
INT32 scrmapsize;
|
|
|
|
INT32 x1; // clip rendering horizontally for efficiency
|
|
INT16 *ceilingclip, *floorclip;
|
|
|
|
boolean use;
|
|
} viewmorph = {
|
|
0,
|
|
|
|
FRACUNIT,
|
|
NULL,
|
|
0,
|
|
|
|
0,
|
|
NULL, NULL,
|
|
|
|
false
|
|
};
|
|
|
|
void R_CheckViewMorph(void)
|
|
{
|
|
float zoomfactor, rollcos, rollsin;
|
|
float x1, y1, x2, y2;
|
|
fixed_t temp;
|
|
INT32 end, vx, vy, pos, usedpos;
|
|
INT32 usedx, usedy, halfwidth = vid.width/2, halfheight = vid.height/2;
|
|
|
|
angle_t rollangle = viewroll;
|
|
|
|
rollangle >>= ANGLETOFINESHIFT;
|
|
rollangle = ((rollangle+2) & ~3) & FINEMASK; // Limit the distinct number of angles to reduce recalcs from angles changing a lot.
|
|
|
|
if (rollangle == viewmorph.rollangle &&
|
|
viewmorph.scrmapsize == vid.width*vid.height)
|
|
return; // No change
|
|
|
|
viewmorph.rollangle = rollangle;
|
|
|
|
if (viewmorph.rollangle == 0)
|
|
{
|
|
viewmorph.use = false;
|
|
viewmorph.x1 = 0;
|
|
if (viewmorph.zoomneeded != FRACUNIT)
|
|
R_SetViewSize();
|
|
viewmorph.zoomneeded = FRACUNIT;
|
|
|
|
return;
|
|
}
|
|
|
|
if (viewmorph.scrmapsize != vid.width*vid.height)
|
|
{
|
|
viewmorph.scrmapsize = vid.width*vid.height;
|
|
viewmorph.scrmap = static_cast<INT32*>(realloc(viewmorph.scrmap, vid.width*vid.height * sizeof(INT32)));
|
|
viewmorph.ceilingclip = static_cast<INT16*>(realloc(viewmorph.ceilingclip, vid.width * sizeof(INT16)));
|
|
viewmorph.floorclip = static_cast<INT16*>(realloc(viewmorph.floorclip, vid.width * sizeof(INT16)));
|
|
}
|
|
|
|
temp = FINECOSINE(rollangle);
|
|
rollcos = FIXED_TO_FLOAT(temp);
|
|
temp = FINESINE(rollangle);
|
|
rollsin = FIXED_TO_FLOAT(temp);
|
|
|
|
// Calculate maximum zoom needed
|
|
x1 = (vid.width*fabsf(rollcos) + vid.height*fabsf(rollsin)) / vid.width;
|
|
y1 = (vid.height*fabsf(rollcos) + vid.width*fabsf(rollsin)) / vid.height;
|
|
|
|
temp = std::max(x1, y1)*FRACUNIT;
|
|
if (temp < FRACUNIT)
|
|
temp = FRACUNIT;
|
|
else
|
|
temp |= 0x3FFF; // Limit how many times the viewport needs to be recalculated
|
|
|
|
//CONS_Printf("Setting zoom to %f\n", FIXED_TO_FLOAT(temp));
|
|
|
|
if (temp != viewmorph.zoomneeded)
|
|
{
|
|
viewmorph.zoomneeded = temp;
|
|
R_SetViewSize();
|
|
}
|
|
|
|
zoomfactor = FIXED_TO_FLOAT(viewmorph.zoomneeded);
|
|
|
|
end = vid.width * vid.height - 1;
|
|
|
|
pos = 0;
|
|
|
|
// Pre-multiply rollcos and rollsin to use for positional stuff
|
|
rollcos /= zoomfactor;
|
|
rollsin /= zoomfactor;
|
|
|
|
x1 = -(halfwidth * rollcos - halfheight * rollsin);
|
|
y1 = -(halfheight * rollcos + halfwidth * rollsin);
|
|
|
|
viewmorph.x1 = (INT32)(halfwidth - (halfwidth * fabsf(rollcos) + halfheight * fabsf(rollsin)));
|
|
//CONS_Printf("saving %d cols\n", viewmorph.x1);
|
|
|
|
// Set ceilingclip and floorclip
|
|
for (vx = 0; vx < vid.width; vx++)
|
|
{
|
|
viewmorph.ceilingclip[vx] = vid.height;
|
|
viewmorph.floorclip[vx] = -1;
|
|
}
|
|
x2 = x1;
|
|
y2 = y1;
|
|
for (vx = 0; vx < vid.width; vx++)
|
|
{
|
|
INT16 xa, ya, xb, yb;
|
|
xa = x2+halfwidth;
|
|
ya = y2+halfheight-1;
|
|
xb = vid.width-1-xa;
|
|
yb = vid.height-1-ya;
|
|
|
|
viewmorph.ceilingclip[xa] = std::min(viewmorph.ceilingclip[xa], ya);
|
|
viewmorph.floorclip[xa] = std::max(viewmorph.floorclip[xa], ya);
|
|
viewmorph.ceilingclip[xb] = std::min(viewmorph.ceilingclip[xb], yb);
|
|
viewmorph.floorclip[xb] = std::max(viewmorph.floorclip[xb], yb);
|
|
x2 += rollcos;
|
|
y2 += rollsin;
|
|
}
|
|
x2 = x1;
|
|
y2 = y1;
|
|
for (vy = 0; vy < vid.height; vy++)
|
|
{
|
|
INT16 xa, ya, xb, yb;
|
|
xa = x2+halfwidth;
|
|
ya = y2+halfheight;
|
|
xb = vid.width-1-xa;
|
|
yb = vid.height-1-ya;
|
|
|
|
viewmorph.ceilingclip[xa] = std::min(viewmorph.ceilingclip[xa], ya);
|
|
viewmorph.floorclip[xa] = std::max(viewmorph.floorclip[xa], ya);
|
|
viewmorph.ceilingclip[xb] = std::min(viewmorph.ceilingclip[xb], yb);
|
|
viewmorph.floorclip[xb] = std::max(viewmorph.floorclip[xb], yb);
|
|
x2 -= rollsin;
|
|
y2 += rollcos;
|
|
}
|
|
|
|
//CONS_Printf("Top left corner is %f %f\n", x1, y1);
|
|
|
|
x1 += halfwidth;
|
|
y1 += halfheight;
|
|
|
|
for (vy = 0; vy < halfheight; vy++)
|
|
{
|
|
x2 = x1;
|
|
y2 = y1;
|
|
x1 -= rollsin;
|
|
y1 += rollcos;
|
|
|
|
for (vx = 0; vx < vid.width; vx++)
|
|
{
|
|
usedx = x2;
|
|
usedy = y2;
|
|
|
|
usedpos = usedx + usedy*vid.width;
|
|
|
|
viewmorph.scrmap[pos] = usedpos;
|
|
viewmorph.scrmap[end-pos] = end-usedpos;
|
|
|
|
x2 += rollcos;
|
|
y2 += rollsin;
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
viewmorph.use = true;
|
|
}
|
|
|
|
void R_ApplyViewMorph(void)
|
|
{
|
|
if (!viewmorph.use)
|
|
return;
|
|
|
|
UINT8 *tmpscr = vid.screens[4];
|
|
UINT8 *srcscr = vid.screens[0];
|
|
INT32 p, end = vid.width * vid.height;
|
|
|
|
if (cv_debug & DBG_VIEWMORPH)
|
|
{
|
|
UINT8 border = 32;
|
|
UINT8 grid = 160;
|
|
INT32 ws = vid.width / 4;
|
|
INT32 hs = vid.width * (vid.height / 4);
|
|
|
|
memcpy(tmpscr, srcscr, vid.width*vid.height);
|
|
for (p = 0; p < vid.width; p++)
|
|
{
|
|
tmpscr[viewmorph.scrmap[p]] = border;
|
|
tmpscr[viewmorph.scrmap[p + hs]] = grid;
|
|
tmpscr[viewmorph.scrmap[p + hs*2]] = grid;
|
|
tmpscr[viewmorph.scrmap[p + hs*3]] = grid;
|
|
tmpscr[viewmorph.scrmap[end - 1 - p]] = border;
|
|
}
|
|
for (p = vid.width; p < end; p += vid.width)
|
|
{
|
|
tmpscr[viewmorph.scrmap[p]] = border;
|
|
tmpscr[viewmorph.scrmap[p + ws]] = grid;
|
|
tmpscr[viewmorph.scrmap[p + ws*2]] = grid;
|
|
tmpscr[viewmorph.scrmap[p + ws*3]] = grid;
|
|
tmpscr[viewmorph.scrmap[end - 1 - p]] = border;
|
|
}
|
|
}
|
|
else
|
|
for (p = 0; p < end; p++)
|
|
tmpscr[p] = srcscr[viewmorph.scrmap[p]];
|
|
|
|
VID_BlitLinearScreen(tmpscr, vid.screens[0], vid.width, vid.height, vid.width, vid.width);
|
|
}
|
|
|
|
angle_t R_ViewRollAngle(const player_t *player)
|
|
{
|
|
angle_t roll = 0;
|
|
const UINT8 viewnum = R_GetViewNumber();
|
|
|
|
if (gamestate != GS_LEVEL)
|
|
{
|
|
// FIXME: The way this is implemented is totally
|
|
// incompatible with cameras that aren't directly
|
|
// tied to the player. (podium, titlemap,
|
|
// MT_ALTVIEWMAN in general)
|
|
|
|
// All of these player variables should affect their
|
|
// camera_t in P_MoveChaseCamera, and then this
|
|
// just returns that variable instead.
|
|
return 0;
|
|
}
|
|
|
|
roll = player->viewrollangle;
|
|
|
|
if (cv_tilting.value)
|
|
{
|
|
if (!player->spectator && !camera[viewnum].freecam)
|
|
roll += player->tilt;
|
|
|
|
if (cv_actionmovie.value)
|
|
{
|
|
int xs = intsign(quake.x),
|
|
ys = intsign(quake.y),
|
|
zs = intsign(quake.z);
|
|
roll += (xs ^ ys ^ zs) * ANG1;
|
|
}
|
|
}
|
|
|
|
return roll;
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
boolean setsizeneeded;
|
|
|
|
void R_SetViewSize(void)
|
|
{
|
|
setsizeneeded = true;
|
|
}
|
|
|
|
static void R_SetupViewBuffers(void);
|
|
|
|
//
|
|
// R_ExecuteSetViewSize
|
|
//
|
|
void R_ExecuteSetViewSize(void)
|
|
{
|
|
INT32 i;
|
|
INT32 j;
|
|
INT32 level;
|
|
INT32 startmapl;
|
|
|
|
setsizeneeded = false;
|
|
|
|
if (rendermode == render_none)
|
|
return;
|
|
|
|
// status bar overlay
|
|
st_overlay = cv_showhud.value;
|
|
|
|
viewwidth = vid.width;
|
|
viewheight = vid.height;
|
|
|
|
if (splitscreen)
|
|
{
|
|
viewheight >>= 1;
|
|
|
|
if (splitscreen > 1)
|
|
viewwidth >>= 1;
|
|
}
|
|
|
|
centerx = viewwidth/2;
|
|
centery = viewheight/2;
|
|
centerxfrac = centerx<<FRACBITS;
|
|
centeryfrac = centery<<FRACBITS;
|
|
|
|
R_SetupViewBuffers();
|
|
|
|
// why did we calc all the software crap?
|
|
#ifdef HWRENDER
|
|
if (rendermode == render_opengl)
|
|
{
|
|
HWR_SetViewSize();
|
|
am_recalc = true;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// thing clipping
|
|
for (i = 0; i < viewwidth; i++)
|
|
{
|
|
negonearray[i] = -1;
|
|
screenheightarray[i] = (INT16)viewheight;
|
|
}
|
|
|
|
Z_Free(ds_su);
|
|
Z_Free(ds_sv);
|
|
Z_Free(ds_sz);
|
|
ds_su = ds_sv = ds_sz = NULL;
|
|
|
|
memset(scalelight, 0xFF, sizeof(scalelight));
|
|
|
|
// Calculate the light levels to use for each level/scale combination.
|
|
for (i = 0; i < LIGHTLEVELS; i++)
|
|
{
|
|
startmapl = ((LIGHTLEVELS - 1 - i)*2)*NUMCOLORMAPS/LIGHTLEVELS;
|
|
|
|
for (j = 0; j < MAXLIGHTSCALE; j++)
|
|
{
|
|
level = startmapl - j*vid.width/(viewwidth)/DISTMAP;
|
|
|
|
if (level < 0)
|
|
level = 0;
|
|
|
|
if (level >= NUMCOLORMAPS)
|
|
level = NUMCOLORMAPS - 1;
|
|
|
|
scalelight[i][j] = colormaps + level*256;
|
|
}
|
|
}
|
|
|
|
R_SetupFreelook();
|
|
|
|
am_recalc = true;
|
|
}
|
|
|
|
fixed_t R_GetPlayerFov(player_t *player)
|
|
{
|
|
fixed_t fov = cv_fov.value + player->fovadd;
|
|
return std::max(MINFOV*FRACUNIT, std::min(fov, MAXFOV*FRACUNIT));
|
|
}
|
|
|
|
static void R_CalcFov(fixed_t playerfov)
|
|
{
|
|
angle_t fov = FixedAngle(playerfov/2) + ANGLE_90;
|
|
fovtan = FixedMul(FINETANGENT(fov >> ANGLETOFINESHIFT), viewmorph.zoomneeded);
|
|
if (splitscreen == 1) // Splitscreen FOV should be adjusted to maintain expected vertical view
|
|
fovtan = 17*fovtan/10;
|
|
|
|
projection = projectiony = FixedDiv(centerxfrac, fovtan);
|
|
}
|
|
|
|
static void R_InitViewMapping(void)
|
|
{
|
|
R_InitTextureMapping();
|
|
|
|
if (rendermode == render_soft)
|
|
{
|
|
// this is only used for planes rendering in software mode
|
|
INT32 j = viewheight*16;
|
|
|
|
for (INT32 i = 0; i < j; i++)
|
|
{
|
|
fixed_t dy = abs(i - viewheight*8) << FRACBITS;
|
|
dy = FixedMul(dy, fovtan);
|
|
yslopetab[i] = FixedDiv(centerx*FRACUNIT, dy);
|
|
}
|
|
}
|
|
|
|
// setup sky scaling
|
|
R_SetSkyScale();
|
|
}
|
|
|
|
static void R_SetupViewBuffers(void)
|
|
{
|
|
R_CalcFov(cv_fov.value);
|
|
R_InitViewBuffer();
|
|
R_InitViewMapping();
|
|
}
|
|
|
|
static void R_SetFov(fixed_t playerfov)
|
|
{
|
|
R_CalcFov(playerfov);
|
|
R_InitViewMapping();
|
|
}
|
|
|
|
//
|
|
// R_Init
|
|
//
|
|
|
|
void R_Init(void)
|
|
{
|
|
// screensize independent
|
|
//I_OutputMsg("\nR_InitData");
|
|
R_InitData();
|
|
|
|
R_SetViewSize(); // setsizeneeded is set true
|
|
|
|
// this is now done by SCR_Recalc() at the first mode set
|
|
//I_OutputMsg("\nR_InitLightTables");
|
|
R_InitLightTables();
|
|
|
|
//I_OutputMsg("\nR_InitTranslucencyTables\n");
|
|
//R_InitTranslucencyTables();
|
|
|
|
R_InitDrawNodes();
|
|
|
|
framecount = 0;
|
|
}
|
|
|
|
//
|
|
// R_IsPointInSubsector, same as above but returns 0 if not in subsector
|
|
//
|
|
subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y)
|
|
{
|
|
node_t *node;
|
|
INT32 side, i;
|
|
size_t nodenum;
|
|
subsector_t *ret;
|
|
|
|
nodenum = numnodes - 1;
|
|
|
|
while (!(nodenum & NF_SUBSECTOR))
|
|
{
|
|
node = &nodes[nodenum];
|
|
side = R_PointOnSide(x, y, node);
|
|
nodenum = node->children[side];
|
|
}
|
|
|
|
ret = &subsectors[nodenum & ~NF_SUBSECTOR];
|
|
for (i = 0; i < ret->numlines; i++)
|
|
if (P_PointOnLineSide(x, y, segs[ret->firstline + i].linedef) != segs[ret->firstline + i].side)
|
|
return NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// R_SetupFrame
|
|
//
|
|
|
|
mobj_t *viewmobj = NULL;
|
|
|
|
static void R_SetupCommonFrame(player_t * player)
|
|
{
|
|
newview->player = player;
|
|
|
|
newview->x += quake.x;
|
|
newview->y += quake.y;
|
|
newview->z += quake.z;
|
|
|
|
newview->roll = R_ViewRollAngle(player);
|
|
|
|
R_InterpolateView(R_GetTimeFrac(RTF_CAMERA), false);
|
|
}
|
|
|
|
static void R_SetupAimingFrame(player_t *player, camera_t *thiscam)
|
|
{
|
|
if (player->awayviewtics && player->awayviewmobj
|
|
&& !(thiscam && thiscam->freecam)) // dont force this if we wanna freecam!
|
|
{
|
|
newview->aim = player->awayviewaiming;
|
|
newview->angle = player->awayviewmobj->angle;
|
|
}
|
|
else if (thiscam && thiscam->chase)
|
|
{
|
|
newview->aim = thiscam->aiming;
|
|
newview->angle = thiscam->angle;
|
|
}
|
|
else if (P_IsLocalPlayer(player) && !demo.playback && player->playerstate != PST_DEAD)
|
|
{
|
|
newview->aim = localaiming[viewssnum];
|
|
newview->angle = localangle[viewssnum];
|
|
}
|
|
else
|
|
{
|
|
newview->aim = player->aiming;
|
|
newview->angle = player->mo->angle;
|
|
}
|
|
}
|
|
|
|
static void R_SetupSkyScale(player_t *player, camera_t *thiscam, mapheader_t *mh)
|
|
{
|
|
if (!mh->skybox_scalex && !mh->skybox_scaley && !mh->skybox_scalez)
|
|
return;
|
|
|
|
auto setup_skyscale = [&](fixed_t mox, fixed_t moy, fixed_t zscale)
|
|
{
|
|
if (skyboxmo[1])
|
|
{
|
|
fixed_t x = 0, y = 0;
|
|
|
|
if (mh->skybox_scalex > 0)
|
|
x = (mox - skyboxmo[1]->x) / mh->skybox_scalex;
|
|
else if (mh->skybox_scalex < 0)
|
|
x = (mox - skyboxmo[1]->x) * -mh->skybox_scalex;
|
|
|
|
if (mh->skybox_scaley > 0)
|
|
y = (moy - skyboxmo[1]->y) / mh->skybox_scaley;
|
|
else if (mh->skybox_scaley < 0)
|
|
y = (moy - skyboxmo[1]->y) * -mh->skybox_scaley;
|
|
|
|
switch (viewmobj->angle)
|
|
{
|
|
case 0:
|
|
newview->x += x;
|
|
newview->y += y;
|
|
break;
|
|
case ANGLE_90:
|
|
newview->x -= y;
|
|
newview->y += x;
|
|
break;
|
|
case ANGLE_180:
|
|
newview->x -= x;
|
|
newview->y -= y;
|
|
break;
|
|
case ANGLE_270:
|
|
newview->x += y;
|
|
newview->y -= x;
|
|
break;
|
|
default:
|
|
angle_t ang = viewmobj->angle>>ANGLETOFINESHIFT;
|
|
newview->x += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang));
|
|
newview->y += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mh->skybox_scalez > 0)
|
|
newview->z += zscale / mh->skybox_scalez;
|
|
else if (mh->skybox_scalez < 0)
|
|
newview->z += zscale * -mh->skybox_scalez;
|
|
};
|
|
|
|
if (player->awayviewtics && player->awayviewmobj)
|
|
{
|
|
setup_skyscale(player->awayviewmobj->x, player->awayviewmobj->y, (player->awayviewmobj->z + 20*FRACUNIT));
|
|
}
|
|
else if (thiscam && thiscam->chase)
|
|
{
|
|
setup_skyscale(thiscam->x, thiscam->y, (thiscam->z + (thiscam->height>>1)));
|
|
}
|
|
else
|
|
{
|
|
setup_skyscale(player->mo->x, player->mo->y, player->viewz);
|
|
}
|
|
}
|
|
|
|
void R_SkyboxFrame(UINT8 pnum)
|
|
{
|
|
player_t *player = &players[displayplayers[pnum]];
|
|
camera_t *thiscam = &camera[pnum];
|
|
mapheader_t *mh = mapheaderinfo[gamemap-1];
|
|
|
|
R_SetViewContext(static_cast<viewcontext_e>(VIEWCONTEXT_SKY1 + pnum));
|
|
|
|
// cut-away view stuff
|
|
newview->sky = true;
|
|
viewmobj = skyboxmo[0];
|
|
|
|
#ifdef PARANOIA
|
|
if (P_MobjWasRemoved(viewmobj))
|
|
{
|
|
const size_t playeri = (size_t)(player - players);
|
|
I_Error("R_SkyboxFrame: viewmobj null (player %s)", sizeu1(playeri));
|
|
}
|
|
#endif
|
|
|
|
R_SetupAimingFrame(player, thiscam);
|
|
|
|
if (!P_MobjWasRemoved(viewmobj))
|
|
{
|
|
newview->angle += viewmobj->angle;
|
|
|
|
newview->x = viewmobj->x;
|
|
newview->y = viewmobj->y;
|
|
newview->z = (viewmobj->spawnpoint) ? (((fixed_t)viewmobj->spawnpoint->angle) << FRACBITS) : 0;
|
|
}
|
|
|
|
if (mh)
|
|
{
|
|
R_SetupSkyScale(player, thiscam, mh);
|
|
}
|
|
|
|
R_SetupCommonFrame(player);
|
|
}
|
|
|
|
void R_SetupFrame(UINT8 pnum, boolean skybox)
|
|
{
|
|
player_t *player = &players[displayplayers[pnum]];
|
|
camera_t *thiscam = &camera[pnum];
|
|
boolean chasecam = (cv_chasecam[pnum].value);
|
|
|
|
R_SetViewContext(static_cast<viewcontext_e>(VIEWCONTEXT_PLAYER1 + pnum));
|
|
|
|
if (player->spectator || (thiscam && thiscam->freecam))
|
|
{
|
|
// Free flying spectator uses demo freecam. This
|
|
// requires chasecam to be enabled.
|
|
chasecam = true;
|
|
}
|
|
|
|
if (player->playerstate == PST_DEAD || player->exiting)
|
|
chasecam = true; // force chasecam on
|
|
|
|
if (thiscam)
|
|
{
|
|
if (thiscam->reset)
|
|
{
|
|
R_ResetViewInterpolation(pnum);
|
|
thiscam->reset = false;
|
|
}
|
|
|
|
if (chasecam && !thiscam->chase)
|
|
{
|
|
P_ResetCamera(player, thiscam);
|
|
thiscam->chase = true;
|
|
}
|
|
else if (!chasecam)
|
|
thiscam->chase = false;
|
|
}
|
|
|
|
newview->sky = !skybox;
|
|
|
|
R_SetupAimingFrame(player, thiscam);
|
|
|
|
if (player->awayviewtics && player->awayviewmobj // cut-away view stuff
|
|
&& !(thiscam && thiscam->freecam)) // dont force this when we wanna freecam!
|
|
{
|
|
viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN whos altview man?
|
|
I_Assert(viewmobj != NULL);
|
|
|
|
newview->x = viewmobj->x;
|
|
newview->y = viewmobj->y;
|
|
newview->z = viewmobj->z + 20*FRACUNIT;
|
|
|
|
R_SetupCommonFrame(player);
|
|
}
|
|
else if (thiscam && chasecam) // use outside cam view
|
|
{
|
|
viewmobj = NULL;
|
|
I_Assert(thiscam != NULL);
|
|
|
|
newview->x = thiscam->x;
|
|
newview->y = thiscam->y;
|
|
newview->z = thiscam->z + (thiscam->height>>1);
|
|
|
|
R_SetupCommonFrame(player);
|
|
}
|
|
else if (player->mo) // use the player's eyes view
|
|
{
|
|
viewmobj = player->mo;
|
|
I_Assert(viewmobj != NULL);
|
|
|
|
newview->x = viewmobj->x;
|
|
newview->y = viewmobj->y;
|
|
newview->z = player->viewz;
|
|
|
|
R_SetupCommonFrame(player);
|
|
}
|
|
}
|
|
|
|
static void R_PortalFrame(portal_t *portal)
|
|
{
|
|
viewx = portal->viewx;
|
|
viewy = portal->viewy;
|
|
viewz = portal->viewz;
|
|
|
|
viewangle = portal->viewangle;
|
|
viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
|
|
viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
|
|
|
|
portalclipstart = portal->start;
|
|
portalclipend = portal->end;
|
|
|
|
if (portal->clipline != -1)
|
|
{
|
|
portalclipline = &lines[portal->clipline];
|
|
viewsector = portalcullsector = portalclipline->frontsector;
|
|
}
|
|
else
|
|
{
|
|
portalclipline = NULL;
|
|
portalcullsector = NULL;
|
|
viewsector = R_PointInSubsectorFast(viewx, viewy)->sector;
|
|
}
|
|
}
|
|
|
|
static void Mask_Pre(maskcount_t* m)
|
|
{
|
|
m->drawsegs[0] = ds_p - drawsegs;
|
|
m->vissprites[0] = visspritecount;
|
|
m->viewx = viewx;
|
|
m->viewy = viewy;
|
|
m->viewz = viewz;
|
|
m->viewsector = viewsector;
|
|
}
|
|
|
|
static void Mask_Post(maskcount_t* m)
|
|
{
|
|
m->drawsegs[1] = ds_p - drawsegs;
|
|
m->vissprites[1] = visspritecount;
|
|
}
|
|
|
|
// ================
|
|
// R_RenderView
|
|
// ================
|
|
|
|
// viewx, viewy, viewangle, all that good stuff must be set
|
|
static void R_RenderViewpoint(maskcount_t* mask, boolean drawprecip)
|
|
{
|
|
Mask_Pre(mask);
|
|
|
|
curdrawsegs = ds_p;
|
|
|
|
R_RenderBSPNode((INT32)numnodes - 1);
|
|
|
|
if (drawprecip)
|
|
R_AddPrecipitationSprites();
|
|
|
|
Mask_Post(mask);
|
|
}
|
|
|
|
// FAB NOTE FOR WIN32 PORT !! I'm not finished already,
|
|
// but I suspect network may have problems with the video buffer being locked
|
|
// for all duration of rendering, and being released only once at the end..
|
|
// I mean, there is a win16lock() or something that lasts all the rendering,
|
|
// so maybe we should release screen lock before each netupdate below..?
|
|
|
|
static fixed_t viewfov[MAXSPLITSCREENPLAYERS];
|
|
|
|
void R_RenderPlayerView(player_t *player)
|
|
{
|
|
INT32 nummasks = 1;
|
|
maskcount_t* masks = static_cast<maskcount_t*>(malloc(sizeof(maskcount_t)));
|
|
const boolean skybox = (skyboxmo[0] && cv_skybox.value);
|
|
|
|
// load previous saved value of skyVisible for the player
|
|
skyVisible = skyVisiblePerPlayer[viewssnum];
|
|
|
|
const fixed_t fov = R_GetPlayerFov(player);
|
|
|
|
if (viewfov[viewssnum] != fov)
|
|
{
|
|
viewfov[viewssnum] = fov;
|
|
R_SetFov(fov);
|
|
}
|
|
|
|
Portal_InitList();
|
|
|
|
PS_START_TIMING(ps_skyboxtime);
|
|
if (skybox && skyVisible)
|
|
{
|
|
R_SkyboxFrame(viewssnum);
|
|
|
|
R_ClearClipSegs();
|
|
R_ClearDrawSegs();
|
|
R_ClearSegTables();
|
|
R_ClearPlanes();
|
|
R_ClearSprites();
|
|
|
|
R_SetColumnContext(COLUMNCONTEXT_FLUSH);
|
|
R_RenderViewpoint(&masks[nummasks - 1], false);
|
|
|
|
R_ClipSprites(drawsegs, NULL);
|
|
R_ResetColumnBuffer();
|
|
R_SetColumnContext(COLUMNCONTEXT_DIRECT);
|
|
R_DrawSkyPlanes(); // draw the fucker again to prevent some artifacts
|
|
R_DrawPlanes();
|
|
R_DrawMasked(masks, nummasks);
|
|
}
|
|
PS_STOP_TIMING(ps_skyboxtime);
|
|
|
|
R_SetupFrame(viewssnum, skybox);
|
|
skyVisible = false;
|
|
framecount++;
|
|
validcount++;
|
|
|
|
// Clear buffers.
|
|
R_ClearPlanes();
|
|
|
|
if (viewmorph.use)
|
|
{
|
|
portalclipstart = viewmorph.x1;
|
|
portalclipend = viewwidth-viewmorph.x1-1;
|
|
R_PortalClearClipSegs(portalclipstart, portalclipend);
|
|
memcpy(ceilingclip, viewmorph.ceilingclip, sizeof(INT16)*vid.width);
|
|
memcpy(floorclip, viewmorph.floorclip, sizeof(INT16)*vid.width);
|
|
}
|
|
else
|
|
{
|
|
portalclipstart = 0;
|
|
portalclipend = viewwidth;
|
|
R_ClearClipSegs();
|
|
}
|
|
R_ClearDrawSegs();
|
|
R_ClearSegTables();
|
|
R_ClearSprites();
|
|
|
|
// check for new console commands.
|
|
NetUpdate();
|
|
|
|
R_SetColumnContext(COLUMNCONTEXT_FLUSH);
|
|
ps_numbspcalls.value.i = ps_numpolyobjects.value.i = ps_numdrawnodes.value.i = 0;
|
|
PS_START_TIMING(ps_bsptime);
|
|
R_RenderViewpoint(&masks[nummasks - 1], true);
|
|
PS_STOP_TIMING(ps_bsptime);
|
|
PS_START_TIMING(ps_sw_spritecliptime);
|
|
R_ClipSprites(drawsegs, NULL);
|
|
PS_STOP_TIMING(ps_sw_spritecliptime);
|
|
R_ResetColumnBuffer();
|
|
|
|
ps_numsprites.value.i = numvisiblesprites;
|
|
|
|
PS_START_TIMING(ps_sw_portaltime);
|
|
// Portal rendering. Hijacks the BSP traversal.
|
|
if (portal_base)
|
|
{
|
|
portal_t *portal;
|
|
|
|
for (portal = portal_base; portal; portal = portal_base)
|
|
{
|
|
portalrender = portal->pass; // Recursiveness depth.
|
|
|
|
R_ClearFFloorClips();
|
|
|
|
// Apply the viewpoint stored for the portal.
|
|
R_PortalFrame(portal);
|
|
|
|
// Hack in the clipsegs to delimit the starting
|
|
// clipping for sprites and possibly other similar
|
|
// future items.
|
|
R_PortalClearClipSegs(portal->start, portal->end);
|
|
|
|
// Hack in the top/bottom clip values for the window
|
|
// that were previously stored.
|
|
Portal_ClipApply(portal);
|
|
|
|
validcount++;
|
|
|
|
masks = static_cast<maskcount_t*>(realloc(masks, (++nummasks)*sizeof(maskcount_t)));
|
|
|
|
// Render the BSP from the new viewpoint, and clip
|
|
// any sprites with the new clipsegs and window.
|
|
R_RenderViewpoint(&masks[nummasks - 1], true);
|
|
|
|
R_ClipSprites(ds_p - (masks[nummasks - 1].drawsegs[1] - masks[nummasks - 1].drawsegs[0]), portal);
|
|
R_ResetColumnBuffer();
|
|
|
|
Portal_Remove(portal);
|
|
}
|
|
}
|
|
PS_STOP_TIMING(ps_sw_portaltime);
|
|
|
|
R_SetColumnContext(COLUMNCONTEXT_DIRECT);
|
|
PS_START_TIMING(ps_sw_planetime);
|
|
if (!skybox)
|
|
R_DrawSkyPlanes();
|
|
R_DrawPlanes();
|
|
PS_STOP_TIMING(ps_sw_planetime);
|
|
// draw mid texture and sprite
|
|
// And now 3D floors/sides!
|
|
PS_START_TIMING(ps_sw_maskedtime);
|
|
R_DrawMasked(masks, nummasks);
|
|
PS_STOP_TIMING(ps_sw_maskedtime);
|
|
free(masks);
|
|
|
|
// Check for new console commands.
|
|
NetUpdate();
|
|
|
|
// save value to skyVisiblePerPlayer
|
|
// this is so that P1 can't affect whether P2 can see a skybox or not, or vice versa
|
|
skyVisiblePerPlayer[viewssnum] = skyVisible;
|
|
}
|
|
|
|
// =========================================================================
|
|
// ENGINE COMMANDS & VARS
|
|
// =========================================================================
|
|
|
|
void R_RegisterEngineStuff(void)
|
|
{
|
|
INT32 i;
|
|
|
|
CV_RegisterVar(&cv_gravity);
|
|
CV_RegisterVar(&cv_tailspickup);
|
|
CV_RegisterVar(&cv_soniccd);
|
|
CV_RegisterVar(&cv_allowmlook);
|
|
CV_RegisterVar(&cv_homremoval);
|
|
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
CV_RegisterVar(&cv_flipcam[i]);
|
|
}
|
|
|
|
CV_RegisterVar(&cv_flipcammode);
|
|
|
|
// Enough for dedicated server
|
|
if (dedicated)
|
|
return;
|
|
|
|
CV_RegisterVar(&cv_precachetextures);
|
|
|
|
CV_RegisterVar(&cv_translucency);
|
|
CV_RegisterVar(&cv_drawdist);
|
|
CV_RegisterVar(&cv_drawdist_precip);
|
|
CV_RegisterVar(&cv_lessprecip);
|
|
|
|
CV_RegisterVar(&cv_fovchange);
|
|
CV_RegisterVar(&cv_fov);
|
|
|
|
CV_RegisterVar(&cv_shadow);
|
|
CV_RegisterVar(&cv_shadowoffs);
|
|
CV_RegisterVar(&cv_skybox);
|
|
CV_RegisterVar(&cv_skydome);
|
|
CV_RegisterVar(&cv_ffloorclip);
|
|
CV_RegisterVar(&cv_spriteclip);
|
|
CV_RegisterVar(&cv_secbright);
|
|
CV_RegisterVar(&cv_softcyancut);
|
|
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
CV_RegisterVar(&cv_chasecam[i]);
|
|
|
|
CV_RegisterVar(&cv_cam_dist[i]);
|
|
CV_RegisterVar(&cv_cam_height[i]);
|
|
CV_RegisterVar(&cv_cam_pitch[i]);
|
|
CV_RegisterVar(&cv_cam_speed[i]);
|
|
CV_RegisterVar(&cv_cam_timeover[i]);
|
|
CV_RegisterVar(&cv_cam_lookbackmom[i]);
|
|
CV_RegisterVar(&cv_cam_still[i]);
|
|
CV_RegisterVar(&cv_cam_rotate[i]);
|
|
|
|
CV_RegisterVar(&cv_freecam_speed[i]);
|
|
}
|
|
|
|
CV_RegisterVar(&cv_tilting);
|
|
CV_RegisterVar(&cv_quaketilt);
|
|
CV_RegisterVar(&cv_tiltsmoothing);
|
|
CV_RegisterVar(&cv_actionmovie);
|
|
|
|
CV_RegisterVar(&cv_screenquake);
|
|
|
|
CV_RegisterVar(&cv_driftsparkpulse);
|
|
CV_RegisterVar(&cv_gravstretch);
|
|
CV_RegisterVar(&cv_sloperoll);
|
|
CV_RegisterVar(&cv_sliptideroll);
|
|
CV_RegisterVar(&cv_sloperolldist);
|
|
CV_RegisterVar(&cv_sparkroll);
|
|
CV_RegisterVar(&cv_stairjank);
|
|
CV_RegisterVar(&cv_stairjanksfx);
|
|
|
|
CV_RegisterVar(&cv_showhud);
|
|
CV_RegisterVar(&cv_translucenthud);
|
|
CV_RegisterVar(&cv_uncappedhud);
|
|
|
|
CV_RegisterVar(&cv_maxportals);
|
|
|
|
CV_RegisterVar(&cv_randomdirlight);
|
|
|
|
CV_RegisterVar(&cv_maxinterpdist);
|
|
CV_RegisterVar(&cv_playerfade);
|
|
|
|
CV_RegisterVar(&cv_ripplewater);
|
|
|
|
// Frame interpolation/uncapped
|
|
CV_RegisterVar(&cv_fpscap);
|
|
CV_RegisterVar(&cv_fpscapbg);
|
|
}
|