diff options
Diffstat (limited to 'movieobjects/dmsmdserializer.cpp')
| -rw-r--r-- | movieobjects/dmsmdserializer.cpp | 1706 |
1 files changed, 1706 insertions, 0 deletions
diff --git a/movieobjects/dmsmdserializer.cpp b/movieobjects/dmsmdserializer.cpp new file mode 100644 index 0000000..f1299cc --- /dev/null +++ b/movieobjects/dmsmdserializer.cpp @@ -0,0 +1,1706 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Read SMD and create DMX data +// +//============================================================================= + + +// Because we use STL +#pragma warning( disable: 4530 ) + + +// Standard includes +#include <io.h> +#include <algorithm> +#include <deque> +#include <fstream> +#include <list> +#include <map> +#include <string> + + +// Valve includes +#include "movieobjects/dmeanimationlist.h" +#include "movieobjects/dmechannel.h" +#include "movieobjects/dmedag.h" +#include "movieobjects/dmemesh.h" +#include "movieobjects/dmefaceset.h" +#include "movieobjects/dmematerial.h" +#include "movieobjects/dmemodel.h" +#include "movieobjects/dmsmdserializer.h" +#include "filesystem.h" +#include "tier1/characterset.h" +#include "tier1/fmtstr.h" +#include "tier2/tier2.h" +#include "mathlib/mathlib.h" + + +// Last include +#include "tier0/memdbgon.h" + + +//============================================================================= +// +// CDmSmdSerializer +// +//============================================================================= +//----------------------------------------------------------------------------- +// Convert from SMD -> DME +//----------------------------------------------------------------------------- +bool CDmSmdSerializer::Unserialize( + CUtlBuffer &utlBuf, + const char * /* pszEncodingName */, + int /* nEncodingVersion */, + const char * /* pszSourceFormatName */, + int /* nSourceFormatVersion */, + DmFileId_t nDmFileId, + DmConflictResolution_t /* nDmConflictResolution */, + CDmElement **ppDmRoot ) +{ + if ( !ppDmRoot ) + return false; + + const char *pszFilename = g_pDataModel->GetFileName( nDmFileId ); + + if ( pszFilename ) + { + char szFilename[ MAX_PATH ]; + V_strncpy( szFilename, pszFilename, ARRAYSIZE( szFilename ) ); + V_FixSlashes( szFilename ); + + *ppDmRoot = ReadSMD( utlBuf, nDmFileId, szFilename, NULL ); + } + else + { + *ppDmRoot = ReadSMD( utlBuf, nDmFileId, "utlBuffer", NULL ); + } + + return *ppDmRoot != NULL; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CDmElement *CDmSmdSerializer::ReadSMD( + const char *pszFilename, + CDmeMesh **ppDmeMeshCreated /* = NULL */ ) +{ + char szFilename[ MAX_PATH ]; + V_strncpy( szFilename, pszFilename, ARRAYSIZE( szFilename ) ); + V_FixSlashes( szFilename ); + + CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, utlBuf ) ) + return NULL; + + DmFileId_t nDmFileId = g_pDataModel->FindOrCreateFileId( pszFilename ); + + return ReadSMD( utlBuf, nDmFileId, szFilename, ppDmeMeshCreated ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmSmdSerializer::SetUpAxis( CDmSmdSerializer::Axis_t nUpAxis ) +{ + m_nUpAxis = clamp( nUpAxis, X_AXIS, Z_AXIS ); + + switch ( m_nUpAxis ) + { + case X_AXIS: // X Up + AngleMatrix( RadianEuler( -M_PI / 2.0, M_PI / 2.0, 0.0 ), m_mAdj ); + MatrixInverseTranspose( m_mAdj, m_mAdjNormal ); + break; + case Y_AXIS: // Y Up + SetIdentityMatrix( m_mAdj ); + SetIdentityMatrix( m_mAdjNormal ); + break; + case Z_AXIS: + default: + AngleMatrix( RadianEuler( -M_PI / 2.0, 0.0, 0.0 ), m_mAdj ); + MatrixInverseTranspose( m_mAdj, m_mAdjNormal ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Tests whether the passed buffer is an all whitespace line or not +//----------------------------------------------------------------------------- +static bool ParserIsBlankLine( const char *pszBuf ) +{ + for ( const char *pChar = pszBuf; *pChar; ++pChar ) + { + if ( !V_isspace( static_cast< unsigned char >( *pChar ) ) ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Skips over whitespace +//----------------------------------------------------------------------------- +static const char *ParserSkipSpace( const char *pszBuf ) +{ + // Skip to first non-whitespace character + for ( ;; ) + { + if ( !V_isspace( static_cast< unsigned char >( *pszBuf ) ) ) + break; + + ++pszBuf; + } + + return pszBuf; +} + + +//----------------------------------------------------------------------------- +// Returns true the specified buffer is a comment line +// meaning that it starts with // (with optional white space preceding the //) +//----------------------------------------------------------------------------- +static bool IsCommentLine( const char *pszBuf ) +{ + pszBuf = ParserSkipSpace( pszBuf ); + + if ( *pszBuf && *pszBuf == '/' && *( pszBuf + 1 ) == '/' ) + { + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static bool ParserHandleVersion( const char *pszBuf, int *pnVersion = NULL ) +{ + pszBuf = ParserSkipSpace( pszBuf ); + + if ( !( *pszBuf && V_strnicmp( pszBuf, "version", 7 ) == 0 ) ) + return false; + + if ( pnVersion ) + { + *pnVersion = strtol( pszBuf + 7, NULL, 0 ); // Skip past "version" + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static bool ParserHandleSectionStart( const char *pszBuf, const char *pszSectionId ) +{ + pszBuf = ParserSkipSpace( pszBuf ); + + if ( ! *pszBuf ) + return false; + + const int nCmd = V_stricmp( pszBuf, pszSectionId ); + if ( !nCmd ) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static bool ParserHandleTime( const char *pszBuf, int &nTime ) +{ + if ( sscanf( pszBuf, "time %d", &nTime ) == 1 ) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Strip trailing CR/NL +//----------------------------------------------------------------------------- +static void Chomp( char *pszBuf ) +{ + char *pChar = pszBuf + V_strlen( pszBuf ) - 1; + + while ( pChar >= pszBuf && ( *pChar == '\n' || *pChar == '\r' ) ) + { + *pChar = '\0'; + } +} + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static void GetLine( CUtlBuffer &utlBuf, char *pszBuf, int nMaxLen ) +{ + utlBuf.GetLine( pszBuf, nMaxLen ); + Chomp( pszBuf ); +} + + +//============================================================================= +// +//============================================================================= +class CQcData +{ +public: + CQcData() + : m_nUpAxis( CDmSmdSerializer::Z_AXIS ) + , m_scale( 1.0f ) + {} + + bool ParseQc( const CUtlString &smdPath, const CUtlString &qcPath ); + + bool GetQcData( const CUtlString &smdPath ); + + CDmSmdSerializer::Axis_t m_nUpAxis; + float m_scale; + std::list< std::string > m_cdmaterials; +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static bool HandleQcHints( + const char *pBuf, + CQcData &qcData ) +{ + if ( !IsCommentLine( pBuf ) ) + return false; + + char key[ 512 ]; + key[0] = '\0'; + + char val[ 512 ]; + val[0] = '\0'; + + if ( sscanf( pBuf, "// %511s=%511s", key, val ) == 2 ) + { + if ( Q_stricmp( key, "UPAXIS" ) == 0 ) + { + if ( strpbrk( val, "xX" ) ) + { + qcData.m_nUpAxis = CDmSmdSerializer::X_AXIS; + } + else if ( strpbrk( val, "yY" ) ) + { + qcData.m_nUpAxis = CDmSmdSerializer::Y_AXIS; + } + else if ( strpbrk( val, "zZ" ) ) + { + qcData.m_nUpAxis = CDmSmdSerializer::Z_AXIS; + } + } + } + + key[ ARRAYSIZE(key) - 1 ] = '\0'; + val[ ARRAYSIZE(val) - 1 ] = '\0'; + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static void Tokenize( CUtlVector< CUtlString > &tokens, const char *pszBuf ) +{ + tokens.RemoveAll(); + CUtlString strBuf( pszBuf ); + + for ( char *pszToken = strtok( (char *)strBuf.Get(), " \t\n" ); pszToken; pszToken = strtok( NULL, " \t\n" ) ) + { + tokens.AddToTail( pszToken ); + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmSmdSerializer::ParserGetNodeName( const char *pszBuf, CUtlString &sName ) const +{ + sName = ParserSkipSpace( pszBuf ); + FixNodeName( sName ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmSmdSerializer::ParserHandleSkeletonLine( + const char *pszBuf, + CUtlString &sName, + int &nId, + int &nParentId ) const +{ + const char *pszOrigBuf = pszBuf; + + pszBuf = ParserSkipSpace( pszBuf ); + + char szTmpBuf[ 512 ]; + if ( sscanf( pszBuf, "%d \"%[^\"]\" %d", &nId, szTmpBuf, &nParentId ) == 3 ) + { + ParserGetNodeName( szTmpBuf, sName ); + return true; + } + + Warning( "Warning! Ignoring malformed skeleton line: %s\n", pszOrigBuf ); + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static CUtlString PathJoin( const char *pszStr1, const char *pszStr2 ) +{ + char szPath[MAX_PATH]; + V_ComposeFileName( pszStr1, pszStr2, szPath, sizeof( szPath ) ); + return CUtlString( szPath ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CQcData::ParseQc( + const CUtlString &smdPath, + const CUtlString &qcPath ) +{ + bool bRetVal = false; + + if ( _access( qcPath.Get(), 04 ) == 0 ) + { + try + { + std::string buf; + std::ifstream ifs( qcPath.Get() ); + + CUtlVector< CUtlString > tokens; + + while ( std::getline( ifs, buf ) ) + { + Tokenize( tokens, buf.c_str() ); + + if ( tokens.Count() < 1 ) + continue; + + if ( !V_stricmp( tokens[0], "$upaxis" ) ) + { + if ( strchr( tokens[1].Get(), 'y' ) || strchr( tokens[1].Get(), 'Y' ) ) + { + m_nUpAxis = CDmSmdSerializer::Y_AXIS; + } + else if ( strchr( tokens[1].Get(), 'x' ) || strchr( tokens[1].Get(), 'X' ) ) + { + m_nUpAxis = CDmSmdSerializer::X_AXIS; + } + else + { + m_nUpAxis = CDmSmdSerializer::Z_AXIS; + } + } + else if ( !V_stricmp( tokens[0], "$scale" ) ) + { + m_scale = strtod( tokens[1].Get(), NULL ); + } + else if ( !V_stricmp( tokens[0], "$cdmaterials" ) ) + { + m_cdmaterials.push_back( tokens[1].Get() ); + } + } + + bRetVal = true; + } + catch ( ... ) + { + } + } + + if ( m_cdmaterials.empty() ) + { + // If m_cdmaterials is empty, then put the relative smd path onto the cdmaterials path + + char szBuf0[MAX_PATH]; + V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) ); + V_StripFilename( szBuf0 ); + V_FixSlashes( szBuf0, '/' ); + + CUtlVector< char *, CUtlMemory< char *, int > > sPathArray; + V_SplitString( szBuf0, "/", sPathArray ); + + CUtlString sRelSmdPath; + + bool bJoin = false; + for ( int i = 0; i < sPathArray.Count(); ++i ) + { + if ( !bJoin && !V_stricmp( sPathArray[i], "models" ) ) + { + bJoin = true; + } + + if ( bJoin ) + { + sRelSmdPath = PathJoin( sRelSmdPath.Get(), sPathArray[i] ); + } + } + sRelSmdPath.FixSlashes( '/' ); + + m_cdmaterials.push_back( sRelSmdPath.Get() ); + } + + return bRetVal; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CQcData::GetQcData( + const CUtlString &smdPath ) +{ + try + { + // Look for same thing named with a .qc extension + char szBuf0[MAX_PATH]; + char szBuf1[MAX_PATH]; + + V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) ); + V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) ); + if ( _access( szBuf0, 04 ) == 0 ) + return ParseQc( smdPath, szBuf0 ); + + // Remove "_reference" if found + char *pszRef = V_stristr( szBuf0, "_reference.qc" ); + if ( pszRef ) + { + *pszRef = '\0'; + + V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) ); + if ( _access( szBuf0, 04 ) == 0 ) + return ParseQc( smdPath, szBuf0 ); + } + else + { + // Add _reference + + V_SetExtension( szBuf0, "", ARRAYSIZE( szBuf0 ) ); + V_strcat( szBuf0, "_reference", ARRAYSIZE( szBuf0 ) ); + V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) ); + if ( _access( szBuf0, 04 ) == 0 ) + return ParseQc( smdPath, szBuf0 ); + } + + // Look for any *.qc file in the same directory as the smd that contains the smd pathname + V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) ); + V_FixSlashes( szBuf0 ); + + V_FileBase( szBuf0, szBuf1, ARRAYSIZE( szBuf1 ) ); + CUtlString sFileBase0( "\"" ); + sFileBase0 += szBuf1; + sFileBase0 += "\""; + + V_SetExtension( szBuf1, ".smd", ARRAYSIZE( szBuf1 ) ); + CUtlString sFileBase1( "\"" ); + sFileBase1 += szBuf1; + sFileBase1 += "\""; + + V_ExtractFilePath( szBuf0, szBuf1, ARRAYSIZE( szBuf1 ) ); + CUtlString sFilePath = szBuf1; + + if ( sFileBase0.Length() > 0 && sFilePath.Length() > 0 ) + { + struct _finddata_t qcFile; + long hFile; + + CUtlVector< CUtlString > tokens; + + /* Find first .qc file in current directory */ + + CUtlString sQcGlob = sFilePath; + sQcGlob += "*.qc"; + + if ( ( hFile = _findfirst( sQcGlob.Get(), &qcFile ) ) != -1L ) + { + /* Find the rest of the .qc files */ + do { + CUtlString sQcFile = sFilePath; + sQcFile += qcFile.name; + + std::ifstream ifs( sQcFile.Get() ); + std::string buf; + + while ( std::getline( ifs, buf ) ) + { + if ( V_stristr( buf.c_str(), sFileBase0.Get() ) || V_stristr( buf.c_str(), sFileBase1.Get() ) ) + { + _findclose( hFile ); + return ParseQc( smdPath, sQcFile ); + } + } + } while( _findnext( hFile, &qcFile ) == 0 ); + + _findclose( hFile ); + } + } + } + catch ( const std::exception &e ) + { + Error( "Exception: %s\n", e.what() ); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +#define MAX_WEIGHTS_PER_VERTEX 3 + + +//============================================================================= +// +//============================================================================= +class CVertexWeight +{ +public: + int m_nBoneIndex; + float m_flWeight; + + CVertexWeight() + : m_nBoneIndex( -1 ) + , m_flWeight( 0.0f ) + {} + + inline void Reset() + { + m_nBoneIndex = -1; + m_flWeight = 0.0f; + } + + inline bool operator==( const CVertexWeight &rhs ) const + { + return m_nBoneIndex == rhs.m_nBoneIndex && m_flWeight == rhs.m_flWeight; + } + + inline bool operator!=( const CVertexWeight &rhs ) const + { + return m_nBoneIndex != rhs.m_nBoneIndex || m_flWeight != rhs.m_flWeight; + } +}; + + +//============================================================================= +// +//============================================================================= +class CVertex +{ +public: + Vector m_vPosition; + Vector m_vNormal; + Vector2D m_vUV; + uint m_nWeights; + CVertexWeight m_vertexWeights[ MAX_WEIGHTS_PER_VERTEX ]; + + CVertex() + { + Reset(); + } + + inline bool operator==( const CVertex &rhs ) const + { + if ( m_vPosition != rhs.m_vPosition || + m_vNormal != rhs.m_vNormal || + m_vUV != rhs.m_vUV || + m_nWeights != rhs.m_nWeights ) + return false; + + for ( uint i = 0; i != m_nWeights; ++i ) + { + if ( m_vertexWeights[ i ] != rhs.m_vertexWeights[ i ] ) + return false; + } + + return true; + } + + inline void Reset() + { + m_nWeights = 0; + for ( int i = 0; i < ARRAYSIZE( m_vertexWeights ); ++i ) + { + m_vertexWeights[i].Reset(); + } + } +}; + + +//============================================================================= +// +//============================================================================= +class CTriangle +{ +public: + uint m_nVertices; + CVertex m_vertices[ 3 ]; + + CTriangle() + { + Reset(); + } + + bool Valid() const; + + inline void Reset() + { + m_nVertices = 0; + m_vertices[ 0U ].Reset(); + m_vertices[ 1U ].Reset(); + m_vertices[ 2U ].Reset(); + } +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CTriangle::Valid() const +{ + // A triangle is valid it it has three vertices and all three vertices are unique + + if ( m_nVertices != 3U ) + return false; + + if ( m_vertices[0] == m_vertices[1] ) + return false; + + if ( m_vertices[1] == m_vertices[2] ) + return false; + + if ( m_vertices[2] == m_vertices[0] ) + return false; + + return true; +} + + +//============================================================================= +// +//============================================================================= +class CShadingGroup +{ +public: + std::string m_materialPath; + std::map< int, std::deque< int > > m_componentListMap; +}; + + +//============================================================================= +// +//============================================================================= +enum ParserState_t +{ + kUnknown, + kPreamble, + kGeneral, + kNodes, + kSkeleton, + kTriangles +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static bool ParserAddJoint( + CDmSmdSerializer::SmdJointMap_t &smdJointMap, + int nId, + const char *pszName, + int nParentId, + const char *pszFilename, + int nLineNumber ) +{ + if ( nId < 0 ) + { + Error( "Error! %s(%d) : node error : invalid node id %d, must be >= 0: Line '%d \"%s\" %d'\n", + pszFilename, nLineNumber, nId, nId, pszName, nParentId ); + return false; + } + + if ( smdJointMap.IsValidIndex( smdJointMap.Find( nId ) ) ) + { + Error( "Error! %s(%d) : node error : node id %d already defined\n", + pszFilename, nLineNumber, nId ); + return false; + } + + CDmSmdSerializer::SmdJoint_t &smdJoint = smdJointMap.Element( smdJointMap.Insert( nId ) ); + + smdJoint.m_nId = nId; + smdJoint.m_sName = pszName; + smdJoint.m_nParentId = MAX( -1, nParentId ); + smdJoint.m_nLineNumber = nLineNumber; + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static bool ParserCreateJoint( + const CDmSmdSerializer::SmdJointMap_t &smdJointMap, + CDmeModel *pDmeModel, + CDmeChannelsClip *pDmeChannelsClip, + CDmSmdSerializer::SmdJoint_t *pSmdJoint, + const char *pszFilename ) +{ + const CUtlString &sName = pSmdJoint->m_sName; + const int nId = pSmdJoint->m_nId; + const int nParentId = pSmdJoint->m_nParentId; + const int nLineNumber = pSmdJoint->m_nLineNumber; + + CDmeJoint *pDmeJoint = CreateElement< CDmeJoint >( sName.Get(), pDmeModel->GetFileId() ); + + if ( !pDmeJoint ) + { + Error( "%s(%d) : node error : can't create joint %d(%s)\n", + pszFilename, nLineNumber, nId, sName.Get() ); + return false; + } + + if ( nParentId < 0 ) + { + if ( !pDmeModel ) + { + Error( "%s(%d) : node error : No DmeModel passed for root joint %d(%s)\n", + pszFilename, nLineNumber, nId, sName.Get() ); + DestroyElement( pDmeJoint ); + + return false; + } + + pDmeModel->AddChild( pDmeJoint ); + CDmAttribute *pRootJointAttr = pDmeJoint->AddAttribute( "__rootJoint", AT_BOOL ); + pRootJointAttr->AddFlag( FATTRIB_DONTSAVE ); + pRootJointAttr->SetValue( true ); + } + else + { + if ( nParentId == nId ) + { + Error( "%s(%d) : node error : joint %d(%s) is its own parent\n", + pszFilename, nLineNumber, nId, sName.Get() ); + DestroyElement( pDmeJoint ); + + return false; + } + + const int nParentIdIndex = smdJointMap.Find( nParentId ); + + if ( !smdJointMap.IsValidIndex( nParentIdIndex ) ) + { + Error( "%s(%d) : node error : joint %d(%s) has invalid parentId (%d)\n", + pszFilename, nLineNumber, nId, sName.Get(), nParentId ); + DestroyElement( pDmeJoint ); + + return false; + } + + CDmeDag *pDmeDagParent = smdJointMap.Element( nParentIdIndex ).m_pDmeDag; + if ( pDmeDagParent ) + { + pDmeDagParent->AddChild( pDmeJoint ); + } + else + { + Error( "%s(%d) : node error : joint %d(%s) has invalid parentId (%d)\n", + pszFilename, nLineNumber, nId, sName.Get(), nParentId ); + } + } + + if ( pDmeChannelsClip ) + { + CDmeTransform *pDmeTransform = pDmeJoint->GetTransform(); + + CDmeChannel *pDmePosChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_p", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() ); + pDmePosChannel->SetMode( CM_PLAY ); + pDmePosChannel->SetOutput( pDmeTransform, "position" ); + CDmeVector3Log *pDmePosLog = pDmePosChannel->CreateLog< Vector >(); + pDmePosLog->SetValueThreshold( 1.0e-6 ); + pDmeChannelsClip->m_Channels.AddToTail( pDmePosChannel ); + + CDmAttribute *pPosLogAttr = pDmeJoint->AddAttribute( "__posLog", AT_ELEMENT ); + pPosLogAttr->AddFlag( FATTRIB_DONTSAVE ); + pPosLogAttr->SetValue( pDmePosLog ); + + CDmeChannel *pDmeRotChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_o", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() ); + pDmeRotChannel->SetMode( CM_PLAY ); + pDmeRotChannel->SetOutput( pDmeTransform, "orientation" ); + CDmeQuaternionLog *pDmeRotLog = pDmeRotChannel->CreateLog< Quaternion >(); + pDmeRotLog->SetValueThreshold( 1.0e-6 ); + pDmeChannelsClip->m_Channels.AddToTail( pDmeRotChannel ); + + CDmAttribute *pRotLogAttr = pDmeJoint->AddAttribute( "__rotLog", AT_ELEMENT ); + pRotLogAttr->AddFlag( FATTRIB_DONTSAVE ); + pRotLogAttr->SetValue( pDmeRotLog ); + } + + pSmdJoint->m_pDmeDag = pDmeJoint; + pDmeModel->AddJoint( pDmeJoint ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Used by ParserAddJoints, sorts joints by parent index +//----------------------------------------------------------------------------- +static bool SmdJointLessFunc( const CDmSmdSerializer::SmdJoint_t *pLhs, const CDmSmdSerializer::SmdJoint_t *pRhs ) +{ + // try to preserve joints without parents in their original order as much as possible + if ( pLhs->m_nParentId < 0 ) + return pLhs->m_nId < pRhs->m_nId; + + return pLhs->m_nParentId < pRhs->m_nParentId; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static void ParserCreateJoints( + CDmSmdSerializer::SmdJointMap_t &smdJointMap, + CDmeModel *pDmeModel, + CDmeChannelsClip *pDmeChannelsClip, + const char *pszFilename ) +{ + // Sort the joints in order of parent, pointers only temporary and no elements + // added/removed from map while pointers exist, so pointers are stable + CUtlVector< CDmSmdSerializer::SmdJoint_t * > smdJointList; + FOR_EACH_MAP_FAST( smdJointMap, nSmdJointMapIndex ) + { + smdJointList.AddToTail( &smdJointMap.Element( nSmdJointMapIndex ) ); + } + + std::stable_sort( smdJointList.Base(), smdJointList.Base() + smdJointList.Count(), SmdJointLessFunc ); + + for ( int i = 0; i < smdJointList.Count(); ++i ) + { + CDmSmdSerializer::SmdJoint_t *pSmdJoint = smdJointList[i]; + pSmdJoint->m_nActualId = i; + + ParserCreateJoint( smdJointMap, pDmeModel, pDmeChannelsClip, pSmdJoint, pszFilename ); + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmSmdSerializer::ParserSetJoint( + const SmdJointMap_t &smdJointMap, + int nFrame, + int nId, + const Vector &vPosition, + const RadianEuler &eRadianEulerXYZ, + const char *pszFilename, + int nLineNumber ) +{ + const SmdJointMap_t::IndexType_t nIdIndex = smdJointMap.Find( nId ); + if ( !smdJointMap.IsValidIndex( nIdIndex ) ) + { + Error( "%s(%d) : skeleton error : can't find joint %d\n", + pszFilename, nLineNumber, nId ); + return; + } + + CDmeDag *pDmeDag = smdJointMap.Element( nIdIndex ).m_pDmeDag; + if ( !pDmeDag ) + { + Error( "%s(%d) : skeleton error : no dmedag created for can't find joint %d (%s)\n", + pszFilename, nLineNumber, nId, smdJointMap.Element( nIdIndex ).m_sName.Get() ); + return; + } + + const Quaternion qOrientation = eRadianEulerXYZ; + + if ( pDmeDag->GetValue( "__rootJoint", false ) && GetUpAxis() != CDmSmdSerializer::Y_AXIS ) + { + matrix3x4_t mPre; + matrix3x4_t mPost; + AngleMatrix( qOrientation, vPosition, mPre ); + ConcatTransforms( m_mAdj, mPre, mPost ); + pDmeDag->GetTransform()->SetTransform( mPost ); + } + else + { + pDmeDag->GetTransform()->SetPosition( vPosition ); + pDmeDag->GetTransform()->SetOrientation( qOrientation ); + } + + if ( m_bOptAnimation ) + { + const DmeTime_t tCurrent( static_cast< float >( nFrame ), DmeFramerate_t( m_flFrameRate ) ); + + CDmeTransform *pDmeTransform = pDmeDag->GetTransform(); + + CDmeVector3Log *pDmePosLog = pDmeDag->GetValueElement< CDmeVector3Log >( "__posLog" ); + pDmePosLog->SetKey( tCurrent, pDmeTransform->GetPosition() ); + + CDmeQuaternionLog *pDmeRotLog = pDmeDag->GetValueElement< CDmeQuaternionLog >( "__rotLog" ); + pDmeRotLog->SetKey( tCurrent, pDmeTransform->GetOrientation() ); + } +} + +//----------------------------------------------------------------------------- +// Returns -1 if invalid +//----------------------------------------------------------------------------- +static int GetActualId( const CDmSmdSerializer::SmdJointMap_t &smdJointMap, int nId ) +{ + const CDmSmdSerializer::SmdJointMap_t::IndexType_t nIdIndex = smdJointMap.Find( nId ); + + if ( !smdJointMap.IsValidIndex( nIdIndex ) ) + { + Error( "Error! Invalid node id %d looked up\n", nId ); + return -1; + } + + return smdJointMap.Element( nIdIndex ).m_nActualId; +} + + +//----------------------------------------------------------------------------- +// Used by HandleVertexWeights, sorts vertex weights by weight +//----------------------------------------------------------------------------- +static int VertexWeightLessFunc( const void *pLhs, const void *pRhs ) +{ + const CVertexWeight *pVertexWeightL = reinterpret_cast< const CVertexWeight * >( pLhs ); + const CVertexWeight *pVertexWeightR = reinterpret_cast< const CVertexWeight * >( pRhs ); + + if ( pVertexWeightL->m_nBoneIndex < 0 ) + { + if ( pVertexWeightR->m_nBoneIndex < 0 ) + { + return 0; + } + else + { + return 1; + } + } + else if ( pVertexWeightR->m_nBoneIndex < 0 ) + { + return -1; + } + + if ( pVertexWeightL->m_flWeight > pVertexWeightR->m_flWeight ) + { + return -1; + } + else if ( pVertexWeightL->m_flWeight < pVertexWeightR->m_flWeight ) + { + return 1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static void HandleVertexWeights( + const CDmSmdSerializer::SmdJointMap_t &smdJointMap, + int nFallbackBoneIndexId, + CVertex &vertex, + const char *pszLine, + const char *pszFilename, + int nLineNumber ) +{ + for ( int i = 0; i < ARRAYSIZE( vertex.m_vertexWeights ); ++i ) + { + vertex.m_vertexWeights[i].Reset(); + } + + CUtlVector< CVertexWeight > tmpVertexWeights; + + CUtlVector< CUtlString > tokens; + Tokenize( tokens, pszLine ); + + const float flEps = 1.0e-6; + + uint nTokenEnd = tokens.Count(); + if ( nTokenEnd > 10 ) + { + int nId = -1; + float flWeight = 0.0f; + for ( uint i = 10; i < nTokenEnd; ++i ) + { + nId = strtol( tokens[ i ].Get(), NULL, 0 ); + ++i; + flWeight = strtod( tokens[ i ].Get(), NULL ); + + if ( nId < 0 || flWeight < flEps ) + continue; + + const int nActualId = GetActualId( smdJointMap, nId ); + + if ( nActualId < 0 ) + { + Error( "%s(%d) : triangle error : ignoring unknown joint id(%d) with actual id(%d) for vertex weight\n", + pszFilename, nLineNumber, nId, nActualId ); + continue; + } + + CVertexWeight &tmpVertexWeight = tmpVertexWeights[ tmpVertexWeights.AddToTail() ]; + tmpVertexWeight.m_nBoneIndex = nActualId; + tmpVertexWeight.m_flWeight = flWeight; + } + + // Sort vertex weights inversely by weight + qsort( tmpVertexWeights.Base(), tmpVertexWeights.Count(), sizeof( CVertexWeight ), VertexWeightLessFunc ); + + // Take at most the top ARRAYSIZE( vertex.m_vertexWeights ) from tmpVertexWeights + // Figure out actual weight count and total weight + + float flTotalWeight = 0.0f; + vertex.m_nWeights = 0; + + for ( int i = 0; i < tmpVertexWeights.Count() && i < ARRAYSIZE( vertex.m_vertexWeights ); ++i ) + { + CVertexWeight &vertexWeight( vertex.m_vertexWeights[ i ] ); + vertexWeight = tmpVertexWeights[i]; + flTotalWeight += vertexWeight.m_flWeight; + vertex.m_nWeights += 1; + } + + // If there are valid user specified weights then renormalize + if ( vertex.m_nWeights > 0 && flTotalWeight > flEps ) + { + for ( uint i = 0; i != ARRAYSIZE( vertex.m_vertexWeights ); ++i ) + { + CVertexWeight &vertexWeight( vertex.m_vertexWeights[ i ] ); + if ( vertexWeight.m_nBoneIndex >= 0 ) + { + vertexWeight.m_flWeight /= flTotalWeight; + } + } + } + else + { + // No valid user weights, assign to fallback bone with weight of 1 + vertex.m_vertexWeights[ 0 ].m_nBoneIndex = GetActualId( smdJointMap, nFallbackBoneIndexId ); + if ( vertex.m_vertexWeights[0].m_nBoneIndex < 0 ) + { + // Vertex is not weighted, can't find fallback bone, this is invalid + vertex.m_nWeights = 0; + vertex.m_vertexWeights[0].m_nBoneIndex = -1; + vertex.m_vertexWeights[0].m_flWeight = 0.0f; + } + else + { + vertex.m_nWeights = 1; + vertex.m_vertexWeights[0].m_flWeight = 1.0f; + } + + // Assign rest of weights to -1, 0.0f + for ( uint i = 1; i < ARRAYSIZE( vertex.m_vertexWeights ); ++i ) + { + vertex.m_vertexWeights[i].m_nBoneIndex = -1; + vertex.m_vertexWeights[i].m_flWeight = 0.0f; + } + } + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static CDmeMesh *CreateDmeMesh( + CDmeModel *pDmeModel, + const char *pszFilename, + int nLineNumber ) +{ + char szFileBase[MAX_PATH]; + szFileBase[0] = '\0'; + + const char *pszMeshName = "mesh"; + + if ( pszFilename ) + { + V_FileBase( pszFilename, szFileBase, ARRAYSIZE( szFileBase ) ); + + if ( V_strlen( szFileBase ) > 0 ) + { + pszMeshName = szFileBase; + } + } + + CDmeDag *pDmeDag = CreateElement< CDmeDag >( pszMeshName, pDmeModel->GetFileId() ); + if ( !pDmeDag ) + return NULL; + + pDmeModel->AddChild( pDmeDag ); + + CUtlString sMeshName = pDmeDag->GetName(); + sMeshName += "Shape"; + + CDmeMesh *pDmeMesh = CreateElement< CDmeMesh >( sMeshName.Get(), pDmeDag->GetFileId() ); + if ( !pDmeMesh ) + return NULL; + + CDmeVertexData *pDmeVertexData = pDmeMesh->FindOrCreateBaseState( "bind" ); + pDmeMesh->SetCurrentBaseState( "bind" ); + + pDmeVertexData->FlipVCoordinate( true ); + pDmeVertexData->CreateField( CDmeVertexData::FIELD_POSITION ); + pDmeVertexData->CreateField( CDmeVertexData::FIELD_NORMAL ); + pDmeVertexData->CreateField( CDmeVertexData::FIELD_TEXCOORD ); + FieldIndex_t nJointWeightField; + FieldIndex_t nJointIndexField; + pDmeVertexData->CreateJointWeightsAndIndices( MAX_WEIGHTS_PER_VERTEX, &nJointWeightField, &nJointIndexField ); + + pDmeDag->SetShape( pDmeMesh ); + + pDmeVertexData->Resolve(); + pDmeMesh->Resolve(); + pDmeDag->Resolve(); + + return pDmeMesh; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static CDmeFaceSet *FindOrCreateFaceSet( + CDmeMesh *pDmeMesh, + const CUtlString &sMaterial, + const char *pszFilename, + int nLineNumber ) +{ + // Could cache in a hashed map or something... + for ( int i = 0; i < pDmeMesh->FaceSetCount(); ++i ) + { + CDmeFaceSet *pDmeFaceSet = pDmeMesh->GetFaceSet( i ); + if ( !pDmeFaceSet ) + continue; + + CDmeMaterial *pDmeMaterial = pDmeFaceSet->GetMaterial(); + if ( !pDmeMaterial ) + continue; + + if ( !V_strcmp( pDmeMaterial->GetMaterialName(), sMaterial.Get() ) ) + return pDmeFaceSet; + } + + char szFaceSetName[ MAX_PATH ]; + V_FileBase( sMaterial.Get(), szFaceSetName, ARRAYSIZE( szFaceSetName ) ); + + CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( szFaceSetName, pDmeMesh->GetFileId() ); + Assert( pDmeFaceSet ); + + CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( szFaceSetName, pDmeMesh->GetFileId() ); + Assert( pDmeMaterial ); + + pDmeMaterial->SetMaterial( sMaterial.Get() ); + pDmeFaceSet->SetMaterial( pDmeMaterial ); + + pDmeMesh->AddFaceSet( pDmeFaceSet ); + + return pDmeFaceSet; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static void CreatePolygon( + CDmeMesh *pDmeMesh, + const CUtlString &sMaterial, + CTriangle &triangle, + const char *pszFilename, + int nLineNumber ) +{ + if ( !triangle.Valid() || !pDmeMesh ) + return; + + CDmeVertexData *pDmeVertexData = pDmeMesh->GetBindBaseState(); + Assert( pDmeVertexData ); + + FieldIndex_t nPositionField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + CDmrArray< Vector > positionData = pDmeVertexData->GetVertexData( nPositionField ); + + FieldIndex_t nNormalField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ); + CDmrArray< Vector > normalData = pDmeVertexData->GetVertexData( nNormalField ); + + FieldIndex_t nUvField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD ); + CDmrArray< Vector2D > uvData = pDmeVertexData->GetVertexData( nUvField ); + + FieldIndex_t nJointWeightField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS ); + CDmrArray< float > jointWeightData = pDmeVertexData->GetVertexData( nJointWeightField ); + + FieldIndex_t nJointIndexField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES ); + CDmrArray< int > jointIndexData = pDmeVertexData->GetVertexData( nJointIndexField ); + + CDmeFaceSet *pDmeFaceSet = FindOrCreateFaceSet( pDmeMesh, sMaterial, pszFilename, nLineNumber ); + if ( !pDmeFaceSet ) + { + Error( "%s(%d) : mesh error : couldn't find or create DmeFace set for material \"%s\" on mesh \"%s\"\n", + pszFilename, nLineNumber, sMaterial.Get(), pDmeMesh->GetName() ); + return; + } + + for ( uint i = 0; i < 3; ++i ) + { + const int nNewVertexIndex = pDmeVertexData->AddVertexIndices( 1 ); + + CVertex &cVertex = triangle.m_vertices[i]; + + { + // TODO: Make two positions if they are skinned differently + int nPositionIndex = positionData.Find( cVertex.m_vPosition ); + if ( !positionData.IsValidIndex( nPositionIndex ) ) + { + nPositionIndex = positionData.AddToTail( cVertex.m_vPosition ); + + for ( int j = 0; j < ARRAYSIZE( cVertex.m_vertexWeights ); ++j ) + { + jointWeightData.AddToTail( cVertex.m_vertexWeights[j].m_flWeight ); + jointIndexData.AddToTail( cVertex.m_vertexWeights[j].m_nBoneIndex ); + } + } + + pDmeVertexData->SetVertexIndices( nPositionField, nNewVertexIndex, 1, &nPositionIndex ); + + const int nFaceSetIndex = pDmeFaceSet->AddIndices( 1 ); + pDmeFaceSet->SetIndices( nFaceSetIndex, 1, const_cast< int * >( &nNewVertexIndex ) ); + + } + + { + int nNormalIndex = normalData.Find( cVertex.m_vNormal ); + if ( !normalData.IsValidIndex( nNormalIndex ) ) + { + nNormalIndex = normalData.AddToTail( cVertex.m_vNormal ); + } + + pDmeVertexData->SetVertexIndices( nNormalField, nNewVertexIndex, 1, &nNormalIndex ); + } + + + { + int nUvIndex = uvData.Find( cVertex.m_vUV ); + if ( !uvData.IsValidIndex( nUvIndex ) ) + { + nUvIndex = uvData.AddToTail( cVertex.m_vUV ); + } + + pDmeVertexData->SetVertexIndices( nUvField, nNewVertexIndex, 1, &nUvIndex ); + } + + } + + static const int nFaceDelimiter = -1; + const int nFaceSetIndex = pDmeFaceSet->AddIndices( 1 ); + pDmeFaceSet->SetIndices( nFaceSetIndex, 1, const_cast< int * >( &nFaceDelimiter ) ); + + triangle.Reset(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static CDmeFaceSet *FindOrAddFaceSet( + CDmeMesh *pDmeMesh, + CUtlMap< CUtlString, CDmeFaceSet * > &faceListMap, + const char *pszMaterialPath ) +{ + CUtlMap< CUtlString, CDmeFaceSet * >::IndexType_t nIndex = faceListMap.Find( CUtlString( pszMaterialPath ) ); + + if ( faceListMap.IsValidIndex( nIndex ) ) + return faceListMap.Element( nIndex ); + + CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( pszMaterialPath, pDmeMesh->GetFileId() ); + CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( pszMaterialPath, pDmeMesh->GetFileId() ); + Assert( pDmeFaceSet && pDmeMaterial ); + pDmeMaterial->SetMaterial( pszMaterialPath ); + pDmeMesh->AddFaceSet( pDmeFaceSet ); + + return faceListMap.Element( faceListMap.Insert( CUtlString( pszMaterialPath ), pDmeFaceSet ) ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CDmeChannelsClip *FindOrCreateChannelsClip( CDmElement *pDmeRoot, const char *pszAnimationName ) +{ + CDmeAnimationList *pDmeAnimationList = pDmeRoot->GetValueElement< CDmeAnimationList >( "animationList" ); + CDmeChannelsClip *pDmeChannelsClip = NULL; + + if ( pDmeAnimationList ) + { + pDmeChannelsClip = pDmeAnimationList->GetAnimation( 0 ); + } + else + { + CDmeModel *pDmeModel = pDmeRoot->GetValueElement< CDmeModel >( "skeleton" ); + + pDmeAnimationList = CreateElement< CDmeAnimationList >( pszAnimationName, pDmeRoot->GetFileId() ); + pDmeChannelsClip = CreateElement< CDmeChannelsClip >( pszAnimationName, pDmeRoot->GetFileId() ); + pDmeAnimationList->AddAnimation( pDmeChannelsClip ); + pDmeRoot->SetValue( "animationList", pDmeAnimationList ); + pDmeModel->SetValue( "animationList", pDmeAnimationList ); + } + + return pDmeChannelsClip; +} + +//----------------------------------------------------------------------------- +// Common function both ReadSMD & Unserialize can call +//----------------------------------------------------------------------------- +CDmElement *CDmSmdSerializer::ReadSMD( + CUtlBuffer &utlBuf, + DmFileId_t nDmFileId, + const char *pszFilename, + CDmeMesh **ppDmeMeshCreated ) +{ + CDmElement *pDmeRoot = CreateElement< CDmElement >( "root", nDmFileId ); + CDmeModel *pDmeModel = CreateElement< CDmeModel >( "model", nDmFileId ); + + char szAnimationName[ MAX_PATH ] = ""; + + if ( pszFilename ) + { + V_FileBase( pszFilename, szAnimationName, ARRAYSIZE( szAnimationName ) ); + } + else + { + pszFilename = "unknown"; + } + + if ( V_strlen( szAnimationName ) > 0 ) + { + pDmeModel->SetName( szAnimationName ); + } + else + { + V_strcpy_safe( szAnimationName, "anim" ); + } + + pDmeRoot->SetValue( "skeleton", pDmeModel ); + + if ( !m_bOptAnimation ) + { + // Don't set root.model if animation only, studiomdl can't handle it + pDmeRoot->SetValue( "model", pDmeModel ); + } + + SmdJointMap_t smdJointMap( CDefOps< int >::LessFunc ); + + CUtlString sMaterial; + + CTriangle triangle; + + uint nLineNumber = 0; + uint nBadPreambleCount = 0; + ParserState_t nParserState = kPreamble; + int nFrame = 0; + + CDmeChannelsClip *pDmeChannelsClip = NULL; + + if ( m_bOptAnimation ) + { + pDmeChannelsClip = FindOrCreateChannelsClip( pDmeRoot, szAnimationName ); + } + + bool bStartTimeSet = false; + + CQcData qcData; + + CDmeMesh *pDmeMesh = NULL; + + char szLine[ 4096 ]; + + while ( utlBuf.IsValid() ) + { + GetLine( utlBuf, szLine, ARRAYSIZE( szLine ) ); + ++nLineNumber; + + const char *pszLine = ParserSkipSpace( szLine ); + + if ( ParserIsBlankLine( pszLine ) ) + continue; + + if ( nParserState == kPreamble ) + { + if ( HandleQcHints( pszLine, qcData ) ) + continue; + + if ( ParserHandleVersion( pszLine ) ) + { + if ( qcData.m_nUpAxis != m_nUpAxis ) + { + Warning( "Importer UpAxis (%d) is different from UpAxis in SMD data (%d), using value found in SMD/QC\n", m_nUpAxis, qcData.m_nUpAxis ); + } + + SetUpAxis( qcData.m_nUpAxis ); + nParserState = kGeneral; + continue; + } + + Error( "%s(%d) : preamble error : expecting comment: \"%s\"\n", + pszFilename, nLineNumber, szLine ); + + ++nBadPreambleCount; + if ( nBadPreambleCount > 10 ) + { + Error( "%s(%d) : preamble error : too many errors, not an SMD file, aborting\n", + pszFilename, nLineNumber ); + break; + } + } + else if ( nParserState == kGeneral ) + { + if ( ParserHandleSectionStart( pszLine, "nodes" ) ) + { + nParserState = kNodes; + } + else if ( ParserHandleSectionStart( pszLine, "skeleton" ) ) + { + nParserState = kSkeleton; + } + else if ( ParserHandleSectionStart( pszLine, "triangles" ) ) + { + nParserState = kTriangles; + } + } + else if ( nParserState == kNodes ) + { + if ( ParserHandleSectionStart( pszLine, "end" ) ) + { + ParserCreateJoints( smdJointMap, pDmeModel, pDmeChannelsClip, pszFilename ); + + nParserState = kGeneral; + continue; + } + + CUtlString sName; + int nId; + int nParentId; + + if ( !m_bOptImportSkeleton || !ParserHandleSkeletonLine( pszLine, sName, nId, nParentId ) ) + continue; + + if ( !ParserAddJoint( smdJointMap, nId, sName, nParentId, pszFilename, nLineNumber ) ) + continue; + } + else if ( nParserState == kSkeleton ) + { + if ( ParserHandleSectionStart( pszLine, "end" ) ) + { + nParserState = kGeneral; + continue; + } + + if ( m_bOptAnimation && ParserHandleTime( pszLine, nFrame ) ) + { + if ( !bStartTimeSet ) + { + pDmeChannelsClip->SetStartTime( DmeTime_t( static_cast< float >( nFrame ), DmeFramerate_t( m_flFrameRate ) ) ); + bStartTimeSet = true; + } + continue; + } + + int nId; + Vector vPosition; + RadianEuler eRadianEulerXYZ; + + if ( sscanf( pszLine, "%d %f %f %f %f %f %f", &nId, &vPosition.x, &vPosition.y, &vPosition.z, &eRadianEulerXYZ.x, &eRadianEulerXYZ.y, &eRadianEulerXYZ.z ) == 7 ) + { + if ( !m_bOptImportSkeleton ) + continue; + + ParserSetJoint( smdJointMap, nFrame, nId, vPosition, eRadianEulerXYZ, pszFilename, nLineNumber ); + } + } + else if ( nParserState == kTriangles ) + { + if ( ParserHandleSectionStart( pszLine, "end" ) ) + { + triangle.Reset(); + nParserState = kGeneral; + continue; + } + + int nFallbackSkinJoint; + Vector vPosition; + Vector vNormal; + Vector2D vUV; + char szShadingGroup[ ARRAYSIZE( szLine ) ]; + + if ( sscanf( pszLine, "%d %f %f %f %f %f %f %f %f", + &nFallbackSkinJoint, + &vPosition.x, &vPosition.y, &vPosition.z, + &vNormal.x, &vNormal.y, &vNormal.z, &vUV.x, &vUV.y ) == 9 ) + { + if ( triangle.m_nVertices >= 3 ) + { + Error( "%s(%d) : triangle error : Too many vertices in triangle\n", + pszFilename, nLineNumber ); + continue; + } + + CVertex &vertex( triangle.m_vertices[ triangle.m_nVertices++ ] ); + vertex.m_vUV = vUV; + + if ( GetUpAxis() != CDmSmdSerializer::Y_AXIS ) + { + VectorTransform( vPosition, m_mAdj, vertex.m_vPosition ); + VectorTransform( vNormal, m_mAdjNormal, vertex.m_vNormal ); + } + else + { + vertex.m_vPosition = vPosition; + vertex.m_vNormal = vNormal; + } + + if ( m_bOptImportSkeleton ) + { + HandleVertexWeights( smdJointMap, nFallbackSkinJoint, vertex, pszLine, pszFilename, nLineNumber ); + } + + if ( !pDmeMesh ) + { + pDmeMesh = CreateDmeMesh( pDmeModel, pszFilename, nLineNumber ); + if ( !pDmeMesh ) + { + Error( "%s(%d) : Couldn't create DmeMesh\n", + pszFilename, nLineNumber ); + break; + } + } + + CreatePolygon( pDmeMesh, sMaterial, triangle, pszFilename, nLineNumber ); + } + else if ( sscanf( pszLine, "%1024s", szShadingGroup ) == 1 ) + { + char pszMaterialPath[ ARRAYSIZE( szShadingGroup ) ]; + V_StripExtension( szShadingGroup, pszMaterialPath, ARRAYSIZE( pszMaterialPath ) ); + + sMaterial = pszMaterialPath; + + triangle.Reset(); + } + else + { + Error( "%s(%d) : triangle error : unexpected data in triangles\n", + pszFilename, nLineNumber ); + triangle.Reset(); + } + } + else + { + Error( "%s(%d) : parser error : unknown parser state\n", + pszFilename, nLineNumber ); + nParserState = kGeneral; + } + } + + pDmeModel->CaptureJointsToBaseState( "bind" ); + + if ( pDmeChannelsClip ) + { + // Reset skeleton values to the first sample + FOR_EACH_MAP_FAST( smdJointMap, nJointMapIndex ) + { + CDmeDag *pDmeDag = smdJointMap.Element( nJointMapIndex ).m_pDmeDag; + if ( pDmeDag ) + { + CDmeTransform *pDmeTransform = pDmeDag->GetTransform(); + + CDmeVector3Log *pDmePosLog = pDmeDag->GetValueElement< CDmeVector3Log >( "__posLog" ); + pDmeTransform->SetPosition( pDmePosLog->GetKeyValue( 0 ) ); + + CDmeQuaternionLog *pDmeRotLog = pDmeDag->GetValueElement< CDmeQuaternionLog >( "__rotLog" ); + pDmeTransform->SetOrientation( pDmeRotLog->GetKeyValue( 0 ) ); + } + } + + pDmeChannelsClip->SetDuration( DmeTime_t( nFrame, DmeFramerate_t( m_flFrameRate ) ) - pDmeChannelsClip->GetStartTime() ); + } + + return pDmeRoot; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmSmdSerializer::FixNodeName( CUtlString &sName ) const +{ + char szTmpBuf0[ MAX_PATH ]; + char szTmpBuf1[ MAX_PATH ]; + + V_strncpy( szTmpBuf0, sName.Get(), ARRAYSIZE( szTmpBuf0 ) ); + + // Strip trailing quotes + char *pszName = szTmpBuf0 + sName.Length() - 1; + while ( pszName >= szTmpBuf0 && *pszName && *pszName == '"' ) + { + *pszName = '\0'; + --pszName; + } + + // Strip leading quotes + pszName = szTmpBuf0; + while ( *pszName && *pszName == '"') + { + ++pszName; + } + + if ( m_bOptAutoStripPrefix ) + { + // Get the string before the '.' + V_FileBase( pszName, szTmpBuf1, ARRAYSIZE( szTmpBuf1 ) ); + V_strncpy( szTmpBuf0, szTmpBuf1, ARRAYSIZE( szTmpBuf0 ) ); + pszName = szTmpBuf0; + } + + if ( !m_sNodeDelPrefix.IsEmpty() && StringHasPrefix( pszName, m_sNodeDelPrefix.Get() ) ) + { + pszName = const_cast< char * >( StringAfterPrefix( pszName, m_sNodeDelPrefix.Get() ) ); + } + + if ( m_sNodeAddPrefix.IsEmpty() ) + { + sName = pszName; + } + else + { + sName = m_sNodeAddPrefix; + sName += pszName; + } +}
\ No newline at end of file |