mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-11-29 13:30:34 +01:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
498 lines
12 KiB
C
498 lines
12 KiB
C
/*
|
|
cl_view.c - player rendering positioning
|
|
Copyright (C) 2009 Uncle Mike
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "client.h"
|
|
#include "const.h"
|
|
#include "entity_types.h"
|
|
#include "vgui_draw.h"
|
|
#include "sound.h"
|
|
#include "input.h" // touch
|
|
#include "platform/platform.h" // GL_UpdateSwapInterval
|
|
|
|
/*
|
|
===============
|
|
V_CalcViewRect
|
|
|
|
calc frame rectangle (Quake1 style)
|
|
===============
|
|
*/
|
|
void V_CalcViewRect( void )
|
|
{
|
|
qboolean full = false;
|
|
int sb_lines;
|
|
float size;
|
|
|
|
// intermission is always full screen
|
|
if( cl.intermission ) size = 120.0f;
|
|
else size = scr_viewsize->value;
|
|
|
|
if( size >= 120.0f )
|
|
sb_lines = 0; // no status bar at all
|
|
else if( size >= 110.0f )
|
|
sb_lines = 24; // no inventory
|
|
else sb_lines = 48;
|
|
|
|
if( scr_viewsize->value >= 100.0f )
|
|
{
|
|
full = true;
|
|
size = 100.0f;
|
|
}
|
|
else size = scr_viewsize->value;
|
|
|
|
if( cl.intermission )
|
|
{
|
|
size = 100.0f;
|
|
sb_lines = 0;
|
|
full = true;
|
|
}
|
|
size /= 100.0f;
|
|
|
|
clgame.viewport[2] = refState.width * size;
|
|
clgame.viewport[3] = refState.height * size;
|
|
|
|
if( clgame.viewport[3] > refState.height - sb_lines )
|
|
clgame.viewport[3] = refState.height - sb_lines;
|
|
if( clgame.viewport[3] > refState.height )
|
|
clgame.viewport[3] = refState.height;
|
|
|
|
clgame.viewport[0] = ( refState.width - clgame.viewport[2] ) / 2;
|
|
if( full ) clgame.viewport[1] = 0;
|
|
else clgame.viewport[1] = ( refState.height - sb_lines - clgame.viewport[3] ) / 2;
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
V_SetupViewModel
|
|
===============
|
|
*/
|
|
void V_SetupViewModel( void )
|
|
{
|
|
cl_entity_t *view = &clgame.viewent;
|
|
player_info_t *info = &cl.players[cl.playernum];
|
|
|
|
if( !cl.local.weaponstarttime )
|
|
cl.local.weaponstarttime = cl.time;
|
|
|
|
// setup the viewent variables
|
|
view->curstate.colormap = (info->topcolor & 0xFFFF)|((info->bottomcolor << 8) & 0xFFFF);
|
|
view->curstate.number = cl.playernum + 1;
|
|
view->index = cl.playernum + 1;
|
|
view->model = CL_ModelHandle( cl.local.viewmodel );
|
|
view->curstate.modelindex = cl.local.viewmodel;
|
|
view->curstate.sequence = cl.local.weaponsequence;
|
|
view->curstate.rendermode = kRenderNormal;
|
|
|
|
// alias models has another animation methods
|
|
if( view->model && view->model->type == mod_studio )
|
|
{
|
|
view->curstate.animtime = cl.local.weaponstarttime;
|
|
view->curstate.frame = 0.0f;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
V_SetRefParams
|
|
===============
|
|
*/
|
|
void V_SetRefParams( ref_params_t *fd )
|
|
{
|
|
memset( fd, 0, sizeof( ref_params_t ));
|
|
|
|
// probably this is not needs
|
|
VectorCopy( refState.vieworg, fd->vieworg );
|
|
VectorCopy( refState.viewangles, fd->viewangles );
|
|
|
|
fd->frametime = host.frametime;
|
|
fd->time = cl.time;
|
|
|
|
fd->intermission = cl.intermission;
|
|
fd->paused = (cl.paused != 0);
|
|
fd->spectator = (cls.spectator != 0);
|
|
fd->onground = (cl.local.onground != -1);
|
|
fd->waterlevel = cl.local.waterlevel;
|
|
|
|
VectorCopy( cl.simvel, fd->simvel );
|
|
VectorCopy( cl.simorg, fd->simorg );
|
|
|
|
VectorCopy( cl.viewheight, fd->viewheight );
|
|
fd->idealpitch = cl.local.idealpitch;
|
|
|
|
VectorCopy( cl.viewangles, fd->cl_viewangles );
|
|
fd->health = cl.local.health;
|
|
VectorCopy( cl.crosshairangle, fd->crosshairangle );
|
|
fd->viewsize = scr_viewsize->value;
|
|
|
|
VectorCopy( cl.punchangle, fd->punchangle );
|
|
fd->maxclients = cl.maxclients;
|
|
fd->viewentity = cl.viewentity;
|
|
fd->playernum = cl.playernum;
|
|
fd->max_entities = clgame.maxEntities;
|
|
fd->demoplayback = cls.demoplayback;
|
|
fd->hardware = 1; // OpenGL
|
|
|
|
if( cl.first_frame || cl.skip_interp )
|
|
{
|
|
cl.first_frame = false; // now can be unlocked
|
|
fd->smoothing = true; // NOTE: currently this used to prevent ugly un-duck effect while level is changed
|
|
}
|
|
else fd->smoothing = cl.local.pushmsec; // enable smoothing in multiplayer by server request (AMX uses)
|
|
|
|
// get pointers to movement vars and user cmd
|
|
fd->movevars = &clgame.movevars;
|
|
fd->cmd = cl.cmd;
|
|
|
|
// setup viewport
|
|
fd->viewport[0] = clgame.viewport[0];
|
|
fd->viewport[1] = clgame.viewport[1];
|
|
fd->viewport[2] = clgame.viewport[2];
|
|
fd->viewport[3] = clgame.viewport[3];
|
|
|
|
fd->onlyClientDraw = 0; // reset clientdraw
|
|
fd->nextView = 0; // reset nextview
|
|
}
|
|
|
|
/*
|
|
===============
|
|
V_MergeOverviewRefdef
|
|
|
|
merge refdef with overview settings
|
|
===============
|
|
*/
|
|
void V_RefApplyOverview( ref_viewpass_t *rvp )
|
|
{
|
|
ref_overview_t *ov = &clgame.overView;
|
|
float aspect;
|
|
float size_x, size_y;
|
|
vec2_t mins, maxs;
|
|
|
|
if( !CL_IsDevOverviewMode( ))
|
|
return;
|
|
|
|
// NOTE: Xash3D may use 16:9 or 16:10 aspects
|
|
aspect = (float)refState.width / (float)refState.height;
|
|
|
|
size_x = fabs( 8192.0f / ov->flZoom );
|
|
size_y = fabs( 8192.0f / (ov->flZoom * aspect ));
|
|
|
|
// compute rectangle
|
|
ov->xLeft = -(size_x / 2);
|
|
ov->xRight = (size_x / 2);
|
|
ov->yTop = -(size_y / 2);
|
|
ov->yBottom = (size_y / 2);
|
|
|
|
if( CL_IsDevOverviewMode() == 1 )
|
|
{
|
|
Con_NPrintf( 0, " Overview: Zoom %.2f, Map Origin (%.2f, %.2f, %.2f), Z Min %.2f, Z Max %.2f, Rotated %i\n",
|
|
ov->flZoom, ov->origin[0], ov->origin[1], ov->origin[2], ov->zNear, ov->zFar, ov->rotated );
|
|
}
|
|
|
|
VectorCopy( ov->origin, rvp->vieworigin );
|
|
rvp->vieworigin[2] = ov->zFar + ov->zNear;
|
|
Vector2Copy( rvp->vieworigin, mins );
|
|
Vector2Copy( rvp->vieworigin, maxs );
|
|
|
|
mins[!ov->rotated] += ov->xLeft;
|
|
maxs[!ov->rotated] += ov->xRight;
|
|
mins[ov->rotated] += ov->yTop;
|
|
maxs[ov->rotated] += ov->yBottom;
|
|
|
|
rvp->viewangles[0] = 90.0f;
|
|
rvp->viewangles[1] = 90.0f;
|
|
rvp->viewangles[2] = (ov->rotated) ? (ov->flZoom < 0.0f) ? 180.0f : 0.0f : (ov->flZoom < 0.0f) ? -90.0f : 90.0f;
|
|
|
|
SetBits( rvp->flags, RF_DRAW_OVERVIEW );
|
|
|
|
ref.dllFuncs.GL_OrthoBounds( mins, maxs );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
V_GetRefParams
|
|
=============
|
|
*/
|
|
void V_GetRefParams( ref_params_t *fd, ref_viewpass_t *rvp )
|
|
{
|
|
// part1: deniable updates
|
|
VectorCopy( fd->simvel, cl.simvel );
|
|
VectorCopy( fd->simorg, cl.simorg );
|
|
VectorCopy( fd->punchangle, cl.punchangle );
|
|
VectorCopy( fd->viewheight, cl.viewheight );
|
|
|
|
// part2: really used updates
|
|
VectorCopy( fd->crosshairangle, cl.crosshairangle );
|
|
VectorCopy( fd->cl_viewangles, cl.viewangles );
|
|
|
|
// setup ref_viewpass
|
|
rvp->viewport[0] = fd->viewport[0];
|
|
rvp->viewport[1] = fd->viewport[1];
|
|
rvp->viewport[2] = fd->viewport[2];
|
|
rvp->viewport[3] = fd->viewport[3];
|
|
|
|
VectorCopy( fd->vieworg, rvp->vieworigin );
|
|
VectorCopy( fd->viewangles, rvp->viewangles );
|
|
|
|
rvp->viewentity = fd->viewentity;
|
|
|
|
// calc FOV
|
|
rvp->fov_x = bound( 10.0f, cl.local.scr_fov, 150.0f ); // this is a final fov value
|
|
|
|
// first we need to compute FOV and other things that needs for frustum properly work
|
|
rvp->fov_y = V_CalcFov( &rvp->fov_x, clgame.viewport[2], clgame.viewport[3] );
|
|
|
|
// adjust FOV for widescreen
|
|
if( refState.wideScreen && r_adjust_fov->value )
|
|
V_AdjustFov( &rvp->fov_x, &rvp->fov_y, clgame.viewport[2], clgame.viewport[3], false );
|
|
|
|
rvp->flags = 0;
|
|
|
|
if( fd->onlyClientDraw )
|
|
SetBits( rvp->flags, RF_ONLY_CLIENTDRAW );
|
|
SetBits( rvp->flags, RF_DRAW_WORLD );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
V_PreRender
|
|
|
|
==================
|
|
*/
|
|
qboolean V_PreRender( void )
|
|
{
|
|
// too early
|
|
if( !ref.initialized )
|
|
return false;
|
|
|
|
if( host.status == HOST_NOFOCUS )
|
|
return false;
|
|
|
|
if( host.status == HOST_SLEEP )
|
|
return false;
|
|
|
|
// if the screen is disabled (loading plaque is up)
|
|
if( cls.disable_screen )
|
|
{
|
|
if(( host.realtime - cls.disable_screen ) > cl_timeout->value )
|
|
{
|
|
Con_Reportf( "V_PreRender: loading plaque timed out\n" );
|
|
cls.disable_screen = 0.0f;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ref.dllFuncs.R_BeginFrame( !cl.paused && ( cls.state == ca_active ));
|
|
|
|
GL_UpdateSwapInterval( );
|
|
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
==================
|
|
V_RenderView
|
|
|
|
==================
|
|
*/
|
|
void V_RenderView( void )
|
|
{
|
|
ref_params_t rp;
|
|
ref_viewpass_t rvp;
|
|
int viewnum = 0;
|
|
|
|
if( !cl.video_prepped || ( !ui_renderworld->value && UI_IsVisible() && !cl.background ))
|
|
return; // still loading
|
|
|
|
V_CalcViewRect (); // compute viewport rectangle
|
|
V_SetRefParams( &rp );
|
|
V_SetupViewModel ();
|
|
ref.dllFuncs.R_Set2DMode( false );
|
|
SCR_DirtyScreen();
|
|
ref.dllFuncs.GL_BackendStartFrame ();
|
|
|
|
do
|
|
{
|
|
clgame.dllFuncs.pfnCalcRefdef( &rp );
|
|
V_GetRefParams( &rp, &rvp );
|
|
V_RefApplyOverview( &rvp );
|
|
|
|
if( viewnum == 0 && FBitSet( rvp.flags, RF_ONLY_CLIENTDRAW ))
|
|
{
|
|
ref.dllFuncs.R_ClearScreen();
|
|
}
|
|
|
|
GL_RenderFrame( &rvp );
|
|
S_UpdateFrame( &rvp );
|
|
viewnum++;
|
|
|
|
} while( rp.nextView );
|
|
|
|
// draw debug triangles on a server
|
|
SV_DrawDebugTriangles ();
|
|
ref.dllFuncs.GL_BackendEndFrame ();
|
|
}
|
|
|
|
#define POINT_SIZE 16.0f
|
|
#define NODE_INTERVAL_X(x) (x * 16.0f)
|
|
#define NODE_INTERVAL_Y(x) (x * 16.0f)
|
|
|
|
void R_DrawLeafNode( float x, float y, float scale )
|
|
{
|
|
float downScale = scale * 0.25f;// * POINT_SIZE;
|
|
|
|
ref.dllFuncs.R_DrawStretchPic( x - downScale * 0.5f, y - downScale * 0.5f, downScale, downScale, 0, 0, 1, 1, R_GetBuiltinTexture( REF_PARTICLE_TEXTURE ) );
|
|
}
|
|
|
|
void R_DrawNodeConnection( float x, float y, float x2, float y2 )
|
|
{
|
|
ref.dllFuncs.Begin( TRI_LINES );
|
|
ref.dllFuncs.Vertex3f( x, y, 0 );
|
|
ref.dllFuncs.Vertex3f( x2, y2, 0 );
|
|
ref.dllFuncs.End( );
|
|
}
|
|
|
|
void R_ShowTree_r( mnode_t *node, float x, float y, float scale, int shownodes, mleaf_t *viewleaf )
|
|
{
|
|
float downScale = scale * 0.8f;
|
|
|
|
downScale = Q_max( downScale, 1.0f );
|
|
|
|
if( !node ) return;
|
|
|
|
world.recursion_level++;
|
|
|
|
if( node->contents < 0 )
|
|
{
|
|
mleaf_t *leaf = (mleaf_t *)node;
|
|
|
|
if( world.recursion_level > world.max_recursion )
|
|
world.max_recursion = world.recursion_level;
|
|
|
|
if( shownodes == 1 )
|
|
{
|
|
if( cl.worldmodel->leafs == leaf )
|
|
ref.dllFuncs.Color4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
|
else if( viewleaf && viewleaf == leaf )
|
|
ref.dllFuncs.Color4f( 1.0f, 0.0f, 0.0f, 1.0f );
|
|
else ref.dllFuncs.Color4f( 0.0f, 1.0f, 0.0f, 1.0f );
|
|
R_DrawLeafNode( x, y, scale );
|
|
}
|
|
world.recursion_level--;
|
|
return;
|
|
}
|
|
|
|
if( shownodes == 1 )
|
|
{
|
|
ref.dllFuncs.Color4f( 0.0f, 0.0f, 1.0f, 1.0f );
|
|
R_DrawLeafNode( x, y, scale );
|
|
}
|
|
else if( shownodes == 2 )
|
|
{
|
|
R_DrawNodeConnection( x, y, x - scale, y + scale );
|
|
R_DrawNodeConnection( x, y, x + scale, y + scale );
|
|
}
|
|
|
|
R_ShowTree_r( node->children[1], x - scale, y + scale, downScale, shownodes, viewleaf );
|
|
R_ShowTree_r( node->children[0], x + scale, y + scale, downScale, shownodes, viewleaf );
|
|
|
|
world.recursion_level--;
|
|
}
|
|
|
|
void R_ShowTree( void )
|
|
{
|
|
float x = (float)((refState.width - (int)POINT_SIZE) >> 1);
|
|
float y = NODE_INTERVAL_Y(1.0f);
|
|
mleaf_t *viewleaf;
|
|
|
|
if( !cl.worldmodel || !CVAR_TO_BOOL( r_showtree ))
|
|
return;
|
|
|
|
world.recursion_level = 0;
|
|
viewleaf = Mod_PointInLeaf( refState.vieworg, cl.worldmodel->nodes );
|
|
|
|
//pglEnable( GL_BLEND );
|
|
//pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
//pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
|
|
//pglLineWidth( 2.0f );
|
|
ref.dllFuncs.Color4f( 1, 0.7f, 0, 1.0f );
|
|
//pglDisable( GL_TEXTURE_2D );
|
|
R_ShowTree_r( cl.worldmodel->nodes, x, y, world.max_recursion * 3.5f, 2, viewleaf );
|
|
//pglEnable( GL_TEXTURE_2D );
|
|
//pglLineWidth( 1.0f );
|
|
|
|
R_ShowTree_r( cl.worldmodel->nodes, x, y, world.max_recursion * 3.5f, 1, viewleaf );
|
|
|
|
Con_NPrintf( 0, "max recursion %d\n", world.max_recursion );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
V_PostRender
|
|
|
|
==================
|
|
*/
|
|
void V_PostRender( void )
|
|
{
|
|
static double oldtime;
|
|
qboolean draw_2d = false;
|
|
|
|
ref.dllFuncs.R_AllowFog( false );
|
|
ref.dllFuncs.R_Set2DMode( true );
|
|
|
|
if( cls.state == ca_active && cls.signon == SIGNONS && cls.scrshot_action != scrshot_mapshot )
|
|
{
|
|
SCR_TileClear();
|
|
CL_DrawHUD( CL_ACTIVE );
|
|
VGui_Paint();
|
|
}
|
|
|
|
switch( cls.scrshot_action )
|
|
{
|
|
case scrshot_inactive:
|
|
case scrshot_normal:
|
|
case scrshot_snapshot:
|
|
draw_2d = true;
|
|
break;
|
|
}
|
|
|
|
if( draw_2d )
|
|
{
|
|
SCR_RSpeeds();
|
|
SCR_NetSpeeds();
|
|
SCR_DrawNetGraph();
|
|
SV_DrawOrthoTriangles();
|
|
CL_DrawDemoRecording();
|
|
CL_DrawHUD( CL_CHANGELEVEL );
|
|
ref.dllFuncs.R_ShowTextures();
|
|
R_ShowTree();
|
|
Con_DrawConsole();
|
|
UI_UpdateMenu( host.realtime );
|
|
Con_DrawVersion();
|
|
Con_DrawDebug(); // must be last
|
|
Touch_Draw();
|
|
OSK_Draw();
|
|
|
|
S_ExtraUpdate();
|
|
}
|
|
|
|
SCR_MakeScreenShot();
|
|
ref.dllFuncs.R_AllowFog( true );
|
|
ref.dllFuncs.R_EndFrame();
|
|
}
|