|
|
|
/*
|
|
|
|
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 "gl_local.h"
|
|
|
|
#include "vgui_draw.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
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.0 )
|
|
|
|
{
|
|
|
|
full = true;
|
|
|
|
size = 100.0f;
|
|
|
|
}
|
|
|
|
else size = scr_viewsize->value;
|
|
|
|
|
|
|
|
if( cl.intermission )
|
|
|
|
{
|
|
|
|
size = 100.0f;
|
|
|
|
sb_lines = 0;
|
|
|
|
full = true;
|
|
|
|
}
|
|
|
|
size /= 100.0;
|
|
|
|
|
|
|
|
clgame.viewport[2] = glState.width * size;
|
|
|
|
clgame.viewport[3] = glState.height * size;
|
|
|
|
|
|
|
|
if( clgame.viewport[3] > glState.height - sb_lines )
|
|
|
|
clgame.viewport[3] = glState.height - sb_lines;
|
|
|
|
if( clgame.viewport[3] > glState.height )
|
|
|
|
clgame.viewport[3] = glState.height;
|
|
|
|
|
|
|
|
clgame.viewport[0] = ( glState.width - clgame.viewport[2] ) / 2;
|
|
|
|
if( full ) clgame.viewport[1] = 0;
|
|
|
|
else clgame.viewport[1] = ( glState.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( RI.vieworg, fd->vieworg );
|
|
|
|
VectorCopy( RI.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.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)glState.width / (float)glState.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 );
|
|
|
|
|
|
|
|
Mod_SetOrthoBounds( 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( glState.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( !glw_state.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 )
|
|
|
|
{
|
|
|
|
MsgDev( D_ERROR, "V_PreRender: loading plaque timed out\n" );
|
|
|
|
cls.disable_screen = 0.0f;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
R_BeginFrame( !cl.paused );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
V_RenderView
|
|
|
|
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
void V_RenderView( void )
|
|
|
|
{
|
|
|
|
ref_params_t rp;
|
|
|
|
ref_viewpass_t rvp;
|
|
|
|
int viewnum = 0;
|
|
|
|
|
|
|
|
if( !cl.video_prepped || ( UI_IsVisible() && !cl.background ))
|
|
|
|
return; // still loading
|
|
|
|
|
|
|
|
V_CalcViewRect (); // compute viewport rectangle
|
|
|
|
V_SetRefParams( &rp );
|
|
|
|
V_SetupViewModel ();
|
|
|
|
R_Set2DMode( false );
|
|
|
|
SCR_DirtyScreen();
|
|
|
|
GL_BackendStartFrame ();
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
clgame.dllFuncs.pfnCalcRefdef( &rp );
|
|
|
|
V_GetRefParams( &rp, &rvp );
|
|
|
|
V_RefApplyOverview( &rvp );
|
|
|
|
|
|
|
|
if( viewnum == 0 && FBitSet( rvp.flags, RF_ONLY_CLIENTDRAW ))
|
|
|
|
{
|
|
|
|
pglClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
|
|
|
|
pglClear( GL_COLOR_BUFFER_BIT );
|
|
|
|
}
|
|
|
|
|
|
|
|
R_RenderFrame( &rvp );
|
|
|
|
viewnum++;
|
|
|
|
|
|
|
|
} while( rp.nextView );
|
|
|
|
|
|
|
|
// draw debug triangles on a server
|
|
|
|
SV_DrawDebugTriangles ();
|
|
|
|
GL_BackendEndFrame ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
V_PostRender
|
|
|
|
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
void V_PostRender( void )
|
|
|
|
{
|
|
|
|
static double oldtime;
|
|
|
|
qboolean draw_2d = false;
|
|
|
|
|
|
|
|
R_AllowFog( false );
|
|
|
|
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 );
|
|
|
|
R_ShowTextures();
|
|
|
|
Con_DrawConsole();
|
|
|
|
UI_UpdateMenu( host.realtime );
|
|
|
|
Con_DrawVersion();
|
|
|
|
Con_DrawDebug(); // must be last
|
|
|
|
|
|
|
|
S_ExtraUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
SCR_MakeScreenShot();
|
|
|
|
R_AllowFog( true );
|
|
|
|
R_EndFrame();
|
|
|
|
}
|