You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1464 lines
39 KiB
1464 lines
39 KiB
5 years ago
|
//========= 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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|