summaryrefslogtreecommitdiff
path: root/movieobjects/dmobjserializer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'movieobjects/dmobjserializer.cpp')
-rw-r--r--movieobjects/dmobjserializer.cpp1464
1 files changed, 1464 insertions, 0 deletions
diff --git a/movieobjects/dmobjserializer.cpp b/movieobjects/dmobjserializer.cpp
new file mode 100644
index 0000000..e8c3a83
--- /dev/null
+++ b/movieobjects/dmobjserializer.cpp
@@ -0,0 +1,1464 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Serialize and Unserialize Wavefront OBJ <-> DME Data
+//
+//=============================================================================
+
+
+// Valve includes
+#include "tier1/characterset.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmemesh.h"
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmematerial.h"
+#include "movieobjects/dmobjserializer.h"
+#include "movieobjects/dmecombinationoperator.h"
+#include "movieobjects/dmemodel.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier1/UtlStringMap.h"
+#include "mathlib/mathlib.h"
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+class CFaceSetData
+{
+public:
+ void Clear();
+
+ inline CUtlVector< int > *GetFaceSetIndices( const char *pFaceSetName )
+ {
+ return &m_faceSetIndices[ pFaceSetName ];
+ }
+
+ void AddToMesh( CDmeMesh *pMesh );
+protected:
+
+ CUtlStringMap< CUtlVector< int > > m_faceSetIndices;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CFaceSetData::Clear()
+{
+ m_faceSetIndices.Clear();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CFaceSetData::AddToMesh( CDmeMesh *pMesh )
+{
+ const int nFaceSets( m_faceSetIndices.GetNumStrings() );
+ for ( int i( 0 ); i < nFaceSets; ++i )
+ {
+ const char *pName( m_faceSetIndices.String( i ) );
+ CUtlVector< int > &faceSetIndices( m_faceSetIndices[ pName ] );
+
+ if ( faceSetIndices.Count() )
+ {
+ CDmeFaceSet *pFaceSet = CreateElement< CDmeFaceSet >( pName, pMesh->GetFileId() );
+
+ CDmeMaterial *pMaterial = CreateElement< CDmeMaterial >( pName, pMesh->GetFileId() );
+ pMaterial->SetMaterial( pName );
+
+ pFaceSet->AddIndices( faceSetIndices.Count() );
+ pFaceSet->SetIndices( 0, faceSetIndices.Count(), faceSetIndices.Base() );
+
+ pFaceSet->SetMaterial( pMaterial );
+
+ pMesh->AddFaceSet( pFaceSet );
+ }
+ }
+
+ Clear();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+class CVertexData
+{
+public:
+ void Clear();
+
+ inline void AddPosition( const Vector &p ) { m_positions.AddToTail( p ); }
+
+ inline void AddPositionIndex( int i ) { m_pIndices.AddToTail( i ); }
+
+ inline void AddNormal( const Vector &n ) { m_normals.AddToTail( n ); }
+
+ inline void AddNormalIndex( int i ) { m_nIndices.AddToTail( i ); }
+
+ inline void AddUV( const Vector2D &uv ) { AddUniqueValue( uv, m_uvs, m_uvIndexMap, FLT_EPSILON * 0.1f ); }
+
+ inline void AddUVIndex( int i ) { Assert( i < m_uvIndexMap.Count() ); m_uvIndices.AddToTail( m_uvIndexMap[ i ] ); }
+
+ inline int VertexCount() const { return m_pIndices.Count(); }
+
+ CDmeVertexDataBase *AddToMesh( CDmeMesh *pMesh, bool bAbsolute, const char *pName, bool bDelta );
+
+protected:
+ template < class T_t > void AddUniqueValue(
+ const T_t &v,
+ CUtlVector< T_t > &vs,
+ CUtlVector< int > &map,
+ float flThresh = FLT_EPSILON )
+ {
+ const int nVs( vs.Count() );
+ for ( int i( 0 ); i < nVs; ++i )
+ {
+ if ( v.DistToSqr( vs[ i ] ) < flThresh )
+ {
+ map.AddToTail( i );
+ return;
+ }
+ }
+
+ map.AddToTail( vs.Count() );
+ vs.AddToTail( v );
+ }
+
+ CDmeVertexDataBase *Add( CDmeMesh *pMesh, const char *pName = "bind" );
+
+ CDmeVertexDeltaData *AddDelta( CDmeMesh *pMesh, bool bAbsolute, const char *pName = "bind" );
+
+ CUtlVector< Vector > m_positions;
+ CUtlVector< int > m_pIndices;
+
+ CUtlVector< Vector > m_normals;
+ CUtlVector< int > m_nIndices;
+
+ CUtlVector< Vector2D > m_uvs;
+ CUtlVector< int > m_uvIndexMap;
+ CUtlVector< int > m_uvIndices;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CVertexData::Clear()
+{
+ m_positions.RemoveAll();
+ m_pIndices.RemoveAll();
+
+ m_normals.RemoveAll();
+ m_nIndices.RemoveAll();
+
+ m_uvs.RemoveAll();
+ m_uvIndexMap.RemoveAll();
+ m_uvIndices.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDataBase *CVertexData::AddToMesh( CDmeMesh *pMesh, bool bAbsolute, const char *pName, bool delta )
+{
+ if ( delta )
+ return AddDelta( pMesh, bAbsolute, pName );
+
+ return Add( pMesh, pName );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDataBase *CVertexData::Add( CDmeMesh *pMesh, const char *pName )
+{
+ CDmeVertexDataBase *pVertexData( NULL );
+
+ if ( m_positions.Count() && m_pIndices.Count() )
+ {
+ {
+ CDmeVertexData *pBaseVertexData = pMesh->FindOrCreateBaseState( pName );
+ pMesh->SetCurrentBaseState( pName );
+ pBaseVertexData->AddVertexIndices( m_pIndices.Count() );
+ pVertexData = pBaseVertexData;
+ }
+
+ pVertexData->FlipVCoordinate( true );
+
+ const FieldIndex_t pIndex( pVertexData->CreateField( CDmeVertexData::FIELD_POSITION ) );
+ pVertexData->AddVertexData( pIndex, m_positions.Count() );
+ pVertexData->SetVertexData( pIndex, 0, m_positions.Count(), AT_VECTOR3, m_positions.Base() );
+ pVertexData->SetVertexIndices( pIndex, 0, m_pIndices.Count(), m_pIndices.Base() );
+
+ if ( pVertexData && m_normals.Count() && m_nIndices.Count() )
+ {
+ Assert( m_pIndices.Count() == m_nIndices.Count() );
+
+ const FieldIndex_t nIndex( pVertexData->CreateField( CDmeVertexData::FIELD_NORMAL ) );
+ pVertexData->AddVertexData( nIndex, m_normals.Count() );
+ pVertexData->SetVertexData( nIndex, 0, m_normals.Count(), AT_VECTOR3, m_normals.Base() );
+ pVertexData->SetVertexIndices( nIndex, 0, m_nIndices.Count(), m_nIndices.Base() );
+ }
+
+ if ( pVertexData && m_uvs.Count() && m_uvIndices.Count() )
+ {
+ Assert( m_pIndices.Count() == m_uvIndices.Count() );
+
+ const FieldIndex_t uvIndex( pVertexData->CreateField( CDmeVertexData::FIELD_TEXCOORD ) );
+ pVertexData->AddVertexData( uvIndex, m_uvs.Count() );
+ pVertexData->SetVertexData( uvIndex, 0, m_uvs.Count(), AT_VECTOR2, m_uvs.Base() );
+ pVertexData->SetVertexIndices( uvIndex, 0, m_uvIndices.Count(), m_uvIndices.Base() );
+ }
+ }
+
+ return pVertexData;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CVertexData::AddDelta( CDmeMesh *pMesh, bool bAbsolute, const char *pName )
+{
+ CDmeVertexDeltaData *pDelta( NULL );
+
+ if ( m_positions.Count() )
+ {
+ CDmeVertexData *pBind = pMesh->FindBaseState( "bind" );
+ if ( pBind == NULL )
+ return NULL;
+
+ const FieldIndex_t pBindIndex( pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+
+ if ( pBindIndex < 0 )
+ return NULL;
+
+ CDmrArrayConst< Vector > pBindData( pBind->GetVertexData( pBindIndex ) );
+
+ const int pCount( m_positions.Count() );
+ if ( pBindData.Count() != pCount )
+ return NULL;
+
+ for ( int i( 0 ); i < pCount; ++i )
+ {
+ m_positions[ i ] -= pBindData[ i ];
+ }
+
+ int *pIndices = reinterpret_cast< int * >( alloca( pCount * sizeof( int ) ) );
+ int nNonZero( 0 );
+ for ( int i( 0 ); i < pCount; ++i )
+ {
+ const Vector &v( m_positions[ i ] );
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ m_positions[ nNonZero ] = v;
+ pIndices[ nNonZero ] = i;
+ ++nNonZero;
+ }
+ }
+
+ pDelta = pMesh->FindOrCreateDeltaState( pName );
+ pDelta->FlipVCoordinate( true );
+ pDelta->SetValue( "corrected", !bAbsolute );
+
+ const FieldIndex_t pIndex( pDelta->CreateField( CDmeVertexData::FIELD_POSITION ) );
+ pDelta->AddVertexData( pIndex, nNonZero );
+ pDelta->SetVertexData( pIndex, 0, nNonZero, AT_VECTOR3, m_positions.Base() );
+ pDelta->SetVertexIndices( pIndex, 0, nNonZero, pIndices );
+
+ const FieldIndex_t nBindNormalIndex = pBind->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nBindNormalIndex >= 0 )
+ {
+ CDmrArrayConst< Vector > bindNormalData( pBind->GetVertexData( nBindNormalIndex ) );
+ const int nNormalCount = m_normals.Count();
+ if ( bindNormalData.Count() == nNormalCount )
+ {
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ m_normals[ i ] -= bindNormalData[ i ];
+ }
+
+ int *pNormalIndices = reinterpret_cast< int * >( stackalloc( nNormalCount * sizeof( int ) ) );
+ int nNormalDeltaCount = 0;
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ const Vector &n = m_normals[ i ];
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( n.x ) >= ( 1 / 4096.0f ) || fabs( n.y ) >= ( 1 / 4096.0f ) || fabs( n.z ) >= ( 1 / 4096.0f ) )
+ {
+ m_normals[ nNormalDeltaCount ] = n;
+ pNormalIndices[ nNormalDeltaCount ] = i;
+ ++nNormalDeltaCount;
+ }
+ }
+ }
+ }
+ }
+
+ return pDelta;
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from DME -> OBJ
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::Serialize( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ return false; // For now
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from OBJ -> DME
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
+ const char *pSourceFormatName, int nSourceFormatVersion,
+ DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
+{
+ *ppRoot = ReadOBJ( buf, fileid, "bind" );
+ return *ppRoot != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from OBJ -> DME
+// If mesh is not NULL, the OBJ is added as a delta state to the mesh
+//-----------------------------------------------------------------------------
+CDmElement *CDmObjSerializer::ReadOBJ(
+ const char *pFilename,
+ CDmeMesh **ppCreatedMesh,
+ bool bLoadAllDeltas /* = true */,
+ bool bAbsolute /* = true */ )
+{
+ char filename[ MAX_PATH ];
+ Q_strncpy( filename, pFilename, sizeof( filename ) );
+ Q_FixSlashes( filename );
+
+ CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ if ( !g_pFullFileSystem->ReadFile( filename, NULL, utlBuf ) )
+ return NULL;
+
+ char baseFile[ MAX_PATH ];
+ Q_FileBase( filename, baseFile, sizeof( baseFile ) );
+
+ CDmeMesh *pMesh( NULL );
+
+ DmFileId_t nFileId = g_pDataModel->FindOrCreateFileId( pFilename );
+
+ CDmElement *pRoot = ReadOBJ( utlBuf, nFileId, baseFile, filename, NULL, &pMesh, bAbsolute );
+ if ( pRoot && pMesh )
+ {
+ if ( ppCreatedMesh )
+ {
+ *ppCreatedMesh = pMesh;
+ }
+
+ CDmeCombinationOperator *pCombo( NULL );
+
+ // Check if there are deltas in the directory with the same prefix
+ // But only if the rest of the file is <prefix>=<suffix>.obj or is <prefix>_zero.obj
+
+ char *pSuffix = Q_strrchr( baseFile, '=' );
+ if ( !pSuffix || !*pSuffix )
+ {
+ pSuffix = Q_strrchr( baseFile, '_' );
+ if ( !pSuffix || !*pSuffix )
+ return pRoot;
+
+ if ( Q_stricmp( pSuffix, "_zero" ) )
+ return pRoot;
+ }
+
+ char findGlob[ MAX_PATH ];
+ Q_strncpy( findGlob, baseFile, sizeof( findGlob ) );
+ pSuffix = findGlob + ( pSuffix - baseFile );
+ *( pSuffix + 0 ) = '_'; // Just in case it was <prefix>=<suffix>.obj
+ *( pSuffix + 1 ) = '*';
+ *( pSuffix + 2 ) = '.';
+ Q_strncpy( pSuffix + 3, Q_GetFileExtension( filename ), sizeof( findGlob ) - ( pSuffix - findGlob + 3 ) );
+
+ char path[ MAX_PATH ];
+ Q_ExtractFilePath( filename, path, sizeof( path ) );
+
+ m_objDirectory = path;
+
+ char findPath[ MAX_PATH ];
+ Q_ComposeFileName( path, findGlob, findPath, sizeof( findPath ) );
+
+ FileFindHandle_t hFind;
+
+ char deltaFile[ MAX_PATH ];
+ char deltaPath[ MAX_PATH ];
+
+ for ( const char *pFindFile( g_pFullFileSystem->FindFirst( findPath, &hFind ) ); pFindFile && *pFindFile; pFindFile = g_pFullFileSystem->FindNext( hFind ) )
+ {
+ Q_FileBase( pFindFile, deltaFile, sizeof( deltaFile ) );
+
+ if ( Q_stricmp( baseFile, deltaFile ) )
+ {
+ Q_ComposeFileName( path, pFindFile, deltaPath, sizeof( deltaPath ) );
+
+ if ( !g_pFullFileSystem->FileExists( deltaPath ) )
+ continue;
+
+ char *pControlName = strchr( deltaFile, '_' );
+ if ( pControlName && *( pControlName + 1 ) )
+ {
+ ++pControlName;
+ char *pDeltaName( pControlName );
+ for ( char *pPlus( strchr( pDeltaName, '+' ) ); pPlus; pPlus = strchr( pPlus, '+' ) )
+ {
+ *pPlus = '_';
+ }
+ }
+
+ if ( !strchr( pControlName, '_' ) )
+ {
+ if ( pCombo == NULL )
+ {
+ pCombo = CreateElement< CDmeCombinationOperator >( "combinationOperator", pRoot->GetFileId() );
+ pRoot->SetValue( "combinationOperator", pCombo );
+ }
+ }
+
+ DeltaInfo_t &deltaInfo = m_deltas[ pControlName ];
+ deltaInfo.m_filename = pFindFile;
+ deltaInfo.m_pMesh = pMesh;
+ deltaInfo.m_pComboOp = pCombo;
+
+ if ( bLoadAllDeltas )
+ {
+ GetDelta( pControlName, bAbsolute );
+ }
+ }
+ }
+
+ g_pFullFileSystem->FindClose( hFind );
+
+ if ( pCombo )
+ {
+ pCombo->AddTarget( pMesh );
+ pMesh->ComputeAllCorrectedPositionsFromActualPositions();
+ }
+ }
+
+ return pRoot;
+}
+
+
+//-----------------------------------------------------------------------------
+// Common function both ReadOBJ & Unserialize can call
+//-----------------------------------------------------------------------------
+CDmElement *CDmObjSerializer::ReadOBJ( CUtlBuffer &buf,
+ DmFileId_t dmFileId,
+ const char *pName,
+ const char *pFilename /* = NULL */,
+ CDmeMesh *pBaseMesh /* = NULL */,
+ CDmeMesh **ppCreatedMesh /* = NULL */,
+ bool bAbsolute /* = true */ )
+{
+ CDmElement *pRoot( NULL );
+ CDmeModel *pModel( NULL );
+
+ if ( !pBaseMesh )
+ {
+ pRoot = CreateElement< CDmElement >( "root", dmFileId );
+ pModel = CreateElement< CDmeModel >( "model", dmFileId );
+
+ pRoot->SetValue( "skeleton", pModel );
+ pRoot->SetValue( "model", pModel );
+ }
+
+ m_mtlLib.RemoveAll();
+
+ char tmpBuf0[ 4096 ];
+ char tmpBuf1[ 4096 ];
+
+ characterset_t breakSet;
+ CharacterSetBuild( &breakSet, "/\\" );
+
+ const char *pBuf;
+
+ Vector p;
+ Vector2D uv;
+
+ CVertexData vertexData;
+ CFaceSetData faceSetData;
+
+ CUtlString groupName;
+
+ CDmeDag *pDmeDag( NULL );
+ CDmeMesh *pDmeMesh( NULL );
+ CUtlVector< int > *pFaceIndices( NULL );
+
+ while ( buf.IsValid() )
+ {
+ buf.GetLine( tmpBuf0, sizeof( tmpBuf0 ) );
+
+ pBuf = SkipSpace( tmpBuf0 );
+
+ if ( sscanf( tmpBuf0, "v %f %f %f", &p.x, &p.y, &p.z ) == 3 )
+ {
+ if ( pDmeDag )
+ {
+ vertexData.AddToMesh( pDmeMesh, bAbsolute, "bind", false );
+ faceSetData.AddToMesh( pDmeMesh );
+
+ pDmeDag = NULL;
+ pDmeMesh = NULL;
+ pFaceIndices = NULL;
+ }
+
+ vertexData.AddPosition( p );
+
+ continue;
+ }
+
+ if ( sscanf( pBuf, "vn %f %f %f", &p.x, &p.y, &p.z ) == 3 )
+ {
+ vertexData.AddNormal( p );
+ continue;
+ }
+
+ if ( !pBaseMesh )
+ {
+ if ( sscanf( pBuf, "vt %f %f", &uv.x, &uv.y ) == 2 )
+ {
+ vertexData.AddUV( uv );
+ continue;
+ }
+
+ if ( pFilename && sscanf( pBuf, "mtllib %4096s", tmpBuf1 ) == 1 )
+ {
+ CUtlString mtlLib( tmpBuf1 );
+
+ Q_strncpy( tmpBuf0, pFilename, sizeof( tmpBuf0 ) );
+ Q_FixSlashes( tmpBuf0 );
+ Q_StripFilename( tmpBuf0 );
+
+ char mtlLibPath[ MAX_PATH ];
+
+ Q_ComposeFileName( tmpBuf0, tmpBuf1, mtlLibPath, sizeof( mtlLibPath ) );
+ CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ if ( g_pFullFileSystem->ReadFile( mtlLibPath, NULL, utlBuf ) )
+ {
+ ParseMtlLib( utlBuf );
+ }
+
+ continue;
+ }
+
+ if ( sscanf( pBuf, "usemtl %4096s", tmpBuf1 ) == 1 )
+ {
+ // Remove any 'SG' suffix from the material
+ const uint sLen = Q_strlen( tmpBuf1 );
+ if ( sLen && !Q_strcmp( tmpBuf1 + sLen - 2, "SG" ) )
+ {
+ tmpBuf1[ sLen - 2 ] = '\0';
+ }
+
+ const char *pTexture( FindMtlEntry( tmpBuf1 ) );
+ if ( pTexture )
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( pTexture );
+ }
+ else
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( tmpBuf1 );
+ }
+
+ continue;
+ }
+
+ if ( sscanf( pBuf, "g %4096s", tmpBuf1 ) == 1 )
+ {
+ groupName = tmpBuf1;
+ if ( pFaceIndices == NULL )
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( tmpBuf1 );
+ }
+ continue;
+ }
+ if ( *pBuf == 'f' && ( *( pBuf + 1 ) == ' ' || *( pBuf + 1 ) == '\t' ) )
+ {
+ if ( pDmeDag == NULL )
+ {
+ pDmeDag = CreateElement< CDmeDag >( pName ? pName : ( groupName.IsEmpty() ? "obj" : groupName.Get() ), pRoot->GetFileId() );
+ Assert( pDmeDag );
+ pDmeMesh = CreateElement< CDmeMesh >( pName ? pName : ( groupName.IsEmpty() ? "obj" : groupName.Get() ), pRoot->GetFileId() );
+ if ( ppCreatedMesh && *ppCreatedMesh == NULL )
+ {
+ // Only the first mesh created...
+ *ppCreatedMesh = pDmeMesh;
+ }
+ pDmeDag->SetShape( pDmeMesh );
+ if ( pModel )
+ {
+ pModel->AddJoint( pDmeDag );
+ pModel->AddChild( pDmeDag );
+ }
+ }
+
+ if ( pFaceIndices == NULL )
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( "facetSet" );
+ }
+
+ int v;
+ int t;
+ int n;
+
+ pBuf = SkipSpace( pBuf + 1 );
+ int nLen = Q_strlen( pBuf );
+
+ CUtlBuffer bufParse( pBuf, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+
+ while ( bufParse.IsValid() )
+ {
+ if ( !ParseVertex( bufParse, breakSet, v, t, n ) )
+ break;
+
+ pFaceIndices->AddToTail( vertexData.VertexCount() );
+ if ( v > 0 )
+ {
+ vertexData.AddPositionIndex( v - 1 );
+ }
+
+ if ( n > 0 )
+ {
+ vertexData.AddNormalIndex( n - 1 );
+ }
+
+ if ( t > 0 )
+ {
+ vertexData.AddUVIndex( t - 1 );
+ }
+ }
+
+ pFaceIndices->AddToTail( -1 );
+ continue;
+ }
+ }
+ }
+
+ CDmeVertexDataBase *pVertexData( NULL );
+
+ if ( pBaseMesh )
+ {
+ pVertexData = vertexData.AddToMesh( pBaseMesh, bAbsolute, pName, true );
+ }
+ else
+ {
+ pVertexData = vertexData.AddToMesh( pDmeMesh, bAbsolute, "bind", false );
+ faceSetData.AddToMesh( pDmeMesh );
+ }
+
+ if ( pModel )
+ {
+ pModel->CaptureJointsToBaseState( "bind" );
+ }
+
+ if ( pBaseMesh )
+ return pVertexData;
+
+ return pRoot;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmObjSerializer::OutputVectors(
+ CUtlBuffer &b,
+ const char *pPrefix,
+ const CUtlVector< Vector > &vData,
+ const matrix3x4_t &matrix )
+{
+ Vector v;
+
+ const int nv( vData.Count() );
+
+ for ( int i( 0 ); i < nv; ++i )
+ {
+ VectorTransform( vData[ i ], matrix, v );
+ b << pPrefix << v << "\n";
+ }
+
+ return nv;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmObjSerializer::OutputVectors(
+ CUtlBuffer &b,
+ const char *pPrefix,
+ const CUtlVector< Vector2D > &vData )
+{
+ Vector v;
+
+ const int nv( vData.Count() );
+
+ for ( int i( 0 ); i < nv; ++i )
+ {
+ b << pPrefix << vData[ i ] << "\n";
+ }
+
+ return nv;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::MeshToObj(
+ CUtlBuffer &b,
+ const matrix3x4_t &parentWorldMatrix,
+ CDmeMesh *pMesh,
+ const char *pDeltaName,
+ bool absolute )
+{
+ CUtlVector< CDmeMesh::DeltaComputation_t > compList;
+
+ if ( pDeltaName )
+ {
+ pMesh->ComputeDependentDeltaStateList( compList );
+ }
+
+ const int nCompList( compList.Count() );
+
+ CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) );
+ if ( !pBase )
+ return;
+
+ const FieldIndex_t nPosIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( nPosIndex < 0 )
+ return;
+
+ int nPositionCount = 0;
+ int nTextureCount = 0;
+ int nNormalCount = 0;
+
+ b << "g " << pMesh->GetName() << "\n";
+
+ CDmrArrayConst< Vector > pArray( pBase->GetVertexData( nPosIndex ) );
+ const CUtlVector< int > *ppIndices( &pBase->GetVertexIndexData( nPosIndex ) );
+ const CUtlVector< Vector > &pConstData( pArray.Get() );
+
+ if ( nCompList )
+ {
+ CUtlVector< Vector > pData;
+ pData.CopyArray( pConstData.Base(), pConstData.Count() );
+
+ if ( absolute )
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
+
+ const CUtlVector< int > &depDeltas( compList[ i ].m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pTmpDeltaState = pMesh->GetDeltaState( depDeltas[ j ] );
+ b << "# Dependent Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
+ }
+ }
+ }
+ else
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
+ }
+ }
+
+ nPositionCount = OutputVectors( b, "v ", pData, parentWorldMatrix );
+ }
+ else
+ {
+ nPositionCount = OutputVectors( b, "v ", pConstData, parentWorldMatrix );
+ }
+
+ const CUtlVector< int > *puvIndices( NULL );
+ const FieldIndex_t uvIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD ) );
+ if ( uvIndex >= 0 )
+ {
+ CDmrArrayConst< Vector2D > uvArray( pBase->GetVertexData( uvIndex ) );
+ const CUtlVector< Vector2D > &uvData( uvArray.Get() );
+ puvIndices = &pBase->GetVertexIndexData( uvIndex );
+
+ nTextureCount = OutputVectors( b, "vt ", uvData );
+ }
+
+ const CUtlVector< int > *pnIndices( NULL );
+ const FieldIndex_t nIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ) );
+ if ( nIndex >= 0 )
+ {
+ matrix3x4_t normalMatrix;
+ MatrixInverseTranspose( parentWorldMatrix, normalMatrix );
+
+ CDmrArrayConst< Vector > nArray( pBase->GetVertexData( nIndex ) );
+ const CUtlVector< Vector > &nConstData( nArray.Get() );
+ pnIndices = &pBase->GetVertexIndexData( nIndex );
+
+ if ( nCompList )
+ {
+ CUtlVector< Vector > nData;
+ nData.CopyArray( nConstData.Base(), nConstData.Count() );
+
+ if ( absolute )
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
+
+ const CUtlVector< int > &depDeltas( compList[ i ].m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pTmpDeltaState = pMesh->GetDeltaState( depDeltas[ j ] );
+ b << "# Dependent Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
+ }
+ }
+ }
+ else
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
+ }
+ }
+
+ nNormalCount = OutputVectors( b, "vn ", nData, normalMatrix );
+ }
+ else
+ {
+ nNormalCount = OutputVectors( b, "vn ", nConstData, normalMatrix );
+ }
+ }
+
+ const int pCount( ppIndices->Count() );
+ const int uvCount( puvIndices ? puvIndices->Count() : 0 );
+ const int nCount( pnIndices ? pnIndices->Count() : 0 );
+
+ const int nFaceSets( pMesh->FaceSetCount() );
+ for ( int i= 0 ; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pFaceSet( pMesh->GetFaceSet( i ) );
+ CDmeMaterial *pMaterial( pFaceSet->GetMaterial() );
+ if ( pMaterial )
+ {
+ b << "usemtl " << pMaterial->GetMaterialName() << "\n";
+ }
+
+ const int nIndices( pFaceSet->NumIndices() );
+ const int *pnFaceSetIndex( pFaceSet->GetIndices() );
+ const int *const pEnd( pnFaceSetIndex + nIndices );
+ int fIndex;
+
+ const char *const pFaceStart( "f " );
+ const char *const pFaceNext( " " );
+ const char *pFaceSep( pFaceStart );
+
+ if ( pCount == uvCount && pCount == nCount )
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+ const CUtlVector< int > &uvIndices( *puvIndices );
+ const CUtlVector< int > &nvIndices( *pnIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << '/' << ( uvIndices[ fIndex ] + m_nTextureOffset ) << '/' << ( nvIndices[ fIndex ] + m_nNormalOffset );
+ pFaceSep = pFaceNext;
+ }
+ }
+ else if ( pCount == uvCount )
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+ const CUtlVector< int > &uvIndices( *puvIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << '/' << ( uvIndices[ fIndex ] + m_nTextureOffset ) << '/';
+ pFaceSep = pFaceNext;
+ }
+ }
+ else if ( pCount == nCount )
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+ const CUtlVector< int > &nvIndices( *pnIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << "//" << ( nvIndices[ fIndex ] + m_nNormalOffset );
+ pFaceSep = pFaceNext;
+ }
+ }
+ else
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << "//";
+ pFaceSep = pFaceNext;
+ }
+ }
+ }
+
+ m_nPositionOffset += nPositionCount;
+ m_nTextureOffset += nTextureCount;
+ m_nNormalOffset += nNormalCount;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::DagToObj(
+ CUtlBuffer &b,
+ const matrix3x4_t &parentWorldMatrix,
+ CDmeDag *pDag,
+ const char *pDeltaName,
+ bool absolute )
+{
+ matrix3x4_t inclusiveMatrix;
+ pDag->GetTransform()->GetTransform( inclusiveMatrix );
+
+ ConcatTransforms( parentWorldMatrix, inclusiveMatrix, inclusiveMatrix );
+
+ CDmeMesh *pMesh( CastElement< CDmeMesh >( pDag->GetShape() ) );
+ if ( pMesh )
+ {
+ MeshToObj( b, inclusiveMatrix, pMesh, pDeltaName, absolute );
+ }
+
+ const int nChildren( pDag->GetChildCount() );
+ for ( int i( 0 ); i < nChildren; ++i )
+ {
+ DagToObj( b, inclusiveMatrix, pDag->GetChild( i ), pDeltaName, absolute );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::FindDeltaMeshes( CDmeDag *pDag, CUtlVector< CDmeMesh * > &meshes )
+{
+ CDmeMesh *pMesh( CastElement< CDmeMesh >( pDag->GetShape() ) );
+ if ( pMesh && pMesh->DeltaStateCount() )
+ {
+ meshes.AddToTail( pMesh );
+ }
+
+ const int nChildren( pDag->GetChildCount() );
+ for ( int i( 0 ); i < nChildren; ++i )
+ {
+ FindDeltaMeshes( pDag->GetChild( i ), meshes );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from OBJ -> DME
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::WriteOBJ( const char *pFilename, CDmElement *pRoot, bool bWriteOBJs, const char *pDeltaName, bool absolute )
+{
+ CDmeDag *pModel = pRoot->GetValueElement< CDmeDag >( "model" );
+
+ if ( !pModel )
+ return false;
+
+ matrix3x4_t identityMatrix;
+ SetIdentityMatrix( identityMatrix );
+
+ if ( !pDeltaName )
+ {
+ CUtlBuffer b( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ b << "# OBJ\n";
+ b << "#\n";
+
+ m_nPositionOffset = 1; // OBJs start indexing at 1
+ m_nTextureOffset = 1;
+ m_nNormalOffset = 1;
+
+ DagToObj( b, identityMatrix, pModel, pDeltaName, absolute );
+
+ g_pFullFileSystem->WriteFile( pFilename, NULL, b );
+
+ // Filesystem is silly
+ // On WIN32 filesystem changes all of the characters to lowercase grrrr.....
+ rename( pFilename, pFilename );
+ }
+
+ if ( !bWriteOBJs )
+ return true;
+
+ CUtlVector< CDmeMesh * > deltaMeshes;
+ FindDeltaMeshes( pModel, deltaMeshes );
+
+ if ( deltaMeshes.Count() )
+ {
+ char base[ MAX_PATH ];
+ Q_FileBase( pFilename, base, sizeof( base ) );
+
+ char path[ MAX_PATH ];
+ Q_ExtractFilePath( pFilename, path, sizeof( path ) );
+
+ char *pSuffix = strchr( base, '=' );
+ if ( !pSuffix )
+ {
+ pSuffix = strchr( base, '_' );
+ }
+
+ if ( pSuffix )
+ {
+ *( pSuffix + 0 ) = '_';
+ *( pSuffix + 1 ) = '\0';
+ }
+
+ char filename[ MAX_PATH ];
+
+ const int nDeltaMeshes( deltaMeshes.Count() );
+ for ( int i( 0 ); i < nDeltaMeshes; ++i )
+ {
+ CDmeMesh *pDeltaMesh( deltaMeshes[ i ] );
+ const int nDeltas( pDeltaMesh->DeltaStateCount() );
+ for ( int j( 0 ); j < nDeltas; ++j )
+ {
+ CDmeVertexDeltaData *pDelta( pDeltaMesh->GetDeltaState( j ) );
+
+ if ( !pDeltaName || !Q_strcmp( pDeltaName, pDelta->GetName() ) )
+ {
+ CUtlBuffer b( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ b << "# Delta OBJ: " << pDelta->GetName() << "\n";
+ b << "#\n";
+
+ Q_strncpy( filename, pDelta->GetName(), sizeof( filename ) );
+ // Change _ to +
+ const char *const pEnd( filename + sizeof( filename ) );
+ for ( char *pChar = filename; *pChar && pChar < pEnd; ++pChar )
+ {
+ if ( *pChar == '_' )
+ {
+ *pChar = '+';
+ }
+ }
+
+ CUtlString deltaFile( base );
+ deltaFile += filename;
+ deltaFile += ".obj";
+
+ m_nPositionOffset = 1; // OBJs use 1 based indexes
+ m_nTextureOffset = 1;
+ m_nNormalOffset = 1;
+
+ DagToObj( b, identityMatrix, pModel, pDelta->GetName(), absolute );
+
+ Q_ComposeFileName( path, deltaFile.Get(), filename, sizeof( filename ) );
+ g_pFullFileSystem->WriteFile( filename, NULL, b );
+ // On WIN32 filesystem changes all of the characters to lowercase grrrr.....
+ rename( filename, filename );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::ParseMtlLib( CUtlBuffer &buf )
+{
+ char tmpBuf0[ 4096 ];
+
+ int nCurrentMtl = -1;
+ while ( buf.IsValid() )
+ {
+ buf.GetLine( tmpBuf0, sizeof(tmpBuf0) );
+
+ if ( StringHasPrefix( tmpBuf0, "newmtl " ) )
+ {
+ char mtlName[1024];
+ if ( sscanf( tmpBuf0, "newmtl %s", mtlName ) == 1 )
+ {
+ // Remove any 'SG' suffix from the material
+ const uint sLen = Q_strlen( mtlName );
+ if ( sLen > 2 && !Q_strcmp( mtlName + sLen - 2, "SG" ) )
+ {
+ mtlName[ sLen - 2 ] = '\0';
+ }
+
+ nCurrentMtl = m_mtlLib.AddToTail( );
+ m_mtlLib[nCurrentMtl].m_MtlName = mtlName;
+ m_mtlLib[nCurrentMtl].m_TgaName = mtlName;
+ }
+ continue;
+ }
+
+ if ( StringHasPrefix( tmpBuf0, "map_Kd " ) )
+ {
+ if ( nCurrentMtl < 0 )
+ continue;
+
+ char tgaPath[MAX_PATH];
+ char tgaName[1024];
+ if ( sscanf( tmpBuf0, "map_Kd %s", tgaPath ) == 1 )
+ {
+ // Try a cheesy hack - look for /materialsrc/ and set the material name off the entire path minus extension
+ Q_strncpy( tmpBuf0, tgaPath, sizeof( tmpBuf0 ) );
+ Q_FixSlashes( tmpBuf0, '/' );
+ const char *pMaterialSrc = Q_strstr( tmpBuf0, "/materialsrc/" );
+ if ( pMaterialSrc )
+ {
+ pMaterialSrc += Q_strlen( "/materialsrc/" );
+ Q_StripExtension( pMaterialSrc, tgaName, sizeof( tgaName) );
+ m_mtlLib[ nCurrentMtl ].m_TgaName = tgaName;
+ }
+ else
+ {
+ Q_FileBase( tgaPath, tgaName, sizeof(tgaName) );
+ m_mtlLib[nCurrentMtl].m_TgaName = tgaName;
+ }
+ }
+ continue;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDmObjSerializer::FindMtlEntry( const char *pTgaName )
+{
+ int nCount = m_mtlLib.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( m_mtlLib[i].m_MtlName, pTgaName ) )
+ return m_mtlLib[i].m_TgaName;
+ }
+ return pTgaName;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::ParseVertex( CUtlBuffer& bufParse, characterset_t &breakSet, int &v, int &t, int &n )
+{
+ char cmd[1024];
+ int nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ if ( nLen <= 0 )
+ return false;
+
+ v = atoi( cmd );
+ n = 0;
+ t = 0;
+
+ char c = *(char*)bufParse.PeekGet();
+ bool bHasTexCoord = IN_CHARACTERSET( breakSet, c ) != 0;
+ bool bHasNormal = false;
+ if ( bHasTexCoord )
+ {
+ // Snag the '/'
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen == 1 );
+
+ c = *(char*)bufParse.PeekGet();
+ if ( !IN_CHARACTERSET( breakSet, c ) )
+ {
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen > 0 );
+ t = atoi( cmd );
+
+ c = *(char*)bufParse.PeekGet();
+ bHasNormal = IN_CHARACTERSET( breakSet, c ) != 0;
+ }
+ else
+ {
+ bHasNormal = true;
+ bHasTexCoord = false;
+ }
+
+ if ( bHasNormal )
+ {
+ // Snag the '/'
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen == 1 );
+
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen > 0 );
+ n = atoi( cmd );
+ }
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDmObjSerializer::SkipSpace(
+ const char *pBuf )
+{
+ while ( *pBuf == ' ' || *pBuf == '\t' )
+ ++pBuf;
+
+ return pBuf;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmObjSerializer::GetDelta( const char *pDeltaName, bool bAbsolute )
+{
+ if ( !m_deltas.Defined( pDeltaName ) )
+ return NULL;
+
+ DeltaInfo_t &deltaInfo( m_deltas[ pDeltaName ] );
+
+ if ( deltaInfo.m_pDeltaData )
+ return deltaInfo.m_pDeltaData;
+
+ if ( !LoadDependentDeltas( pDeltaName ) )
+ return NULL;
+
+ CUtlBuffer utlBuf;
+
+ char deltaPath[ MAX_PATH ];
+ Q_ComposeFileName( m_objDirectory, deltaInfo.m_filename, deltaPath, sizeof( deltaPath ) );
+ Q_FixSlashes( deltaPath );
+
+ if ( !g_pFullFileSystem->ReadFile( deltaPath, NULL, utlBuf ) )
+ return NULL;
+
+ if ( deltaInfo.m_pComboOp && !strchr( pDeltaName, '_' ) )
+ {
+ deltaInfo.m_pComboOp->FindOrCreateControl( pDeltaName, false, true );
+ }
+
+ deltaInfo.m_pDeltaData = CastElement< CDmeVertexDeltaData >( ReadOBJ( utlBuf, deltaInfo.m_pMesh->GetFileId(), pDeltaName, deltaPath, deltaInfo.m_pMesh, NULL, bAbsolute ) );
+
+ return deltaInfo.m_pDeltaData;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::LoadDependentDeltas( const char *pDeltaName )
+{
+ // TODO: Load Dependent Deltas
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Counts the number of _'s in a string
+//-----------------------------------------------------------------------------
+int ComputeDimensionality( const char *pDeltaName )
+{
+ const char *pUnderBar = pDeltaName;
+ int nDimensions = 0;
+
+ while ( pUnderBar )
+ {
+ ++nDimensions;
+ pUnderBar = strchr( pUnderBar, '_' );
+ if ( pUnderBar )
+ {
+ ++pUnderBar;
+ }
+ }
+
+ return nDimensions;
+}
+
+/*
+//-----------------------------------------------------------------------------
+// Generates a sorted list in order of dimensionality of the delta states
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::ComputeDeltaStateComputationList( CUtlVector< CUtlVector< int > > &dependentDeltaList )
+{
+ // Do all combinations in order of dimensionality, lowest dimension first
+
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ compList[i].m_nDeltaIndex = i;
+ compList[i].m_nDimensionality = ComputeDeltaStateDimensionality( i );
+ }
+ qsort( compList.Base(), nCount, sizeof(DeltaComputation_t), DeltaStateLessFunc );
+}
+
+
+
+{
+ CUtlVector< CUtlString > atomicControls;
+ deltaStateUsage.SetCount( nCount );
+
+ // Build a list of atomic controls
+ int nCurrentDelta;
+ for ( nCurrentDelta = 0; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ if ( pInfo[nCurrentDelta].m_nDimensionality != 1 )
+ break;
+ int j = atomicControls.AddToTail( GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex )->GetName() );
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+
+ for ( ; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex );
+ int nLen = Q_strlen( pDeltaState->GetName() );
+ char *pTempBuf = (char*)_alloca( nLen + 1 );
+ memcpy( pTempBuf, pDeltaState->GetName(), nLen+1 );
+ char *pNext;
+ for ( char *pUnderBar = pTempBuf; pUnderBar; pUnderBar = pNext )
+ {
+ pNext = strchr( pUnderBar, '_' );
+ if ( pNext )
+ {
+ *pNext = 0;
+ ++pNext;
+ }
+
+ // Find this name in the list of strings
+ int j;
+ int nControlCount = atomicControls.Count();
+ for ( j = 0; j < nControlCount; ++j )
+ {
+ if ( !Q_stricmp( pUnderBar, atomicControls[j] ) )
+ break;
+ }
+ if ( j == nControlCount )
+ {
+ j = atomicControls.AddToTail( pUnderBar );
+ }
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+ deltaStateUsage[ nCurrentDelta ].Sort( DeltaStateUsageLessFunc );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::ComputeDependentUsage( CUtlVector< CUtlVector< int > > &deltaUsage )
+{
+ const int nDeltas = m_deltas.GetNumStrings();
+ compList.EnsureCount( nDeltas );
+
+ CUtlVector< CUtlVector< int > > deltaStateUsage;
+ const int nCount( compList.Count() );
+ BuildAtomicControlLists( nCount, compList.Base(), deltaStateUsage );
+
+ // Now build up a list of dependent delta states based on usage
+ // NOTE: Usage is sorted in ascending order.
+ for ( int i = 1; i < nCount; ++i )
+ {
+ int nUsageCount1 = deltaStateUsage[i].Count();
+ for ( int j = 0; j < i; ++j )
+ {
+ // At the point they have the same dimensionality, no more need to check
+ if ( compList[j].m_nDimensionality == compList[i].m_nDimensionality )
+ break;
+
+ int ii = 0;
+ bool bSubsetFound = true;
+ int nUsageCount2 = deltaStateUsage[j].Count();
+ for ( int ji = 0; ji < nUsageCount2; ++ji )
+ {
+ for ( bSubsetFound = false; ii < nUsageCount1; ++ii )
+ {
+ if ( deltaStateUsage[j][ji] == deltaStateUsage[i][ii] )
+ {
+ ++ii;
+ bSubsetFound = true;
+ break;
+ }
+
+ if ( deltaStateUsage[j][ji] < deltaStateUsage[i][ii] )
+ break;
+ }
+
+ if ( !bSubsetFound )
+ break;
+ }
+
+ if ( bSubsetFound )
+ {
+ compList[i].m_DependentDeltas.AddToTail( compList[j].m_nDeltaIndex );
+ }
+ }
+ }
+}
+*/ \ No newline at end of file