992 lines
24 KiB
992 lines
24 KiB
//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//============================================================================= |
|
|
|
#include <assert.h> |
|
#include "hud.h" |
|
#include "cl_util.h" |
|
#include "const.h" |
|
#include "com_model.h" |
|
#include "studio.h" |
|
#include "entity_state.h" |
|
#include "cl_entity.h" |
|
#include "dlight.h" |
|
#include "triangleapi.h" |
|
|
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <memory.h> |
|
#include <math.h> |
|
|
|
#include "studio_util.h" |
|
#include "r_studioint.h" |
|
|
|
#include "StudioModelRenderer.h" |
|
#include "GameStudioModelRenderer.h" |
|
|
|
// Predicted values saved off in hl_weapons.cpp |
|
void Game_GetSequence( int *seq, int *gaitseq ); |
|
void Game_GetOrientation( float *o, float *a ); |
|
|
|
float g_flStartScaleTime; |
|
int iPrevRenderState; |
|
int iRenderStateChanged; |
|
|
|
// Global engine <-> studio model rendering code interface |
|
extern engine_studio_api_t IEngineStudio; |
|
|
|
typedef struct |
|
{ |
|
vec3_t origin; |
|
vec3_t angles; |
|
|
|
vec3_t realangles; |
|
|
|
float animtime; |
|
float frame; |
|
int sequence; |
|
int gaitsequence; |
|
float framerate; |
|
|
|
int m_fSequenceLoops; |
|
int m_fSequenceFinished; |
|
|
|
byte controller[ 4 ]; |
|
byte blending[ 2 ]; |
|
|
|
latchedvars_t lv; |
|
} client_anim_state_t; |
|
|
|
static client_anim_state_t g_state; |
|
static client_anim_state_t g_clientstate; |
|
|
|
// The renderer object, created on the stack. |
|
CGameStudioModelRenderer g_StudioRenderer; |
|
/* |
|
==================== |
|
CGameStudioModelRenderer |
|
|
|
==================== |
|
*/ |
|
CGameStudioModelRenderer::CGameStudioModelRenderer( void ) |
|
{ |
|
// If you want to predict animations locally, set this to TRUE |
|
// NOTE: The animation code is somewhat broken, but gives you a sense for how |
|
// to do client side animation of the predicted player in a third person game. |
|
m_bLocal = false; |
|
} |
|
|
|
/* |
|
==================== |
|
StudioSetupBones |
|
|
|
==================== |
|
*/ |
|
void CGameStudioModelRenderer::StudioSetupBones ( void ) |
|
{ |
|
int i; |
|
double f; |
|
|
|
mstudiobone_t *pbones; |
|
mstudioseqdesc_t *pseqdesc; |
|
mstudioanim_t *panim; |
|
|
|
static float pos[MAXSTUDIOBONES][3]; |
|
static vec4_t q[MAXSTUDIOBONES]; |
|
float bonematrix[3][4]; |
|
|
|
static float pos2[MAXSTUDIOBONES][3]; |
|
static vec4_t q2[MAXSTUDIOBONES]; |
|
static float pos3[MAXSTUDIOBONES][3]; |
|
static vec4_t q3[MAXSTUDIOBONES]; |
|
static float pos4[MAXSTUDIOBONES][3]; |
|
static vec4_t q4[MAXSTUDIOBONES]; |
|
|
|
// Use default bone setup for nonplayers |
|
if ( !m_pCurrentEntity->player ) |
|
{ |
|
CStudioModelRenderer::StudioSetupBones(); |
|
return; |
|
} |
|
|
|
// Bound sequence number. |
|
if ( m_pCurrentEntity->curstate.sequence >= m_pStudioHeader->numseq ) |
|
{ |
|
m_pCurrentEntity->curstate.sequence = 0; |
|
} |
|
|
|
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pCurrentEntity->curstate.sequence; |
|
|
|
if ( m_pPlayerInfo && m_pPlayerInfo->gaitsequence != 0 ) |
|
{ |
|
f = m_pPlayerInfo->gaitframe; |
|
} |
|
else |
|
{ |
|
f = StudioEstimateFrame( pseqdesc ); |
|
} |
|
|
|
// This game knows how to do three way blending |
|
if ( pseqdesc->numblends == 3 ) |
|
{ |
|
float s; |
|
|
|
// Get left anim |
|
panim = StudioGetAnim( m_pRenderModel, pseqdesc ); |
|
|
|
// Blending is 0-127 == Left to Middle, 128 to 255 == Middle to right |
|
if ( m_pCurrentEntity->curstate.blending[0] <= 127 ) |
|
{ |
|
StudioCalcRotations( pos, q, pseqdesc, panim, f ); |
|
|
|
// Scale 0-127 blending up to 0-255 |
|
s = m_pCurrentEntity->curstate.blending[0]; |
|
s = ( s * 2.0 ); |
|
} |
|
else |
|
{ |
|
|
|
// Skip ahead to middle |
|
panim += m_pStudioHeader->numbones; |
|
|
|
StudioCalcRotations( pos, q, pseqdesc, panim, f ); |
|
|
|
// Scale 127-255 blending up to 0-255 |
|
s = m_pCurrentEntity->curstate.blending[0]; |
|
s = 2.0 * ( s - 127.0 ); |
|
} |
|
|
|
// Normalize interpolant |
|
s /= 255.0; |
|
|
|
// Go to middle or right |
|
panim += m_pStudioHeader->numbones; |
|
|
|
StudioCalcRotations( pos2, q2, pseqdesc, panim, f ); |
|
|
|
// Spherically interpolate the bones |
|
StudioSlerpBones( q, pos, q2, pos2, s ); |
|
} |
|
else |
|
{ |
|
panim = StudioGetAnim( m_pRenderModel, pseqdesc ); |
|
StudioCalcRotations( pos, q, pseqdesc, panim, f ); |
|
} |
|
|
|
// Are we in the process of transitioning from one sequence to another. |
|
if ( m_fDoInterp && |
|
m_pCurrentEntity->latched.sequencetime && |
|
( m_pCurrentEntity->latched.sequencetime + 0.2 > m_clTime ) && |
|
( m_pCurrentEntity->latched.prevsequence < m_pStudioHeader->numseq )) |
|
{ |
|
// blend from last sequence |
|
static float pos1b[MAXSTUDIOBONES][3]; |
|
static vec4_t q1b[MAXSTUDIOBONES]; |
|
float s; |
|
|
|
// Blending value into last sequence |
|
unsigned char prevseqblending = m_pCurrentEntity->latched.prevseqblending[ 0 ]; |
|
|
|
// Point at previous sequenece |
|
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pCurrentEntity->latched.prevsequence; |
|
|
|
// Know how to do three way blends |
|
if ( pseqdesc->numblends == 3 ) |
|
{ |
|
float s; |
|
|
|
// Get left animation |
|
panim = StudioGetAnim( m_pRenderModel, pseqdesc ); |
|
|
|
if ( prevseqblending <= 127 ) |
|
{ |
|
// Set up bones based on final frame of previous sequence |
|
StudioCalcRotations( pos1b, q1b, pseqdesc, panim, m_pCurrentEntity->latched.prevframe ); |
|
|
|
s = prevseqblending; |
|
s = ( s * 2.0 ); |
|
} |
|
else |
|
{ |
|
// Skip to middle blend |
|
panim += m_pStudioHeader->numbones; |
|
|
|
StudioCalcRotations( pos1b, q1b, pseqdesc, panim, m_pCurrentEntity->latched.prevframe ); |
|
|
|
s = prevseqblending; |
|
s = 2.0 * ( s - 127.0 ); |
|
} |
|
|
|
// Normalize |
|
s /= 255.0; |
|
|
|
panim += m_pStudioHeader->numbones; |
|
StudioCalcRotations( pos2, q2, pseqdesc, panim, m_pCurrentEntity->latched.prevframe ); |
|
|
|
// Interpolate bones |
|
StudioSlerpBones( q1b, pos1b, q2, pos2, s ); |
|
} |
|
else |
|
{ |
|
panim = StudioGetAnim( m_pRenderModel, pseqdesc ); |
|
// clip prevframe |
|
StudioCalcRotations( pos1b, q1b, pseqdesc, panim, m_pCurrentEntity->latched.prevframe ); |
|
} |
|
|
|
// Now blend last frame of previous sequence with current sequence. |
|
s = 1.0 - (m_clTime - m_pCurrentEntity->latched.sequencetime) / 0.2; |
|
StudioSlerpBones( q, pos, q1b, pos1b, s ); |
|
} |
|
else |
|
{ |
|
m_pCurrentEntity->latched.prevframe = f; |
|
} |
|
|
|
// Now convert quaternions and bone positions into matrices |
|
pbones = (mstudiobone_t *)((byte *)m_pStudioHeader + m_pStudioHeader->boneindex); |
|
|
|
for (i = 0; i < m_pStudioHeader->numbones; i++) |
|
{ |
|
QuaternionMatrix( q[i], bonematrix ); |
|
|
|
bonematrix[0][3] = pos[i][0]; |
|
bonematrix[1][3] = pos[i][1]; |
|
bonematrix[2][3] = pos[i][2]; |
|
|
|
if (pbones[i].parent == -1) |
|
{ |
|
if ( IEngineStudio.IsHardware() ) |
|
{ |
|
ConcatTransforms ((*m_protationmatrix), bonematrix, (*m_pbonetransform)[i]); |
|
ConcatTransforms ((*m_protationmatrix), bonematrix, (*m_plighttransform)[i]); |
|
} |
|
else |
|
{ |
|
ConcatTransforms ((*m_paliastransform), bonematrix, (*m_pbonetransform)[i]); |
|
ConcatTransforms ((*m_protationmatrix), bonematrix, (*m_plighttransform)[i]); |
|
} |
|
|
|
// Apply client-side effects to the transformation matrix |
|
StudioFxTransform( m_pCurrentEntity, (*m_pbonetransform)[i] ); |
|
} |
|
else |
|
{ |
|
ConcatTransforms ((*m_pbonetransform)[pbones[i].parent], bonematrix, (*m_pbonetransform)[i]); |
|
ConcatTransforms ((*m_plighttransform)[pbones[i].parent], bonematrix, (*m_plighttransform)[i]); |
|
} |
|
} |
|
} |
|
|
|
/* |
|
==================== |
|
StudioEstimateGait |
|
|
|
==================== |
|
*/ |
|
void CGameStudioModelRenderer::StudioEstimateGait( entity_state_t *pplayer ) |
|
{ |
|
float dt; |
|
vec3_t est_velocity; |
|
|
|
dt = (m_clTime - m_clOldTime); |
|
dt = max( 0.0, dt ); |
|
dt = min( 1.0, dt ); |
|
|
|
if (dt == 0 || m_pPlayerInfo->renderframe == m_nFrameCount) |
|
{ |
|
m_flGaitMovement = 0; |
|
return; |
|
} |
|
|
|
// VectorAdd( pplayer->velocity, pplayer->prediction_error, est_velocity ); |
|
if ( m_fGaitEstimation ) |
|
{ |
|
VectorSubtract( m_pCurrentEntity->origin, m_pPlayerInfo->prevgaitorigin, est_velocity ); |
|
VectorCopy( m_pCurrentEntity->origin, m_pPlayerInfo->prevgaitorigin ); |
|
m_flGaitMovement = Length( est_velocity ); |
|
if (dt <= 0 || m_flGaitMovement / dt < 5) |
|
{ |
|
m_flGaitMovement = 0; |
|
est_velocity[0] = 0; |
|
est_velocity[1] = 0; |
|
} |
|
} |
|
else |
|
{ |
|
VectorCopy( pplayer->velocity, est_velocity ); |
|
m_flGaitMovement = Length( est_velocity ) * dt; |
|
} |
|
|
|
if (est_velocity[1] == 0 && est_velocity[0] == 0) |
|
{ |
|
float flYawDiff = m_pCurrentEntity->angles[YAW] - m_pPlayerInfo->gaityaw; |
|
flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360; |
|
if (flYawDiff > 180) |
|
flYawDiff -= 360; |
|
if (flYawDiff < -180) |
|
flYawDiff += 360; |
|
|
|
if (dt < 0.25) |
|
flYawDiff *= dt * 4; |
|
else |
|
flYawDiff *= dt; |
|
|
|
m_pPlayerInfo->gaityaw += flYawDiff; |
|
m_pPlayerInfo->gaityaw = m_pPlayerInfo->gaityaw - (int)(m_pPlayerInfo->gaityaw / 360) * 360; |
|
|
|
m_flGaitMovement = 0; |
|
} |
|
else |
|
{ |
|
m_pPlayerInfo->gaityaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI); |
|
if (m_pPlayerInfo->gaityaw > 180) |
|
m_pPlayerInfo->gaityaw = 180; |
|
if (m_pPlayerInfo->gaityaw < -180) |
|
m_pPlayerInfo->gaityaw = -180; |
|
} |
|
|
|
} |
|
|
|
/* |
|
==================== |
|
StudioProcessGait |
|
|
|
==================== |
|
*/ |
|
void CGameStudioModelRenderer::StudioProcessGait( entity_state_t *pplayer ) |
|
{ |
|
mstudioseqdesc_t *pseqdesc; |
|
float dt; |
|
float flYaw; // view direction relative to movement |
|
|
|
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + m_pCurrentEntity->curstate.sequence; |
|
|
|
m_pCurrentEntity->angles[PITCH] = 0; |
|
m_pCurrentEntity->latched.prevangles[PITCH] = m_pCurrentEntity->angles[PITCH]; |
|
|
|
dt = (m_clTime - m_clOldTime); |
|
dt = max( 0.0, dt ); |
|
dt = min( 1.0, dt ); |
|
|
|
StudioEstimateGait( pplayer ); |
|
|
|
// calc side to side turning |
|
flYaw = m_pCurrentEntity->angles[YAW] - m_pPlayerInfo->gaityaw; |
|
|
|
flYaw = fmod( flYaw, 360.0 ); |
|
|
|
if (flYaw < -180) |
|
{ |
|
flYaw = flYaw + 360; |
|
} |
|
else if (flYaw > 180) |
|
{ |
|
flYaw = flYaw - 360; |
|
} |
|
|
|
float maxyaw = 120.0; |
|
|
|
if (flYaw > maxyaw) |
|
{ |
|
m_pPlayerInfo->gaityaw = m_pPlayerInfo->gaityaw - 180; |
|
m_flGaitMovement = -m_flGaitMovement; |
|
flYaw = flYaw - 180; |
|
} |
|
else if (flYaw < -maxyaw) |
|
{ |
|
m_pPlayerInfo->gaityaw = m_pPlayerInfo->gaityaw + 180; |
|
m_flGaitMovement = -m_flGaitMovement; |
|
flYaw = flYaw + 180; |
|
} |
|
|
|
float blend_yaw = ( flYaw / 90.0 ) * 128.0 + 127.0; |
|
blend_yaw = min( 255.0, blend_yaw ); |
|
blend_yaw = max( 0.0, blend_yaw ); |
|
|
|
blend_yaw = 255.0 - blend_yaw; |
|
|
|
m_pCurrentEntity->curstate.blending[0] = (int)(blend_yaw); |
|
m_pCurrentEntity->latched.prevblending[0] = m_pCurrentEntity->curstate.blending[0]; |
|
m_pCurrentEntity->latched.prevseqblending[0] = m_pCurrentEntity->curstate.blending[0]; |
|
|
|
m_pCurrentEntity->angles[YAW] = m_pPlayerInfo->gaityaw; |
|
if (m_pCurrentEntity->angles[YAW] < -0) |
|
{ |
|
m_pCurrentEntity->angles[YAW] += 360; |
|
} |
|
m_pCurrentEntity->latched.prevangles[YAW] = m_pCurrentEntity->angles[YAW]; |
|
|
|
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqindex) + pplayer->gaitsequence; |
|
|
|
// Calc gait frame |
|
if (pseqdesc->linearmovement[0] > 0) |
|
{ |
|
m_pPlayerInfo->gaitframe += (m_flGaitMovement / pseqdesc->linearmovement[0]) * pseqdesc->numframes; |
|
} |
|
else |
|
{ |
|
m_pPlayerInfo->gaitframe += pseqdesc->fps * dt * m_pCurrentEntity->curstate.framerate; |
|
} |
|
|
|
// Do modulo |
|
m_pPlayerInfo->gaitframe = m_pPlayerInfo->gaitframe - (int)(m_pPlayerInfo->gaitframe / pseqdesc->numframes) * pseqdesc->numframes; |
|
if (m_pPlayerInfo->gaitframe < 0) |
|
{ |
|
m_pPlayerInfo->gaitframe += pseqdesc->numframes; |
|
} |
|
} |
|
|
|
/* |
|
============================== |
|
SavePlayerState |
|
|
|
For local player, in third person, we need to store real render data and then |
|
setup for with fake/client side animation data |
|
============================== |
|
*/ |
|
void CGameStudioModelRenderer::SavePlayerState( entity_state_t *pplayer ) |
|
{ |
|
client_anim_state_t *st; |
|
cl_entity_t *ent = IEngineStudio.GetCurrentEntity(); |
|
assert( ent ); |
|
if ( !ent ) |
|
return; |
|
|
|
st = &g_state; |
|
|
|
st->angles = ent->curstate.angles; |
|
st->origin = ent->curstate.origin; |
|
|
|
st->realangles = ent->angles; |
|
|
|
st->sequence = ent->curstate.sequence; |
|
st->gaitsequence = pplayer->gaitsequence; |
|
st->animtime = ent->curstate.animtime; |
|
st->frame = ent->curstate.frame; |
|
st->framerate = ent->curstate.framerate; |
|
memcpy( st->blending, ent->curstate.blending, 2 ); |
|
memcpy( st->controller, ent->curstate.controller, 4 ); |
|
|
|
st->lv = ent->latched; |
|
} |
|
|
|
void GetSequenceInfo( void *pmodel, client_anim_state_t *pev, float *pflFrameRate, float *pflGroundSpeed ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if (! pstudiohdr) |
|
return; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
|
|
if (pev->sequence >= pstudiohdr->numseq) |
|
{ |
|
*pflFrameRate = 0.0; |
|
*pflGroundSpeed = 0.0; |
|
return; |
|
} |
|
|
|
pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence; |
|
|
|
if (pseqdesc->numframes > 1) |
|
{ |
|
*pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1); |
|
*pflGroundSpeed = sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] ); |
|
*pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1); |
|
} |
|
else |
|
{ |
|
*pflFrameRate = 256.0; |
|
*pflGroundSpeed = 0.0; |
|
} |
|
} |
|
|
|
int GetSequenceFlags( void *pmodel, client_anim_state_t *pev ) |
|
{ |
|
studiohdr_t *pstudiohdr; |
|
|
|
pstudiohdr = (studiohdr_t *)pmodel; |
|
if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq ) |
|
return 0; |
|
|
|
mstudioseqdesc_t *pseqdesc; |
|
pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence; |
|
|
|
return pseqdesc->flags; |
|
} |
|
|
|
float StudioFrameAdvance ( client_anim_state_t *st, float framerate, float flInterval ) |
|
{ |
|
if (flInterval == 0.0) |
|
{ |
|
flInterval = (gEngfuncs.GetClientTime() - st->animtime); |
|
if (flInterval <= 0.001) |
|
{ |
|
st->animtime = gEngfuncs.GetClientTime(); |
|
return 0.0; |
|
} |
|
} |
|
if (!st->animtime) |
|
flInterval = 0.0; |
|
|
|
st->frame += flInterval * framerate * st->framerate; |
|
st->animtime = gEngfuncs.GetClientTime(); |
|
|
|
if (st->frame < 0.0 || st->frame >= 256.0) |
|
{ |
|
if ( st->m_fSequenceLoops ) |
|
st->frame -= (int)(st->frame / 256.0) * 256.0; |
|
else |
|
st->frame = (st->frame < 0.0) ? 0 : 255; |
|
st->m_fSequenceFinished = TRUE; // just in case it wasn't caught in GetEvents |
|
} |
|
|
|
return flInterval; |
|
} |
|
|
|
/* |
|
============================== |
|
SetupClientAnimation |
|
|
|
Called to set up local player's animation values |
|
============================== |
|
*/ |
|
void CGameStudioModelRenderer::SetupClientAnimation( entity_state_t *pplayer ) |
|
{ |
|
static double oldtime; |
|
double curtime, dt; |
|
|
|
client_anim_state_t *st; |
|
float fr, gs; |
|
|
|
cl_entity_t *ent = IEngineStudio.GetCurrentEntity(); |
|
assert( ent ); |
|
if ( !ent ) |
|
return; |
|
|
|
curtime = gEngfuncs.GetClientTime(); |
|
dt = curtime - oldtime; |
|
dt = min( 1.0, max( 0.0, dt ) ); |
|
|
|
oldtime = curtime; |
|
st = &g_clientstate; |
|
|
|
st->framerate = 1.0; |
|
|
|
int oldseq = st->sequence; |
|
Game_GetSequence( &st->sequence, &st->gaitsequence ); //CVAR_GET_FLOAT( "sequence" ); |
|
Game_GetOrientation( (float *)&st->origin, (float *)&st->angles ); |
|
st->realangles = st->angles; |
|
|
|
if ( st->sequence != oldseq ) |
|
{ |
|
st->frame = 0.0; |
|
st->lv.prevsequence = oldseq; |
|
st->lv.sequencetime = st->animtime; |
|
|
|
memcpy( st->lv.prevseqblending, st->blending, 2 ); |
|
memcpy( st->lv.prevcontroller, st->controller, 4 ); |
|
} |
|
|
|
void *pmodel = (studiohdr_t *)IEngineStudio.Mod_Extradata( ent->model ); |
|
|
|
GetSequenceInfo( pmodel, st, &fr, &gs ); |
|
st->m_fSequenceLoops = ((GetSequenceFlags( pmodel, st ) & STUDIO_LOOPING) != 0); |
|
StudioFrameAdvance( st, fr, dt ); |
|
|
|
// gEngfuncs.Con_Printf( "gs %i frame %f\n", st->gaitsequence, st->frame ); |
|
|
|
ent->angles = st->realangles; |
|
ent->curstate.angles = st->angles; |
|
ent->curstate.origin = st->origin; |
|
|
|
ent->curstate.sequence = st->sequence; |
|
pplayer->gaitsequence = st->gaitsequence; |
|
ent->curstate.animtime = st->animtime; |
|
ent->curstate.frame = st->frame; |
|
ent->curstate.framerate = st->framerate; |
|
memcpy( ent->curstate.blending, st->blending, 2 ); |
|
memcpy( ent->curstate.controller, st->controller, 4 ); |
|
|
|
ent->latched = st->lv; |
|
} |
|
|
|
/* |
|
============================== |
|
RestorePlayerState |
|
|
|
Called to restore original player state information |
|
============================== |
|
*/ |
|
void CGameStudioModelRenderer::RestorePlayerState( entity_state_t *pplayer ) |
|
{ |
|
client_anim_state_t *st; |
|
cl_entity_t *ent = IEngineStudio.GetCurrentEntity(); |
|
assert( ent ); |
|
if ( !ent ) |
|
return; |
|
|
|
st = &g_clientstate; |
|
|
|
st->angles = ent->curstate.angles; |
|
st->origin = ent->curstate.origin; |
|
st->realangles = ent->angles; |
|
|
|
st->sequence = ent->curstate.sequence; |
|
st->gaitsequence = pplayer->gaitsequence; |
|
st->animtime = ent->curstate.animtime; |
|
st->frame = ent->curstate.frame; |
|
st->framerate = ent->curstate.framerate; |
|
memcpy( st->blending, ent->curstate.blending, 2 ); |
|
memcpy( st->controller, ent->curstate.controller, 4 ); |
|
|
|
st->lv = ent->latched; |
|
|
|
st = &g_state; |
|
|
|
ent->curstate.angles = st->angles; |
|
ent->curstate.origin = st->origin; |
|
ent->angles = st->realangles; |
|
|
|
ent->curstate.sequence = st->sequence; |
|
pplayer->gaitsequence = st->gaitsequence; |
|
ent->curstate.animtime = st->animtime; |
|
ent->curstate.frame = st->frame; |
|
ent->curstate.framerate = st->framerate; |
|
memcpy( ent->curstate.blending, st->blending, 2 ); |
|
memcpy( ent->curstate.controller, st->controller, 4 ); |
|
|
|
ent->latched = st->lv; |
|
} |
|
|
|
/* |
|
============================== |
|
StudioDrawPlayer |
|
|
|
============================== |
|
*/ |
|
int CGameStudioModelRenderer::StudioDrawPlayer( int flags, entity_state_t *pplayer ) |
|
{ |
|
int iret = 0; |
|
|
|
bool isLocalPlayer = false; |
|
|
|
// Set up for client? |
|
if ( m_bLocal && IEngineStudio.GetCurrentEntity() == gEngfuncs.GetLocalPlayer() ) |
|
{ |
|
isLocalPlayer = true; |
|
} |
|
|
|
if ( isLocalPlayer ) |
|
{ |
|
// Store original data |
|
SavePlayerState( pplayer ); |
|
|
|
// Copy in client side animation data |
|
SetupClientAnimation( pplayer ); |
|
} |
|
|
|
// Call real draw function |
|
iret = _StudioDrawPlayer( flags, pplayer ); |
|
|
|
// Restore for client? |
|
if ( isLocalPlayer ) |
|
{ |
|
// Restore the original data for the player |
|
RestorePlayerState( pplayer ); |
|
} |
|
|
|
return iret; |
|
} |
|
|
|
/* |
|
==================== |
|
_StudioDrawPlayer |
|
|
|
==================== |
|
*/ |
|
int CGameStudioModelRenderer::_StudioDrawPlayer( int flags, entity_state_t *pplayer ) |
|
{ |
|
alight_t lighting; |
|
vec3_t dir; |
|
|
|
m_pCurrentEntity = IEngineStudio.GetCurrentEntity(); |
|
IEngineStudio.GetTimes( &m_nFrameCount, &m_clTime, &m_clOldTime ); |
|
IEngineStudio.GetViewInfo( m_vRenderOrigin, m_vUp, m_vRight, m_vNormal ); |
|
IEngineStudio.GetAliasScale( &m_fSoftwareXScale, &m_fSoftwareYScale ); |
|
|
|
m_nPlayerIndex = pplayer->number - 1; |
|
|
|
if (m_nPlayerIndex < 0 || m_nPlayerIndex >= gEngfuncs.GetMaxClients()) |
|
return 0; |
|
|
|
m_pRenderModel = IEngineStudio.SetupPlayerModel( m_nPlayerIndex ); |
|
if (m_pRenderModel == NULL) |
|
return 0; |
|
|
|
m_pStudioHeader = (studiohdr_t *)IEngineStudio.Mod_Extradata (m_pRenderModel); |
|
IEngineStudio.StudioSetHeader( m_pStudioHeader ); |
|
IEngineStudio.SetRenderModel( m_pRenderModel ); |
|
|
|
if (pplayer->gaitsequence) |
|
{ |
|
vec3_t orig_angles; |
|
m_pPlayerInfo = IEngineStudio.PlayerInfo( m_nPlayerIndex ); |
|
|
|
VectorCopy( m_pCurrentEntity->angles, orig_angles ); |
|
|
|
StudioProcessGait( pplayer ); |
|
|
|
m_pPlayerInfo->gaitsequence = pplayer->gaitsequence; |
|
m_pPlayerInfo = NULL; |
|
|
|
StudioSetUpTransform( 0 ); |
|
VectorCopy( orig_angles, m_pCurrentEntity->angles ); |
|
} |
|
else |
|
{ |
|
m_pCurrentEntity->curstate.controller[0] = 127; |
|
m_pCurrentEntity->curstate.controller[1] = 127; |
|
m_pCurrentEntity->curstate.controller[2] = 127; |
|
m_pCurrentEntity->curstate.controller[3] = 127; |
|
m_pCurrentEntity->latched.prevcontroller[0] = m_pCurrentEntity->curstate.controller[0]; |
|
m_pCurrentEntity->latched.prevcontroller[1] = m_pCurrentEntity->curstate.controller[1]; |
|
m_pCurrentEntity->latched.prevcontroller[2] = m_pCurrentEntity->curstate.controller[2]; |
|
m_pCurrentEntity->latched.prevcontroller[3] = m_pCurrentEntity->curstate.controller[3]; |
|
|
|
m_pPlayerInfo = IEngineStudio.PlayerInfo( m_nPlayerIndex ); |
|
m_pPlayerInfo->gaitsequence = 0; |
|
|
|
StudioSetUpTransform( 0 ); |
|
} |
|
|
|
if (flags & STUDIO_RENDER) |
|
{ |
|
// see if the bounding box lets us trivially reject, also sets |
|
if (!IEngineStudio.StudioCheckBBox ()) |
|
return 0; |
|
|
|
(*m_pModelsDrawn)++; |
|
(*m_pStudioModelCount)++; // render data cache cookie |
|
|
|
if (m_pStudioHeader->numbodyparts == 0) |
|
return 1; |
|
} |
|
|
|
m_pPlayerInfo = IEngineStudio.PlayerInfo( m_nPlayerIndex ); |
|
StudioSetupBones( ); |
|
StudioSaveBones( ); |
|
m_pPlayerInfo->renderframe = m_nFrameCount; |
|
|
|
m_pPlayerInfo = NULL; |
|
|
|
if (flags & STUDIO_EVENTS) |
|
{ |
|
StudioCalcAttachments( ); |
|
IEngineStudio.StudioClientEvents( ); |
|
// copy attachments into global entity array |
|
if ( m_pCurrentEntity->index > 0 ) |
|
{ |
|
cl_entity_t *ent = gEngfuncs.GetEntityByIndex( m_pCurrentEntity->index ); |
|
|
|
memcpy( ent->attachment, m_pCurrentEntity->attachment, sizeof( vec3_t ) * 4 ); |
|
} |
|
} |
|
|
|
if (flags & STUDIO_RENDER) |
|
{ |
|
/* |
|
if (m_pCvarHiModels->value && m_pRenderModel != m_pCurrentEntity->model ) |
|
{ |
|
// show highest resolution multiplayer model |
|
m_pCurrentEntity->curstate.body = 255; |
|
} |
|
|
|
if (!(m_pCvarDeveloper->value == 0 && gEngfuncs.GetMaxClients() == 1 ) && ( m_pRenderModel == m_pCurrentEntity->model ) ) |
|
{ |
|
m_pCurrentEntity->curstate.body = 1; // force helmet |
|
} |
|
*/ |
|
|
|
lighting.plightvec = dir; |
|
IEngineStudio.StudioDynamicLight(m_pCurrentEntity, &lighting ); |
|
|
|
IEngineStudio.StudioEntityLight( &lighting ); |
|
|
|
// model and frame independant |
|
IEngineStudio.StudioSetupLighting (&lighting); |
|
|
|
m_pPlayerInfo = IEngineStudio.PlayerInfo( m_nPlayerIndex ); |
|
|
|
// get remap colors |
|
m_nTopColor = m_pPlayerInfo->topcolor; |
|
if (m_nTopColor < 0) |
|
m_nTopColor = 0; |
|
if (m_nTopColor > 360) |
|
m_nTopColor = 360; |
|
m_nBottomColor = m_pPlayerInfo->bottomcolor; |
|
if (m_nBottomColor < 0) |
|
m_nBottomColor = 0; |
|
if (m_nBottomColor > 360) |
|
m_nBottomColor = 360; |
|
|
|
IEngineStudio.StudioSetRemapColors( m_nTopColor, m_nBottomColor ); |
|
|
|
StudioRenderModel( ); |
|
m_pPlayerInfo = NULL; |
|
|
|
if (pplayer->weaponmodel) |
|
{ |
|
cl_entity_t saveent = *m_pCurrentEntity; |
|
|
|
model_t *pweaponmodel = IEngineStudio.GetModelByIndex( pplayer->weaponmodel ); |
|
|
|
m_pStudioHeader = (studiohdr_t *)IEngineStudio.Mod_Extradata (pweaponmodel); |
|
IEngineStudio.StudioSetHeader( m_pStudioHeader ); |
|
|
|
StudioMergeBones( pweaponmodel); |
|
|
|
IEngineStudio.StudioSetupLighting (&lighting); |
|
|
|
StudioRenderModel( ); |
|
|
|
StudioCalcAttachments( ); |
|
|
|
*m_pCurrentEntity = saveent; |
|
} |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
/* |
|
==================== |
|
Studio_FxTransform |
|
|
|
==================== |
|
*/ |
|
void CGameStudioModelRenderer::StudioFxTransform( cl_entity_t *ent, float transform[3][4] ) |
|
{ |
|
switch( ent->curstate.renderfx ) |
|
{ |
|
case kRenderFxDistort: |
|
case kRenderFxHologram: |
|
if ( gEngfuncs.pfnRandomLong(0,49) == 0 ) |
|
{ |
|
int axis = gEngfuncs.pfnRandomLong(0,1); |
|
if ( axis == 1 ) // Choose between x & z |
|
axis = 2; |
|
VectorScale( transform[axis], gEngfuncs.pfnRandomFloat(1,1.484), transform[axis] ); |
|
} |
|
else if ( gEngfuncs.pfnRandomLong(0,49) == 0 ) |
|
{ |
|
float offset; |
|
int axis = gEngfuncs.pfnRandomLong(0,1); |
|
if ( axis == 1 ) // Choose between x & z |
|
axis = 2; |
|
offset = gEngfuncs.pfnRandomFloat(-10,10); |
|
transform[gEngfuncs.pfnRandomLong(0,2)][3] += offset; |
|
} |
|
break; |
|
case kRenderFxExplode: |
|
{ |
|
if ( iRenderStateChanged ) |
|
{ |
|
g_flStartScaleTime = m_clTime; |
|
iRenderStateChanged = FALSE; |
|
} |
|
|
|
// Make the Model continue to shrink |
|
float flTimeDelta = m_clTime - g_flStartScaleTime; |
|
if ( flTimeDelta > 0 ) |
|
{ |
|
float flScale = 0.001; |
|
// Goes almost all away |
|
if ( flTimeDelta <= 2.0 ) |
|
flScale = 1.0 - (flTimeDelta / 2.0); |
|
|
|
for (int i = 0; i < 3; i++) |
|
{ |
|
for (int j = 0; j < 3; j++) |
|
transform[i][j] *= flScale; |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
//////////////////////////////////// |
|
// Hooks to class implementation |
|
//////////////////////////////////// |
|
|
|
/* |
|
==================== |
|
R_StudioDrawPlayer |
|
|
|
==================== |
|
*/ |
|
int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) |
|
{ |
|
return g_StudioRenderer.StudioDrawPlayer( flags, pplayer ); |
|
} |
|
|
|
/* |
|
==================== |
|
R_StudioDrawModel |
|
|
|
==================== |
|
*/ |
|
int R_StudioDrawModel( int flags ) |
|
{ |
|
return g_StudioRenderer.StudioDrawModel( flags ); |
|
} |
|
|
|
/* |
|
==================== |
|
R_StudioInit |
|
|
|
==================== |
|
*/ |
|
void R_StudioInit( void ) |
|
{ |
|
g_StudioRenderer.Init(); |
|
} |
|
|
|
// The simple drawing interface we'll pass back to the engine |
|
r_studio_interface_t studio = |
|
{ |
|
STUDIO_INTERFACE_VERSION, |
|
R_StudioDrawModel, |
|
R_StudioDrawPlayer, |
|
}; |
|
|
|
/* |
|
==================== |
|
HUD_GetStudioModelInterface |
|
|
|
Export this function for the engine to use the studio renderer class to render objects. |
|
==================== |
|
*/ |
|
|
|
extern "C" int DLLEXPORT HUD_GetStudioModelInterface( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio ) |
|
{ |
|
if ( version != STUDIO_INTERFACE_VERSION ) |
|
return 0; |
|
|
|
// Point the engine to our callbacks |
|
*ppinterface = &studio; |
|
|
|
// Copy in engine helper functions |
|
memcpy( &IEngineStudio, pstudio, sizeof( IEngineStudio ) ); |
|
|
|
// Initialize local variables, etc. |
|
R_StudioInit(); |
|
|
|
// Success |
|
return 1; |
|
}
|
|
|