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.

1463 lines
39 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Serialize and Unserialize Wavefront OBJ <-> DME Data
  4. //
  5. //=============================================================================
  6. // Valve includes
  7. #include "tier1/characterset.h"
  8. #include "movieobjects/dmedag.h"
  9. #include "movieobjects/dmemesh.h"
  10. #include "movieobjects/dmefaceset.h"
  11. #include "movieobjects/dmematerial.h"
  12. #include "movieobjects/dmobjserializer.h"
  13. #include "movieobjects/dmecombinationoperator.h"
  14. #include "movieobjects/dmemodel.h"
  15. #include "filesystem.h"
  16. #include "tier2/tier2.h"
  17. #include "tier1/UtlStringMap.h"
  18. #include "mathlib/mathlib.h"
  19. //-----------------------------------------------------------------------------
  20. //
  21. //-----------------------------------------------------------------------------
  22. class CFaceSetData
  23. {
  24. public:
  25. void Clear();
  26. inline CUtlVector< int > *GetFaceSetIndices( const char *pFaceSetName )
  27. {
  28. return &m_faceSetIndices[ pFaceSetName ];
  29. }
  30. void AddToMesh( CDmeMesh *pMesh );
  31. protected:
  32. CUtlStringMap< CUtlVector< int > > m_faceSetIndices;
  33. };
  34. //-----------------------------------------------------------------------------
  35. //
  36. //-----------------------------------------------------------------------------
  37. void CFaceSetData::Clear()
  38. {
  39. m_faceSetIndices.Clear();
  40. }
  41. //-----------------------------------------------------------------------------
  42. //
  43. //-----------------------------------------------------------------------------
  44. void CFaceSetData::AddToMesh( CDmeMesh *pMesh )
  45. {
  46. const int nFaceSets( m_faceSetIndices.GetNumStrings() );
  47. for ( int i( 0 ); i < nFaceSets; ++i )
  48. {
  49. const char *pName( m_faceSetIndices.String( i ) );
  50. CUtlVector< int > &faceSetIndices( m_faceSetIndices[ pName ] );
  51. if ( faceSetIndices.Count() )
  52. {
  53. CDmeFaceSet *pFaceSet = CreateElement< CDmeFaceSet >( pName, pMesh->GetFileId() );
  54. CDmeMaterial *pMaterial = CreateElement< CDmeMaterial >( pName, pMesh->GetFileId() );
  55. pMaterial->SetMaterial( pName );
  56. pFaceSet->AddIndices( faceSetIndices.Count() );
  57. pFaceSet->SetIndices( 0, faceSetIndices.Count(), faceSetIndices.Base() );
  58. pFaceSet->SetMaterial( pMaterial );
  59. pMesh->AddFaceSet( pFaceSet );
  60. }
  61. }
  62. Clear();
  63. }
  64. //-----------------------------------------------------------------------------
  65. //
  66. //-----------------------------------------------------------------------------
  67. class CVertexData
  68. {
  69. public:
  70. void Clear();
  71. inline void AddPosition( const Vector &p ) { m_positions.AddToTail( p ); }
  72. inline void AddPositionIndex( int i ) { m_pIndices.AddToTail( i ); }
  73. inline void AddNormal( const Vector &n ) { m_normals.AddToTail( n ); }
  74. inline void AddNormalIndex( int i ) { m_nIndices.AddToTail( i ); }
  75. inline void AddUV( const Vector2D &uv ) { AddUniqueValue( uv, m_uvs, m_uvIndexMap, FLT_EPSILON * 0.1f ); }
  76. inline void AddUVIndex( int i ) { Assert( i < m_uvIndexMap.Count() ); m_uvIndices.AddToTail( m_uvIndexMap[ i ] ); }
  77. inline int VertexCount() const { return m_pIndices.Count(); }
  78. CDmeVertexDataBase *AddToMesh( CDmeMesh *pMesh, bool bAbsolute, const char *pName, bool bDelta );
  79. protected:
  80. template < class T_t > void AddUniqueValue(
  81. const T_t &v,
  82. CUtlVector< T_t > &vs,
  83. CUtlVector< int > &map,
  84. float flThresh = FLT_EPSILON )
  85. {
  86. const int nVs( vs.Count() );
  87. for ( int i( 0 ); i < nVs; ++i )
  88. {
  89. if ( v.DistToSqr( vs[ i ] ) < flThresh )
  90. {
  91. map.AddToTail( i );
  92. return;
  93. }
  94. }
  95. map.AddToTail( vs.Count() );
  96. vs.AddToTail( v );
  97. }
  98. CDmeVertexDataBase *Add( CDmeMesh *pMesh, const char *pName = "bind" );
  99. CDmeVertexDeltaData *AddDelta( CDmeMesh *pMesh, bool bAbsolute, const char *pName = "bind" );
  100. CUtlVector< Vector > m_positions;
  101. CUtlVector< int > m_pIndices;
  102. CUtlVector< Vector > m_normals;
  103. CUtlVector< int > m_nIndices;
  104. CUtlVector< Vector2D > m_uvs;
  105. CUtlVector< int > m_uvIndexMap;
  106. CUtlVector< int > m_uvIndices;
  107. };
  108. //-----------------------------------------------------------------------------
  109. //
  110. //-----------------------------------------------------------------------------
  111. void CVertexData::Clear()
  112. {
  113. m_positions.RemoveAll();
  114. m_pIndices.RemoveAll();
  115. m_normals.RemoveAll();
  116. m_nIndices.RemoveAll();
  117. m_uvs.RemoveAll();
  118. m_uvIndexMap.RemoveAll();
  119. m_uvIndices.RemoveAll();
  120. }
  121. //-----------------------------------------------------------------------------
  122. //
  123. //-----------------------------------------------------------------------------
  124. CDmeVertexDataBase *CVertexData::AddToMesh( CDmeMesh *pMesh, bool bAbsolute, const char *pName, bool delta )
  125. {
  126. if ( delta )
  127. return AddDelta( pMesh, bAbsolute, pName );
  128. return Add( pMesh, pName );
  129. }
  130. //-----------------------------------------------------------------------------
  131. //
  132. //-----------------------------------------------------------------------------
  133. CDmeVertexDataBase *CVertexData::Add( CDmeMesh *pMesh, const char *pName )
  134. {
  135. CDmeVertexDataBase *pVertexData( NULL );
  136. if ( m_positions.Count() && m_pIndices.Count() )
  137. {
  138. {
  139. CDmeVertexData *pBaseVertexData = pMesh->FindOrCreateBaseState( pName );
  140. pMesh->SetCurrentBaseState( pName );
  141. pBaseVertexData->AddVertexIndices( m_pIndices.Count() );
  142. pVertexData = pBaseVertexData;
  143. }
  144. pVertexData->FlipVCoordinate( true );
  145. const FieldIndex_t pIndex( pVertexData->CreateField( CDmeVertexData::FIELD_POSITION ) );
  146. pVertexData->AddVertexData( pIndex, m_positions.Count() );
  147. pVertexData->SetVertexData( pIndex, 0, m_positions.Count(), AT_VECTOR3, m_positions.Base() );
  148. pVertexData->SetVertexIndices( pIndex, 0, m_pIndices.Count(), m_pIndices.Base() );
  149. if ( pVertexData && m_normals.Count() && m_nIndices.Count() )
  150. {
  151. Assert( m_pIndices.Count() == m_nIndices.Count() );
  152. const FieldIndex_t nIndex( pVertexData->CreateField( CDmeVertexData::FIELD_NORMAL ) );
  153. pVertexData->AddVertexData( nIndex, m_normals.Count() );
  154. pVertexData->SetVertexData( nIndex, 0, m_normals.Count(), AT_VECTOR3, m_normals.Base() );
  155. pVertexData->SetVertexIndices( nIndex, 0, m_nIndices.Count(), m_nIndices.Base() );
  156. }
  157. if ( pVertexData && m_uvs.Count() && m_uvIndices.Count() )
  158. {
  159. Assert( m_pIndices.Count() == m_uvIndices.Count() );
  160. const FieldIndex_t uvIndex( pVertexData->CreateField( CDmeVertexData::FIELD_TEXCOORD ) );
  161. pVertexData->AddVertexData( uvIndex, m_uvs.Count() );
  162. pVertexData->SetVertexData( uvIndex, 0, m_uvs.Count(), AT_VECTOR2, m_uvs.Base() );
  163. pVertexData->SetVertexIndices( uvIndex, 0, m_uvIndices.Count(), m_uvIndices.Base() );
  164. }
  165. }
  166. return pVertexData;
  167. }
  168. //-----------------------------------------------------------------------------
  169. //
  170. //-----------------------------------------------------------------------------
  171. CDmeVertexDeltaData *CVertexData::AddDelta( CDmeMesh *pMesh, bool bAbsolute, const char *pName )
  172. {
  173. CDmeVertexDeltaData *pDelta( NULL );
  174. if ( m_positions.Count() )
  175. {
  176. CDmeVertexData *pBind = pMesh->FindBaseState( "bind" );
  177. if ( pBind == NULL )
  178. return NULL;
  179. const FieldIndex_t pBindIndex( pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
  180. if ( pBindIndex < 0 )
  181. return NULL;
  182. CDmrArrayConst< Vector > pBindData( pBind->GetVertexData( pBindIndex ) );
  183. const int pCount( m_positions.Count() );
  184. if ( pBindData.Count() != pCount )
  185. return NULL;
  186. for ( int i( 0 ); i < pCount; ++i )
  187. {
  188. m_positions[ i ] -= pBindData[ i ];
  189. }
  190. int *pIndices = reinterpret_cast< int * >( alloca( pCount * sizeof( int ) ) );
  191. int nNonZero( 0 );
  192. for ( int i( 0 ); i < pCount; ++i )
  193. {
  194. const Vector &v( m_positions[ i ] );
  195. // Kind of a magic number but it's because of 16 bit compression of the delta values
  196. if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
  197. {
  198. m_positions[ nNonZero ] = v;
  199. pIndices[ nNonZero ] = i;
  200. ++nNonZero;
  201. }
  202. }
  203. pDelta = pMesh->FindOrCreateDeltaState( pName );
  204. pDelta->FlipVCoordinate( true );
  205. pDelta->SetValue( "corrected", !bAbsolute );
  206. const FieldIndex_t pIndex( pDelta->CreateField( CDmeVertexData::FIELD_POSITION ) );
  207. pDelta->AddVertexData( pIndex, nNonZero );
  208. pDelta->SetVertexData( pIndex, 0, nNonZero, AT_VECTOR3, m_positions.Base() );
  209. pDelta->SetVertexIndices( pIndex, 0, nNonZero, pIndices );
  210. const FieldIndex_t nBindNormalIndex = pBind->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
  211. if ( nBindNormalIndex >= 0 )
  212. {
  213. CDmrArrayConst< Vector > bindNormalData( pBind->GetVertexData( nBindNormalIndex ) );
  214. const int nNormalCount = m_normals.Count();
  215. if ( bindNormalData.Count() == nNormalCount )
  216. {
  217. for ( int i = 0; i < nNormalCount; ++i )
  218. {
  219. m_normals[ i ] -= bindNormalData[ i ];
  220. }
  221. int *pNormalIndices = reinterpret_cast< int * >( stackalloc( nNormalCount * sizeof( int ) ) );
  222. int nNormalDeltaCount = 0;
  223. for ( int i = 0; i < nNormalCount; ++i )
  224. {
  225. const Vector &n = m_normals[ i ];
  226. // Kind of a magic number but it's because of 16 bit compression of the delta values
  227. if ( fabs( n.x ) >= ( 1 / 4096.0f ) || fabs( n.y ) >= ( 1 / 4096.0f ) || fabs( n.z ) >= ( 1 / 4096.0f ) )
  228. {
  229. m_normals[ nNormalDeltaCount ] = n;
  230. pNormalIndices[ nNormalDeltaCount ] = i;
  231. ++nNormalDeltaCount;
  232. }
  233. }
  234. }
  235. }
  236. }
  237. return pDelta;
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Convert from DME -> OBJ
  241. //-----------------------------------------------------------------------------
  242. bool CDmObjSerializer::Serialize( CUtlBuffer &buf, CDmElement *pRoot )
  243. {
  244. return false; // For now
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Convert from OBJ -> DME
  248. //-----------------------------------------------------------------------------
  249. bool CDmObjSerializer::Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
  250. const char *pSourceFormatName, int nSourceFormatVersion,
  251. DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
  252. {
  253. *ppRoot = ReadOBJ( buf, fileid, "bind" );
  254. return *ppRoot != NULL;
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Convert from OBJ -> DME
  258. // If mesh is not NULL, the OBJ is added as a delta state to the mesh
  259. //-----------------------------------------------------------------------------
  260. CDmElement *CDmObjSerializer::ReadOBJ(
  261. const char *pFilename,
  262. CDmeMesh **ppCreatedMesh,
  263. bool bLoadAllDeltas /* = true */,
  264. bool bAbsolute /* = true */ )
  265. {
  266. char filename[ MAX_PATH ];
  267. Q_strncpy( filename, pFilename, sizeof( filename ) );
  268. Q_FixSlashes( filename );
  269. CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  270. if ( !g_pFullFileSystem->ReadFile( filename, NULL, utlBuf ) )
  271. return NULL;
  272. char baseFile[ MAX_PATH ];
  273. Q_FileBase( filename, baseFile, sizeof( baseFile ) );
  274. CDmeMesh *pMesh( NULL );
  275. DmFileId_t nFileId = g_pDataModel->FindOrCreateFileId( pFilename );
  276. CDmElement *pRoot = ReadOBJ( utlBuf, nFileId, baseFile, filename, NULL, &pMesh, bAbsolute );
  277. if ( pRoot && pMesh )
  278. {
  279. if ( ppCreatedMesh )
  280. {
  281. *ppCreatedMesh = pMesh;
  282. }
  283. CDmeCombinationOperator *pCombo( NULL );
  284. // Check if there are deltas in the directory with the same prefix
  285. // But only if the rest of the file is <prefix>=<suffix>.obj or is <prefix>_zero.obj
  286. char *pSuffix = Q_strrchr( baseFile, '=' );
  287. if ( !pSuffix || !*pSuffix )
  288. {
  289. pSuffix = Q_strrchr( baseFile, '_' );
  290. if ( !pSuffix || !*pSuffix )
  291. return pRoot;
  292. if ( Q_stricmp( pSuffix, "_zero" ) )
  293. return pRoot;
  294. }
  295. char findGlob[ MAX_PATH ];
  296. Q_strncpy( findGlob, baseFile, sizeof( findGlob ) );
  297. pSuffix = findGlob + ( pSuffix - baseFile );
  298. *( pSuffix + 0 ) = '_'; // Just in case it was <prefix>=<suffix>.obj
  299. *( pSuffix + 1 ) = '*';
  300. *( pSuffix + 2 ) = '.';
  301. Q_strncpy( pSuffix + 3, Q_GetFileExtension( filename ), sizeof( findGlob ) - ( pSuffix - findGlob + 3 ) );
  302. char path[ MAX_PATH ];
  303. Q_ExtractFilePath( filename, path, sizeof( path ) );
  304. m_objDirectory = path;
  305. char findPath[ MAX_PATH ];
  306. Q_ComposeFileName( path, findGlob, findPath, sizeof( findPath ) );
  307. FileFindHandle_t hFind;
  308. char deltaFile[ MAX_PATH ];
  309. char deltaPath[ MAX_PATH ];
  310. for ( const char *pFindFile( g_pFullFileSystem->FindFirst( findPath, &hFind ) ); pFindFile && *pFindFile; pFindFile = g_pFullFileSystem->FindNext( hFind ) )
  311. {
  312. Q_FileBase( pFindFile, deltaFile, sizeof( deltaFile ) );
  313. if ( Q_stricmp( baseFile, deltaFile ) )
  314. {
  315. Q_ComposeFileName( path, pFindFile, deltaPath, sizeof( deltaPath ) );
  316. if ( !g_pFullFileSystem->FileExists( deltaPath ) )
  317. continue;
  318. char *pControlName = strchr( deltaFile, '_' );
  319. if ( pControlName && *( pControlName + 1 ) )
  320. {
  321. ++pControlName;
  322. char *pDeltaName( pControlName );
  323. for ( char *pPlus( strchr( pDeltaName, '+' ) ); pPlus; pPlus = strchr( pPlus, '+' ) )
  324. {
  325. *pPlus = '_';
  326. }
  327. }
  328. if ( !strchr( pControlName, '_' ) )
  329. {
  330. if ( pCombo == NULL )
  331. {
  332. pCombo = CreateElement< CDmeCombinationOperator >( "combinationOperator", pRoot->GetFileId() );
  333. pRoot->SetValue( "combinationOperator", pCombo );
  334. }
  335. }
  336. DeltaInfo_t &deltaInfo = m_deltas[ pControlName ];
  337. deltaInfo.m_filename = pFindFile;
  338. deltaInfo.m_pMesh = pMesh;
  339. deltaInfo.m_pComboOp = pCombo;
  340. if ( bLoadAllDeltas )
  341. {
  342. GetDelta( pControlName, bAbsolute );
  343. }
  344. }
  345. }
  346. g_pFullFileSystem->FindClose( hFind );
  347. if ( pCombo )
  348. {
  349. pCombo->AddTarget( pMesh );
  350. pMesh->ComputeAllCorrectedPositionsFromActualPositions();
  351. }
  352. }
  353. return pRoot;
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Common function both ReadOBJ & Unserialize can call
  357. //-----------------------------------------------------------------------------
  358. CDmElement *CDmObjSerializer::ReadOBJ( CUtlBuffer &buf,
  359. DmFileId_t dmFileId,
  360. const char *pName,
  361. const char *pFilename /* = NULL */,
  362. CDmeMesh *pBaseMesh /* = NULL */,
  363. CDmeMesh **ppCreatedMesh /* = NULL */,
  364. bool bAbsolute /* = true */ )
  365. {
  366. CDmElement *pRoot( NULL );
  367. CDmeModel *pModel( NULL );
  368. if ( !pBaseMesh )
  369. {
  370. pRoot = CreateElement< CDmElement >( "root", dmFileId );
  371. pModel = CreateElement< CDmeModel >( "model", dmFileId );
  372. pRoot->SetValue( "skeleton", pModel );
  373. pRoot->SetValue( "model", pModel );
  374. }
  375. m_mtlLib.RemoveAll();
  376. char tmpBuf0[ 4096 ];
  377. char tmpBuf1[ 4096 ];
  378. characterset_t breakSet;
  379. CharacterSetBuild( &breakSet, "/\\" );
  380. const char *pBuf;
  381. Vector p;
  382. Vector2D uv;
  383. CVertexData vertexData;
  384. CFaceSetData faceSetData;
  385. CUtlString groupName;
  386. CDmeDag *pDmeDag( NULL );
  387. CDmeMesh *pDmeMesh( NULL );
  388. CUtlVector< int > *pFaceIndices( NULL );
  389. while ( buf.IsValid() )
  390. {
  391. buf.GetLine( tmpBuf0, sizeof( tmpBuf0 ) );
  392. pBuf = SkipSpace( tmpBuf0 );
  393. if ( sscanf( tmpBuf0, "v %f %f %f", &p.x, &p.y, &p.z ) == 3 )
  394. {
  395. if ( pDmeDag )
  396. {
  397. vertexData.AddToMesh( pDmeMesh, bAbsolute, "bind", false );
  398. faceSetData.AddToMesh( pDmeMesh );
  399. pDmeDag = NULL;
  400. pDmeMesh = NULL;
  401. pFaceIndices = NULL;
  402. }
  403. vertexData.AddPosition( p );
  404. continue;
  405. }
  406. if ( sscanf( pBuf, "vn %f %f %f", &p.x, &p.y, &p.z ) == 3 )
  407. {
  408. vertexData.AddNormal( p );
  409. continue;
  410. }
  411. if ( !pBaseMesh )
  412. {
  413. if ( sscanf( pBuf, "vt %f %f", &uv.x, &uv.y ) == 2 )
  414. {
  415. vertexData.AddUV( uv );
  416. continue;
  417. }
  418. if ( pFilename && sscanf( pBuf, "mtllib %4096s", tmpBuf1 ) == 1 )
  419. {
  420. CUtlString mtlLib( tmpBuf1 );
  421. Q_strncpy( tmpBuf0, pFilename, sizeof( tmpBuf0 ) );
  422. Q_FixSlashes( tmpBuf0 );
  423. Q_StripFilename( tmpBuf0 );
  424. char mtlLibPath[ MAX_PATH ];
  425. Q_ComposeFileName( tmpBuf0, tmpBuf1, mtlLibPath, sizeof( mtlLibPath ) );
  426. CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  427. if ( g_pFullFileSystem->ReadFile( mtlLibPath, NULL, utlBuf ) )
  428. {
  429. ParseMtlLib( utlBuf );
  430. }
  431. continue;
  432. }
  433. if ( sscanf( pBuf, "usemtl %4096s", tmpBuf1 ) == 1 )
  434. {
  435. // Remove any 'SG' suffix from the material
  436. const uint sLen = Q_strlen( tmpBuf1 );
  437. if ( sLen && !Q_strcmp( tmpBuf1 + sLen - 2, "SG" ) )
  438. {
  439. tmpBuf1[ sLen - 2 ] = '\0';
  440. }
  441. const char *pTexture( FindMtlEntry( tmpBuf1 ) );
  442. if ( pTexture )
  443. {
  444. pFaceIndices = faceSetData.GetFaceSetIndices( pTexture );
  445. }
  446. else
  447. {
  448. pFaceIndices = faceSetData.GetFaceSetIndices( tmpBuf1 );
  449. }
  450. continue;
  451. }
  452. if ( sscanf( pBuf, "g %4096s", tmpBuf1 ) == 1 )
  453. {
  454. groupName = tmpBuf1;
  455. if ( pFaceIndices == NULL )
  456. {
  457. pFaceIndices = faceSetData.GetFaceSetIndices( tmpBuf1 );
  458. }
  459. continue;
  460. }
  461. if ( *pBuf == 'f' && ( *( pBuf + 1 ) == ' ' || *( pBuf + 1 ) == '\t' ) )
  462. {
  463. if ( pDmeDag == NULL )
  464. {
  465. pDmeDag = CreateElement< CDmeDag >( pName ? pName : ( groupName.IsEmpty() ? "obj" : groupName.Get() ), pRoot->GetFileId() );
  466. Assert( pDmeDag );
  467. pDmeMesh = CreateElement< CDmeMesh >( pName ? pName : ( groupName.IsEmpty() ? "obj" : groupName.Get() ), pRoot->GetFileId() );
  468. if ( ppCreatedMesh && *ppCreatedMesh == NULL )
  469. {
  470. // Only the first mesh created...
  471. *ppCreatedMesh = pDmeMesh;
  472. }
  473. pDmeDag->SetShape( pDmeMesh );
  474. if ( pModel )
  475. {
  476. pModel->AddJoint( pDmeDag );
  477. pModel->AddChild( pDmeDag );
  478. }
  479. }
  480. if ( pFaceIndices == NULL )
  481. {
  482. pFaceIndices = faceSetData.GetFaceSetIndices( "facetSet" );
  483. }
  484. int v;
  485. int t;
  486. int n;
  487. pBuf = SkipSpace( pBuf + 1 );
  488. int nLen = Q_strlen( pBuf );
  489. CUtlBuffer bufParse( pBuf, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  490. while ( bufParse.IsValid() )
  491. {
  492. if ( !ParseVertex( bufParse, breakSet, v, t, n ) )
  493. break;
  494. pFaceIndices->AddToTail( vertexData.VertexCount() );
  495. if ( v > 0 )
  496. {
  497. vertexData.AddPositionIndex( v - 1 );
  498. }
  499. if ( n > 0 )
  500. {
  501. vertexData.AddNormalIndex( n - 1 );
  502. }
  503. if ( t > 0 )
  504. {
  505. vertexData.AddUVIndex( t - 1 );
  506. }
  507. }
  508. pFaceIndices->AddToTail( -1 );
  509. continue;
  510. }
  511. }
  512. }
  513. CDmeVertexDataBase *pVertexData( NULL );
  514. if ( pBaseMesh )
  515. {
  516. pVertexData = vertexData.AddToMesh( pBaseMesh, bAbsolute, pName, true );
  517. }
  518. else
  519. {
  520. pVertexData = vertexData.AddToMesh( pDmeMesh, bAbsolute, "bind", false );
  521. faceSetData.AddToMesh( pDmeMesh );
  522. }
  523. if ( pModel )
  524. {
  525. pModel->CaptureJointsToBaseState( "bind" );
  526. }
  527. if ( pBaseMesh )
  528. return pVertexData;
  529. return pRoot;
  530. }
  531. //-----------------------------------------------------------------------------
  532. //
  533. //-----------------------------------------------------------------------------
  534. int CDmObjSerializer::OutputVectors(
  535. CUtlBuffer &b,
  536. const char *pPrefix,
  537. const CUtlVector< Vector > &vData,
  538. const matrix3x4_t &matrix )
  539. {
  540. Vector v;
  541. const int nv( vData.Count() );
  542. for ( int i( 0 ); i < nv; ++i )
  543. {
  544. VectorTransform( vData[ i ], matrix, v );
  545. b << pPrefix << v << "\n";
  546. }
  547. return nv;
  548. }
  549. //-----------------------------------------------------------------------------
  550. //
  551. //-----------------------------------------------------------------------------
  552. int CDmObjSerializer::OutputVectors(
  553. CUtlBuffer &b,
  554. const char *pPrefix,
  555. const CUtlVector< Vector2D > &vData )
  556. {
  557. Vector v;
  558. const int nv( vData.Count() );
  559. for ( int i( 0 ); i < nv; ++i )
  560. {
  561. b << pPrefix << vData[ i ] << "\n";
  562. }
  563. return nv;
  564. }
  565. //-----------------------------------------------------------------------------
  566. //
  567. //-----------------------------------------------------------------------------
  568. void CDmObjSerializer::MeshToObj(
  569. CUtlBuffer &b,
  570. const matrix3x4_t &parentWorldMatrix,
  571. CDmeMesh *pMesh,
  572. const char *pDeltaName,
  573. bool absolute )
  574. {
  575. CUtlVector< CDmeMesh::DeltaComputation_t > compList;
  576. if ( pDeltaName )
  577. {
  578. pMesh->ComputeDependentDeltaStateList( compList );
  579. }
  580. const int nCompList( compList.Count() );
  581. CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) );
  582. if ( !pBase )
  583. return;
  584. const FieldIndex_t nPosIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
  585. if ( nPosIndex < 0 )
  586. return;
  587. int nPositionCount = 0;
  588. int nTextureCount = 0;
  589. int nNormalCount = 0;
  590. b << "g " << pMesh->GetName() << "\n";
  591. CDmrArrayConst< Vector > pArray( pBase->GetVertexData( nPosIndex ) );
  592. const CUtlVector< int > *ppIndices( &pBase->GetVertexIndexData( nPosIndex ) );
  593. const CUtlVector< Vector > &pConstData( pArray.Get() );
  594. if ( nCompList )
  595. {
  596. CUtlVector< Vector > pData;
  597. pData.CopyArray( pConstData.Base(), pConstData.Count() );
  598. if ( absolute )
  599. {
  600. for ( int i ( 0 ); i < nCompList; ++i )
  601. {
  602. CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
  603. if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
  604. continue;
  605. b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
  606. pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
  607. const CUtlVector< int > &depDeltas( compList[ i ].m_DependentDeltas );
  608. const int nDepDeltas( depDeltas.Count() );
  609. for ( int j( 0 ); j < nDepDeltas; ++j )
  610. {
  611. pTmpDeltaState = pMesh->GetDeltaState( depDeltas[ j ] );
  612. b << "# Dependent Delta: " << pTmpDeltaState->GetName() << "\n";
  613. pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
  614. }
  615. }
  616. }
  617. else
  618. {
  619. for ( int i ( 0 ); i < nCompList; ++i )
  620. {
  621. CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
  622. if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
  623. continue;
  624. b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
  625. pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
  626. }
  627. }
  628. nPositionCount = OutputVectors( b, "v ", pData, parentWorldMatrix );
  629. }
  630. else
  631. {
  632. nPositionCount = OutputVectors( b, "v ", pConstData, parentWorldMatrix );
  633. }
  634. const CUtlVector< int > *puvIndices( NULL );
  635. const FieldIndex_t uvIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD ) );
  636. if ( uvIndex >= 0 )
  637. {
  638. CDmrArrayConst< Vector2D > uvArray( pBase->GetVertexData( uvIndex ) );
  639. const CUtlVector< Vector2D > &uvData( uvArray.Get() );
  640. puvIndices = &pBase->GetVertexIndexData( uvIndex );
  641. nTextureCount = OutputVectors( b, "vt ", uvData );
  642. }
  643. const CUtlVector< int > *pnIndices( NULL );
  644. const FieldIndex_t nIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ) );
  645. if ( nIndex >= 0 )
  646. {
  647. matrix3x4_t normalMatrix;
  648. MatrixInverseTranspose( parentWorldMatrix, normalMatrix );
  649. CDmrArrayConst< Vector > nArray( pBase->GetVertexData( nIndex ) );
  650. const CUtlVector< Vector > &nConstData( nArray.Get() );
  651. pnIndices = &pBase->GetVertexIndexData( nIndex );
  652. if ( nCompList )
  653. {
  654. CUtlVector< Vector > nData;
  655. nData.CopyArray( nConstData.Base(), nConstData.Count() );
  656. if ( absolute )
  657. {
  658. for ( int i ( 0 ); i < nCompList; ++i )
  659. {
  660. CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
  661. if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
  662. continue;
  663. b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
  664. pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
  665. const CUtlVector< int > &depDeltas( compList[ i ].m_DependentDeltas );
  666. const int nDepDeltas( depDeltas.Count() );
  667. for ( int j( 0 ); j < nDepDeltas; ++j )
  668. {
  669. pTmpDeltaState = pMesh->GetDeltaState( depDeltas[ j ] );
  670. b << "# Dependent Delta: " << pTmpDeltaState->GetName() << "\n";
  671. pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
  672. }
  673. }
  674. }
  675. else
  676. {
  677. for ( int i ( 0 ); i < nCompList; ++i )
  678. {
  679. CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
  680. if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
  681. continue;
  682. b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
  683. pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
  684. }
  685. }
  686. nNormalCount = OutputVectors( b, "vn ", nData, normalMatrix );
  687. }
  688. else
  689. {
  690. nNormalCount = OutputVectors( b, "vn ", nConstData, normalMatrix );
  691. }
  692. }
  693. const int pCount( ppIndices->Count() );
  694. const int uvCount( puvIndices ? puvIndices->Count() : 0 );
  695. const int nCount( pnIndices ? pnIndices->Count() : 0 );
  696. const int nFaceSets( pMesh->FaceSetCount() );
  697. for ( int i= 0 ; i < nFaceSets; ++i )
  698. {
  699. CDmeFaceSet *pFaceSet( pMesh->GetFaceSet( i ) );
  700. CDmeMaterial *pMaterial( pFaceSet->GetMaterial() );
  701. if ( pMaterial )
  702. {
  703. b << "usemtl " << pMaterial->GetMaterialName() << "\n";
  704. }
  705. const int nIndices( pFaceSet->NumIndices() );
  706. const int *pnFaceSetIndex( pFaceSet->GetIndices() );
  707. const int *const pEnd( pnFaceSetIndex + nIndices );
  708. int fIndex;
  709. const char *const pFaceStart( "f " );
  710. const char *const pFaceNext( " " );
  711. const char *pFaceSep( pFaceStart );
  712. if ( pCount == uvCount && pCount == nCount )
  713. {
  714. const CUtlVector< int > &pIndices( *ppIndices );
  715. const CUtlVector< int > &uvIndices( *puvIndices );
  716. const CUtlVector< int > &nvIndices( *pnIndices );
  717. while ( pnFaceSetIndex < pEnd )
  718. {
  719. fIndex = *pnFaceSetIndex++;
  720. if ( fIndex < 0 )
  721. {
  722. b << "\n";
  723. pFaceSep = pFaceStart;
  724. continue;
  725. }
  726. b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << '/' << ( uvIndices[ fIndex ] + m_nTextureOffset ) << '/' << ( nvIndices[ fIndex ] + m_nNormalOffset );
  727. pFaceSep = pFaceNext;
  728. }
  729. }
  730. else if ( pCount == uvCount )
  731. {
  732. const CUtlVector< int > &pIndices( *ppIndices );
  733. const CUtlVector< int > &uvIndices( *puvIndices );
  734. while ( pnFaceSetIndex < pEnd )
  735. {
  736. fIndex = *pnFaceSetIndex++;
  737. if ( fIndex < 0 )
  738. {
  739. b << "\n";
  740. pFaceSep = pFaceStart;
  741. continue;
  742. }
  743. b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << '/' << ( uvIndices[ fIndex ] + m_nTextureOffset ) << '/';
  744. pFaceSep = pFaceNext;
  745. }
  746. }
  747. else if ( pCount == nCount )
  748. {
  749. const CUtlVector< int > &pIndices( *ppIndices );
  750. const CUtlVector< int > &nvIndices( *pnIndices );
  751. while ( pnFaceSetIndex < pEnd )
  752. {
  753. fIndex = *pnFaceSetIndex++;
  754. if ( fIndex < 0 )
  755. {
  756. b << "\n";
  757. pFaceSep = pFaceStart;
  758. continue;
  759. }
  760. b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << "//" << ( nvIndices[ fIndex ] + m_nNormalOffset );
  761. pFaceSep = pFaceNext;
  762. }
  763. }
  764. else
  765. {
  766. const CUtlVector< int > &pIndices( *ppIndices );
  767. while ( pnFaceSetIndex < pEnd )
  768. {
  769. fIndex = *pnFaceSetIndex++;
  770. if ( fIndex < 0 )
  771. {
  772. b << "\n";
  773. pFaceSep = pFaceStart;
  774. continue;
  775. }
  776. b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << "//";
  777. pFaceSep = pFaceNext;
  778. }
  779. }
  780. }
  781. m_nPositionOffset += nPositionCount;
  782. m_nTextureOffset += nTextureCount;
  783. m_nNormalOffset += nNormalCount;
  784. }
  785. //-----------------------------------------------------------------------------
  786. //
  787. //-----------------------------------------------------------------------------
  788. void CDmObjSerializer::DagToObj(
  789. CUtlBuffer &b,
  790. const matrix3x4_t &parentWorldMatrix,
  791. CDmeDag *pDag,
  792. const char *pDeltaName,
  793. bool absolute )
  794. {
  795. matrix3x4_t inclusiveMatrix;
  796. pDag->GetTransform()->GetTransform( inclusiveMatrix );
  797. ConcatTransforms( parentWorldMatrix, inclusiveMatrix, inclusiveMatrix );
  798. CDmeMesh *pMesh( CastElement< CDmeMesh >( pDag->GetShape() ) );
  799. if ( pMesh )
  800. {
  801. MeshToObj( b, inclusiveMatrix, pMesh, pDeltaName, absolute );
  802. }
  803. const int nChildren( pDag->GetChildCount() );
  804. for ( int i( 0 ); i < nChildren; ++i )
  805. {
  806. DagToObj( b, inclusiveMatrix, pDag->GetChild( i ), pDeltaName, absolute );
  807. }
  808. }
  809. //-----------------------------------------------------------------------------
  810. //
  811. //-----------------------------------------------------------------------------
  812. void CDmObjSerializer::FindDeltaMeshes( CDmeDag *pDag, CUtlVector< CDmeMesh * > &meshes )
  813. {
  814. CDmeMesh *pMesh( CastElement< CDmeMesh >( pDag->GetShape() ) );
  815. if ( pMesh && pMesh->DeltaStateCount() )
  816. {
  817. meshes.AddToTail( pMesh );
  818. }
  819. const int nChildren( pDag->GetChildCount() );
  820. for ( int i( 0 ); i < nChildren; ++i )
  821. {
  822. FindDeltaMeshes( pDag->GetChild( i ), meshes );
  823. }
  824. }
  825. //-----------------------------------------------------------------------------
  826. // Convert from OBJ -> DME
  827. //-----------------------------------------------------------------------------
  828. bool CDmObjSerializer::WriteOBJ( const char *pFilename, CDmElement *pRoot, bool bWriteOBJs, const char *pDeltaName, bool absolute )
  829. {
  830. CDmeDag *pModel = pRoot->GetValueElement< CDmeDag >( "model" );
  831. if ( !pModel )
  832. return false;
  833. matrix3x4_t identityMatrix;
  834. SetIdentityMatrix( identityMatrix );
  835. if ( !pDeltaName )
  836. {
  837. CUtlBuffer b( 0, 0, CUtlBuffer::TEXT_BUFFER );
  838. b << "# OBJ\n";
  839. b << "#\n";
  840. m_nPositionOffset = 1; // OBJs start indexing at 1
  841. m_nTextureOffset = 1;
  842. m_nNormalOffset = 1;
  843. DagToObj( b, identityMatrix, pModel, pDeltaName, absolute );
  844. g_pFullFileSystem->WriteFile( pFilename, NULL, b );
  845. // Filesystem is silly
  846. // On WIN32 filesystem changes all of the characters to lowercase grrrr.....
  847. rename( pFilename, pFilename );
  848. }
  849. if ( !bWriteOBJs )
  850. return true;
  851. CUtlVector< CDmeMesh * > deltaMeshes;
  852. FindDeltaMeshes( pModel, deltaMeshes );
  853. if ( deltaMeshes.Count() )
  854. {
  855. char base[ MAX_PATH ];
  856. Q_FileBase( pFilename, base, sizeof( base ) );
  857. char path[ MAX_PATH ];
  858. Q_ExtractFilePath( pFilename, path, sizeof( path ) );
  859. char *pSuffix = strchr( base, '=' );
  860. if ( !pSuffix )
  861. {
  862. pSuffix = strchr( base, '_' );
  863. }
  864. if ( pSuffix )
  865. {
  866. *( pSuffix + 0 ) = '_';
  867. *( pSuffix + 1 ) = '\0';
  868. }
  869. char filename[ MAX_PATH ];
  870. const int nDeltaMeshes( deltaMeshes.Count() );
  871. for ( int i( 0 ); i < nDeltaMeshes; ++i )
  872. {
  873. CDmeMesh *pDeltaMesh( deltaMeshes[ i ] );
  874. const int nDeltas( pDeltaMesh->DeltaStateCount() );
  875. for ( int j( 0 ); j < nDeltas; ++j )
  876. {
  877. CDmeVertexDeltaData *pDelta( pDeltaMesh->GetDeltaState( j ) );
  878. if ( !pDeltaName || !Q_strcmp( pDeltaName, pDelta->GetName() ) )
  879. {
  880. CUtlBuffer b( 0, 0, CUtlBuffer::TEXT_BUFFER );
  881. b << "# Delta OBJ: " << pDelta->GetName() << "\n";
  882. b << "#\n";
  883. Q_strncpy( filename, pDelta->GetName(), sizeof( filename ) );
  884. // Change _ to +
  885. const char *const pEnd( filename + sizeof( filename ) );
  886. for ( char *pChar = filename; *pChar && pChar < pEnd; ++pChar )
  887. {
  888. if ( *pChar == '_' )
  889. {
  890. *pChar = '+';
  891. }
  892. }
  893. CUtlString deltaFile( base );
  894. deltaFile += filename;
  895. deltaFile += ".obj";
  896. m_nPositionOffset = 1; // OBJs use 1 based indexes
  897. m_nTextureOffset = 1;
  898. m_nNormalOffset = 1;
  899. DagToObj( b, identityMatrix, pModel, pDelta->GetName(), absolute );
  900. Q_ComposeFileName( path, deltaFile.Get(), filename, sizeof( filename ) );
  901. g_pFullFileSystem->WriteFile( filename, NULL, b );
  902. // On WIN32 filesystem changes all of the characters to lowercase grrrr.....
  903. rename( filename, filename );
  904. }
  905. }
  906. }
  907. }
  908. return true;
  909. }
  910. //-----------------------------------------------------------------------------
  911. //
  912. //-----------------------------------------------------------------------------
  913. void CDmObjSerializer::ParseMtlLib( CUtlBuffer &buf )
  914. {
  915. char tmpBuf0[ 4096 ];
  916. int nCurrentMtl = -1;
  917. while ( buf.IsValid() )
  918. {
  919. buf.GetLine( tmpBuf0, sizeof(tmpBuf0) );
  920. if ( StringHasPrefix( tmpBuf0, "newmtl " ) )
  921. {
  922. char mtlName[1024];
  923. if ( sscanf( tmpBuf0, "newmtl %s", mtlName ) == 1 )
  924. {
  925. // Remove any 'SG' suffix from the material
  926. const uint sLen = Q_strlen( mtlName );
  927. if ( sLen > 2 && !Q_strcmp( mtlName + sLen - 2, "SG" ) )
  928. {
  929. mtlName[ sLen - 2 ] = '\0';
  930. }
  931. nCurrentMtl = m_mtlLib.AddToTail( );
  932. m_mtlLib[nCurrentMtl].m_MtlName = mtlName;
  933. m_mtlLib[nCurrentMtl].m_TgaName = mtlName;
  934. }
  935. continue;
  936. }
  937. if ( StringHasPrefix( tmpBuf0, "map_Kd " ) )
  938. {
  939. if ( nCurrentMtl < 0 )
  940. continue;
  941. char tgaPath[MAX_PATH];
  942. char tgaName[1024];
  943. if ( sscanf( tmpBuf0, "map_Kd %s", tgaPath ) == 1 )
  944. {
  945. // Try a cheesy hack - look for /materialsrc/ and set the material name off the entire path minus extension
  946. Q_strncpy( tmpBuf0, tgaPath, sizeof( tmpBuf0 ) );
  947. Q_FixSlashes( tmpBuf0, '/' );
  948. const char *pMaterialSrc = Q_strstr( tmpBuf0, "/materialsrc/" );
  949. if ( pMaterialSrc )
  950. {
  951. pMaterialSrc += Q_strlen( "/materialsrc/" );
  952. Q_StripExtension( pMaterialSrc, tgaName, sizeof( tgaName) );
  953. m_mtlLib[ nCurrentMtl ].m_TgaName = tgaName;
  954. }
  955. else
  956. {
  957. Q_FileBase( tgaPath, tgaName, sizeof(tgaName) );
  958. m_mtlLib[nCurrentMtl].m_TgaName = tgaName;
  959. }
  960. }
  961. continue;
  962. }
  963. }
  964. }
  965. //-----------------------------------------------------------------------------
  966. //
  967. //-----------------------------------------------------------------------------
  968. const char *CDmObjSerializer::FindMtlEntry( const char *pTgaName )
  969. {
  970. int nCount = m_mtlLib.Count();
  971. for ( int i = 0; i < nCount; ++i )
  972. {
  973. if ( !Q_stricmp( m_mtlLib[i].m_MtlName, pTgaName ) )
  974. return m_mtlLib[i].m_TgaName;
  975. }
  976. return pTgaName;
  977. }
  978. //-----------------------------------------------------------------------------
  979. //
  980. //-----------------------------------------------------------------------------
  981. bool CDmObjSerializer::ParseVertex( CUtlBuffer& bufParse, characterset_t &breakSet, int &v, int &t, int &n )
  982. {
  983. char cmd[1024];
  984. int nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  985. if ( nLen <= 0 )
  986. return false;
  987. v = atoi( cmd );
  988. n = 0;
  989. t = 0;
  990. char c = *(char*)bufParse.PeekGet();
  991. bool bHasTexCoord = IN_CHARACTERSET( breakSet, c ) != 0;
  992. bool bHasNormal = false;
  993. if ( bHasTexCoord )
  994. {
  995. // Snag the '/'
  996. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  997. Assert( nLen == 1 );
  998. c = *(char*)bufParse.PeekGet();
  999. if ( !IN_CHARACTERSET( breakSet, c ) )
  1000. {
  1001. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  1002. Assert( nLen > 0 );
  1003. t = atoi( cmd );
  1004. c = *(char*)bufParse.PeekGet();
  1005. bHasNormal = IN_CHARACTERSET( breakSet, c ) != 0;
  1006. }
  1007. else
  1008. {
  1009. bHasNormal = true;
  1010. bHasTexCoord = false;
  1011. }
  1012. if ( bHasNormal )
  1013. {
  1014. // Snag the '/'
  1015. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  1016. Assert( nLen == 1 );
  1017. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  1018. Assert( nLen > 0 );
  1019. n = atoi( cmd );
  1020. }
  1021. }
  1022. return true;
  1023. }
  1024. //-----------------------------------------------------------------------------
  1025. //
  1026. //-----------------------------------------------------------------------------
  1027. const char *CDmObjSerializer::SkipSpace(
  1028. const char *pBuf )
  1029. {
  1030. while ( *pBuf == ' ' || *pBuf == '\t' )
  1031. ++pBuf;
  1032. return pBuf;
  1033. }
  1034. //-----------------------------------------------------------------------------
  1035. //
  1036. //-----------------------------------------------------------------------------
  1037. CDmeVertexDeltaData *CDmObjSerializer::GetDelta( const char *pDeltaName, bool bAbsolute )
  1038. {
  1039. if ( !m_deltas.Defined( pDeltaName ) )
  1040. return NULL;
  1041. DeltaInfo_t &deltaInfo( m_deltas[ pDeltaName ] );
  1042. if ( deltaInfo.m_pDeltaData )
  1043. return deltaInfo.m_pDeltaData;
  1044. if ( !LoadDependentDeltas( pDeltaName ) )
  1045. return NULL;
  1046. CUtlBuffer utlBuf;
  1047. char deltaPath[ MAX_PATH ];
  1048. Q_ComposeFileName( m_objDirectory, deltaInfo.m_filename, deltaPath, sizeof( deltaPath ) );
  1049. Q_FixSlashes( deltaPath );
  1050. if ( !g_pFullFileSystem->ReadFile( deltaPath, NULL, utlBuf ) )
  1051. return NULL;
  1052. if ( deltaInfo.m_pComboOp && !strchr( pDeltaName, '_' ) )
  1053. {
  1054. deltaInfo.m_pComboOp->FindOrCreateControl( pDeltaName, false, true );
  1055. }
  1056. deltaInfo.m_pDeltaData = CastElement< CDmeVertexDeltaData >( ReadOBJ( utlBuf, deltaInfo.m_pMesh->GetFileId(), pDeltaName, deltaPath, deltaInfo.m_pMesh, NULL, bAbsolute ) );
  1057. return deltaInfo.m_pDeltaData;
  1058. }
  1059. //-----------------------------------------------------------------------------
  1060. //
  1061. //-----------------------------------------------------------------------------
  1062. bool CDmObjSerializer::LoadDependentDeltas( const char *pDeltaName )
  1063. {
  1064. // TODO: Load Dependent Deltas
  1065. return true;
  1066. }
  1067. //-----------------------------------------------------------------------------
  1068. // Counts the number of _'s in a string
  1069. //-----------------------------------------------------------------------------
  1070. int ComputeDimensionality( const char *pDeltaName )
  1071. {
  1072. const char *pUnderBar = pDeltaName;
  1073. int nDimensions = 0;
  1074. while ( pUnderBar )
  1075. {
  1076. ++nDimensions;
  1077. pUnderBar = strchr( pUnderBar, '_' );
  1078. if ( pUnderBar )
  1079. {
  1080. ++pUnderBar;
  1081. }
  1082. }
  1083. return nDimensions;
  1084. }
  1085. /*
  1086. //-----------------------------------------------------------------------------
  1087. // Generates a sorted list in order of dimensionality of the delta states
  1088. // NOTE: This assumes a naming scheme where delta state names have _ that separate control names
  1089. //-----------------------------------------------------------------------------
  1090. void CDmObjSerializer::ComputeDeltaStateComputationList( CUtlVector< CUtlVector< int > > &dependentDeltaList )
  1091. {
  1092. // Do all combinations in order of dimensionality, lowest dimension first
  1093. for ( int i = 0; i < nDeltas; ++i )
  1094. {
  1095. compList[i].m_nDeltaIndex = i;
  1096. compList[i].m_nDimensionality = ComputeDeltaStateDimensionality( i );
  1097. }
  1098. qsort( compList.Base(), nCount, sizeof(DeltaComputation_t), DeltaStateLessFunc );
  1099. }
  1100. {
  1101. CUtlVector< CUtlString > atomicControls;
  1102. deltaStateUsage.SetCount( nCount );
  1103. // Build a list of atomic controls
  1104. int nCurrentDelta;
  1105. for ( nCurrentDelta = 0; nCurrentDelta < nCount; ++nCurrentDelta )
  1106. {
  1107. if ( pInfo[nCurrentDelta].m_nDimensionality != 1 )
  1108. break;
  1109. int j = atomicControls.AddToTail( GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex )->GetName() );
  1110. deltaStateUsage[ nCurrentDelta ].AddToTail( j );
  1111. }
  1112. for ( ; nCurrentDelta < nCount; ++nCurrentDelta )
  1113. {
  1114. CDmeVertexDeltaData *pDeltaState = GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex );
  1115. int nLen = Q_strlen( pDeltaState->GetName() );
  1116. char *pTempBuf = (char*)_alloca( nLen + 1 );
  1117. memcpy( pTempBuf, pDeltaState->GetName(), nLen+1 );
  1118. char *pNext;
  1119. for ( char *pUnderBar = pTempBuf; pUnderBar; pUnderBar = pNext )
  1120. {
  1121. pNext = strchr( pUnderBar, '_' );
  1122. if ( pNext )
  1123. {
  1124. *pNext = 0;
  1125. ++pNext;
  1126. }
  1127. // Find this name in the list of strings
  1128. int j;
  1129. int nControlCount = atomicControls.Count();
  1130. for ( j = 0; j < nControlCount; ++j )
  1131. {
  1132. if ( !Q_stricmp( pUnderBar, atomicControls[j] ) )
  1133. break;
  1134. }
  1135. if ( j == nControlCount )
  1136. {
  1137. j = atomicControls.AddToTail( pUnderBar );
  1138. }
  1139. deltaStateUsage[ nCurrentDelta ].AddToTail( j );
  1140. }
  1141. deltaStateUsage[ nCurrentDelta ].Sort( DeltaStateUsageLessFunc );
  1142. }
  1143. }
  1144. //-----------------------------------------------------------------------------
  1145. //
  1146. //-----------------------------------------------------------------------------
  1147. void CDmObjSerializer::ComputeDependentUsage( CUtlVector< CUtlVector< int > > &deltaUsage )
  1148. {
  1149. const int nDeltas = m_deltas.GetNumStrings();
  1150. compList.EnsureCount( nDeltas );
  1151. CUtlVector< CUtlVector< int > > deltaStateUsage;
  1152. const int nCount( compList.Count() );
  1153. BuildAtomicControlLists( nCount, compList.Base(), deltaStateUsage );
  1154. // Now build up a list of dependent delta states based on usage
  1155. // NOTE: Usage is sorted in ascending order.
  1156. for ( int i = 1; i < nCount; ++i )
  1157. {
  1158. int nUsageCount1 = deltaStateUsage[i].Count();
  1159. for ( int j = 0; j < i; ++j )
  1160. {
  1161. // At the point they have the same dimensionality, no more need to check
  1162. if ( compList[j].m_nDimensionality == compList[i].m_nDimensionality )
  1163. break;
  1164. int ii = 0;
  1165. bool bSubsetFound = true;
  1166. int nUsageCount2 = deltaStateUsage[j].Count();
  1167. for ( int ji = 0; ji < nUsageCount2; ++ji )
  1168. {
  1169. for ( bSubsetFound = false; ii < nUsageCount1; ++ii )
  1170. {
  1171. if ( deltaStateUsage[j][ji] == deltaStateUsage[i][ii] )
  1172. {
  1173. ++ii;
  1174. bSubsetFound = true;
  1175. break;
  1176. }
  1177. if ( deltaStateUsage[j][ji] < deltaStateUsage[i][ii] )
  1178. break;
  1179. }
  1180. if ( !bSubsetFound )
  1181. break;
  1182. }
  1183. if ( bSubsetFound )
  1184. {
  1185. compList[i].m_DependentDeltas.AddToTail( compList[j].m_nDeltaIndex );
  1186. }
  1187. }
  1188. }
  1189. }
  1190. */