//---------------------------------------------------------------- // ClientModel.cpp // // A non-interactive client-side model // // Copyright 2002-2004 Raven Software //---------------------------------------------------------------- #include "../../idlib/precompiled.h" #pragma hdrstop #include "../Game_local.h" #include "ClientModel.h" /* =============================================================================== rvClientModel =============================================================================== */ CLASS_DECLARATION( rvClientEntity, rvClientModel ) END_CLASS /* ================ rvClientModel::rvClientModel ================ */ rvClientModel::rvClientModel ( void ) { memset ( &renderEntity, 0, sizeof(renderEntity) ); worldAxis = mat3_identity; entityDefHandle = -1; } /* ================ rvClientModel::~rvClientModel ================ */ rvClientModel::~rvClientModel ( void ) { FreeEntityDef ( ); } /* ================ rvClientModel::FreeEntityDef ================ */ void rvClientModel::FreeEntityDef ( void ) { if ( entityDefHandle >= 0 ) { gameRenderWorld->FreeEntityDef ( entityDefHandle ); entityDefHandle = -1; } } /* ================ rvClientModel::Spawn ================ */ void rvClientModel::Spawn ( void ) { const char* spawnarg; spawnArgs.GetString ( "classname", "", classname ); // parse static models the same way the editor display does gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity ); renderEntity.entityNum = entityNumber; spawnarg = spawnArgs.GetString( "model" ); if ( spawnarg && *spawnarg ) { SetModel( spawnarg ); } } /* ================ rvClientModel::Think ================ */ void rvClientModel::Think ( void ) { if( bindMaster && (bindMaster->GetRenderEntity()->hModel && bindMaster->GetModelDefHandle() == -1) ) { return; } UpdateBind(); Present(); } /* ================ rvClientModel::Present ================ */ void rvClientModel::Present(void) { // Hide client entities bound to a hidden entity if ( bindMaster && (bindMaster->IsHidden ( ) || (bindMaster->GetRenderEntity()->hModel && bindMaster->GetModelDefHandle() == -1) ) ) { return; } renderEntity.origin = worldOrigin; renderEntity.axis = worldAxis; // add to refresh list if ( entityDefHandle == -1 ) { entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity ); } else { gameRenderWorld->UpdateEntityDef( entityDefHandle, &renderEntity ); } } /* ================ rvClientModel::SetCustomShader ================ */ bool rvClientModel::SetCustomShader ( const char* shaderName ) { if ( shaderName == NULL ) { return false; } const idMaterial* material = declManager->FindMaterial( shaderName ); if ( material == NULL ) { return false; } renderEntity.customShader = material; return true; } /* ================ rvClientModel::Save ================ */ void rvClientModel::Save( idSaveGame *savefile ) const { savefile->WriteRenderEntity( renderEntity ); savefile->WriteInt( entityDefHandle ); savefile->WriteString ( classname ); // cnicholson: Added unsaved var } /* ================ rvClientModel::Restore ================ */ void rvClientModel::Restore( idRestoreGame *savefile ) { savefile->ReadRenderEntity( renderEntity, NULL ); savefile->ReadInt( entityDefHandle ); savefile->ReadString ( classname ); // cnicholson: Added unrestored var // restore must retrieve entityDefHandle from the renderer if ( entityDefHandle != -1 ) { entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity ); } } /* ================ rvClientModel::SetModel ================ */ void rvClientModel::SetModel( const char* modelname ) { FreeEntityDef(); renderEntity.hModel = renderModelManager->FindModel( modelname ); if ( renderEntity.hModel ) { renderEntity.hModel->Reset(); } renderEntity.callback = NULL; renderEntity.numJoints = 0; renderEntity.joints = NULL; if ( renderEntity.hModel ) { renderEntity.bounds = renderEntity.hModel->Bounds( &renderEntity ); } else { renderEntity.bounds.Zero(); } } /* ============== rvClientModel::ProjectOverlay ============== */ void rvClientModel::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) { float s, c; idMat3 axis, axistemp; idVec3 localOrigin, localAxis[2]; idPlane localPlane[2]; // make sure the entity has a valid model handle if ( entityDefHandle < 0 ) { return; } // only do this on dynamic md5 models if ( renderEntity.hModel->IsDynamicModel() != DM_CACHED ) { return; } idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c ); axis[2] = -dir; axis[2].NormalVectors( axistemp[0], axistemp[1] ); axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s; axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c; renderEntity.axis.ProjectVector( origin - renderEntity.origin, localOrigin ); renderEntity.axis.ProjectVector( axis[0], localAxis[0] ); renderEntity.axis.ProjectVector( axis[1], localAxis[1] ); size = 1.0f / size; localAxis[0] *= size; localAxis[1] *= size; localPlane[0] = localAxis[0]; localPlane[0][3] = -( localOrigin * localAxis[0] ) + 0.5f; localPlane[1] = localAxis[1]; localPlane[1][3] = -( localOrigin * localAxis[1] ) + 0.5f; const idMaterial *mtr = declManager->FindMaterial( material ); // project an overlay onto the model gameRenderWorld->ProjectOverlay( entityDefHandle, localPlane, mtr ); // make sure non-animating models update their overlay UpdateVisuals(); } /* ================ rvClientModel::UpdateRenderEntity ================ */ bool rvClientModel::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) { if ( gameLocal.inCinematic && gameLocal.skipCinematic ) { return false; } idAnimator *animator = GetAnimator(); if ( animator ) { return animator->CreateFrame( gameLocal.time, false ); } return false; } /* ================ rvClientModel::ModelCallback NOTE: may not change the game state whatsoever! ================ */ bool rvClientModel::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) { rvClientEntity *cent; cent = gameLocal.clientEntities[ renderEntity->entityNum ]; if ( !cent ) { gameLocal.Error( "rvClientModel::ModelCallback: callback with NULL client entity '%d'", renderEntity->entityNum ); return false; } if( !cent->IsType( rvClientModel::GetClassType() ) ) { gameLocal.Error( "rvClientModel::ModelCallback: callback with non-client model on client entity '%d'", renderEntity->entityNum ); return false; } return ((rvClientModel*)cent)->UpdateRenderEntity( renderEntity, renderView ); } /* ================ rvClientModel::GetPhysicsToVisualTransform ================ */ bool rvClientModel::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) { return false; } /* ================ rvClientModel::UpdateModelTransform ================ */ void rvClientModel::UpdateModelTransform( void ) { idVec3 origin; idMat3 axis; if ( GetPhysicsToVisualTransform( origin, axis ) ) { renderEntity.axis = axis * worldAxis; renderEntity.origin = worldOrigin + origin * renderEntity.axis; } else { renderEntity.axis = worldAxis; renderEntity.origin = worldOrigin; } } /* ================ rvClientModel::UpdateModel ================ */ void rvClientModel::UpdateModel( void ) { UpdateModelTransform(); idAnimator *animator = GetAnimator(); if ( animator && animator->ModelHandle() ) { // set the callback to update the joints renderEntity.callback = rvClientModel::ModelCallback; } } /* ================ rvClientModel::UpdateVisuals ================ */ void rvClientModel::UpdateVisuals( void ) { UpdateModel(); UpdateSound(); } /* ================ rvClientModel::SetSkin ================ */ void rvClientModel::SetSkin( const idDeclSkin *skin ) { renderEntity.customSkin = skin; UpdateVisuals(); } /* =============================================================================== rvAnimatedClientEntity =============================================================================== */ CLASS_DECLARATION( rvClientModel, rvAnimatedClientEntity ) END_CLASS /* ================ rvAnimatedClientEntity::rvAnimatedClientEntity ================ */ rvAnimatedClientEntity::rvAnimatedClientEntity ( void ) { } /* ================ rvAnimatedClientEntity::~rvAnimatedClientEntity ================ */ rvAnimatedClientEntity::~rvAnimatedClientEntity ( void ) { } /* ================ rvAnimatedClientEntity::Spawn ================ */ void rvAnimatedClientEntity::Spawn( void ) { SetModel( spawnArgs.GetString( "model" ) ); } /* ================ rvAnimatedClientEntity::Think ================ */ void rvAnimatedClientEntity::Think ( void ) { UpdateAnimation(); rvClientEntity::Think(); } /* ================ rvAnimatedClientEntity::UpdateAnimation ================ */ void rvAnimatedClientEntity::UpdateAnimation( void ) { // is the model an MD5? if ( !animator.ModelHandle() ) { // no, so nothing to do return; } // call any frame commands that have happened in the past frame animator.ServiceAnims( gameLocal.previousTime, gameLocal.time ); // if the model is animating then we have to update it if ( !animator.FrameHasChanged( gameLocal.time ) ) { // still fine the way it was return; } // get the latest frame bounds animator.GetBounds( gameLocal.time, renderEntity.bounds ); if ( renderEntity.bounds.IsCleared() ) { gameLocal.DPrintf( "rvAnimatedClientEntity %s %d: inside out bounds - %d\n", GetClassname(), entityNumber, gameLocal.time ); } // update the renderEntity UpdateVisuals(); Present(); // the animation is updated animator.ClearForceUpdate(); } /* ================ rvAnimatedClientEntity::SetModel ================ */ void rvAnimatedClientEntity::SetModel( const char *modelname ) { FreeEntityDef(); renderEntity.hModel = animator.SetModel( modelname ); if ( !renderEntity.hModel ) { rvClientModel::SetModel( modelname ); return; } if ( !renderEntity.customSkin ) { renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin(); } // set the callback to update the joints renderEntity.callback = rvClientModel::ModelCallback; animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints ); animator.GetBounds( gameLocal.time, renderEntity.bounds ); //UpdateVisuals(); Present(); }