summaryrefslogtreecommitdiff
path: root/utils/hlfaceposer/expclass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hlfaceposer/expclass.cpp')
-rw-r--r--utils/hlfaceposer/expclass.cpp735
1 files changed, 735 insertions, 0 deletions
diff --git a/utils/hlfaceposer/expclass.cpp b/utils/hlfaceposer/expclass.cpp
new file mode 100644
index 0000000..9ed9eaa
--- /dev/null
+++ b/utils/hlfaceposer/expclass.cpp
@@ -0,0 +1,735 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include <mxtk/mx.h>
+#include "expressions.h"
+#include "expclass.h"
+#include "hlfaceposer.h"
+#include "StudioModel.h"
+#include "filesystem.h"
+#include "FlexPanel.h"
+#include "ControlPanel.h"
+#include "mxExpressionTray.h"
+#include "UtlBuffer.h"
+#include "filesystem.h"
+#include "ExpressionTool.h"
+#include "faceposer_models.h"
+#include "mdlviewer.h"
+#include "phonemeconverter.h"
+#include "ProgressDialog.h"
+#include "tier1/fmtstr.h"
+#include "tier1/utlstring.h"
+#include "tier1/utlvector.h"
+
+
+#undef ALIGN4
+#undef ALIGN16
+#define ALIGN4( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3)
+#define ALIGN16( a ) a = (byte *)((int)((byte *)a + 15) & ~ 15)
+
+char const *GetGlobalFlexControllerName( int index );
+int GetGlobalFlexControllerCount( void );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *classname -
+//-----------------------------------------------------------------------------
+CExpClass::CExpClass( const char *classname )
+{
+ Q_strncpy( m_szClassName, classname, sizeof( m_szClassName ) );
+ Q_FileBase( m_szClassName, m_szBaseName, sizeof( m_szBaseName ) );
+ m_szFileName[ 0 ] = 0;
+ m_bDirty = false;
+ m_nSelectedExpression = -1;
+ m_bIsPhonemeClass = Q_strstr( classname, "phonemes" ) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExpClass::~CExpClass( void )
+{
+ m_Expressions.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+//-----------------------------------------------------------------------------
+int CExpClass::FindExpressionIndex( CExpression *exp )
+{
+ for ( int i = 0 ; i < GetNumExpressions(); i++ )
+ {
+ CExpression *e = GetExpression( i );
+ if ( e == exp )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpClass::Save( void )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ const char *filename = GetFileName();
+ if ( !filename || !filename[ 0 ] )
+ return;
+
+ Con_Printf( "Saving changes to %s to file %s\n", GetName(), GetFileName() );
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ int i, j;
+
+ int numflexmaps = 0;
+ int flexmap[128]; // maps file local controlls into global controls
+
+ CExpression *expr = NULL;
+ // find all used controllers
+ int fc = GetGlobalFlexControllerCount();
+ for ( j = 0; j < fc; ++j )
+ {
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+ Assert( expr );
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+
+ if ( settings[j] != 0 ||
+ weights[j] != 0 )
+ {
+ flexmap[ numflexmaps++ ] = j;
+ break;
+ }
+ }
+ }
+
+ buf.Printf( "$keys" );
+ for (j = 0; j < numflexmaps; j++)
+ {
+ buf.Printf( " %s", GetGlobalFlexControllerName( flexmap[j] ) );
+ }
+ buf.Printf( "\n" );
+
+ buf.Printf( "$hasweighting\n" );
+
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+
+ buf.Printf( "\"%s\" ", expr->name );
+
+ // isalpha returns non zero for ents > 256
+ if (expr->index <= 'z')
+ {
+ buf.Printf( "\"%c\" ", expr->index );
+ }
+ else
+ {
+ buf.Printf( "\"0x%04x\" ", expr->index );
+ }
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ for (j = 0; j < numflexmaps; j++)
+ {
+ buf.Printf( "%.3f %.3f ", settings[flexmap[j]], weights[flexmap[j]] );
+ }
+
+ if ( Q_strstr( expr->name, "Right Side Smile" ) )
+ {
+ Con_Printf( "wrote %s with checksum %s\n",
+ expr->name, expr->GetBitmapCheckSum() );
+ }
+
+ buf.Printf( "\"%s\"\n", expr->description );
+ }
+
+ char relative[ 512 ];
+ filesystem->FullPathToRelativePath( filename, relative, sizeof( relative ) );
+
+ MakeFileWriteable( relative );
+ FileHandle_t fh = filesystem->Open( relative, "wt" );
+ if ( !fh )
+ {
+ Con_ErrorPrintf( "Unable to write to %s (read-only?)\n", relative );
+ return;
+ }
+ else
+ {
+ filesystem->Write( buf.Base(), buf.TellPut(), fh );
+ filesystem->Close(fh);
+ }
+
+ SetDirty( false );
+
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+ if ( expr )
+ {
+ expr->ResetUndo();
+ expr->SetDirty( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpClass::Export( void )
+{
+ char vfefilename[ 512 ];
+ Q_StripExtension( GetFileName(), vfefilename, sizeof( vfefilename ) );
+ Q_DefaultExtension( vfefilename, ".vfe", sizeof( vfefilename ) );
+
+ Con_Printf( "Exporting %s to %s\n", GetName(), vfefilename );
+
+ int i, j;
+
+ int numflexmaps = 0;
+ int flexmap[128]; // maps file local controlls into global controls
+ CExpression *expr = NULL;
+
+ // find all used controllers
+ int fc_count = GetGlobalFlexControllerCount();
+
+ for (j = 0; j < fc_count; j++)
+ {
+ int k = j;
+
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+ Assert( expr );
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ if ( settings[k] != 0 || weights[k] != 0 )
+ {
+ flexmap[numflexmaps++] = k;
+ break;
+ }
+ }
+ }
+
+ byte *pData = (byte *)calloc( 1024 * 1024, 1 );
+ byte *pDataStart = pData;
+
+ flexsettinghdr_t *fhdr = (flexsettinghdr_t *)pData;
+
+ fhdr->id = ('V' << 16) + ('F' << 8) + ('E');
+ fhdr->version = 0;
+ V_strncpy( fhdr->name, vfefilename, sizeof( fhdr->name ) );
+
+ // allocate room for header
+ pData += sizeof( flexsettinghdr_t );
+ ALIGN4( pData );
+
+ // store flex settings
+ flexsetting_t *pSetting = (flexsetting_t *)pData;
+ fhdr->numflexsettings = GetNumExpressions();
+ fhdr->flexsettingindex = pData - pDataStart;
+ pData += sizeof( flexsetting_t ) * fhdr->numflexsettings;
+ ALIGN4( pData );
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ expr = GetExpression( i );
+ Assert( expr );
+
+ pSetting[i].index = expr->index;
+ pSetting[i].settingindex = pData - (byte *)(&pSetting[i]);
+
+ flexweight_t *pFlexWeights = (flexweight_t *)pData;
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ for (j = 0; j < numflexmaps; j++)
+ {
+ if (settings[flexmap[j]] != 0 || weights[flexmap[j]] != 0)
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = j;
+ pFlexWeights->weight = settings[flexmap[j]];
+ pFlexWeights->influence = weights[flexmap[j]];
+ pFlexWeights++;
+ }
+ pData = (byte *)pFlexWeights;
+ ALIGN4( pData );
+ }
+ }
+
+ // store indexed table
+ int numindexes = 1;
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ if (pSetting[i].index >= numindexes)
+ numindexes = pSetting[i].index + 1;
+ }
+
+ int *pIndex = (int *)pData;
+ fhdr->numindexes = numindexes;
+ fhdr->indexindex = pData - pDataStart;
+ pData += sizeof( int ) * numindexes;
+ ALIGN4( pData );
+ for (i = 0; i < numindexes; i++)
+ {
+ pIndex[i] = -1;
+ }
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ pIndex[pSetting[i].index] = i;
+ }
+
+ // store flex setting names
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ expr = GetExpression( i );
+ pSetting[i].nameindex = pData - (byte *)(&pSetting[i]);
+ strcpy( (char *)pData, expr->name );
+ pData += strlen( expr->name ) + 1;
+ }
+ ALIGN4( pData );
+
+ // store key names
+ char **pKeynames = (char **)pData;
+ fhdr->numkeys = numflexmaps;
+ fhdr->keynameindex = pData - pDataStart;
+ pData += sizeof( char *) * numflexmaps;
+
+ for (i = 0; i < numflexmaps; i++)
+ {
+ pKeynames[i] = (char *)(pData - pDataStart);
+ strcpy( (char *)pData, GetGlobalFlexControllerName( flexmap[i] ) );
+ pData += strlen( GetGlobalFlexControllerName( flexmap[i] ) ) + 1;
+ }
+ ALIGN4( pData );
+
+ // allocate room for remapping
+ int *keymapping = (int *)pData;
+ fhdr->keymappingindex = pData - pDataStart;
+ pData += sizeof( int ) * numflexmaps;
+ for (i = 0; i < numflexmaps; i++)
+ {
+ keymapping[i] = -1;
+ }
+ ALIGN4( pData );
+
+ fhdr->length = pData - pDataStart;
+
+ char relative[ 512 ];
+ filesystem->FullPathToRelativePath( vfefilename, relative, sizeof( relative ) );
+
+ MakeFileWriteable( relative );
+ FileHandle_t fh = filesystem->Open( relative, "wb" );
+ if ( !fh )
+ {
+ Con_ErrorPrintf( "Unable to write to %s (read-only?)\n", relative );
+ return;
+ }
+ else
+ {
+ filesystem->Write( pDataStart, fhdr->length, fh );
+ filesystem->Close(fh);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpClass::GetBaseName( void ) const
+{
+ return m_szBaseName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpClass::GetName( void ) const
+{
+ return m_szClassName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpClass::GetFileName( void ) const
+{
+ return m_szFileName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CExpClass::SetFileName( const char *filename )
+{
+ strcpy( m_szFileName, filename );
+}
+
+bool IsUsingPerPlayerExpressions();
+
+void CExpClass::ReloadBitmaps( void )
+{
+ bool bUsingPerPlayerOverrides = IsUsingPerPlayerExpressions();
+
+ int c = models->Count();
+ for ( int model = 0; model < MAX_FP_MODELS; model++ )
+ {
+ // Only reload bitmaps for current model index
+ if ( bUsingPerPlayerOverrides && model != models->GetActiveModelIndex() )
+ continue;
+
+ models->ForceActiveModelIndex( model );
+
+ for ( int i = 0 ; i < GetNumExpressions(); i++ )
+ {
+ CExpression *e = GetExpression( i );
+ if ( !e )
+ continue;
+
+ if ( e->m_Bitmap[ model ].valid )
+ {
+ DeleteObject( e->m_Bitmap[ model ].image );
+ e->m_Bitmap[ model ].valid = false;
+ }
+
+ if ( model >= c )
+ continue;
+
+ if ( !LoadBitmapFromFile( e->GetBitmapFilename( model ), e->m_Bitmap[ model ] ) )
+ {
+ e->CreateNewBitmap( model );
+ }
+ }
+ }
+
+ models->UnForceActiveModelIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+// *description -
+// *flexsettings -
+// selectnewitem -
+// Output : CExpression
+//-----------------------------------------------------------------------------
+CExpression *CExpClass::AddExpression( const char *name, const char *description, float *flexsettings, float *flexweights, bool selectnewitem, bool bDirtyClass )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return NULL;
+
+ CExpression *exp = FindExpression( name );
+ if ( exp )
+ {
+ Con_ErrorPrintf( "Can't create, an expression with the name '%s' already exists.\n", name );
+ return NULL;
+ }
+
+ // Add to end of list
+ int idx = m_Expressions.AddToTail();
+
+ exp = &m_Expressions[ idx ];
+
+ float *settings = exp->GetSettings();
+ float *weights = exp->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ exp->SetExpressionClass( GetName() );
+ strcpy( exp->name, name );
+ strcpy( exp->description, description );
+ memcpy( settings, flexsettings, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( weights, flexweights, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ exp->index = '_';
+
+ if ( IsPhonemeClass() )
+ {
+ exp->index = TextToPhoneme( name );
+ }
+
+ exp->m_Bitmap[ models->GetActiveModelIndex() ].valid = false;
+ if ( !LoadBitmapFromFile( exp->GetBitmapFilename( models->GetActiveModelIndex() ), exp->m_Bitmap[ models->GetActiveModelIndex() ] ) )
+ {
+ exp->CreateNewBitmap( models->GetActiveModelIndex() );
+ }
+
+ if ( selectnewitem )
+ {
+ SelectExpression( idx );
+ }
+
+ if ( bDirtyClass )
+ {
+ SetDirty( true );
+ }
+
+ return exp;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+// Output : CExpression
+//-----------------------------------------------------------------------------
+CExpression *CExpClass::FindExpression( const char *name )
+{
+ for ( int i = 0 ; i < m_Expressions.Size(); i++ )
+ {
+ CExpression *exp = &m_Expressions[ i ];
+ if ( !stricmp( exp->name, name ) )
+ {
+ return exp;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+//-----------------------------------------------------------------------------
+void CExpClass::DeleteExpression( const char *name )
+{
+
+ for ( int i = 0 ; i < m_Expressions.Size(); i++ )
+ {
+ CExpression *exp = &m_Expressions[ i ];
+ if ( !stricmp( exp->name, name ) )
+ {
+ SetDirty( true );
+
+ m_Expressions.Remove( i );
+ return;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpClass::GetNumExpressions( void )
+{
+ return m_Expressions.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : num -
+// Output : CExpression
+//-----------------------------------------------------------------------------
+CExpression *CExpClass::GetExpression( int num )
+{
+ if ( num < 0 || num >= m_Expressions.Size() )
+ {
+ return NULL;
+ }
+
+ CExpression *exp = &m_Expressions[ num ];
+ return exp;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpClass::GetDirty( void )
+{
+ return m_bDirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dirty -
+//-----------------------------------------------------------------------------
+void CExpClass::SetDirty( bool dirty )
+{
+ m_bDirty = dirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpClass::GetIndex( void )
+{
+ for ( int i = 0; i < expressions->GetNumClasses(); i++ )
+ {
+ CExpClass *cl = expressions->GetClass( i );
+ if ( cl == this )
+ return i;
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : num -
+//-----------------------------------------------------------------------------
+void CExpClass::SelectExpression( int num, bool deselect )
+{
+ m_nSelectedExpression = num;
+
+ g_pFlexPanel->setExpression( num );
+ g_pExpressionTrayTool->Select( num, deselect );
+ g_pExpressionTrayTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpClass::GetSelectedExpression( void )
+{
+ return m_nSelectedExpression;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpClass::DeselectExpression( void )
+{
+ m_nSelectedExpression = -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : exp1 -
+// exp2 -
+//-----------------------------------------------------------------------------
+void CExpClass::SwapExpressionOrder( int exp1, int exp2 )
+{
+ CExpression temp1 = m_Expressions[ exp1 ];
+ CExpression temp2 = m_Expressions[ exp2 ];
+
+ m_Expressions.Remove( exp1 );
+ m_Expressions.InsertBefore( exp1, temp2 );
+ m_Expressions.Remove( exp2 );
+ m_Expressions.InsertBefore( exp2, temp1 );
+}
+
+void CExpClass::BuildValidChecksums( CUtlRBTree< CRC32_t > &tree )
+{
+ for ( int i = 0; i < m_Expressions.Size(); i++ )
+ {
+ CExpression *exp = &m_Expressions[ i ];
+ if ( !exp )
+ continue;
+
+ CRC32_t crc = exp->GetBitmapCRC();
+ tree.Insert( crc );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: After a class is loaded, check the class directory and delete any bmp files that aren't
+// still referenced
+//-----------------------------------------------------------------------------
+void CExpClass::CheckBitmapConsistency( void )
+{
+ char path[ 512 ];
+
+ Q_snprintf( path, sizeof( path ), "expressions/%s/%s/*.bmp", models->GetActiveModelName(), GetBaseName() );
+ Q_FixSlashes( path );
+ Q_strlower( path );
+
+ g_pProgressDialog->Start( CFmtStr( "%s / %s - Reconcile Expression Thumbnails", models->GetActiveModelName(), GetBaseName() ), "", true );
+
+ CUtlVector< CUtlString > workList;
+
+ FileFindHandle_t hFindFile;
+ char const *fn = filesystem->FindFirstEx( path, "MOD", &hFindFile );
+ 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() )
+ {
+ char kill[ 512 ];
+ Q_snprintf( kill, sizeof( kill ), "expressions/%s/%s/%s", models->GetActiveModelName(), GetBaseName(), fn );
+ Q_FixSlashes( kill );
+ Q_strlower( kill );
+
+ // Delete it
+ Con_ErrorPrintf( "Removing unused bitmap file '%s'\n", kill );
+
+ filesystem->RemoveFile( kill, "MOD" );
+ }
+
+ if ( g_pProgressDialog->IsCancelled() )
+ {
+ Msg( "Cancelled\n" );
+ break;
+ }
+ }
+
+ g_pProgressDialog->Finish();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Does this class have expression indices based on phoneme lookups
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpClass::IsPhonemeClass( void ) const
+{
+ return m_bIsPhonemeClass;
+} \ No newline at end of file