Team Fortress 2 Source Code as on 22/4/2020
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.

1705 lines
45 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Read SMD and create DMX data
  4. //
  5. //=============================================================================
  6. // Because we use STL
  7. #pragma warning( disable: 4530 )
  8. // Standard includes
  9. #include <io.h>
  10. #include <algorithm>
  11. #include <deque>
  12. #include <fstream>
  13. #include <list>
  14. #include <map>
  15. #include <string>
  16. // Valve includes
  17. #include "movieobjects/dmeanimationlist.h"
  18. #include "movieobjects/dmechannel.h"
  19. #include "movieobjects/dmedag.h"
  20. #include "movieobjects/dmemesh.h"
  21. #include "movieobjects/dmefaceset.h"
  22. #include "movieobjects/dmematerial.h"
  23. #include "movieobjects/dmemodel.h"
  24. #include "movieobjects/dmsmdserializer.h"
  25. #include "filesystem.h"
  26. #include "tier1/characterset.h"
  27. #include "tier1/fmtstr.h"
  28. #include "tier2/tier2.h"
  29. #include "mathlib/mathlib.h"
  30. // Last include
  31. #include "tier0/memdbgon.h"
  32. //=============================================================================
  33. //
  34. // CDmSmdSerializer
  35. //
  36. //=============================================================================
  37. //-----------------------------------------------------------------------------
  38. // Convert from SMD -> DME
  39. //-----------------------------------------------------------------------------
  40. bool CDmSmdSerializer::Unserialize(
  41. CUtlBuffer &utlBuf,
  42. const char * /* pszEncodingName */,
  43. int /* nEncodingVersion */,
  44. const char * /* pszSourceFormatName */,
  45. int /* nSourceFormatVersion */,
  46. DmFileId_t nDmFileId,
  47. DmConflictResolution_t /* nDmConflictResolution */,
  48. CDmElement **ppDmRoot )
  49. {
  50. if ( !ppDmRoot )
  51. return false;
  52. const char *pszFilename = g_pDataModel->GetFileName( nDmFileId );
  53. if ( pszFilename )
  54. {
  55. char szFilename[ MAX_PATH ];
  56. V_strncpy( szFilename, pszFilename, ARRAYSIZE( szFilename ) );
  57. V_FixSlashes( szFilename );
  58. *ppDmRoot = ReadSMD( utlBuf, nDmFileId, szFilename, NULL );
  59. }
  60. else
  61. {
  62. *ppDmRoot = ReadSMD( utlBuf, nDmFileId, "utlBuffer", NULL );
  63. }
  64. return *ppDmRoot != NULL;
  65. }
  66. //-----------------------------------------------------------------------------
  67. //
  68. //-----------------------------------------------------------------------------
  69. CDmElement *CDmSmdSerializer::ReadSMD(
  70. const char *pszFilename,
  71. CDmeMesh **ppDmeMeshCreated /* = NULL */ )
  72. {
  73. char szFilename[ MAX_PATH ];
  74. V_strncpy( szFilename, pszFilename, ARRAYSIZE( szFilename ) );
  75. V_FixSlashes( szFilename );
  76. CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  77. if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, utlBuf ) )
  78. return NULL;
  79. DmFileId_t nDmFileId = g_pDataModel->FindOrCreateFileId( pszFilename );
  80. return ReadSMD( utlBuf, nDmFileId, szFilename, ppDmeMeshCreated );
  81. }
  82. //-----------------------------------------------------------------------------
  83. //
  84. //-----------------------------------------------------------------------------
  85. void CDmSmdSerializer::SetUpAxis( CDmSmdSerializer::Axis_t nUpAxis )
  86. {
  87. m_nUpAxis = clamp( nUpAxis, X_AXIS, Z_AXIS );
  88. switch ( m_nUpAxis )
  89. {
  90. case X_AXIS: // X Up
  91. AngleMatrix( RadianEuler( -M_PI / 2.0, M_PI / 2.0, 0.0 ), m_mAdj );
  92. MatrixInverseTranspose( m_mAdj, m_mAdjNormal );
  93. break;
  94. case Y_AXIS: // Y Up
  95. SetIdentityMatrix( m_mAdj );
  96. SetIdentityMatrix( m_mAdjNormal );
  97. break;
  98. case Z_AXIS:
  99. default:
  100. AngleMatrix( RadianEuler( -M_PI / 2.0, 0.0, 0.0 ), m_mAdj );
  101. MatrixInverseTranspose( m_mAdj, m_mAdjNormal );
  102. break;
  103. }
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Tests whether the passed buffer is an all whitespace line or not
  107. //-----------------------------------------------------------------------------
  108. static bool ParserIsBlankLine( const char *pszBuf )
  109. {
  110. for ( const char *pChar = pszBuf; *pChar; ++pChar )
  111. {
  112. if ( !V_isspace( static_cast< unsigned char >( *pChar ) ) )
  113. return false;
  114. }
  115. return true;
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Skips over whitespace
  119. //-----------------------------------------------------------------------------
  120. static const char *ParserSkipSpace( const char *pszBuf )
  121. {
  122. // Skip to first non-whitespace character
  123. for ( ;; )
  124. {
  125. if ( !V_isspace( static_cast< unsigned char >( *pszBuf ) ) )
  126. break;
  127. ++pszBuf;
  128. }
  129. return pszBuf;
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Returns true the specified buffer is a comment line
  133. // meaning that it starts with // (with optional white space preceding the //)
  134. //-----------------------------------------------------------------------------
  135. static bool IsCommentLine( const char *pszBuf )
  136. {
  137. pszBuf = ParserSkipSpace( pszBuf );
  138. if ( *pszBuf && *pszBuf == '/' && *( pszBuf + 1 ) == '/' )
  139. {
  140. return true;
  141. }
  142. return false;
  143. }
  144. //-----------------------------------------------------------------------------
  145. //
  146. //-----------------------------------------------------------------------------
  147. static bool ParserHandleVersion( const char *pszBuf, int *pnVersion = NULL )
  148. {
  149. pszBuf = ParserSkipSpace( pszBuf );
  150. if ( !( *pszBuf && V_strnicmp( pszBuf, "version", 7 ) == 0 ) )
  151. return false;
  152. if ( pnVersion )
  153. {
  154. *pnVersion = strtol( pszBuf + 7, NULL, 0 ); // Skip past "version"
  155. }
  156. return true;
  157. }
  158. //-----------------------------------------------------------------------------
  159. //
  160. //-----------------------------------------------------------------------------
  161. static bool ParserHandleSectionStart( const char *pszBuf, const char *pszSectionId )
  162. {
  163. pszBuf = ParserSkipSpace( pszBuf );
  164. if ( ! *pszBuf )
  165. return false;
  166. const int nCmd = V_stricmp( pszBuf, pszSectionId );
  167. if ( !nCmd )
  168. return true;
  169. return false;
  170. }
  171. //-----------------------------------------------------------------------------
  172. //
  173. //-----------------------------------------------------------------------------
  174. static bool ParserHandleTime( const char *pszBuf, int &nTime )
  175. {
  176. if ( sscanf( pszBuf, "time %d", &nTime ) == 1 )
  177. return true;
  178. return false;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Strip trailing CR/NL
  182. //-----------------------------------------------------------------------------
  183. static void Chomp( char *pszBuf )
  184. {
  185. char *pChar = pszBuf + V_strlen( pszBuf ) - 1;
  186. while ( pChar >= pszBuf && ( *pChar == '\n' || *pChar == '\r' ) )
  187. {
  188. *pChar = '\0';
  189. }
  190. }
  191. //-----------------------------------------------------------------------------
  192. //
  193. //-----------------------------------------------------------------------------
  194. static void GetLine( CUtlBuffer &utlBuf, char *pszBuf, int nMaxLen )
  195. {
  196. utlBuf.GetLine( pszBuf, nMaxLen );
  197. Chomp( pszBuf );
  198. }
  199. //=============================================================================
  200. //
  201. //=============================================================================
  202. class CQcData
  203. {
  204. public:
  205. CQcData()
  206. : m_nUpAxis( CDmSmdSerializer::Z_AXIS )
  207. , m_scale( 1.0f )
  208. {}
  209. bool ParseQc( const CUtlString &smdPath, const CUtlString &qcPath );
  210. bool GetQcData( const CUtlString &smdPath );
  211. CDmSmdSerializer::Axis_t m_nUpAxis;
  212. float m_scale;
  213. std::list< std::string > m_cdmaterials;
  214. };
  215. //-----------------------------------------------------------------------------
  216. //
  217. //-----------------------------------------------------------------------------
  218. static bool HandleQcHints(
  219. const char *pBuf,
  220. CQcData &qcData )
  221. {
  222. if ( !IsCommentLine( pBuf ) )
  223. return false;
  224. char key[ 512 ];
  225. key[0] = '\0';
  226. char val[ 512 ];
  227. val[0] = '\0';
  228. if ( sscanf( pBuf, "// %511s=%511s", key, val ) == 2 )
  229. {
  230. if ( Q_stricmp( key, "UPAXIS" ) == 0 )
  231. {
  232. if ( strpbrk( val, "xX" ) )
  233. {
  234. qcData.m_nUpAxis = CDmSmdSerializer::X_AXIS;
  235. }
  236. else if ( strpbrk( val, "yY" ) )
  237. {
  238. qcData.m_nUpAxis = CDmSmdSerializer::Y_AXIS;
  239. }
  240. else if ( strpbrk( val, "zZ" ) )
  241. {
  242. qcData.m_nUpAxis = CDmSmdSerializer::Z_AXIS;
  243. }
  244. }
  245. }
  246. key[ ARRAYSIZE(key) - 1 ] = '\0';
  247. val[ ARRAYSIZE(val) - 1 ] = '\0';
  248. return true;
  249. }
  250. //-----------------------------------------------------------------------------
  251. //
  252. //-----------------------------------------------------------------------------
  253. static void Tokenize( CUtlVector< CUtlString > &tokens, const char *pszBuf )
  254. {
  255. tokens.RemoveAll();
  256. CUtlString strBuf( pszBuf );
  257. for ( char *pszToken = strtok( (char *)strBuf.Get(), " \t\n" ); pszToken; pszToken = strtok( NULL, " \t\n" ) )
  258. {
  259. tokens.AddToTail( pszToken );
  260. }
  261. }
  262. //-----------------------------------------------------------------------------
  263. //
  264. //-----------------------------------------------------------------------------
  265. void CDmSmdSerializer::ParserGetNodeName( const char *pszBuf, CUtlString &sName ) const
  266. {
  267. sName = ParserSkipSpace( pszBuf );
  268. FixNodeName( sName );
  269. }
  270. //-----------------------------------------------------------------------------
  271. //
  272. //-----------------------------------------------------------------------------
  273. bool CDmSmdSerializer::ParserHandleSkeletonLine(
  274. const char *pszBuf,
  275. CUtlString &sName,
  276. int &nId,
  277. int &nParentId ) const
  278. {
  279. const char *pszOrigBuf = pszBuf;
  280. pszBuf = ParserSkipSpace( pszBuf );
  281. char szTmpBuf[ 512 ];
  282. if ( sscanf( pszBuf, "%d \"%[^\"]\" %d", &nId, szTmpBuf, &nParentId ) == 3 )
  283. {
  284. ParserGetNodeName( szTmpBuf, sName );
  285. return true;
  286. }
  287. Warning( "Warning! Ignoring malformed skeleton line: %s\n", pszOrigBuf );
  288. return false;
  289. }
  290. //-----------------------------------------------------------------------------
  291. //
  292. //-----------------------------------------------------------------------------
  293. static CUtlString PathJoin( const char *pszStr1, const char *pszStr2 )
  294. {
  295. char szPath[MAX_PATH];
  296. V_ComposeFileName( pszStr1, pszStr2, szPath, sizeof( szPath ) );
  297. return CUtlString( szPath );
  298. }
  299. //-----------------------------------------------------------------------------
  300. //
  301. //-----------------------------------------------------------------------------
  302. bool CQcData::ParseQc(
  303. const CUtlString &smdPath,
  304. const CUtlString &qcPath )
  305. {
  306. bool bRetVal = false;
  307. if ( _access( qcPath.Get(), 04 ) == 0 )
  308. {
  309. try
  310. {
  311. std::string buf;
  312. std::ifstream ifs( qcPath.Get() );
  313. CUtlVector< CUtlString > tokens;
  314. while ( std::getline( ifs, buf ) )
  315. {
  316. Tokenize( tokens, buf.c_str() );
  317. if ( tokens.Count() < 1 )
  318. continue;
  319. if ( !V_stricmp( tokens[0], "$upaxis" ) )
  320. {
  321. if ( strchr( tokens[1].Get(), 'y' ) || strchr( tokens[1].Get(), 'Y' ) )
  322. {
  323. m_nUpAxis = CDmSmdSerializer::Y_AXIS;
  324. }
  325. else if ( strchr( tokens[1].Get(), 'x' ) || strchr( tokens[1].Get(), 'X' ) )
  326. {
  327. m_nUpAxis = CDmSmdSerializer::X_AXIS;
  328. }
  329. else
  330. {
  331. m_nUpAxis = CDmSmdSerializer::Z_AXIS;
  332. }
  333. }
  334. else if ( !V_stricmp( tokens[0], "$scale" ) )
  335. {
  336. m_scale = strtod( tokens[1].Get(), NULL );
  337. }
  338. else if ( !V_stricmp( tokens[0], "$cdmaterials" ) )
  339. {
  340. m_cdmaterials.push_back( tokens[1].Get() );
  341. }
  342. }
  343. bRetVal = true;
  344. }
  345. catch ( ... )
  346. {
  347. }
  348. }
  349. if ( m_cdmaterials.empty() )
  350. {
  351. // If m_cdmaterials is empty, then put the relative smd path onto the cdmaterials path
  352. char szBuf0[MAX_PATH];
  353. V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) );
  354. V_StripFilename( szBuf0 );
  355. V_FixSlashes( szBuf0, '/' );
  356. CUtlVector< char *, CUtlMemory< char *, int > > sPathArray;
  357. V_SplitString( szBuf0, "/", sPathArray );
  358. CUtlString sRelSmdPath;
  359. bool bJoin = false;
  360. for ( int i = 0; i < sPathArray.Count(); ++i )
  361. {
  362. if ( !bJoin && !V_stricmp( sPathArray[i], "models" ) )
  363. {
  364. bJoin = true;
  365. }
  366. if ( bJoin )
  367. {
  368. sRelSmdPath = PathJoin( sRelSmdPath.Get(), sPathArray[i] );
  369. }
  370. }
  371. sRelSmdPath.FixSlashes( '/' );
  372. m_cdmaterials.push_back( sRelSmdPath.Get() );
  373. }
  374. return bRetVal;
  375. }
  376. //-----------------------------------------------------------------------------
  377. //
  378. //-----------------------------------------------------------------------------
  379. bool CQcData::GetQcData(
  380. const CUtlString &smdPath )
  381. {
  382. try
  383. {
  384. // Look for same thing named with a .qc extension
  385. char szBuf0[MAX_PATH];
  386. char szBuf1[MAX_PATH];
  387. V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) );
  388. V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) );
  389. if ( _access( szBuf0, 04 ) == 0 )
  390. return ParseQc( smdPath, szBuf0 );
  391. // Remove "_reference" if found
  392. char *pszRef = V_stristr( szBuf0, "_reference.qc" );
  393. if ( pszRef )
  394. {
  395. *pszRef = '\0';
  396. V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) );
  397. if ( _access( szBuf0, 04 ) == 0 )
  398. return ParseQc( smdPath, szBuf0 );
  399. }
  400. else
  401. {
  402. // Add _reference
  403. V_SetExtension( szBuf0, "", ARRAYSIZE( szBuf0 ) );
  404. V_strcat( szBuf0, "_reference", ARRAYSIZE( szBuf0 ) );
  405. V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) );
  406. if ( _access( szBuf0, 04 ) == 0 )
  407. return ParseQc( smdPath, szBuf0 );
  408. }
  409. // Look for any *.qc file in the same directory as the smd that contains the smd pathname
  410. V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) );
  411. V_FixSlashes( szBuf0 );
  412. V_FileBase( szBuf0, szBuf1, ARRAYSIZE( szBuf1 ) );
  413. CUtlString sFileBase0( "\"" );
  414. sFileBase0 += szBuf1;
  415. sFileBase0 += "\"";
  416. V_SetExtension( szBuf1, ".smd", ARRAYSIZE( szBuf1 ) );
  417. CUtlString sFileBase1( "\"" );
  418. sFileBase1 += szBuf1;
  419. sFileBase1 += "\"";
  420. V_ExtractFilePath( szBuf0, szBuf1, ARRAYSIZE( szBuf1 ) );
  421. CUtlString sFilePath = szBuf1;
  422. if ( sFileBase0.Length() > 0 && sFilePath.Length() > 0 )
  423. {
  424. struct _finddata_t qcFile;
  425. long hFile;
  426. CUtlVector< CUtlString > tokens;
  427. /* Find first .qc file in current directory */
  428. CUtlString sQcGlob = sFilePath;
  429. sQcGlob += "*.qc";
  430. if ( ( hFile = _findfirst( sQcGlob.Get(), &qcFile ) ) != -1L )
  431. {
  432. /* Find the rest of the .qc files */
  433. do {
  434. CUtlString sQcFile = sFilePath;
  435. sQcFile += qcFile.name;
  436. std::ifstream ifs( sQcFile.Get() );
  437. std::string buf;
  438. while ( std::getline( ifs, buf ) )
  439. {
  440. if ( V_stristr( buf.c_str(), sFileBase0.Get() ) || V_stristr( buf.c_str(), sFileBase1.Get() ) )
  441. {
  442. _findclose( hFile );
  443. return ParseQc( smdPath, sQcFile );
  444. }
  445. }
  446. } while( _findnext( hFile, &qcFile ) == 0 );
  447. _findclose( hFile );
  448. }
  449. }
  450. }
  451. catch ( const std::exception &e )
  452. {
  453. Error( "Exception: %s\n", e.what() );
  454. }
  455. return false;
  456. }
  457. //-----------------------------------------------------------------------------
  458. //
  459. //-----------------------------------------------------------------------------
  460. #define MAX_WEIGHTS_PER_VERTEX 3
  461. //=============================================================================
  462. //
  463. //=============================================================================
  464. class CVertexWeight
  465. {
  466. public:
  467. int m_nBoneIndex;
  468. float m_flWeight;
  469. CVertexWeight()
  470. : m_nBoneIndex( -1 )
  471. , m_flWeight( 0.0f )
  472. {}
  473. inline void Reset()
  474. {
  475. m_nBoneIndex = -1;
  476. m_flWeight = 0.0f;
  477. }
  478. inline bool operator==( const CVertexWeight &rhs ) const
  479. {
  480. return m_nBoneIndex == rhs.m_nBoneIndex && m_flWeight == rhs.m_flWeight;
  481. }
  482. inline bool operator!=( const CVertexWeight &rhs ) const
  483. {
  484. return m_nBoneIndex != rhs.m_nBoneIndex || m_flWeight != rhs.m_flWeight;
  485. }
  486. };
  487. //=============================================================================
  488. //
  489. //=============================================================================
  490. class CVertex
  491. {
  492. public:
  493. Vector m_vPosition;
  494. Vector m_vNormal;
  495. Vector2D m_vUV;
  496. uint m_nWeights;
  497. CVertexWeight m_vertexWeights[ MAX_WEIGHTS_PER_VERTEX ];
  498. CVertex()
  499. {
  500. Reset();
  501. }
  502. inline bool operator==( const CVertex &rhs ) const
  503. {
  504. if ( m_vPosition != rhs.m_vPosition ||
  505. m_vNormal != rhs.m_vNormal ||
  506. m_vUV != rhs.m_vUV ||
  507. m_nWeights != rhs.m_nWeights )
  508. return false;
  509. for ( uint i = 0; i != m_nWeights; ++i )
  510. {
  511. if ( m_vertexWeights[ i ] != rhs.m_vertexWeights[ i ] )
  512. return false;
  513. }
  514. return true;
  515. }
  516. inline void Reset()
  517. {
  518. m_nWeights = 0;
  519. for ( int i = 0; i < ARRAYSIZE( m_vertexWeights ); ++i )
  520. {
  521. m_vertexWeights[i].Reset();
  522. }
  523. }
  524. };
  525. //=============================================================================
  526. //
  527. //=============================================================================
  528. class CTriangle
  529. {
  530. public:
  531. uint m_nVertices;
  532. CVertex m_vertices[ 3 ];
  533. CTriangle()
  534. {
  535. Reset();
  536. }
  537. bool Valid() const;
  538. inline void Reset()
  539. {
  540. m_nVertices = 0;
  541. m_vertices[ 0U ].Reset();
  542. m_vertices[ 1U ].Reset();
  543. m_vertices[ 2U ].Reset();
  544. }
  545. };
  546. //-----------------------------------------------------------------------------
  547. //
  548. //-----------------------------------------------------------------------------
  549. bool CTriangle::Valid() const
  550. {
  551. // A triangle is valid it it has three vertices and all three vertices are unique
  552. if ( m_nVertices != 3U )
  553. return false;
  554. if ( m_vertices[0] == m_vertices[1] )
  555. return false;
  556. if ( m_vertices[1] == m_vertices[2] )
  557. return false;
  558. if ( m_vertices[2] == m_vertices[0] )
  559. return false;
  560. return true;
  561. }
  562. //=============================================================================
  563. //
  564. //=============================================================================
  565. class CShadingGroup
  566. {
  567. public:
  568. std::string m_materialPath;
  569. std::map< int, std::deque< int > > m_componentListMap;
  570. };
  571. //=============================================================================
  572. //
  573. //=============================================================================
  574. enum ParserState_t
  575. {
  576. kUnknown,
  577. kPreamble,
  578. kGeneral,
  579. kNodes,
  580. kSkeleton,
  581. kTriangles
  582. };
  583. //-----------------------------------------------------------------------------
  584. //
  585. //-----------------------------------------------------------------------------
  586. static bool ParserAddJoint(
  587. CDmSmdSerializer::SmdJointMap_t &smdJointMap,
  588. int nId,
  589. const char *pszName,
  590. int nParentId,
  591. const char *pszFilename,
  592. int nLineNumber )
  593. {
  594. if ( nId < 0 )
  595. {
  596. Error( "Error! %s(%d) : node error : invalid node id %d, must be >= 0: Line '%d \"%s\" %d'\n",
  597. pszFilename, nLineNumber, nId, nId, pszName, nParentId );
  598. return false;
  599. }
  600. if ( smdJointMap.IsValidIndex( smdJointMap.Find( nId ) ) )
  601. {
  602. Error( "Error! %s(%d) : node error : node id %d already defined\n",
  603. pszFilename, nLineNumber, nId );
  604. return false;
  605. }
  606. CDmSmdSerializer::SmdJoint_t &smdJoint = smdJointMap.Element( smdJointMap.Insert( nId ) );
  607. smdJoint.m_nId = nId;
  608. smdJoint.m_sName = pszName;
  609. smdJoint.m_nParentId = MAX( -1, nParentId );
  610. smdJoint.m_nLineNumber = nLineNumber;
  611. return true;
  612. }
  613. //-----------------------------------------------------------------------------
  614. //
  615. //-----------------------------------------------------------------------------
  616. static bool ParserCreateJoint(
  617. const CDmSmdSerializer::SmdJointMap_t &smdJointMap,
  618. CDmeModel *pDmeModel,
  619. CDmeChannelsClip *pDmeChannelsClip,
  620. CDmSmdSerializer::SmdJoint_t *pSmdJoint,
  621. const char *pszFilename )
  622. {
  623. const CUtlString &sName = pSmdJoint->m_sName;
  624. const int nId = pSmdJoint->m_nId;
  625. const int nParentId = pSmdJoint->m_nParentId;
  626. const int nLineNumber = pSmdJoint->m_nLineNumber;
  627. CDmeJoint *pDmeJoint = CreateElement< CDmeJoint >( sName.Get(), pDmeModel->GetFileId() );
  628. if ( !pDmeJoint )
  629. {
  630. Error( "%s(%d) : node error : can't create joint %d(%s)\n",
  631. pszFilename, nLineNumber, nId, sName.Get() );
  632. return false;
  633. }
  634. if ( nParentId < 0 )
  635. {
  636. if ( !pDmeModel )
  637. {
  638. Error( "%s(%d) : node error : No DmeModel passed for root joint %d(%s)\n",
  639. pszFilename, nLineNumber, nId, sName.Get() );
  640. DestroyElement( pDmeJoint );
  641. return false;
  642. }
  643. pDmeModel->AddChild( pDmeJoint );
  644. CDmAttribute *pRootJointAttr = pDmeJoint->AddAttribute( "__rootJoint", AT_BOOL );
  645. pRootJointAttr->AddFlag( FATTRIB_DONTSAVE );
  646. pRootJointAttr->SetValue( true );
  647. }
  648. else
  649. {
  650. if ( nParentId == nId )
  651. {
  652. Error( "%s(%d) : node error : joint %d(%s) is its own parent\n",
  653. pszFilename, nLineNumber, nId, sName.Get() );
  654. DestroyElement( pDmeJoint );
  655. return false;
  656. }
  657. const int nParentIdIndex = smdJointMap.Find( nParentId );
  658. if ( !smdJointMap.IsValidIndex( nParentIdIndex ) )
  659. {
  660. Error( "%s(%d) : node error : joint %d(%s) has invalid parentId (%d)\n",
  661. pszFilename, nLineNumber, nId, sName.Get(), nParentId );
  662. DestroyElement( pDmeJoint );
  663. return false;
  664. }
  665. CDmeDag *pDmeDagParent = smdJointMap.Element( nParentIdIndex ).m_pDmeDag;
  666. if ( pDmeDagParent )
  667. {
  668. pDmeDagParent->AddChild( pDmeJoint );
  669. }
  670. else
  671. {
  672. Error( "%s(%d) : node error : joint %d(%s) has invalid parentId (%d)\n",
  673. pszFilename, nLineNumber, nId, sName.Get(), nParentId );
  674. }
  675. }
  676. if ( pDmeChannelsClip )
  677. {
  678. CDmeTransform *pDmeTransform = pDmeJoint->GetTransform();
  679. CDmeChannel *pDmePosChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_p", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() );
  680. pDmePosChannel->SetMode( CM_PLAY );
  681. pDmePosChannel->SetOutput( pDmeTransform, "position" );
  682. CDmeVector3Log *pDmePosLog = pDmePosChannel->CreateLog< Vector >();
  683. pDmePosLog->SetValueThreshold( 1.0e-6 );
  684. pDmeChannelsClip->m_Channels.AddToTail( pDmePosChannel );
  685. CDmAttribute *pPosLogAttr = pDmeJoint->AddAttribute( "__posLog", AT_ELEMENT );
  686. pPosLogAttr->AddFlag( FATTRIB_DONTSAVE );
  687. pPosLogAttr->SetValue( pDmePosLog );
  688. CDmeChannel *pDmeRotChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_o", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() );
  689. pDmeRotChannel->SetMode( CM_PLAY );
  690. pDmeRotChannel->SetOutput( pDmeTransform, "orientation" );
  691. CDmeQuaternionLog *pDmeRotLog = pDmeRotChannel->CreateLog< Quaternion >();
  692. pDmeRotLog->SetValueThreshold( 1.0e-6 );
  693. pDmeChannelsClip->m_Channels.AddToTail( pDmeRotChannel );
  694. CDmAttribute *pRotLogAttr = pDmeJoint->AddAttribute( "__rotLog", AT_ELEMENT );
  695. pRotLogAttr->AddFlag( FATTRIB_DONTSAVE );
  696. pRotLogAttr->SetValue( pDmeRotLog );
  697. }
  698. pSmdJoint->m_pDmeDag = pDmeJoint;
  699. pDmeModel->AddJoint( pDmeJoint );
  700. return true;
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Used by ParserAddJoints, sorts joints by parent index
  704. //-----------------------------------------------------------------------------
  705. static bool SmdJointLessFunc( const CDmSmdSerializer::SmdJoint_t *pLhs, const CDmSmdSerializer::SmdJoint_t *pRhs )
  706. {
  707. // try to preserve joints without parents in their original order as much as possible
  708. if ( pLhs->m_nParentId < 0 )
  709. return pLhs->m_nId < pRhs->m_nId;
  710. return pLhs->m_nParentId < pRhs->m_nParentId;
  711. }
  712. //-----------------------------------------------------------------------------
  713. //
  714. //-----------------------------------------------------------------------------
  715. static void ParserCreateJoints(
  716. CDmSmdSerializer::SmdJointMap_t &smdJointMap,
  717. CDmeModel *pDmeModel,
  718. CDmeChannelsClip *pDmeChannelsClip,
  719. const char *pszFilename )
  720. {
  721. // Sort the joints in order of parent, pointers only temporary and no elements
  722. // added/removed from map while pointers exist, so pointers are stable
  723. CUtlVector< CDmSmdSerializer::SmdJoint_t * > smdJointList;
  724. FOR_EACH_MAP_FAST( smdJointMap, nSmdJointMapIndex )
  725. {
  726. smdJointList.AddToTail( &smdJointMap.Element( nSmdJointMapIndex ) );
  727. }
  728. std::stable_sort( smdJointList.Base(), smdJointList.Base() + smdJointList.Count(), SmdJointLessFunc );
  729. for ( int i = 0; i < smdJointList.Count(); ++i )
  730. {
  731. CDmSmdSerializer::SmdJoint_t *pSmdJoint = smdJointList[i];
  732. pSmdJoint->m_nActualId = i;
  733. ParserCreateJoint( smdJointMap, pDmeModel, pDmeChannelsClip, pSmdJoint, pszFilename );
  734. }
  735. }
  736. //-----------------------------------------------------------------------------
  737. //
  738. //-----------------------------------------------------------------------------
  739. void CDmSmdSerializer::ParserSetJoint(
  740. const SmdJointMap_t &smdJointMap,
  741. int nFrame,
  742. int nId,
  743. const Vector &vPosition,
  744. const RadianEuler &eRadianEulerXYZ,
  745. const char *pszFilename,
  746. int nLineNumber )
  747. {
  748. const SmdJointMap_t::IndexType_t nIdIndex = smdJointMap.Find( nId );
  749. if ( !smdJointMap.IsValidIndex( nIdIndex ) )
  750. {
  751. Error( "%s(%d) : skeleton error : can't find joint %d\n",
  752. pszFilename, nLineNumber, nId );
  753. return;
  754. }
  755. CDmeDag *pDmeDag = smdJointMap.Element( nIdIndex ).m_pDmeDag;
  756. if ( !pDmeDag )
  757. {
  758. Error( "%s(%d) : skeleton error : no dmedag created for can't find joint %d (%s)\n",
  759. pszFilename, nLineNumber, nId, smdJointMap.Element( nIdIndex ).m_sName.Get() );
  760. return;
  761. }
  762. const Quaternion qOrientation = eRadianEulerXYZ;
  763. if ( pDmeDag->GetValue( "__rootJoint", false ) && GetUpAxis() != CDmSmdSerializer::Y_AXIS )
  764. {
  765. matrix3x4_t mPre;
  766. matrix3x4_t mPost;
  767. AngleMatrix( qOrientation, vPosition, mPre );
  768. ConcatTransforms( m_mAdj, mPre, mPost );
  769. pDmeDag->GetTransform()->SetTransform( mPost );
  770. }
  771. else
  772. {
  773. pDmeDag->GetTransform()->SetPosition( vPosition );
  774. pDmeDag->GetTransform()->SetOrientation( qOrientation );
  775. }
  776. if ( m_bOptAnimation )
  777. {
  778. const DmeTime_t tCurrent( static_cast< float >( nFrame ), DmeFramerate_t( m_flFrameRate ) );
  779. CDmeTransform *pDmeTransform = pDmeDag->GetTransform();
  780. CDmeVector3Log *pDmePosLog = pDmeDag->GetValueElement< CDmeVector3Log >( "__posLog" );
  781. pDmePosLog->SetKey( tCurrent, pDmeTransform->GetPosition() );
  782. CDmeQuaternionLog *pDmeRotLog = pDmeDag->GetValueElement< CDmeQuaternionLog >( "__rotLog" );
  783. pDmeRotLog->SetKey( tCurrent, pDmeTransform->GetOrientation() );
  784. }
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Returns -1 if invalid
  788. //-----------------------------------------------------------------------------
  789. static int GetActualId( const CDmSmdSerializer::SmdJointMap_t &smdJointMap, int nId )
  790. {
  791. const CDmSmdSerializer::SmdJointMap_t::IndexType_t nIdIndex = smdJointMap.Find( nId );
  792. if ( !smdJointMap.IsValidIndex( nIdIndex ) )
  793. {
  794. Error( "Error! Invalid node id %d looked up\n", nId );
  795. return -1;
  796. }
  797. return smdJointMap.Element( nIdIndex ).m_nActualId;
  798. }
  799. //-----------------------------------------------------------------------------
  800. // Used by HandleVertexWeights, sorts vertex weights by weight
  801. //-----------------------------------------------------------------------------
  802. static int VertexWeightLessFunc( const void *pLhs, const void *pRhs )
  803. {
  804. const CVertexWeight *pVertexWeightL = reinterpret_cast< const CVertexWeight * >( pLhs );
  805. const CVertexWeight *pVertexWeightR = reinterpret_cast< const CVertexWeight * >( pRhs );
  806. if ( pVertexWeightL->m_nBoneIndex < 0 )
  807. {
  808. if ( pVertexWeightR->m_nBoneIndex < 0 )
  809. {
  810. return 0;
  811. }
  812. else
  813. {
  814. return 1;
  815. }
  816. }
  817. else if ( pVertexWeightR->m_nBoneIndex < 0 )
  818. {
  819. return -1;
  820. }
  821. if ( pVertexWeightL->m_flWeight > pVertexWeightR->m_flWeight )
  822. {
  823. return -1;
  824. }
  825. else if ( pVertexWeightL->m_flWeight < pVertexWeightR->m_flWeight )
  826. {
  827. return 1;
  828. }
  829. return 0;
  830. }
  831. //-----------------------------------------------------------------------------
  832. //
  833. //-----------------------------------------------------------------------------
  834. static void HandleVertexWeights(
  835. const CDmSmdSerializer::SmdJointMap_t &smdJointMap,
  836. int nFallbackBoneIndexId,
  837. CVertex &vertex,
  838. const char *pszLine,
  839. const char *pszFilename,
  840. int nLineNumber )
  841. {
  842. for ( int i = 0; i < ARRAYSIZE( vertex.m_vertexWeights ); ++i )
  843. {
  844. vertex.m_vertexWeights[i].Reset();
  845. }
  846. CUtlVector< CVertexWeight > tmpVertexWeights;
  847. CUtlVector< CUtlString > tokens;
  848. Tokenize( tokens, pszLine );
  849. const float flEps = 1.0e-6;
  850. uint nTokenEnd = tokens.Count();
  851. if ( nTokenEnd > 10 )
  852. {
  853. int nId = -1;
  854. float flWeight = 0.0f;
  855. for ( uint i = 10; i < nTokenEnd; ++i )
  856. {
  857. nId = strtol( tokens[ i ].Get(), NULL, 0 );
  858. ++i;
  859. flWeight = strtod( tokens[ i ].Get(), NULL );
  860. if ( nId < 0 || flWeight < flEps )
  861. continue;
  862. const int nActualId = GetActualId( smdJointMap, nId );
  863. if ( nActualId < 0 )
  864. {
  865. Error( "%s(%d) : triangle error : ignoring unknown joint id(%d) with actual id(%d) for vertex weight\n",
  866. pszFilename, nLineNumber, nId, nActualId );
  867. continue;
  868. }
  869. CVertexWeight &tmpVertexWeight = tmpVertexWeights[ tmpVertexWeights.AddToTail() ];
  870. tmpVertexWeight.m_nBoneIndex = nActualId;
  871. tmpVertexWeight.m_flWeight = flWeight;
  872. }
  873. // Sort vertex weights inversely by weight
  874. qsort( tmpVertexWeights.Base(), tmpVertexWeights.Count(), sizeof( CVertexWeight ), VertexWeightLessFunc );
  875. // Take at most the top ARRAYSIZE( vertex.m_vertexWeights ) from tmpVertexWeights
  876. // Figure out actual weight count and total weight
  877. float flTotalWeight = 0.0f;
  878. vertex.m_nWeights = 0;
  879. for ( int i = 0; i < tmpVertexWeights.Count() && i < ARRAYSIZE( vertex.m_vertexWeights ); ++i )
  880. {
  881. CVertexWeight &vertexWeight( vertex.m_vertexWeights[ i ] );
  882. vertexWeight = tmpVertexWeights[i];
  883. flTotalWeight += vertexWeight.m_flWeight;
  884. vertex.m_nWeights += 1;
  885. }
  886. // If there are valid user specified weights then renormalize
  887. if ( vertex.m_nWeights > 0 && flTotalWeight > flEps )
  888. {
  889. for ( uint i = 0; i != ARRAYSIZE( vertex.m_vertexWeights ); ++i )
  890. {
  891. CVertexWeight &vertexWeight( vertex.m_vertexWeights[ i ] );
  892. if ( vertexWeight.m_nBoneIndex >= 0 )
  893. {
  894. vertexWeight.m_flWeight /= flTotalWeight;
  895. }
  896. }
  897. }
  898. else
  899. {
  900. // No valid user weights, assign to fallback bone with weight of 1
  901. vertex.m_vertexWeights[ 0 ].m_nBoneIndex = GetActualId( smdJointMap, nFallbackBoneIndexId );
  902. if ( vertex.m_vertexWeights[0].m_nBoneIndex < 0 )
  903. {
  904. // Vertex is not weighted, can't find fallback bone, this is invalid
  905. vertex.m_nWeights = 0;
  906. vertex.m_vertexWeights[0].m_nBoneIndex = -1;
  907. vertex.m_vertexWeights[0].m_flWeight = 0.0f;
  908. }
  909. else
  910. {
  911. vertex.m_nWeights = 1;
  912. vertex.m_vertexWeights[0].m_flWeight = 1.0f;
  913. }
  914. // Assign rest of weights to -1, 0.0f
  915. for ( uint i = 1; i < ARRAYSIZE( vertex.m_vertexWeights ); ++i )
  916. {
  917. vertex.m_vertexWeights[i].m_nBoneIndex = -1;
  918. vertex.m_vertexWeights[i].m_flWeight = 0.0f;
  919. }
  920. }
  921. }
  922. }
  923. //-----------------------------------------------------------------------------
  924. //
  925. //-----------------------------------------------------------------------------
  926. static CDmeMesh *CreateDmeMesh(
  927. CDmeModel *pDmeModel,
  928. const char *pszFilename,
  929. int nLineNumber )
  930. {
  931. char szFileBase[MAX_PATH];
  932. szFileBase[0] = '\0';
  933. const char *pszMeshName = "mesh";
  934. if ( pszFilename )
  935. {
  936. V_FileBase( pszFilename, szFileBase, ARRAYSIZE( szFileBase ) );
  937. if ( V_strlen( szFileBase ) > 0 )
  938. {
  939. pszMeshName = szFileBase;
  940. }
  941. }
  942. CDmeDag *pDmeDag = CreateElement< CDmeDag >( pszMeshName, pDmeModel->GetFileId() );
  943. if ( !pDmeDag )
  944. return NULL;
  945. pDmeModel->AddChild( pDmeDag );
  946. CUtlString sMeshName = pDmeDag->GetName();
  947. sMeshName += "Shape";
  948. CDmeMesh *pDmeMesh = CreateElement< CDmeMesh >( sMeshName.Get(), pDmeDag->GetFileId() );
  949. if ( !pDmeMesh )
  950. return NULL;
  951. CDmeVertexData *pDmeVertexData = pDmeMesh->FindOrCreateBaseState( "bind" );
  952. pDmeMesh->SetCurrentBaseState( "bind" );
  953. pDmeVertexData->FlipVCoordinate( true );
  954. pDmeVertexData->CreateField( CDmeVertexData::FIELD_POSITION );
  955. pDmeVertexData->CreateField( CDmeVertexData::FIELD_NORMAL );
  956. pDmeVertexData->CreateField( CDmeVertexData::FIELD_TEXCOORD );
  957. FieldIndex_t nJointWeightField;
  958. FieldIndex_t nJointIndexField;
  959. pDmeVertexData->CreateJointWeightsAndIndices( MAX_WEIGHTS_PER_VERTEX, &nJointWeightField, &nJointIndexField );
  960. pDmeDag->SetShape( pDmeMesh );
  961. pDmeVertexData->Resolve();
  962. pDmeMesh->Resolve();
  963. pDmeDag->Resolve();
  964. return pDmeMesh;
  965. }
  966. //-----------------------------------------------------------------------------
  967. //
  968. //-----------------------------------------------------------------------------
  969. static CDmeFaceSet *FindOrCreateFaceSet(
  970. CDmeMesh *pDmeMesh,
  971. const CUtlString &sMaterial,
  972. const char *pszFilename,
  973. int nLineNumber )
  974. {
  975. // Could cache in a hashed map or something...
  976. for ( int i = 0; i < pDmeMesh->FaceSetCount(); ++i )
  977. {
  978. CDmeFaceSet *pDmeFaceSet = pDmeMesh->GetFaceSet( i );
  979. if ( !pDmeFaceSet )
  980. continue;
  981. CDmeMaterial *pDmeMaterial = pDmeFaceSet->GetMaterial();
  982. if ( !pDmeMaterial )
  983. continue;
  984. if ( !V_strcmp( pDmeMaterial->GetMaterialName(), sMaterial.Get() ) )
  985. return pDmeFaceSet;
  986. }
  987. char szFaceSetName[ MAX_PATH ];
  988. V_FileBase( sMaterial.Get(), szFaceSetName, ARRAYSIZE( szFaceSetName ) );
  989. CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( szFaceSetName, pDmeMesh->GetFileId() );
  990. Assert( pDmeFaceSet );
  991. CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( szFaceSetName, pDmeMesh->GetFileId() );
  992. Assert( pDmeMaterial );
  993. pDmeMaterial->SetMaterial( sMaterial.Get() );
  994. pDmeFaceSet->SetMaterial( pDmeMaterial );
  995. pDmeMesh->AddFaceSet( pDmeFaceSet );
  996. return pDmeFaceSet;
  997. }
  998. //-----------------------------------------------------------------------------
  999. //
  1000. //-----------------------------------------------------------------------------
  1001. static void CreatePolygon(
  1002. CDmeMesh *pDmeMesh,
  1003. const CUtlString &sMaterial,
  1004. CTriangle &triangle,
  1005. const char *pszFilename,
  1006. int nLineNumber )
  1007. {
  1008. if ( !triangle.Valid() || !pDmeMesh )
  1009. return;
  1010. CDmeVertexData *pDmeVertexData = pDmeMesh->GetBindBaseState();
  1011. Assert( pDmeVertexData );
  1012. FieldIndex_t nPositionField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
  1013. CDmrArray< Vector > positionData = pDmeVertexData->GetVertexData( nPositionField );
  1014. FieldIndex_t nNormalField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
  1015. CDmrArray< Vector > normalData = pDmeVertexData->GetVertexData( nNormalField );
  1016. FieldIndex_t nUvField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
  1017. CDmrArray< Vector2D > uvData = pDmeVertexData->GetVertexData( nUvField );
  1018. FieldIndex_t nJointWeightField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS );
  1019. CDmrArray< float > jointWeightData = pDmeVertexData->GetVertexData( nJointWeightField );
  1020. FieldIndex_t nJointIndexField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES );
  1021. CDmrArray< int > jointIndexData = pDmeVertexData->GetVertexData( nJointIndexField );
  1022. CDmeFaceSet *pDmeFaceSet = FindOrCreateFaceSet( pDmeMesh, sMaterial, pszFilename, nLineNumber );
  1023. if ( !pDmeFaceSet )
  1024. {
  1025. Error( "%s(%d) : mesh error : couldn't find or create DmeFace set for material \"%s\" on mesh \"%s\"\n",
  1026. pszFilename, nLineNumber, sMaterial.Get(), pDmeMesh->GetName() );
  1027. return;
  1028. }
  1029. for ( uint i = 0; i < 3; ++i )
  1030. {
  1031. const int nNewVertexIndex = pDmeVertexData->AddVertexIndices( 1 );
  1032. CVertex &cVertex = triangle.m_vertices[i];
  1033. {
  1034. // TODO: Make two positions if they are skinned differently
  1035. int nPositionIndex = positionData.Find( cVertex.m_vPosition );
  1036. if ( !positionData.IsValidIndex( nPositionIndex ) )
  1037. {
  1038. nPositionIndex = positionData.AddToTail( cVertex.m_vPosition );
  1039. for ( int j = 0; j < ARRAYSIZE( cVertex.m_vertexWeights ); ++j )
  1040. {
  1041. jointWeightData.AddToTail( cVertex.m_vertexWeights[j].m_flWeight );
  1042. jointIndexData.AddToTail( cVertex.m_vertexWeights[j].m_nBoneIndex );
  1043. }
  1044. }
  1045. pDmeVertexData->SetVertexIndices( nPositionField, nNewVertexIndex, 1, &nPositionIndex );
  1046. const int nFaceSetIndex = pDmeFaceSet->AddIndices( 1 );
  1047. pDmeFaceSet->SetIndices( nFaceSetIndex, 1, const_cast< int * >( &nNewVertexIndex ) );
  1048. }
  1049. {
  1050. int nNormalIndex = normalData.Find( cVertex.m_vNormal );
  1051. if ( !normalData.IsValidIndex( nNormalIndex ) )
  1052. {
  1053. nNormalIndex = normalData.AddToTail( cVertex.m_vNormal );
  1054. }
  1055. pDmeVertexData->SetVertexIndices( nNormalField, nNewVertexIndex, 1, &nNormalIndex );
  1056. }
  1057. {
  1058. int nUvIndex = uvData.Find( cVertex.m_vUV );
  1059. if ( !uvData.IsValidIndex( nUvIndex ) )
  1060. {
  1061. nUvIndex = uvData.AddToTail( cVertex.m_vUV );
  1062. }
  1063. pDmeVertexData->SetVertexIndices( nUvField, nNewVertexIndex, 1, &nUvIndex );
  1064. }
  1065. }
  1066. static const int nFaceDelimiter = -1;
  1067. const int nFaceSetIndex = pDmeFaceSet->AddIndices( 1 );
  1068. pDmeFaceSet->SetIndices( nFaceSetIndex, 1, const_cast< int * >( &nFaceDelimiter ) );
  1069. triangle.Reset();
  1070. }
  1071. //-----------------------------------------------------------------------------
  1072. //
  1073. //-----------------------------------------------------------------------------
  1074. static CDmeFaceSet *FindOrAddFaceSet(
  1075. CDmeMesh *pDmeMesh,
  1076. CUtlMap< CUtlString, CDmeFaceSet * > &faceListMap,
  1077. const char *pszMaterialPath )
  1078. {
  1079. CUtlMap< CUtlString, CDmeFaceSet * >::IndexType_t nIndex = faceListMap.Find( CUtlString( pszMaterialPath ) );
  1080. if ( faceListMap.IsValidIndex( nIndex ) )
  1081. return faceListMap.Element( nIndex );
  1082. CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( pszMaterialPath, pDmeMesh->GetFileId() );
  1083. CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( pszMaterialPath, pDmeMesh->GetFileId() );
  1084. Assert( pDmeFaceSet && pDmeMaterial );
  1085. pDmeMaterial->SetMaterial( pszMaterialPath );
  1086. pDmeMesh->AddFaceSet( pDmeFaceSet );
  1087. return faceListMap.Element( faceListMap.Insert( CUtlString( pszMaterialPath ), pDmeFaceSet ) );
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. //
  1091. //-----------------------------------------------------------------------------
  1092. CDmeChannelsClip *FindOrCreateChannelsClip( CDmElement *pDmeRoot, const char *pszAnimationName )
  1093. {
  1094. CDmeAnimationList *pDmeAnimationList = pDmeRoot->GetValueElement< CDmeAnimationList >( "animationList" );
  1095. CDmeChannelsClip *pDmeChannelsClip = NULL;
  1096. if ( pDmeAnimationList )
  1097. {
  1098. pDmeChannelsClip = pDmeAnimationList->GetAnimation( 0 );
  1099. }
  1100. else
  1101. {
  1102. CDmeModel *pDmeModel = pDmeRoot->GetValueElement< CDmeModel >( "skeleton" );
  1103. pDmeAnimationList = CreateElement< CDmeAnimationList >( pszAnimationName, pDmeRoot->GetFileId() );
  1104. pDmeChannelsClip = CreateElement< CDmeChannelsClip >( pszAnimationName, pDmeRoot->GetFileId() );
  1105. pDmeAnimationList->AddAnimation( pDmeChannelsClip );
  1106. pDmeRoot->SetValue( "animationList", pDmeAnimationList );
  1107. pDmeModel->SetValue( "animationList", pDmeAnimationList );
  1108. }
  1109. return pDmeChannelsClip;
  1110. }
  1111. //-----------------------------------------------------------------------------
  1112. // Common function both ReadSMD & Unserialize can call
  1113. //-----------------------------------------------------------------------------
  1114. CDmElement *CDmSmdSerializer::ReadSMD(
  1115. CUtlBuffer &utlBuf,
  1116. DmFileId_t nDmFileId,
  1117. const char *pszFilename,
  1118. CDmeMesh **ppDmeMeshCreated )
  1119. {
  1120. CDmElement *pDmeRoot = CreateElement< CDmElement >( "root", nDmFileId );
  1121. CDmeModel *pDmeModel = CreateElement< CDmeModel >( "model", nDmFileId );
  1122. char szAnimationName[ MAX_PATH ] = "";
  1123. if ( pszFilename )
  1124. {
  1125. V_FileBase( pszFilename, szAnimationName, ARRAYSIZE( szAnimationName ) );
  1126. }
  1127. else
  1128. {
  1129. pszFilename = "unknown";
  1130. }
  1131. if ( V_strlen( szAnimationName ) > 0 )
  1132. {
  1133. pDmeModel->SetName( szAnimationName );
  1134. }
  1135. else
  1136. {
  1137. V_strcpy_safe( szAnimationName, "anim" );
  1138. }
  1139. pDmeRoot->SetValue( "skeleton", pDmeModel );
  1140. if ( !m_bOptAnimation )
  1141. {
  1142. // Don't set root.model if animation only, studiomdl can't handle it
  1143. pDmeRoot->SetValue( "model", pDmeModel );
  1144. }
  1145. SmdJointMap_t smdJointMap( CDefOps< int >::LessFunc );
  1146. CUtlString sMaterial;
  1147. CTriangle triangle;
  1148. uint nLineNumber = 0;
  1149. uint nBadPreambleCount = 0;
  1150. ParserState_t nParserState = kPreamble;
  1151. int nFrame = 0;
  1152. CDmeChannelsClip *pDmeChannelsClip = NULL;
  1153. if ( m_bOptAnimation )
  1154. {
  1155. pDmeChannelsClip = FindOrCreateChannelsClip( pDmeRoot, szAnimationName );
  1156. }
  1157. bool bStartTimeSet = false;
  1158. CQcData qcData;
  1159. CDmeMesh *pDmeMesh = NULL;
  1160. char szLine[ 4096 ];
  1161. while ( utlBuf.IsValid() )
  1162. {
  1163. GetLine( utlBuf, szLine, ARRAYSIZE( szLine ) );
  1164. ++nLineNumber;
  1165. const char *pszLine = ParserSkipSpace( szLine );
  1166. if ( ParserIsBlankLine( pszLine ) )
  1167. continue;
  1168. if ( nParserState == kPreamble )
  1169. {
  1170. if ( HandleQcHints( pszLine, qcData ) )
  1171. continue;
  1172. if ( ParserHandleVersion( pszLine ) )
  1173. {
  1174. if ( qcData.m_nUpAxis != m_nUpAxis )
  1175. {
  1176. Warning( "Importer UpAxis (%d) is different from UpAxis in SMD data (%d), using value found in SMD/QC\n", m_nUpAxis, qcData.m_nUpAxis );
  1177. }
  1178. SetUpAxis( qcData.m_nUpAxis );
  1179. nParserState = kGeneral;
  1180. continue;
  1181. }
  1182. Error( "%s(%d) : preamble error : expecting comment: \"%s\"\n",
  1183. pszFilename, nLineNumber, szLine );
  1184. ++nBadPreambleCount;
  1185. if ( nBadPreambleCount > 10 )
  1186. {
  1187. Error( "%s(%d) : preamble error : too many errors, not an SMD file, aborting\n",
  1188. pszFilename, nLineNumber );
  1189. break;
  1190. }
  1191. }
  1192. else if ( nParserState == kGeneral )
  1193. {
  1194. if ( ParserHandleSectionStart( pszLine, "nodes" ) )
  1195. {
  1196. nParserState = kNodes;
  1197. }
  1198. else if ( ParserHandleSectionStart( pszLine, "skeleton" ) )
  1199. {
  1200. nParserState = kSkeleton;
  1201. }
  1202. else if ( ParserHandleSectionStart( pszLine, "triangles" ) )
  1203. {
  1204. nParserState = kTriangles;
  1205. }
  1206. }
  1207. else if ( nParserState == kNodes )
  1208. {
  1209. if ( ParserHandleSectionStart( pszLine, "end" ) )
  1210. {
  1211. ParserCreateJoints( smdJointMap, pDmeModel, pDmeChannelsClip, pszFilename );
  1212. nParserState = kGeneral;
  1213. continue;
  1214. }
  1215. CUtlString sName;
  1216. int nId;
  1217. int nParentId;
  1218. if ( !m_bOptImportSkeleton || !ParserHandleSkeletonLine( pszLine, sName, nId, nParentId ) )
  1219. continue;
  1220. if ( !ParserAddJoint( smdJointMap, nId, sName, nParentId, pszFilename, nLineNumber ) )
  1221. continue;
  1222. }
  1223. else if ( nParserState == kSkeleton )
  1224. {
  1225. if ( ParserHandleSectionStart( pszLine, "end" ) )
  1226. {
  1227. nParserState = kGeneral;
  1228. continue;
  1229. }
  1230. if ( m_bOptAnimation && ParserHandleTime( pszLine, nFrame ) )
  1231. {
  1232. if ( !bStartTimeSet )
  1233. {
  1234. pDmeChannelsClip->SetStartTime( DmeTime_t( static_cast< float >( nFrame ), DmeFramerate_t( m_flFrameRate ) ) );
  1235. bStartTimeSet = true;
  1236. }
  1237. continue;
  1238. }
  1239. int nId;
  1240. Vector vPosition;
  1241. RadianEuler eRadianEulerXYZ;
  1242. if ( sscanf( pszLine, "%d %f %f %f %f %f %f", &nId, &vPosition.x, &vPosition.y, &vPosition.z, &eRadianEulerXYZ.x, &eRadianEulerXYZ.y, &eRadianEulerXYZ.z ) == 7 )
  1243. {
  1244. if ( !m_bOptImportSkeleton )
  1245. continue;
  1246. ParserSetJoint( smdJointMap, nFrame, nId, vPosition, eRadianEulerXYZ, pszFilename, nLineNumber );
  1247. }
  1248. }
  1249. else if ( nParserState == kTriangles )
  1250. {
  1251. if ( ParserHandleSectionStart( pszLine, "end" ) )
  1252. {
  1253. triangle.Reset();
  1254. nParserState = kGeneral;
  1255. continue;
  1256. }
  1257. int nFallbackSkinJoint;
  1258. Vector vPosition;
  1259. Vector vNormal;
  1260. Vector2D vUV;
  1261. char szShadingGroup[ ARRAYSIZE( szLine ) ];
  1262. if ( sscanf( pszLine, "%d %f %f %f %f %f %f %f %f",
  1263. &nFallbackSkinJoint,
  1264. &vPosition.x, &vPosition.y, &vPosition.z,
  1265. &vNormal.x, &vNormal.y, &vNormal.z, &vUV.x, &vUV.y ) == 9 )
  1266. {
  1267. if ( triangle.m_nVertices >= 3 )
  1268. {
  1269. Error( "%s(%d) : triangle error : Too many vertices in triangle\n",
  1270. pszFilename, nLineNumber );
  1271. continue;
  1272. }
  1273. CVertex &vertex( triangle.m_vertices[ triangle.m_nVertices++ ] );
  1274. vertex.m_vUV = vUV;
  1275. if ( GetUpAxis() != CDmSmdSerializer::Y_AXIS )
  1276. {
  1277. VectorTransform( vPosition, m_mAdj, vertex.m_vPosition );
  1278. VectorTransform( vNormal, m_mAdjNormal, vertex.m_vNormal );
  1279. }
  1280. else
  1281. {
  1282. vertex.m_vPosition = vPosition;
  1283. vertex.m_vNormal = vNormal;
  1284. }
  1285. if ( m_bOptImportSkeleton )
  1286. {
  1287. HandleVertexWeights( smdJointMap, nFallbackSkinJoint, vertex, pszLine, pszFilename, nLineNumber );
  1288. }
  1289. if ( !pDmeMesh )
  1290. {
  1291. pDmeMesh = CreateDmeMesh( pDmeModel, pszFilename, nLineNumber );
  1292. if ( !pDmeMesh )
  1293. {
  1294. Error( "%s(%d) : Couldn't create DmeMesh\n",
  1295. pszFilename, nLineNumber );
  1296. break;
  1297. }
  1298. }
  1299. CreatePolygon( pDmeMesh, sMaterial, triangle, pszFilename, nLineNumber );
  1300. }
  1301. else if ( sscanf( pszLine, "%1024s", szShadingGroup ) == 1 )
  1302. {
  1303. char pszMaterialPath[ ARRAYSIZE( szShadingGroup ) ];
  1304. V_StripExtension( szShadingGroup, pszMaterialPath, ARRAYSIZE( pszMaterialPath ) );
  1305. sMaterial = pszMaterialPath;
  1306. triangle.Reset();
  1307. }
  1308. else
  1309. {
  1310. Error( "%s(%d) : triangle error : unexpected data in triangles\n",
  1311. pszFilename, nLineNumber );
  1312. triangle.Reset();
  1313. }
  1314. }
  1315. else
  1316. {
  1317. Error( "%s(%d) : parser error : unknown parser state\n",
  1318. pszFilename, nLineNumber );
  1319. nParserState = kGeneral;
  1320. }
  1321. }
  1322. pDmeModel->CaptureJointsToBaseState( "bind" );
  1323. if ( pDmeChannelsClip )
  1324. {
  1325. // Reset skeleton values to the first sample
  1326. FOR_EACH_MAP_FAST( smdJointMap, nJointMapIndex )
  1327. {
  1328. CDmeDag *pDmeDag = smdJointMap.Element( nJointMapIndex ).m_pDmeDag;
  1329. if ( pDmeDag )
  1330. {
  1331. CDmeTransform *pDmeTransform = pDmeDag->GetTransform();
  1332. CDmeVector3Log *pDmePosLog = pDmeDag->GetValueElement< CDmeVector3Log >( "__posLog" );
  1333. pDmeTransform->SetPosition( pDmePosLog->GetKeyValue( 0 ) );
  1334. CDmeQuaternionLog *pDmeRotLog = pDmeDag->GetValueElement< CDmeQuaternionLog >( "__rotLog" );
  1335. pDmeTransform->SetOrientation( pDmeRotLog->GetKeyValue( 0 ) );
  1336. }
  1337. }
  1338. pDmeChannelsClip->SetDuration( DmeTime_t( nFrame, DmeFramerate_t( m_flFrameRate ) ) - pDmeChannelsClip->GetStartTime() );
  1339. }
  1340. return pDmeRoot;
  1341. }
  1342. //-----------------------------------------------------------------------------
  1343. //
  1344. //-----------------------------------------------------------------------------
  1345. void CDmSmdSerializer::FixNodeName( CUtlString &sName ) const
  1346. {
  1347. char szTmpBuf0[ MAX_PATH ];
  1348. char szTmpBuf1[ MAX_PATH ];
  1349. V_strncpy( szTmpBuf0, sName.Get(), ARRAYSIZE( szTmpBuf0 ) );
  1350. // Strip trailing quotes
  1351. char *pszName = szTmpBuf0 + sName.Length() - 1;
  1352. while ( pszName >= szTmpBuf0 && *pszName && *pszName == '"' )
  1353. {
  1354. *pszName = '\0';
  1355. --pszName;
  1356. }
  1357. // Strip leading quotes
  1358. pszName = szTmpBuf0;
  1359. while ( *pszName && *pszName == '"')
  1360. {
  1361. ++pszName;
  1362. }
  1363. if ( m_bOptAutoStripPrefix )
  1364. {
  1365. // Get the string before the '.'
  1366. V_FileBase( pszName, szTmpBuf1, ARRAYSIZE( szTmpBuf1 ) );
  1367. V_strncpy( szTmpBuf0, szTmpBuf1, ARRAYSIZE( szTmpBuf0 ) );
  1368. pszName = szTmpBuf0;
  1369. }
  1370. if ( !m_sNodeDelPrefix.IsEmpty() && StringHasPrefix( pszName, m_sNodeDelPrefix.Get() ) )
  1371. {
  1372. pszName = const_cast< char * >( StringAfterPrefix( pszName, m_sNodeDelPrefix.Get() ) );
  1373. }
  1374. if ( m_sNodeAddPrefix.IsEmpty() )
  1375. {
  1376. sName = pszName;
  1377. }
  1378. else
  1379. {
  1380. sName = m_sNodeAddPrefix;
  1381. sName += pszName;
  1382. }
  1383. }