diff options
Diffstat (limited to 'game/client/game_controls/basemodelpanel.cpp')
| -rw-r--r-- | game/client/game_controls/basemodelpanel.cpp | 947 |
1 files changed, 947 insertions, 0 deletions
diff --git a/game/client/game_controls/basemodelpanel.cpp b/game/client/game_controls/basemodelpanel.cpp new file mode 100644 index 0000000..2058873 --- /dev/null +++ b/game/client/game_controls/basemodelpanel.cpp @@ -0,0 +1,947 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include <KeyValues.h> +#include <vgui/ISurface.h> +#include <vgui/ISystem.h> +#include <vgui/IScheme.h> +#include <vgui_controls/AnimationController.h> +#include <vgui_controls/EditablePanel.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui/ISurface.h> +#include <vgui/IImage.h> +#include <vgui_controls/Label.h> + +#include "materialsystem/imaterialsystem.h" +#include "engine/ivmodelinfo.h" + +#include "c_sceneentity.h" +#include "gamestringpool.h" +#include "model_types.h" +#include "view_shared.h" +#include "view.h" +#include "ivrenderview.h" +#include "iefx.h" +#include "dlight.h" +#include "activitylist.h" + +#include "basemodelpanel.h" + +bool UseHWMorphModels(); + + +using namespace vgui; + +DECLARE_BUILD_FACTORY( CModelPanel ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CModelPanel::CModelPanel( vgui::Panel *pParent, const char *pName ) : vgui::EditablePanel( pParent, pName ) +{ + m_nFOV = 54; + m_hModel = NULL; + m_pModelInfo = NULL; + m_hScene = NULL; + m_iDefaultAnimation = 0; + m_bPanelDirty = true; + m_bStartFramed = false; + m_bAllowOffscreen = false; + + ListenForGameEvent( "game_newmap" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CModelPanel::~CModelPanel() +{ + if ( m_pModelInfo ) + { + delete m_pModelInfo; + m_pModelInfo = NULL; + } + + DeleteVCDData(); + DeleteModelData(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_nFOV = inResourceData->GetInt( "fov", 54 ); + m_bStartFramed = inResourceData->GetInt( "start_framed", false ); + m_bAllowOffscreen = inResourceData->GetInt( "allow_offscreen", false ); + + // do we have a valid "model" section in the .res file? + for ( KeyValues *pData = inResourceData->GetFirstSubKey() ; pData != NULL ; pData = pData->GetNextKey() ) + { + if ( !Q_stricmp( pData->GetName(), "model" ) ) + { + ParseModelInfo( pData ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::OnCommand( const char *command ) +{ + if (!Q_strnicmp("animation", command, 9)) + { + UpdateModel(); + SetSequence( command + 9 + 1 ); + return; + } + + BaseClass::OnCommand(command); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::ParseModelInfo( KeyValues *inResourceData ) +{ + // delete any current info + if ( m_pModelInfo ) + { + delete m_pModelInfo; + m_pModelInfo = NULL; + } + + m_pModelInfo = new CModelPanelModelInfo; + + if ( !m_pModelInfo ) + return; + + m_pModelInfo->m_pszModelName = ReadAndAllocStringValue( inResourceData, "modelname" ); + m_pModelInfo->m_pszModelName_HWM = ReadAndAllocStringValue( inResourceData, "modelname_hwm" ); + m_pModelInfo->m_nSkin = inResourceData->GetInt( "skin", -1 ); + m_pModelInfo->m_vecAbsAngles.Init( inResourceData->GetFloat( "angles_x", 0.0 ), inResourceData->GetFloat( "angles_y", 0.0 ), inResourceData->GetFloat( "angles_z", 0.0 ) ); + m_pModelInfo->m_vecOriginOffset.Init( inResourceData->GetFloat( "origin_x", 110.0 ), inResourceData->GetFloat( "origin_y", 5.0 ), inResourceData->GetFloat( "origin_z", 5.0 ) ); + m_pModelInfo->m_vecFramedOriginOffset.Init( inResourceData->GetFloat( "frame_origin_x", 110.0 ), inResourceData->GetFloat( "frame_origin_y", 5.0 ), inResourceData->GetFloat( "frame_origin_z", 5.0 ) ); + m_pModelInfo->m_pszVCD = ReadAndAllocStringValue( inResourceData, "vcd" ); + m_pModelInfo->m_bUseSpotlight = ( inResourceData->GetInt( "spotlight", 0 ) == 1 ); + m_pModelInfo->m_vecViewportOffset.Init(); + + for ( KeyValues *pData = inResourceData->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + if ( !Q_stricmp( pData->GetName(), "animation" ) ) + { + OnAddAnimation( pData ); + } + else if ( !Q_stricmp( pData->GetName(), "attached_model" ) ) + { + CModelPanelAttachedModelInfo *pAttachedModelInfo = new CModelPanelAttachedModelInfo; + + if ( pAttachedModelInfo ) + { + pAttachedModelInfo->m_pszModelName = ReadAndAllocStringValue( pData, "modelname" ); + pAttachedModelInfo->m_nSkin = pData->GetInt( "skin", -1 ); + + m_pModelInfo->m_AttachedModelsInfo.AddToTail( pAttachedModelInfo ); + } + } + } + + m_bPanelDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::OnAddAnimation( KeyValues *pData ) +{ + if ( !pData ) + return; + + CModelPanelModelAnimation *pAnimation = new CModelPanelModelAnimation; + + if ( pAnimation ) + { + pAnimation->m_pszName = ReadAndAllocStringValue( pData, "name" ); + pAnimation->m_pszSequence = ReadAndAllocStringValue( pData, "sequence" ); + pAnimation->m_pszActivity = ReadAndAllocStringValue( pData, "activity" ); + pAnimation->m_bDefault = ( pData->GetInt( "default", 0 ) == 1 ); + + for ( KeyValues *pAnimData = pData->GetFirstSubKey(); pAnimData != NULL; pAnimData = pAnimData->GetNextKey() ) + { + if ( !Q_stricmp( pAnimData->GetName(), "pose_parameters" ) ) + { + pAnimation->m_pPoseParameters = pAnimData->MakeCopy(); + } + } + + m_pModelInfo->m_Animations.AddToTail( pAnimation ); + if ( pAnimation->m_bDefault ) + { + m_iDefaultAnimation = m_pModelInfo->m_Animations.Find( pAnimation ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::FireGameEvent( IGameEvent * event ) +{ + const char *type = event->GetName(); + + if ( Q_strcmp( type, "game_newmap" ) == 0 ) + { + // force the models to re-setup themselves + m_bPanelDirty = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetDefaultAnimation( const char *pszName ) +{ + if ( m_pModelInfo ) + { + for ( int i = 0; i < m_pModelInfo->m_Animations.Count(); i++ ) + { + if ( m_pModelInfo->m_Animations[i] && m_pModelInfo->m_Animations[i]->m_pszName ) + { + if ( !Q_stricmp( m_pModelInfo->m_Animations[i]->m_pszName, pszName ) ) + { + m_iDefaultAnimation = i; + return; + } + } + } + } + + Assert( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Replaces the current model with a new one, without changing the camera settings +//----------------------------------------------------------------------------- +void CModelPanel::SwapModel( const char *pszName, const char *pszAttached ) +{ + if ( !m_pModelInfo || !pszName || !pszName[0] ) + return; + + int len = Q_strlen( pszName ) + 1; + char *pAlloced = new char[ len ]; + Assert( pAlloced ); + Q_strncpy( pAlloced, pszName, len ); + m_pModelInfo->m_pszModelName = pAlloced; + + ClearAttachedModelInfos(); + + if ( pszAttached ) + { + CModelPanelAttachedModelInfo *pAttachedModelInfo = new CModelPanelAttachedModelInfo; + if ( pAttachedModelInfo ) + { + len = Q_strlen( pszAttached ) + 1; + pAlloced = new char[ len ]; + Assert( pAlloced ); + Q_strncpy( pAlloced, pszAttached, len ); + pAttachedModelInfo->m_pszModelName = pAlloced; + pAttachedModelInfo->m_nSkin = 0; + + m_pModelInfo->m_AttachedModelsInfo.AddToTail( pAttachedModelInfo ); + } + } + + m_bPanelDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::DeleteVCDData( void ) +{ + if ( m_hScene.Get() ) + { + m_hScene->StopClientOnlyScene(); + + m_hScene->Remove(); + m_hScene = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetupVCD( void ) +{ + if ( !m_pModelInfo ) + return; + + DeleteVCDData(); + + C_SceneEntity *pEnt = new class C_SceneEntity; + + if ( !pEnt ) + return; + + if ( pEnt->InitializeAsClientEntity( "", RENDER_GROUP_OTHER ) == false ) + { + // we failed to initialize this entity so just return gracefully + pEnt->Remove(); + return; + } + + // setup the handle + m_hScene = pEnt; + + // setup the scene + pEnt->SetupClientOnlyScene( m_pModelInfo->m_pszVCD, m_hModel, true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::ClearAttachedModelInfos( void ) +{ + if ( m_pModelInfo ) + { + m_pModelInfo->m_AttachedModelsInfo.PurgeAndDeleteElements(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::DeleteModelData( void ) +{ + if ( m_hModel.Get() ) + { + m_hModel->Remove(); + m_hModel = NULL; + m_flFrameDistance = 0; + } + + for ( int i = 0 ; i < m_AttachedModels.Count() ; i++ ) + { + if ( m_AttachedModels[i].Get() ) + { + m_AttachedModels[i]->Remove(); + } + m_AttachedModels.Remove( i ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CModelPanel::GetModelName( void ) +{ + if ( !m_pModelInfo ) + return NULL; + + // check to see if we want to use a HWM model + if ( UseHWMorphModels() ) + { + // do we have a valid HWM model filename + if ( m_pModelInfo->m_pszModelName_HWM && ( Q_strlen( m_pModelInfo->m_pszModelName_HWM ) > 0 ) ) + { + // does the file exist + model_t *pModel = (model_t *)engine->LoadModel( m_pModelInfo->m_pszModelName_HWM ); + if ( pModel ) + { + return m_pModelInfo->m_pszModelName_HWM; + } + } + } + + return m_pModelInfo->m_pszModelName; +} + +void CModelPanel::SetBodyGroup( const char* pszBodyGroupName, int nGroup ) +{ + if ( !m_pModelInfo ) + return; + + if ( !m_hModel.Get() ) + return; + + int nBodyGroupNum = m_hModel->FindBodygroupByName( pszBodyGroupName ); + + if ( nBodyGroupNum == -1 ) + return; + + m_pModelInfo->m_mapBodygroupValues.InsertOrReplace( nBodyGroupNum, nGroup ); + m_bPanelDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetupModel( void ) +{ + if ( !m_pModelInfo ) + return; + + MDLCACHE_CRITICAL_SECTION(); + + // remove any current models we're using + DeleteModelData(); + + const char *pszModelName = GetModelName(); + if ( !pszModelName || !pszModelName[0] ) + return; + + // create the new model + CModelPanelModel *pEnt = new CModelPanelModel; + + if ( !pEnt ) + return; + + if ( pEnt->InitializeAsClientEntity( pszModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false ) + { + // we failed to initialize this entity so just return gracefully + pEnt->Remove(); + return; + } + + // setup the handle + m_hModel = pEnt; + + pEnt->DontRecordInTools(); + pEnt->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally + + if ( m_pModelInfo->m_nSkin >= 0 ) + { + pEnt->m_nSkin = m_pModelInfo->m_nSkin; + } + + FOR_EACH_MAP_FAST( m_pModelInfo->m_mapBodygroupValues, i ) + { + pEnt->SetBodygroup( m_pModelInfo->m_mapBodygroupValues.Key( i ), m_pModelInfo->m_mapBodygroupValues[ i ] ); + } + + // do we have any animation information? + if ( m_pModelInfo->m_Animations.Count() > 0 && m_pModelInfo->m_Animations.IsValidIndex( m_iDefaultAnimation ) ) + { + CModelPanelModelAnimation *pAnim = m_pModelInfo->m_Animations[ m_iDefaultAnimation ]; + int sequence = ACT_INVALID; + if ( pAnim->m_pszActivity && pAnim->m_pszActivity[0] ) + { + Activity activity = (Activity)ActivityList_IndexForName( pAnim->m_pszActivity ); + sequence = pEnt->SelectWeightedSequence( activity ); + } + else if ( pAnim->m_pszSequence && pAnim->m_pszSequence[0] ) + { + sequence = pEnt->LookupSequence( pAnim->m_pszSequence ); + } + if ( sequence != ACT_INVALID ) + { + pEnt->ResetSequence( sequence ); + pEnt->SetCycle( 0 ); + + if ( pAnim->m_pPoseParameters ) + { + for ( KeyValues *pData = pAnim->m_pPoseParameters->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + const char *pName = pData->GetName(); + float flValue = pData->GetFloat(); + + pEnt->SetPoseParameter( pName, flValue ); + } + } + + pEnt->m_flAnimTime = gpGlobals->curtime; + } + } + + // setup any attached models + for ( int i = 0 ; i < m_pModelInfo->m_AttachedModelsInfo.Count() ; i++ ) + { + CModelPanelAttachedModelInfo *pInfo = m_pModelInfo->m_AttachedModelsInfo[i]; + C_BaseAnimating *pTemp = new C_BaseAnimating; + + if ( pTemp ) + { + if ( pTemp->InitializeAsClientEntity( pInfo->m_pszModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false ) + { + // we failed to initialize this model so just skip it + pTemp->Remove(); + continue; + } + + pTemp->DontRecordInTools(); + pTemp->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally + pTemp->FollowEntity( m_hModel.Get() ); // attach to parent model + + if ( pInfo->m_nSkin >= 0 ) + { + pTemp->m_nSkin = pInfo->m_nSkin; + } + + pTemp->m_flAnimTime = gpGlobals->curtime; + m_AttachedModels.AddToTail( pTemp ); + } + } + + CalculateFrameDistance(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::InitCubeMaps() +{ + ITexture *pCubemapTexture; + + // Deal with the default cubemap + if ( g_pMaterialSystemHardwareConfig->GetHDREnabled() ) + { + pCubemapTexture = materials->FindTexture( "editor/cubemap.hdr", NULL, true ); + m_DefaultHDREnvCubemap.Init( pCubemapTexture ); + } + else + { + pCubemapTexture = materials->FindTexture( "editor/cubemap", NULL, true ); + m_DefaultEnvCubemap.Init( pCubemapTexture ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: If the panel is marked as dirty, update it and mark it as clean +//----------------------------------------------------------------------------- +void CModelPanel::UpdateModel() +{ + if ( m_bPanelDirty ) + { + InitCubeMaps(); + + SetupModel(); + + // are we trying to play a VCD? + if ( Q_strlen( m_pModelInfo->m_pszVCD ) > 0 ) + { + SetupVCD(); + } + + m_bPanelDirty = false; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::Paint() +{ + BaseClass::Paint(); + + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pLocalPlayer || !m_pModelInfo ) + return; + + MDLCACHE_CRITICAL_SECTION(); + + UpdateModel(); + + if ( !m_hModel.Get() ) + return; + + int i = 0; + int x, y, w, h; + + GetBounds( x, y, w, h ); + ParentLocalToScreen( x, y ); + + if ( !m_bAllowOffscreen && x < 0 ) + { + // prevent x from being pushed off the left side of the screen + // for modes like 1280 x 1024 (prevents model from being drawn in the panel) + x = 0; + } + + Vector vecExtraModelOffset( 0, 0, 0 ); + float flWidthRatio = ((float)w / (float)h ) / ( 4.0f / 3.0f ); + + // is this a player model? + if ( Q_strstr( GetModelName(), "models/player/" ) ) + { + // need to know if the ratio is not 4/3 + // HACK! HACK! to get our player models to appear the way they do in 4/3 if we're using other aspect ratios + if ( flWidthRatio > 1.05f ) + { + vecExtraModelOffset.Init( -60, 0, 0 ); + } + else if ( flWidthRatio < 0.95f ) + { + vecExtraModelOffset.Init( 15, 0, 0 ); + } + } + + m_hModel->SetAbsOrigin( m_pModelInfo->m_vecOriginOffset + vecExtraModelOffset ); + m_hModel->SetAbsAngles( QAngle( m_pModelInfo->m_vecAbsAngles.x, m_pModelInfo->m_vecAbsAngles.y, m_pModelInfo->m_vecAbsAngles.z ) ); + + // do we have a valid sequence? + if ( m_hModel->GetSequence() != -1 ) + { + m_hModel->FrameAdvance( gpGlobals->frametime ); + } + + CMatRenderContextPtr pRenderContext( materials ); + + // figure out what our viewport is right now + int viewportX, viewportY, viewportWidth, viewportHeight; + pRenderContext->GetViewport( viewportX, viewportY, viewportWidth, viewportHeight ); + + // Now draw it. + CViewSetup view; + view.x = x + m_pModelInfo->m_vecViewportOffset.x + viewportX; // we actually want to offset by the + view.y = y + m_pModelInfo->m_vecViewportOffset.y + viewportY; // viewport origin here because Push3DView expects global coords below + view.width = w; + view.height = h; + + view.m_bOrtho = false; + + // scale the FOV for aspect ratios other than 4/3 + view.fov = ScaleFOVByWidthRatio( m_nFOV, flWidthRatio ); + + view.origin = vec3_origin; + view.angles.Init(); + view.zNear = VIEW_NEARZ; + view.zFar = 1000; + + + + // Not supported by queued material system - doesn't appear to be necessary +// ITexture *pLocalCube = pRenderContext->GetLocalCubemap(); + + if ( g_pMaterialSystemHardwareConfig->GetHDREnabled() ) + { + pRenderContext->BindLocalCubemap( m_DefaultHDREnvCubemap ); + } + else + { + pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap ); + } + + pRenderContext->SetLightingOrigin( vec3_origin ); + pRenderContext->SetAmbientLight( 0.4, 0.4, 0.4 ); + + static Vector white[6] = + { + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + Vector( 0.4, 0.4, 0.4 ), + }; + + g_pStudioRender->SetAmbientLightColors( white ); + g_pStudioRender->SetLocalLights( 0, NULL ); + + if ( m_pModelInfo->m_bUseSpotlight ) + { + Vector vecMins, vecMaxs; + m_hModel->GetRenderBounds( vecMins, vecMaxs ); + LightDesc_t spotLight( vec3_origin + Vector( 0, 0, 200 ), Vector( 1, 1, 1 ), m_hModel->GetAbsOrigin() + Vector( 0, 0, ( vecMaxs.z - vecMins.z ) * 0.75 ), 0.035, 0.873 ); + g_pStudioRender->SetLocalLights( 1, &spotLight ); + } + + Frustum dummyFrustum; + render->Push3DView( view, 0, NULL, dummyFrustum ); + + modelrender->SuppressEngineLighting( true ); + float color[3] = { 1.0f, 1.0f, 1.0f }; + render->SetColorModulation( color ); + render->SetBlend( 1.0f ); + m_hModel->DrawModel( STUDIO_RENDER ); + + for ( i = 0 ; i < m_AttachedModels.Count() ; i++ ) + { + if ( m_AttachedModels[i].Get() ) + { + m_AttachedModels[i]->DrawModel( STUDIO_RENDER ); + } + } + + modelrender->SuppressEngineLighting( false ); + + render->PopView( dummyFrustum ); + + pRenderContext->BindLocalCubemap( NULL ); + + /* + vgui::surface()->DrawSetColor( Color(0,0,0,255) ); + vgui::surface()->DrawOutlinedRect( 0,0, GetWide(), GetTall() ); + */ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CModelPanel::FindAnimByName( const char *pszName ) +{ + // first try to find the sequence using pszName as the friendly name + for ( int iIndex = 0 ; iIndex < m_pModelInfo->m_Animations.Count() ; iIndex++ ) + { + CModelPanelModelAnimation *pAnimation = m_pModelInfo->m_Animations[ iIndex ]; + if ( FStrEq( pAnimation->m_pszName, pszName ) ) + return iIndex; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CModelPanel::SetSequence( const char *pszName ) +{ + bool bRetVal = false; + const char *pszAnim = NULL; + + MDLCACHE_CRITICAL_SECTION(); + + if ( m_pModelInfo ) + { + int iIndex = FindAnimByName(pszName); + if ( iIndex != -1 ) + { + pszAnim = m_pModelInfo->m_Animations[iIndex]->m_pszSequence; + } + else + { + // if not, just use the passed name as the sequence + pszAnim = pszName; + } + + if ( m_hModel.Get() ) + { + int sequence = m_hModel->LookupSequence( pszAnim ); + if ( sequence != ACT_INVALID ) + { + m_hModel->ResetSequence( sequence ); + m_hModel->SetCycle( 0 ); + + bRetVal = true; + } + } + } + + return bRetVal; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::SetSkin( int nSkin ) +{ + if ( m_pModelInfo ) + { + m_pModelInfo->m_nSkin = nSkin; + m_bPanelDirty = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CModelPanel::OnSetAnimation( KeyValues *data ) +{ + UpdateModel(); + + // If there's no model, these commands will be ignored. + Assert(m_hModel); + + if ( data ) + { + const char *pszAnimation = data->GetString( "animation", "" ); + const char *pszActivity = data->GetString( "activity", "" ); + if ( pszActivity && pszActivity[0] ) + { + if ( m_hModel ) + { + int iIndex = FindAnimByName(pszActivity); + if ( iIndex != -1 ) + { + pszActivity = m_pModelInfo->m_Animations[iIndex]->m_pszActivity; + } + + Activity activity = (Activity)ActivityList_IndexForName( pszActivity ); + int sequence = m_hModel->SelectWeightedSequence( activity ); + if ( sequence != ACT_INVALID ) + { + m_hModel->ResetSequence( sequence ); + m_hModel->SetCycle( 0 ); + } + } + } + else + { + SetSequence( pszAnimation ); + } + } +} + +void CModelPanel::CalculateFrameDistanceInternal( const model_t *pModel ) +{ + // Get the model space render bounds. + Vector vecMin, vecMax; + modelinfo->GetModelRenderBounds( pModel, vecMin, vecMax ); + Vector vecCenter = ( vecMax + vecMin ) * 0.5f; + vecMin -= vecCenter; + vecMax -= vecCenter; + + // Get the bounds points and transform them by the desired model panel rotation. + Vector aBoundsPoints[8]; + aBoundsPoints[0].Init( vecMax.x, vecMax.y, vecMax.z ); + aBoundsPoints[1].Init( vecMin.x, vecMax.y, vecMax.z ); + aBoundsPoints[2].Init( vecMax.x, vecMin.y, vecMax.z ); + aBoundsPoints[3].Init( vecMin.x, vecMin.y, vecMax.z ); + aBoundsPoints[4].Init( vecMax.x, vecMax.y, vecMin.z ); + aBoundsPoints[5].Init( vecMin.x, vecMax.y, vecMin.z ); + aBoundsPoints[6].Init( vecMax.x, vecMin.y, vecMin.z ); + aBoundsPoints[7].Init( vecMin.x, vecMin.y, vecMin.z ); + + // Translated center point (offset from camera center). + Vector vecTranslateCenter = -vecCenter; + + // Build the rotation matrix. + QAngle angPanelAngles( m_pModelInfo->m_vecAbsAngles.x, m_pModelInfo->m_vecAbsAngles.y, m_pModelInfo->m_vecAbsAngles.z ); + matrix3x4_t matRotation; + AngleMatrix( angPanelAngles, matRotation ); + + Vector aXFormPoints[8]; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + VectorTransform( aBoundsPoints[iPoint], matRotation, aXFormPoints[iPoint] ); + } + + Vector vecXFormCenter; + VectorTransform( -vecTranslateCenter, matRotation, vecXFormCenter ); + + int w, h; + GetSize( w, h ); + float flW = (float)w; + float flH = (float)h; + + float flFOVx = DEG2RAD( m_nFOV * 0.5f ); + float flFOVy = CalcFovY( ( m_nFOV * 0.5f ), flW/flH ); + flFOVy = DEG2RAD( flFOVy ); + + float flTanFOVx = tan( flFOVx ); + float flTanFOVy = tan( flFOVy ); + + // Find the max value of x, y, or z + float flDist = 0.0f; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + float flDistZ = fabs( aXFormPoints[iPoint].z / flTanFOVy - aXFormPoints[iPoint].x ); + float flDistY = fabs( aXFormPoints[iPoint].y / flTanFOVx - aXFormPoints[iPoint].x ); + float flTestDist = MAX( flDistZ, flDistY ); + flDist = MAX( flDist, flTestDist ); + } + + // Scale the object down by 10%. + flDist *= 1.10f; + + // Add the framing offset. + vecXFormCenter += m_pModelInfo->m_vecFramedOriginOffset; + + // Zoom to the frame distance + m_pModelInfo->m_vecOriginOffset.x = flDist - vecXFormCenter.x; + m_pModelInfo->m_vecOriginOffset.y = -vecXFormCenter.y; + m_pModelInfo->m_vecOriginOffset.z = -vecXFormCenter.z; + + // Screen space points. + Vector2D aScreenPoints[8]; + Vector aCameraPoints[8]; + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + aCameraPoints[iPoint] = aXFormPoints[iPoint]; + aCameraPoints[iPoint].x += flDist; + + aScreenPoints[iPoint].x = aCameraPoints[iPoint].y / ( flTanFOVx * aCameraPoints[iPoint].x ); + aScreenPoints[iPoint].y = aCameraPoints[iPoint].z / ( flTanFOVy * aCameraPoints[iPoint].x ); + + aScreenPoints[iPoint].x = ( aScreenPoints[iPoint].x * 0.5f + 0.5f ) * flW; + aScreenPoints[iPoint].y = ( aScreenPoints[iPoint].y * 0.5f + 0.5f ) * flH; + } + + // Find the min/max and center of the 2D bounding box of the object. + Vector2D vecScreenMin( 99999.0f, 99999.0f ), vecScreenMax( -99999.0f, -99999.0f ); + for ( int iPoint = 0; iPoint < 8; ++iPoint ) + { + vecScreenMin.x = MIN( vecScreenMin.x, aScreenPoints[iPoint].x ); + vecScreenMin.y = MIN( vecScreenMin.y, aScreenPoints[iPoint].y ); + vecScreenMax.x = MAX( vecScreenMax.x, aScreenPoints[iPoint].x ); + vecScreenMax.y = MAX( vecScreenMax.y, aScreenPoints[iPoint].y ); + } + + vecScreenMin.x = clamp( vecScreenMin.x, 0.0f, flW ); + vecScreenMin.y = clamp( vecScreenMin.y, 0.0f, flH ); + vecScreenMax.x = clamp( vecScreenMax.x, 0.0f, flW ); + vecScreenMax.y = clamp( vecScreenMax.y, 0.0f, flH ); + + // Offset the view port based on the calculated model 2D center and the center of the viewport. + Vector2D vecScreenCenter = ( vecScreenMax + vecScreenMin ) * 0.5f; + m_pModelInfo->m_vecViewportOffset.x = -( ( flW * 0.5f ) - vecScreenCenter.x ); + m_pModelInfo->m_vecViewportOffset.y = -( ( flH * 0.5f ) - vecScreenCenter.y ); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculates the distance the camera should be at to frame the model on the screen. +//----------------------------------------------------------------------------- +void CModelPanel::CalculateFrameDistance( void ) +{ + m_flFrameDistance = 0; + if ( !m_hModel ) + return; + + // Compute a bounding radius for the model + const model_t *mod = modelinfo->GetModel( m_hModel->GetModelIndex() ); + if ( !mod ) + return; + + if ( m_bStartFramed ) + { + CalculateFrameDistanceInternal( mod ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Moves the camera forward/backward along the current view angle to +// frame the model on the screen. +//----------------------------------------------------------------------------- +void CModelPanel::ZoomToFrameDistance( void ) +{ + if ( !m_flFrameDistance || !m_hModel ) + return; + + const model_t *mod = modelinfo->GetModel( m_hModel->GetModelIndex() ); + if ( !mod ) + return; + + // Move the model to the midpoint + Vector mins, maxs, vecModelCenter; + modelinfo->GetModelRenderBounds( mod, mins, maxs ); + VectorLerp( mins, maxs, 0.5f, vecModelCenter ); + + vecModelCenter += m_pModelInfo->m_vecFramedOriginOffset; + + // Zoom to the frame distance + m_pModelInfo->m_vecOriginOffset.x = m_flFrameDistance; + m_pModelInfo->m_vecOriginOffset.y = -vecModelCenter.y; + m_pModelInfo->m_vecOriginOffset.z = -vecModelCenter.z; +} + |