diff options
Diffstat (limited to 'utils/hlfaceposer/expressions.cpp')
| -rw-r--r-- | utils/hlfaceposer/expressions.cpp | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/utils/hlfaceposer/expressions.cpp b/utils/hlfaceposer/expressions.cpp new file mode 100644 index 0000000..a199f20 --- /dev/null +++ b/utils/hlfaceposer/expressions.cpp @@ -0,0 +1,692 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "hlfaceposer.h" +#include "expressions.h" +#include <mxtk/mx.h> +#include "ControlPanel.h" +#include "StudioModel.h" +#include "expclass.h" +#include "mxExpressionTab.h" +#include "mxExpressionTray.h" +#include "filesystem.h" +#include "faceposer_models.h" +#include "utldict.h" +#include "scriplib.h" +#include "checksum_crc.h" + +bool Sys_Error(const char *pMsg, ...); +extern char g_appTitle[]; + +static CUtlVector< CUtlSymbol > g_GlobalFlexControllers; +static CUtlDict< int, int > g_GlobalFlexControllerLookup; + +void ChecksumFlexControllers( bool bSpew, char const *name, CRC32_t &crc, const float *settings, const float *weights ) +{ + CRC32_Init( &crc ); + + // Walk them alphabetically so that load order doesn't matter + for ( int i = g_GlobalFlexControllerLookup.First() ; + i != g_GlobalFlexControllerLookup.InvalidIndex(); + i = g_GlobalFlexControllerLookup.Next( i ) ) + { + int controllerIndex = g_GlobalFlexControllerLookup[ i ]; + char const *pszName = g_GlobalFlexControllerLookup.GetElementName( i ); + + // Only count active controllers in checksum + float s = settings[ controllerIndex ]; + float w = weights[ controllerIndex ]; + + if ( s == 0.0f && w == 0.0f ) + { + continue; + } + + CRC32_ProcessBuffer( &crc, (void *)pszName, Q_strlen( pszName ) ); + CRC32_ProcessBuffer( &crc, (void *)&s, sizeof( s ) ); + CRC32_ProcessBuffer( &crc, (void *)&w, sizeof( w ) ); + + if ( bSpew ) + { + Msg( "[%d] %s == %f %f\n", controllerIndex, pszName, s, w ); + } + } + + CRC32_Final( &crc ); + + if ( bSpew ) + { + char hex[ 17 ]; + Q_binarytohex( (const byte *)&crc, sizeof( crc ), hex, sizeof( hex ) ); + Msg( "%s checksum = %sf\n", name, hex ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : char const +//----------------------------------------------------------------------------- +char const *GetGlobalFlexControllerName( int index ) +{ + return g_GlobalFlexControllers[ index ].String(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int GetGlobalFlexControllerCount( void ) +{ + return g_GlobalFlexControllers.Count(); +} +//----------------------------------------------------------------------------- +// Purpose: Accumulates throughout runtime session, oh well +// Input : *szName - +// Output : int +//----------------------------------------------------------------------------- +int AddGlobalFlexController( StudioModel *model, const char *szName ) +{ + int idx = g_GlobalFlexControllerLookup.Find( szName ); + if ( idx != g_GlobalFlexControllerLookup.InvalidIndex() ) + { + return g_GlobalFlexControllerLookup[ idx ]; + } + + CUtlSymbol sym; + sym = szName; + idx = g_GlobalFlexControllers.AddToTail( sym ); + g_GlobalFlexControllerLookup.Insert( szName, idx ); + // Con_Printf( "Added global flex controller %i %s from %s\n", idx, szName, model->GetStudioHdr()->name ); + return idx; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *model - +//----------------------------------------------------------------------------- +void SetupModelFlexcontrollerLinks( StudioModel *model ) +{ + if ( !model ) + return; + + CStudioHdr *hdr = model->GetStudioHdr(); + if ( !hdr ) + return; + + if ( hdr->numflexcontrollers() <= 0 ) + return; + + // Already set up!!! + if ( hdr->pFlexcontroller( LocalFlexController_t(0) )->localToGlobal != -1 ) + return; + + for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++) + { + int j = AddGlobalFlexController( model, hdr->pFlexcontroller( i )->pszName() ); + hdr->pFlexcontroller( i )->localToGlobal = j; + model->SetFlexController( i, 0.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CExpressionManager : public IExpressionManager +{ +public: + CExpressionManager( void ); + ~CExpressionManager( void ); + + void Reset( void ); + + void ActivateExpressionClass( CExpClass *cl ); + + // File I/O + void LoadClass( const char *filename ); + void CreateNewClass( const char *filename ); + bool CloseClass( CExpClass *cl ); + + CExpClass *AddCExpClass( const char *classname, const char *filename ); + int GetNumClasses( void ); + + CExpression *GetCopyBuffer( void ); + + bool CanClose( void ); + + CExpClass *GetActiveClass( void ); + CExpClass *GetClass( int num ); + CExpClass *FindClass( const char *classname, bool bMatchBaseNameOnly ); + +private: + // Methods + const char *GetClassnameFromFilename( const char *filename ); + +// UI + void PopulateClassCB( CExpClass *cl ); + + void RemoveCExpClass( CExpClass *cl ); + +private: + // Data + CExpClass *m_pActiveClass; + CUtlVector < CExpClass * > m_Classes; + + CExpression m_CopyBuffer; +}; + +// Expose interface +static CExpressionManager g_ExpressionManager; +IExpressionManager *expressions = &g_ExpressionManager; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CExpressionManager::CExpressionManager( void ) +{ + m_pActiveClass = NULL; + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CExpressionManager::~CExpressionManager( void ) +{ + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CExpressionManager::Reset( void ) +{ + while ( m_Classes.Size() > 0 ) + { + CExpClass *p = m_Classes[ 0 ]; + m_Classes.Remove( 0 ); + delete p; + } + + m_pActiveClass = NULL; + + memset( &m_CopyBuffer, 0, sizeof( m_CopyBuffer ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CExpClass *CExpressionManager::GetActiveClass( void ) +{ + return m_pActiveClass; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : num - +// Output : CExpClass +//----------------------------------------------------------------------------- +CExpClass *CExpressionManager::GetClass( int num ) +{ + return m_Classes[ num ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *classname - +// *filename - +// Output : CExpClass * +//----------------------------------------------------------------------------- +CExpClass * CExpressionManager::AddCExpClass( const char *classname, const char *filename ) +{ + Assert( !FindClass( classname, false ) ); + + CExpClass *pclass = new CExpClass( classname ); + if ( !pclass ) + return NULL; + + m_Classes.AddToTail( pclass ); + + pclass->SetFileName( filename ); + + return pclass; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cl - +//----------------------------------------------------------------------------- +void CExpressionManager::RemoveCExpClass( CExpClass *cl ) +{ + for ( int i = 0; i < m_Classes.Size(); i++ ) + { + CExpClass *p = m_Classes[ i ]; + if ( p == cl ) + { + m_Classes.Remove( i ); + delete p; + break; + } + } + + if ( m_Classes.Size() >= 1 ) + { + ActivateExpressionClass( m_Classes[ 0 ] ); + } + else + { + ActivateExpressionClass( NULL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cl - +//----------------------------------------------------------------------------- +void CExpressionManager::ActivateExpressionClass( CExpClass *cl ) +{ + m_pActiveClass = cl; + int select = 0; + for ( int i = 0; i < GetNumClasses(); i++ ) + { + CExpClass *c = GetClass( i ); + if ( cl == c ) + { + select = i; + break; + } + } + + g_pExpressionClass->select( select ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CExpressionManager::GetNumClasses( void ) +{ + return m_Classes.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *classname - +// Output : CExpClass +//----------------------------------------------------------------------------- +CExpClass *CExpressionManager::FindClass( const char *classname, bool bMatchBaseNameOnly ) +{ + char search[ 256 ]; + if ( bMatchBaseNameOnly ) + { + Q_FileBase( classname, search, sizeof( search ) ); + } + else + { + Q_strncpy( search, classname, sizeof( search ) ); + } + + Q_FixSlashes( search ); + Q_strlower( search ); + + for ( int i = 0; i < m_Classes.Size(); i++ ) + { + CExpClass *cl = m_Classes[ i ]; + + if ( !Q_stricmp( search, bMatchBaseNameOnly ? cl->GetBaseName() : cl->GetName() ) ) + { + return cl; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +// Output : const char +//----------------------------------------------------------------------------- +const char *CExpressionManager::GetClassnameFromFilename( const char *filename ) +{ + char cleanname[ 256 ]; + static char classname[ 256 ]; + classname[ 0 ] = 0; + + Assert( filename && filename[ 0 ] ); + + // Strip the .txt + Q_StripExtension( filename, cleanname, sizeof( cleanname ) ); + + char *p = Q_stristr( cleanname, "expressions" ); + if ( p ) + { + Q_strncpy( classname, p + Q_strlen( "expressions" ) + 1, sizeof( classname ) ); + } + else + { + Assert( 0 ); + Q_strncpy( classname, cleanname, sizeof( classname ) ); + } + + Q_FixSlashes( classname ); + Q_strlower( classname ); + return classname; +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CExpression +//----------------------------------------------------------------------------- +CExpression *CExpressionManager::GetCopyBuffer( void ) +{ + return &m_CopyBuffer; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CExpressionManager::CanClose( void ) +{ + for ( int i = 0; i < m_Classes.Size(); i++ ) + { + CExpClass *pclass = m_Classes[ i ]; + if ( pclass->GetDirty() ) + { + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +//----------------------------------------------------------------------------- +void CExpressionManager::LoadClass( const char *inpath ) +{ + MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); + + if ( inpath[ 0 ] == '/' || inpath[ 0 ] == '\\' ) + ++inpath; + + char filename[ 512 ]; + Q_strncpy( filename, inpath, sizeof( filename ) ); + + CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr(); + if ( !hdr ) + { + Con_ErrorPrintf( "Can't load expressions from %s, must load a .mdl file first!\n", + filename ); + return; + } + + Con_Printf( "Loading expressions from %s\n", filename ); + + const char *classname = GetClassnameFromFilename( filename ); + + // Already loaded, don't do anything + if ( FindClass( classname, false ) ) + return; + + // Import actual data + LoadScriptFile( filename, SCRIPT_USE_RELATIVE_PATH ); + + CExpClass *active = AddCExpClass( classname, filename ); + if ( !active ) + return; + + ActivateExpressionClass( active ); + + int numflexmaps = 0; + int flexmap[128]; // maps file local controls into global controls + LocalFlexController_t localflexmap[128]; // maps file local controls into local controls + bool bHasWeighting = false; + bool bNormalized = false; + + EnableStickySnapshotMode( ); + + while (1) + { + GetToken (true); + if (endofscript) + break; + if (stricmp( token, "$keys" ) == 0) + { + numflexmaps = 0; + while (TokenAvailable()) + { + flexmap[numflexmaps] = -1; + localflexmap[numflexmaps] = LocalFlexController_t(-1); + + GetToken( false ); + bool bFound = false; + for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++) + { + if (stricmp( hdr->pFlexcontroller(i)->pszName(), token ) == 0) + { + localflexmap[numflexmaps] = i; + flexmap[numflexmaps] = AddGlobalFlexController( models->GetActiveStudioModel(), + hdr->pFlexcontroller(i)->pszName() ); + bFound = true; + break; + } + } + if ( !bFound ) + { + flexmap[ numflexmaps ] = AddGlobalFlexController( models->GetActiveStudioModel(), token ); + } + numflexmaps++; + } + } + else if ( !stricmp( token, "$hasweighting" ) ) + { + bHasWeighting = true; + } + else if ( !stricmp( token, "$normalized" ) ) + { + bNormalized = true; + } + else + { + float setting[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ]; + float weight[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ]; + char name[ 256 ]; + char desc[ 256 ]; + int index; + + memset( setting, 0, sizeof( setting ) ); + memset( weight, 0, sizeof( weight ) ); + + strcpy( name, token ); + + // phoneme index + GetToken( false ); + if (token[1] == 'x') + { + sscanf( &token[2], "%x", &index ); + } + else + { + index = (int)token[0]; + } + + // key values + for (int i = 0; i < numflexmaps; i++) + { + if (flexmap[i] > -1) + { + GetToken( false ); + setting[flexmap[i]] = atof( token ); + if (bHasWeighting) + { + GetToken( false ); + weight[flexmap[i]] = atof( token ); + } + else + { + weight[flexmap[i]] = 1.0; + } + + if ( bNormalized && localflexmap[ i ] > -1 ) + { + mstudioflexcontroller_t *pFlex = hdr->pFlexcontroller( localflexmap[i] ); + if ( pFlex->min != pFlex->max ) + { + setting[flexmap[i]] = Lerp( setting[flexmap[i]], pFlex->min, pFlex->max ); + } + } + } + else + { + GetToken( false ); + if (bHasWeighting) + { + GetToken( false ); + } + } + } + + // description + GetToken( false ); + strcpy( desc, token ); + + CExpression *exp = active->AddExpression( name, desc, setting, weight, false, false ); + if ( active->IsPhonemeClass() && exp ) + { + if ( exp->index != index ) + { + Con_Printf( "CExpressionManager::LoadClass (%s): phoneme index for %s in .txt file is wrong (expecting %i got %i), ignoring...\n", + classname, name, exp->index, index ); + } + } + } + } + + active->CheckBitmapConsistency(); + + DisableStickySnapshotMode( ); + + PopulateClassCB( active ); + + active->DeselectExpression(); + + Assert( !active->GetDirty() ); + active->SetDirty( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +//----------------------------------------------------------------------------- +void CExpressionManager::CreateNewClass( const char *filename ) +{ + CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr(); + if ( !hdr ) + { + Con_ErrorPrintf( "Can't create new expression file %s, must load a .mdl file first!\n", filename ); + return; + } + + // Tell the use that the filename was loaded, expressions are empty for now + const char *classname = GetClassnameFromFilename( filename ); + + // Already loaded, don't do anything + if ( FindClass( classname, false ) ) + return; + + Con_Printf( "Creating %s\n", filename ); + + CExpClass *active = AddCExpClass( classname, filename ); + if ( !active ) + return; + + ActivateExpressionClass( active ); + + // Select the newly created class + PopulateClassCB( active ); + + // Select first expression + active->SelectExpression( 0 ); + + // Nothing has changed so far + active->SetDirty( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cl - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CExpressionManager::CloseClass( CExpClass *cl ) +{ + if ( !cl ) + return true; + + if ( cl->GetDirty() ) + { + int retval = mxMessageBox( NULL, va( "Save changes to class '%s'?", cl->GetName() ), g_appTitle, MX_MB_YESNOCANCEL ); + if ( retval == 2 ) + { + return false; + } + if ( retval == 0 ) + { + Con_Printf( "Saving changes to %s : %s\n", cl->GetName(), cl->GetFileName() ); + cl->Save(); + } + } + + // The memory can be freed here, so be more careful + char temp[ 256 ]; + V_strcpy_safe( temp, cl->GetName() ); + + RemoveCExpClass( cl ); + + Con_Printf( "Closed expression class %s\n", temp ); + + CExpClass *active = GetActiveClass(); + if ( !active ) + { + PopulateClassCB( NULL ); + g_pExpressionTrayTool->redraw(); + return true; + } + + // Select the first remaining class + PopulateClassCB( active ); + + // Select first expression + active->DeselectExpression(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : classnum - +//----------------------------------------------------------------------------- +void CExpressionManager::PopulateClassCB( CExpClass *current ) +{ + g_pExpressionClass->removeAll(); + int select = 0; + for ( int i = 0; i < GetNumClasses(); i++ ) + { + CExpClass *cl = GetClass( i ); + if ( !cl ) + continue; + + g_pExpressionClass->add( cl->GetName() ); + + if ( cl == current ) + { + select = i; + } + } + + g_pExpressionClass->select( select ); +}
\ No newline at end of file |