|
|
//====== Copyright � 1996-2006, 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; if ( !g_pFullFileSystem->ReadFile( filename, NULL, utlBuf ) ) return NULL;
char baseFile[ MAX_PATH ]; Q_FileBase( filename, baseFile, sizeof( baseFile ) );
CDmeMesh *pMesh( NULL );
CDmElement *pRoot = ReadOBJ( utlBuf, DMFILEID_INVALID, 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;
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 pIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) ); if ( pIndex < 0 ) return;
int nPositionCount = 0; int nTextureCount = 0; int nNormalCount = 0;
b << "g " << pMesh->GetName() << "\n";
CDmrArrayConst< Vector > pArray( pBase->GetVertexData( pIndex ) ); const CUtlVector< int > *ppIndices( &pBase->GetVertexIndexData( pIndex ) ); 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 *pIndex( pFaceSet->GetIndices() ); const int *const pEnd( pIndex + 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 > &nIndices( *pnIndices );
while ( pIndex < pEnd ) { fIndex = *pIndex++; if ( fIndex < 0 ) { b << "\n"; pFaceSep = pFaceStart; continue; }
b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << '/' << ( uvIndices[ fIndex ] + m_nTextureOffset ) << '/' << ( nIndices[ fIndex ] + m_nNormalOffset ); pFaceSep = pFaceNext; } } else if ( pCount == uvCount ) { const CUtlVector< int > &pIndices( *ppIndices ); const CUtlVector< int > &uvIndices( *puvIndices );
while ( pIndex < pEnd ) { fIndex = *pIndex++; 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 > &nIndices( *pnIndices );
while ( pIndex < pEnd ) { fIndex = *pIndex++; if ( fIndex < 0 ) { b << "\n"; pFaceSep = pFaceStart; continue; }
b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << "//" << ( nIndices[ fIndex ] + m_nNormalOffset ); pFaceSep = pFaceNext; } } else { const CUtlVector< int > &pIndices( *ppIndices );
while ( pIndex < pEnd ) { fIndex = *pIndex++; 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 = "debugempty"; } 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 ); } } } } */
|