//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "hlfaceposer.h" #include "StudioModel.h" #include "faceposer_models.h" #include "filesystem.h" #include "ifaceposerworkspace.h" #include #include "mdlviewer.h" #include "mxexpressiontray.h" #include "ControlPanel.h" #include "checksum_crc.h" #include "ViewerSettings.h" #include "matsyswin.h" #include "KeyValues.h" #include "utlbuffer.h" #include "expression.h" #include "ProgressDialog.h" #include "tier1/UtlString.h" #include "tier1/FmtStr.h" #include "tier1/KeyValues.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" void SetupModelFlexcontrollerLinks( StudioModel *model ); IFaceposerModels::CFacePoserModel::CFacePoserModel( char const *modelfile, StudioModel *model ) { m_pModel = model; m_szActorName[ 0 ] = 0; m_szShortName[ 0 ] = 0; strcpy( m_szModelFileName, modelfile ); Q_FixSlashes( m_szModelFileName ); CStudioHdr *hdr = model->GetStudioHdr(); if ( hdr ) { Q_StripExtension( hdr->pszName(), m_szShortName, sizeof( m_szShortName ) ); } m_bVisibileIn3DView = false; m_bFirstBitmapLoad = true; LoadBitmaps(); } IFaceposerModels::CFacePoserModel::~CFacePoserModel() { FreeBitmaps(); } void IFaceposerModels::CFacePoserModel::LoadBitmaps() { CStudioHdr *hdr = m_pModel ? m_pModel->GetStudioHdr() : NULL; if ( hdr ) { for ( int i = 0 ;i < hdr->GetNumSeq(); i++ ) { mxbitmapdata_t *bm = new mxbitmapdata_t(); AnimBitmap *entry = new AnimBitmap(); entry->needsload = true; entry->bitmap = bm; // Need to load bitmap from disk image via crc, etc. //Assert( 0 ); m_AnimationBitmaps.AddToTail( entry ); } } } CRC32_t IFaceposerModels::CFacePoserModel::GetBitmapCRC( int sequence ) { CStudioHdr *hdr = m_pModel ? m_pModel->GetStudioHdr() : NULL; if ( !hdr ) return (CRC32_t)-1; if ( sequence < 0 || sequence >= hdr->GetNumSeq() ) return (CRC32_t)-1; mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( sequence ); CRC32_t crc; CRC32_Init( &crc ); // For sequences, we'll checsum a bit of data CRC32_ProcessBuffer( &crc, (void *)seqdesc.pszLabel(), Q_strlen( seqdesc.pszLabel() ) ); CRC32_ProcessBuffer( &crc, (void *)seqdesc.pszActivityName(), Q_strlen( seqdesc.pszActivityName() ) ); CRC32_ProcessBuffer( &crc, (void *)&seqdesc.flags, sizeof( seqdesc.flags ) ); //CRC32_ProcessBuffer( &crc, (void *)&seqdesc.numevents, sizeof( seqdesc.numevents ) ); CRC32_ProcessBuffer( &crc, (void *)&seqdesc.numblends, sizeof( seqdesc.numblends ) ); CRC32_ProcessBuffer( &crc, (void *)seqdesc.groupsize, sizeof( seqdesc.groupsize ) ); KeyValues *seqKeyValues = new KeyValues(""); if ( seqKeyValues->LoadFromBuffer( m_pModel->GetFileName( ), m_pModel->GetKeyValueText( sequence ) ) ) { // Yuck, but I need it in a contiguous block of memory... oh well... CUtlBuffer buf; seqKeyValues->RecursiveSaveToFile( buf, 0 ); CRC32_ProcessBuffer( &crc, ( void * )buf.Base(), buf.TellPut() ); } seqKeyValues->deleteThis(); CRC32_Final( &crc ); return crc; } const char *IFaceposerModels::CFacePoserModel::GetBitmapChecksum( int sequence ) { CRC32_t crc = GetBitmapCRC( sequence ); // Create string name out of binary data static char filename[ 512 ]; char hex[ 16 ]; Q_binarytohex( (byte *)&crc, sizeof( crc ), hex, sizeof( hex ) ); Q_snprintf( filename, sizeof( filename ), "%s", hex ); return filename; } const char *IFaceposerModels::CFacePoserModel::GetBitmapFilename( int sequence ) { char *in, *out; static char filename[ 256 ]; filename[ 0 ] = 0; char modelName[512], modelNameTemp[512]; Q_strncpy( modelNameTemp, GetShortModelName(), sizeof( modelNameTemp ) ); in = modelNameTemp; out = modelName; while ( *in ) { if ( V_isalnum( *in ) || *in == '_' || *in == '\\' || *in == '/' || *in == '.' || *in == ':' ) { *out++ = *in; } in++; } *out = 0; Q_snprintf( filename, sizeof( filename ), "expressions/%s/animation/%s.bmp", modelName, GetBitmapChecksum( sequence ) ); Q_FixSlashes( filename ); strlwr( filename ); CreatePath( filename ); return filename; } void IFaceposerModels::CFacePoserModel::FreeBitmaps() { while ( m_AnimationBitmaps.Count() > 0 ) { AnimBitmap *bm = m_AnimationBitmaps[ 0 ]; delete bm->bitmap; delete bm; m_AnimationBitmaps.Remove( 0 ); } } void IFaceposerModels::CFacePoserModel::LoadBitmapForSequence( mxbitmapdata_t *bitmap, int sequence ) { // See if it exists char filename[ 512 ]; Q_strncpy( filename, GetBitmapFilename( sequence ), sizeof( filename ) ); if ( !LoadBitmapFromFile( filename, *bitmap ) ) { CreateNewBitmap( filename, sequence, 256, false, NULL, bitmap ); } } static float FindPoseCycle( StudioModel *model, int sequence ) { float cycle = 0.0f; if ( !model->GetStudioHdr() ) return cycle; KeyValues *seqKeyValues = new KeyValues(""); if ( seqKeyValues->LoadFromBuffer( model->GetFileName( ), model->GetKeyValueText( sequence ) ) ) { // Do we have a build point section? KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer"); if ( pkvAllFaceposer ) { int thumbnail_frame = pkvAllFaceposer->GetInt( "thumbnail_frame", 0 ); if ( thumbnail_frame ) { // Convert frame to cycle if we have valid data int maxFrame = model->GetNumFrames( sequence ) - 1; if ( maxFrame > 0 ) { cycle = thumbnail_frame / (float)maxFrame; } } } } seqKeyValues->deleteThis(); return cycle; } void EnableStickySnapshotMode( void ) { g_pMatSysWindow->EnableStickySnapshotMode( ); } void DisableStickySnapshotMode( void ) { g_pMatSysWindow->DisableStickySnapshotMode( ); } void IFaceposerModels::CreateNewBitmap( int modelindex, char const *pchBitmapFilename, int sequence, int nSnapShotSize, bool bZoomInOnFace, CExpression *pExpression, mxbitmapdata_t *bitmap ) { CFacePoserModel *m = m_Models[ modelindex ]; if ( m ) { m->CreateNewBitmap( pchBitmapFilename, sequence, nSnapShotSize, bZoomInOnFace, pExpression, bitmap ); } } void IFaceposerModels::CFacePoserModel::CreateNewBitmap( char const *pchBitmapFilename, int sequence, int nSnapShotSize, bool bZoomInOnFace, CExpression *pExpression, mxbitmapdata_t *bitmap ) { MatSysWindow *pWnd = g_pMatSysWindow; if ( !pWnd ) return; StudioModel *model = m_pModel; if ( !model ) return; CStudioHdr *hdr = model->GetStudioHdr(); if ( !hdr ) return; if ( sequence < 0 || sequence >= hdr->GetNumSeq() ) return; mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( sequence ); Con_ColorPrintf( FILE_COLOR, "Creating bitmap %s for sequence '%s'\n", pchBitmapFilename, seqdesc.pszLabel() ); model->ClearOverlaysSequences(); int iLayer = model->GetNewAnimationLayer(); model->SetOverlaySequence( iLayer, sequence, 1.0 ); model->SetOverlayRate( iLayer, FindPoseCycle( model, sequence ), 0.0 ); for (int i = 0; i < hdr->GetNumPoseParameters(); i++) { model->SetPoseParameter( i, 0.0 ); } float flexValues[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ] = { 0 }; if ( pExpression ) { float *settings = pExpression->GetSettings(); float *weights = pExpression->GetWeights(); // Save existing settings from model for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); ++i ) { int j = hdr->pFlexcontroller( i )->localToGlobal; if ( j == -1 ) continue; flexValues[ i ] = model->GetFlexController( i ); // Set Value from passed in settings model->SetFlexController( i, settings[ j ] * weights[ j ] ); } } model->ClearLookTargets( ); QAngle oldrot, oldLight; Vector oldtrans; VectorCopy( model->m_angles, oldrot ); VectorCopy( model->m_origin, oldtrans ); VectorCopy( g_viewerSettings.lightrot, oldLight ); model->m_angles.Init(); model->m_origin.Init(); g_viewerSettings.lightrot.Init(); g_viewerSettings.lightrot.y = -180; bool bSaveGround = g_viewerSettings.showGround; g_viewerSettings.showGround = false; if ( bZoomInOnFace ) { Vector size; VectorSubtract( hdr->hull_max(), hdr->hull_min(), size ); float eyeheight = hdr->hull_min().z + 0.9 * size.z; // float width = ( size.x + size.y ) / 2.0f; model->m_origin.x = size.z * .6f; if ( hdr->GetNumAttachments() > 0 ) { for (int i = 0; i < hdr->GetNumAttachments(); i++) { const mstudioattachment_t &attachment = hdr->pAttachment( i ); int iBone = hdr->GetAttachmentBone( i ); if ( Q_stricmp( attachment.pszName(), "eyes" ) ) continue; mstudiobone_t *bone = hdr->pBone( iBone ); if ( !bone ) continue; matrix3x4_t boneToPose; MatrixInvert( bone->poseToBone, boneToPose ); matrix3x4_t attachmentPoseToLocal; ConcatTransforms( boneToPose, attachment.local, attachmentPoseToLocal ); Vector localSpaceEyePosition; VectorITransform( vec3_origin, attachmentPoseToLocal, localSpaceEyePosition ); // Not sure why this must be negative? eyeheight = -localSpaceEyePosition.z + hdr->hull_min().z; break; } } KeyValues *seqKeyValues = new KeyValues(""); if ( seqKeyValues->LoadFromBuffer( model->GetFileName( ), model->GetKeyValueText( sequence ) ) ) { // Do we have a build point section? KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer"); if ( pkvAllFaceposer ) { float flEyeheight = pkvAllFaceposer->GetFloat( "eye_height", -9999.0f ); if ( flEyeheight != -9999.0f ) { eyeheight = flEyeheight; } } } model->m_origin.z += eyeheight; } else { Vector mins, maxs; model->ExtractBbox(mins, maxs); Vector size; VectorSubtract( maxs, mins, size ); float maxdim = size.x; if ( size.y > maxdim ) maxdim = size.y; if ( size.z > maxdim ) maxdim = size.z; float midpoint = mins.z + 0.5 * size.z; model->m_origin.x = 3 * maxdim; model->m_origin.z += midpoint; } g_pMatSysWindow->PushSnapshotMode( nSnapShotSize ); // Snapshots are taken of the back buffer; // we need to render to the back buffer but not move it to the front pWnd->SuppressBufferSwap( true ); pWnd->redraw(); pWnd->SuppressBufferSwap( false ); // make it square, assumes w > h char fullpath[ 512 ]; Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", GetGameDirectory(), pchBitmapFilename ); pWnd->TakeSnapshotRect( fullpath, 0, 0, nSnapShotSize, nSnapShotSize ); g_pMatSysWindow->PopSnapshotMode( ); VectorCopy( oldrot, model->m_angles ); VectorCopy( oldtrans, model->m_origin ); VectorCopy( oldLight, g_viewerSettings.lightrot ); g_viewerSettings.showGround = bSaveGround; if ( pExpression ) { // Save existing settings from model for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); ++i ) { int j = hdr->pFlexcontroller( i )->localToGlobal; if ( j == -1 ) continue; model->SetFlexController( i, flexValues[ i ] ); } } model->ClearOverlaysSequences(); if ( bitmap->valid ) { DeleteObject( bitmap->image ); bitmap->image = 0; bitmap->valid = false; } LoadBitmapFromFile( pchBitmapFilename, *bitmap ); } mxbitmapdata_t *IFaceposerModels::CFacePoserModel::GetBitmapForSequence( int sequence ) { static mxbitmapdata_t nullbitmap; if ( sequence < 0 || sequence >= m_AnimationBitmaps.Count() ) return &nullbitmap; /* if ( m_bFirstBitmapLoad ) { m_bFirstBitmapLoad = false; ReconcileAnimationBitmaps(); } */ AnimBitmap *slot = m_AnimationBitmaps[ sequence ]; if ( slot->needsload ) { slot->needsload = false; LoadBitmapForSequence( slot->bitmap, sequence ); } return m_AnimationBitmaps[ sequence ]->bitmap; } void IFaceposerModels::CFacePoserModel::BuildValidChecksums( CUtlRBTree< CRC32_t > &tree ) { StudioModel *model = m_pModel; if ( !model ) return; CStudioHdr *hdr = model->GetStudioHdr(); if ( !hdr ) return; for ( int i = 0; i < hdr->GetNumSeq(); i++ ) { CRC32_t crc = GetBitmapCRC( i ); tree.Insert( crc ); } } void IFaceposerModels::CFacePoserModel::ReconcileAnimationBitmaps() { // iterate files in directory and see if each checksum is valid and if not delete the .bmp char path[ 512 ]; Q_snprintf( path, sizeof( path ), "expressions/%s/animation/*.bmp", GetShortModelName() ); FileFindHandle_t hFindFile; char const *fn = filesystem->FindFirstEx( path, "MOD", &hFindFile ); g_pProgressDialog->Start( CFmtStr( "%s - Reconcile Animation Thumbnails", GetShortModelName() ), "", true ); CUtlVector< CUtlString > workList; if ( fn ) { while ( fn ) { // Don't do anything with directories if ( !filesystem->FindIsDirectory( hFindFile ) ) { CUtlString s = fn; workList.AddToTail( s ); } fn = filesystem->FindNext( hFindFile ); } filesystem->FindClose( hFindFile ); } CUtlRBTree< CRC32_t > tree( 0, 0, DefLessFunc( CRC32_t ) ); BuildValidChecksums( tree ); for ( int i = 0 ; i < workList.Count(); ++i ) { char testname[ 256 ]; Q_StripExtension( workList[ i ].String(), testname, sizeof( testname ) ); g_pProgressDialog->UpdateText( "%s", testname ); g_pProgressDialog->Update( (float)i / (float)workList.Count() ); CRC32_t check; Q_hextobinary( testname, Q_strlen( testname ), (byte *)&check, sizeof( check ) ); if ( tree.Find( check ) == tree.InvalidIndex() ) { Q_snprintf( testname, sizeof( testname ), "expressions/%s/animation/%s", GetShortModelName(), fn ); char fullpath[ 512 ]; filesystem->RelativePathToFullPath( testname, "MOD", fullpath, sizeof( fullpath ) ); // Delete it Con_ErrorPrintf( "Removing unused bitmap file %s\n", fullpath ); _unlink( fullpath ); } if ( g_pProgressDialog->IsCancelled() ) { Msg( "Cancelled\n" ); break; } } g_pProgressDialog->Finish(); } void IFaceposerModels::CFacePoserModel::RecreateAllAnimationBitmaps() { StudioModel *model = m_pModel; if ( !model ) return; CStudioHdr *hdr = model->GetStudioHdr(); if ( !hdr ) return; g_pProgressDialog->Start( CFmtStr( "%s - Animation Thumbnails", GetShortModelName() ), "", true ); for ( int i = 0; i < hdr->GetNumSeq(); ++i ) { const mstudioseqdesc_t &seq = hdr->pSeqdesc( i ); g_pProgressDialog->UpdateText( "%s", seq.pszLabel() ); g_pProgressDialog->Update( (float)i / (float)hdr->GetNumSeq() ); RecreateAnimationBitmap( i, false ); if ( g_pProgressDialog->IsCancelled() ) { Msg( "Cancelling\n" ); break; } } g_pProgressDialog->Finish(); ReconcileAnimationBitmaps(); } void IFaceposerModels::CFacePoserModel::RecreateAnimationBitmap( int sequence, bool reconcile ) { if ( sequence < 0 || sequence >= m_AnimationBitmaps.Count() ) { Assert( 0 ); return; } AnimBitmap *slot = m_AnimationBitmaps[ sequence ]; slot->needsload = true; if ( slot->bitmap->valid ) { DeleteObject( slot->bitmap->image ); slot->bitmap->image = 0; slot->bitmap->valid = false; } char filename[ 512 ]; Q_snprintf( filename, sizeof( filename ), "%s", GetBitmapFilename( sequence ) ); if ( filesystem->FileExists( filename ) ) { char fullpath[ 512 ]; filesystem->RelativePathToFullPath( filename, "MOD", fullpath, sizeof( fullpath ) ); _unlink( fullpath ); } // Force recreation GetBitmapForSequence( sequence ); if ( reconcile ) { ReconcileAnimationBitmaps( ); } } void IFaceposerModels::CFacePoserModel::Release( void ) { m_pModel->FreeModel( true ); } void IFaceposerModels::CFacePoserModel::Restore( void ) { StudioModel *save = g_pStudioModel; g_pStudioModel = m_pModel; if (m_pModel->LoadModel( m_pModel->GetFileName() ) ) { m_pModel->PostLoadModel( m_pModel->GetFileName() ); m_pModel->SetSequence( m_pModel->LookupSequence( "idle_subtle" ) ); } g_pStudioModel = save; SetupModelFlexcontrollerLinks( m_pModel ); } IFaceposerModels::IFaceposerModels() { m_nLastRenderFrame = -1; m_nForceModelIndex = -1; } IFaceposerModels::~IFaceposerModels() { while ( m_Models.Count() > 0 ) { delete m_Models[ 0 ]; m_Models.Remove( 0 ); } } IFaceposerModels::CFacePoserModel *IFaceposerModels::GetEntry( int index ) { if ( index < 0 || index >= Count() ) return NULL; CFacePoserModel *m = m_Models[ index ]; if ( !m ) return NULL; return m; } int IFaceposerModels::Count( void ) const { return m_Models.Count(); } char const *IFaceposerModels::GetModelName( int index ) { CFacePoserModel *entry = GetEntry( index ); if ( !entry ) return ""; return entry->GetShortModelName(); } char const *IFaceposerModels::GetModelFileName( int index ) { CFacePoserModel *entry = GetEntry( index ); if ( !entry ) return ""; return entry->GetModelFileName(); } void IFaceposerModels::ForceActiveModelIndex( int index ) { m_nForceModelIndex = index; } void IFaceposerModels::UnForceActiveModelIndex() { m_nForceModelIndex = -1; } int IFaceposerModels::GetActiveModelIndex( void ) const { if ( !g_MDLViewer ) return 0; if ( m_nForceModelIndex != -1 ) return m_nForceModelIndex; return g_MDLViewer->GetActiveModelTab(); } char const *IFaceposerModels::GetActiveModelName( void ) { if ( !g_MDLViewer ) return NULL; return GetModelName( GetActiveModelIndex() ); } //----------------------------------------------------------------------------- // Purpose: // Output : StudioModel //----------------------------------------------------------------------------- StudioModel *IFaceposerModels::GetActiveStudioModel( void ) { StudioModel *mdl = GetStudioModel( GetActiveModelIndex() ); if ( !mdl ) return g_pStudioModel; return mdl; } int IFaceposerModels::FindModelByFilename( char const *filename ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; if ( !stricmp( m->GetModelFileName(), filename ) ) return i; } return -1; } void SetupModelFlexcontrollerLinks( StudioModel *model ); int IFaceposerModels::LoadModel( char const *filename ) { MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); int idx = FindModelByFilename( filename ); if ( idx == -1 && Count() < MAX_FP_MODELS ) { StudioModel *model = new StudioModel(); StudioModel *save = g_pStudioModel; g_pStudioModel = model; if ( !model->LoadModel( filename ) ) { delete model; g_pStudioModel = save; return 0; // ?? ERROR } g_pStudioModel = save; model->SetSequence( model->LookupSequence( "idle_subtle" ) ); int idx = model->GetSequence(); model->SetSequence( idx ); SetupModelFlexcontrollerLinks( model ); if (!LoadViewerSettings( filename, model )) { InitViewerSettings( "faceposer" ); } model->ClearOverlaysSequences(); CFacePoserModel *newEntry = new CFacePoserModel( filename, model ); idx = m_Models.AddToTail( newEntry ); g_MDLViewer->InitModelTab(); g_MDLViewer->SetActiveModelTab( idx ); //g_pControlPanel->CenterOnFace(); } return idx; } void IFaceposerModels::FreeModel( int index ) { CFacePoserModel *entry = GetEntry( index ); if ( !entry ) return; StudioModel *m = entry->GetModel(); SaveViewerSettings( m->GetFileName(), m ); m->FreeModel( false ); delete m; delete entry; m_Models.Remove( index ); g_MDLViewer->InitModelTab(); } void IFaceposerModels::CloseAllModels( void ) { int c = Count(); for ( int i = c - 1; i >= 0; i-- ) { FreeModel( i ); } } StudioModel *IFaceposerModels::GetStudioModel( int index ) { CFacePoserModel *m = GetEntry( index ); if ( !m ) return NULL; if ( !m->GetModel() ) return NULL; return m->GetModel(); } CStudioHdr *IFaceposerModels::GetStudioHeader( int index ) { StudioModel *m = GetStudioModel( index ); if ( !m ) return NULL; CStudioHdr *hdr = m->GetStudioHdr(); if ( !hdr ) return NULL; return hdr; } int IFaceposerModels::GetModelIndexForActor( char const *actorname ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; if ( !stricmp( m->GetActorName(), actorname ) ) return i; } return 0; } StudioModel *IFaceposerModels::GetModelForActor( char const *actorname ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; if ( !stricmp( m->GetActorName(), actorname ) ) return m->GetModel(); } return NULL; } char const *IFaceposerModels::GetActorNameForModel( int modelindex ) { CFacePoserModel *m = GetEntry( modelindex ); if ( !m ) return ""; return m->GetActorName(); } void IFaceposerModels::SetActorNameForModel( int modelindex, char const *actorname ) { CFacePoserModel *m = GetEntry( modelindex ); if ( !m ) return; m->SetActorName( actorname ); } void IFaceposerModels::SaveModelList( void ) { workspacefiles->StartStoringFiles( IWorkspaceFiles::MODELDATA ); int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; workspacefiles->StoreFile( IWorkspaceFiles::MODELDATA, m->GetModelFileName() ); } workspacefiles->FinishStoringFiles( IWorkspaceFiles::MODELDATA ); } void IFaceposerModels::LoadModelList( void ) { int files = workspacefiles->GetNumStoredFiles( IWorkspaceFiles::MODELDATA ); for ( int i = 0; i < files; i++ ) { char const *filename = workspacefiles->GetStoredFile( IWorkspaceFiles::MODELDATA, i ); LoadModel( filename ); } } void IFaceposerModels::ReleaseModels( void ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; m->Release(); } } void IFaceposerModels::RestoreModels( void ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; m->Restore(); } } /* void IFaceposerModels::RefreshModels( void ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; m->Refresh(); } } */ int IFaceposerModels::CountVisibleModels( void ) { int num = 0; int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; if ( m->GetVisibleIn3DView() ) { num++; } } return num; } void IFaceposerModels::ShowModelIn3DView( int modelindex, bool show ) { CFacePoserModel *m = GetEntry( modelindex ); if ( !m ) return; m->SetVisibleIn3DView( show ); } bool IFaceposerModels::IsModelShownIn3DView( int modelindex ) { CFacePoserModel *m = GetEntry( modelindex ); if ( !m ) return false; return m->GetVisibleIn3DView(); } int IFaceposerModels::GetIndexForStudioModel( StudioModel *model ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; if ( m->GetModel() == model ) return i; } return -1; } void IFaceposerModels::CheckResetFlexes( void ) { int current_render_frame = g_MDLViewer->GetCurrentFrame(); if ( current_render_frame == m_nLastRenderFrame ) return; m_nLastRenderFrame = current_render_frame; // the phoneme editor just adds to the face, so reset the controllers int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; StudioModel *model = m->GetModel(); if ( !model ) continue; CStudioHdr *hdr = model->GetStudioHdr(); if ( !hdr ) continue; for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ ) { model->SetFlexController( i, 0.0f ); } } } void IFaceposerModels::ClearOverlaysSequences( void ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; StudioModel *model = m->GetModel(); if ( !model ) continue; model->ClearOverlaysSequences(); } } mxbitmapdata_t *IFaceposerModels::GetBitmapForSequence( int modelindex, int sequence ) { static mxbitmapdata_t nullbitmap; CFacePoserModel *m = GetEntry( modelindex ); if ( !m ) return &nullbitmap; return m->GetBitmapForSequence( sequence ); } void IFaceposerModels::RecreateAllAnimationBitmaps( int modelindex ) { CFacePoserModel *m = GetEntry( modelindex ); if ( !m ) return; m->RecreateAllAnimationBitmaps(); } void IFaceposerModels::RecreateAnimationBitmap( int modelindex, int sequence ) { CFacePoserModel *m = GetEntry( modelindex ); if ( !m ) return; m->RecreateAnimationBitmap( sequence, true ); } int IFaceposerModels::CountActiveSources() { int count = 0; int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; StudioModel *model = m->GetModel(); if ( !model ) continue; count += model->m_mouth.GetNumVoiceSources(); } return count; } void IFaceposerModels::ClearModelTargets( bool force /*=false*/ ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; StudioModel *mdl = m->GetModel(); if ( !mdl ) continue; mdl->ClearLookTargets(); } } void IFaceposerModels::SetSolveHeadTurn( int solve ) { int c = Count(); for ( int i = 0; i < c; i++ ) { CFacePoserModel *m = GetEntry( i ); if ( !m ) continue; StudioModel *mdl = m->GetModel(); if ( !mdl ) continue; mdl->SetSolveHeadTurn( solve ); } } static IFaceposerModels g_ModelManager; IFaceposerModels *models = &g_ModelManager;