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.

4017 lines
130 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #pragma warning( disable : 4786 )
  8. #pragma warning( disable : 4748 ) // buffer overrun with optimizations off
  9. // This file has tons of problems with global optimizations. . turn 'em off.
  10. // NOTE: Would be nice to have a test case for this! - not verified in vs2005
  11. #pragma optimize( "g", off )
  12. // use this much memory to build an output file in memory.
  13. #define FILEBUFFER_SIZE ( 4 * 1024 * 1024 )
  14. //#define IGNORE_BONES
  15. #define NVTRISTRIP
  16. #define EMIT_TRILISTS
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <float.h>
  20. #include "mathlib/mathlib.h"
  21. #include "cmdlib.h"
  22. #include "studio.h"
  23. #include "studiomdl.h"
  24. #include "HardwareMatrixState.h"
  25. #include "HardwareVertexCache.h"
  26. #include "optimize.h"
  27. #include <malloc.h>
  28. #include <nvtristrip.h>
  29. #include "FileBuffer.h"
  30. #include "tier1/utlvector.h"
  31. #include "materialsystem/imaterial.h"
  32. #include "tier1/utllinkedlist.h"
  33. #include "tier1/smartptr.h"
  34. #include "tier2/p4helpers.h"
  35. bool g_bDumpGLViewFiles;
  36. extern bool g_IHVTest;
  37. // flush bones between strips rather than deallocating the LRU.
  38. #define USE_FLUSH
  39. extern int FindMaterialByName( const char *pMaterialName );
  40. namespace OptimizedModel
  41. {
  42. //-----------------------------------------------------------------------------
  43. // Defines which debugging output file we will see
  44. //-----------------------------------------------------------------------------
  45. enum
  46. {
  47. WRITEGLVIEW_SHOWMESH = 0x00000001,
  48. WRITEGLVIEW_SHOWSTRIPGROUP = 0x00000002,
  49. WRITEGLVIEW_SHOWSTRIP = 0x00000004,
  50. WRITEGLVIEW_SHOWSUBSTRIP = 0x00000008,
  51. WRITEGLVIEW_SHOWFLEXED = 0x00000010,
  52. WRITEGLVIEW_SHOWSW = 0x00000020,
  53. WRITEGLVIEW_SHOWMESHPROPS = 0x00000040,
  54. WRITEGLVIEW_SHOWVERTNUMBONES = 0x00000080,
  55. WRITEGLVIEW_SHOWSTRIPNUMBONES = 0x00000100,
  56. WRITEGLVIEW_FORCEUINT = 0xffffffff
  57. };
  58. //-----------------------------------------------------------------------------
  59. // This is used to help us figure out where in the file data should go
  60. // and also to display stats
  61. //-----------------------------------------------------------------------------
  62. struct TotalMeshStats_t
  63. {
  64. int m_TotalBodyParts;
  65. int m_TotalModels;
  66. int m_TotalModelLODs;
  67. int m_TotalMeshes;
  68. int m_TotalStrips;
  69. int m_TotalStripGroups;
  70. int m_TotalVerts;
  71. int m_TotalIndices;
  72. int m_TotalBoneStateChanges;
  73. int m_TotalMaterialReplacements;
  74. };
  75. struct Triangle_t
  76. {
  77. Triangle_t()
  78. {
  79. touched = false;
  80. neighborTriID[0] = neighborTriID[1] = neighborTriID[2] = -1;
  81. }
  82. int vertID[3];
  83. int neighborTriID[3];
  84. int boneID[MAX_NUM_BONES_PER_TRI];
  85. int numBones;
  86. bool touched;
  87. };
  88. //-----------------------------------------------------------------------------
  89. // Associates software bone indices with hardware bone indices
  90. //-----------------------------------------------------------------------------
  91. struct BoneStateChange_t
  92. {
  93. int hardwareID;
  94. int newBoneID;
  95. };
  96. struct Strip_t
  97. {
  98. // these are the verts and indices that are used while building.
  99. // (there may be verts in here that aren't use in the stripGroup.)
  100. CUtlVector<Vertex_t> verts;
  101. int numIndices;
  102. unsigned short *pIndices;
  103. unsigned int flags;
  104. int numBones;
  105. // These are the final, sorted verts as they appear in the strip group.
  106. int stripGroupIndexOffset; // offset into stripGroup's indices
  107. int numStripGroupIndices;
  108. int stripGroupVertexOffset; // offset into stripGroup's verts
  109. int numStripGroupVerts;
  110. int numBoneStateChanges;
  111. BoneStateChange_t boneStateChanges[MAX_NUM_BONES_PER_STRIP];
  112. };
  113. //-----------------------------------------------------------------------------
  114. // a list of vertices + triangles for a strip group
  115. //-----------------------------------------------------------------------------
  116. typedef CUtlVector<Vertex_t> VertexList_t;
  117. typedef CUtlVector<Triangle_t> TriangleList_t;
  118. typedef CUtlVector<unsigned short> VertexIndexList_t;
  119. typedef CUtlVector<Strip_t> StripList_t;
  120. typedef CUtlVector<bool> TriangleProcessedList_t;
  121. //-----------------------------------------------------------------------------
  122. // String table
  123. //-----------------------------------------------------------------------------
  124. class CStringTable
  125. {
  126. public:
  127. int StringTableOffset( const char *string )
  128. {
  129. int i;
  130. int size = 0;
  131. for( i = 0; i < m_Strings.Size(); i++ )
  132. {
  133. if( stricmp( m_Strings[i].Base(), string ) == 0 )
  134. {
  135. return size;
  136. }
  137. size += m_Strings[i].Size();
  138. }
  139. return -1;
  140. }
  141. bool StringPresent( const char *string )
  142. {
  143. int i;
  144. for( i = 0; i < m_Strings.Size(); i++ )
  145. {
  146. if( stricmp( m_Strings[i].Base(), string ) == 0 )
  147. {
  148. return true;
  149. }
  150. }
  151. return false;
  152. }
  153. void AddString( const char *newString )
  154. {
  155. if( StringPresent( newString ) )
  156. {
  157. return;
  158. }
  159. CUtlVector<char> &s = m_Strings[m_Strings.AddToTail()];
  160. int size = strlen( newString ) + 1;
  161. s.AddMultipleToTail( size );
  162. strcpy( s.Base(), newString );
  163. }
  164. void Purge()
  165. {
  166. m_Strings.Purge();
  167. }
  168. int CalcSize( void )
  169. {
  170. int size = 0;
  171. int i;
  172. for( i = 0; i < m_Strings.Size(); i++ )
  173. {
  174. size += m_Strings[i].Size();
  175. }
  176. return size;
  177. }
  178. void WriteToMem( char *pDst )
  179. {
  180. int size = 0;
  181. int i;
  182. for( i = 0; i < m_Strings.Size(); i++ )
  183. {
  184. #ifdef _DEBUG
  185. int j = Q_strlen( m_Strings[i].Base() ) + 1;
  186. int k = m_Strings[i].Size();
  187. Assert( j == k );
  188. #endif
  189. memcpy( pDst + size, m_Strings[i].Base(), m_Strings[i].Size() );
  190. size += m_Strings[i].Size();
  191. }
  192. }
  193. private:
  194. typedef CUtlVector<char> CharVector_t;
  195. CUtlVector<CharVector_t> m_Strings;
  196. };
  197. // global string table for the whole vtx file.
  198. static CStringTable s_StringTable;
  199. //-----------------------------------------------------------------------------
  200. // This is all the indices, vertices, and strips that make up this group
  201. // a group can be rendered all in one call to the material system
  202. //-----------------------------------------------------------------------------
  203. struct StripGroup_t
  204. {
  205. VertexIndexList_t indices;
  206. VertexList_t verts;
  207. StripList_t strips;
  208. unsigned int flags;
  209. };
  210. struct Mesh_t
  211. {
  212. CUtlVector<StripGroup_t> stripGroups;
  213. unsigned int flags;
  214. };
  215. struct ModelLOD_t
  216. {
  217. CUtlVector<Mesh_t> meshes;
  218. float switchPoint;
  219. };
  220. struct Model_t
  221. {
  222. CUtlVector<ModelLOD_t> modelLODs;
  223. };
  224. //-----------------------------------------------------------------------------
  225. // Main class that does all the dirty work to stripy + groupify
  226. //-----------------------------------------------------------------------------
  227. class COptimizedModel
  228. {
  229. public:
  230. bool OptimizeFromStudioHdr( studiohdr_t *phdr, s_bodypart_t *pSrcBodyParts, int vertCacheSize,
  231. bool usesFixedFunction, bool bForceSoftwareSkin, bool bHWFlex, int maxBonesPerVert, int maxBonesPerTri,
  232. int maxBonesPerStrip, const char *fileName, const char *glViewFileName );
  233. private:
  234. void CleanupEverything();
  235. // Setup to get the ball rolling
  236. void SetupMeshProcessing( studiohdr_t *phdr, int vertCacheSize,
  237. bool usesFixedFunction, int maxBonesPerVert, int maxBonesPerTri,
  238. int maxBonesPerStrip, const char *fileName );
  239. //
  240. // Methods associated with pre-processing the mesh
  241. //
  242. //
  243. // Methods associated with mesh processing
  244. //
  245. void SourceMeshToTriangleList( s_model_t *pSrcModel, s_mesh_t *pSrcMesh, CUtlVector<mstudioiface_t> &meshTriangleList );
  246. void CreateLODTriangleList( s_model_t *pSrcModel, int nLodID, s_source_t* pLODSource,
  247. mstudiomodel_t *pStudioModel,
  248. mstudiomesh_t *pStudioMesh,
  249. CUtlVector<mstudioiface_t> &meshTriangleList, bool writeDebug );
  250. // This processes the model + breaks it into strips
  251. void ProcessModel( studiohdr_t *phdr, s_bodypart_t *pSrcBodyParts, TotalMeshStats_t& stats,
  252. bool bForceSoftwareSkin, bool bHWFlex );
  253. // processes a single mesh within the model
  254. void ProcessMesh( Mesh_t *pMesh, studiohdr_t *pStudioHeader, CUtlVector<mstudioiface_t> &srcFaces,
  255. mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, bool ForceNoFlex,
  256. bool bForceSoftwareSkin, bool bHWFlex );
  257. // Processes a single strip group
  258. void ProcessStripGroup( StripGroup_t *pStripGroup, bool isHWSkinned, bool isFlexed,
  259. mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh,
  260. CUtlVector<mstudioiface_t> &srcFaces,
  261. TriangleProcessedList_t& trianglesProcessed,
  262. int maxBonesPerVert, int maxBonesPerTri, int maxBonesPerStrip,
  263. bool forceNoFlex, bool bHWFlex );
  264. // Constructs vertices appropriate for a strip group based on source face data
  265. bool GenerateStripGroupVerticesFromFace( mstudioiface_t* pFace,
  266. mstudiomesh_t *pStudioMesh, int maxPreferredBones, Vertex_t* pStripGroupVert );
  267. // Count the number of unique bones in a set of vertices
  268. int CountUniqueBones( int count, Vertex_t *pVertex ) const;
  269. int CountMaxVertBones( int count, Vertex_t *pVertex ) const;
  270. // Counts the unique # of bones in a strip
  271. int CountUniqueBonesInStrip( StripGroup_t *pStripGroup, Strip_t *pStrip );
  272. // Builds SW + HW skinned strips
  273. void BuildSWSkinnedStrips( TriangleList_t& tris,
  274. VertexList_t const& verts, StripGroup_t *pStripGroup );
  275. void BuildHWSkinnedStrips( TriangleList_t& tris, VertexList_t& verts,
  276. StripGroup_t *pStripGroup, int maxBonesPerStrip );
  277. // These methods deal with finding another triangle to batch together
  278. // in a similar matrix state group
  279. int ComputeNewBonesNeeded( Triangle_t const& triangle ) const;
  280. bool AllocateHardwareBonesForTriangle( Triangle_t *tri );
  281. Triangle_t* GetNextTriangle( TriangleList_t& triangles, bool allowNewStrip );
  282. Triangle_t* GetNextUntouchedWithoutBoneStateChange( TriangleList_t& triangles );
  283. Triangle_t* GetNextUntouchedWithLeastBoneStateChanges( TriangleList_t& triangles );
  284. // Actually does the stripification
  285. void Stripify( VertexIndexList_t const& sourceIndices, bool isHWSkinned,
  286. int* pNumIndices, unsigned short** ppIndices );
  287. // Makes sure our vertices are using the correct bones
  288. void SanityCheckVertBones( VertexIndexList_t const& list, VertexList_t const& vertices );
  289. // Sets the flags associated with a particular strip group + mesh
  290. void ComputeStripGroupFlags( StripGroup_t *pStripGroup, bool isHWSkinned, bool isFlexed );
  291. void ComputeMeshFlags( Mesh_t *pMesh, studiohdr_t *pStudioHeader, mstudiomesh_t *pStudioMesh );
  292. bool MeshIsTeeth( studiohdr_t *pStudioHeader, mstudiomesh_t *pStudioMesh );
  293. // Tries to add neighboring vertices that'll fit into the matrix transform state
  294. void BuildStripsRecursive( VertexIndexList_t& indices, TriangleList_t& list, Triangle_t *triangle );
  295. // Figures out all bones affecting a particular triangle
  296. void BuildTriangleBoneData( VertexList_t& list, Triangle_t& tri );
  297. // Memory optimize the strip data
  298. void PostProcessStripGroup( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, StripGroup_t *pStripGroup );
  299. void COptimizedModel::ZeroNumBones( void );
  300. //
  301. // Methods associated with writing VTX files
  302. //
  303. // This writes the strip data out to a VTX file
  304. void WriteVTXFile( studiohdr_t *pHdr, char const* pFileName,
  305. TotalMeshStats_t const& stats );
  306. //
  307. // Methods associated with writing GL View files
  308. //
  309. // This writes the GL debugging files
  310. void WriteGLViewFiles( studiohdr_t *pHdr, const char *glViewFileName );
  311. void OutputMemoryUsage( void );
  312. bool IsVertexFlexed( mstudiomesh_t *pStudioMesh, int vertID ) const;
  313. void BuildNeighborInfo( TriangleList_t& list, int nMaxVertexId );
  314. void ClearTouched( void );
  315. void PrintVert( Vertex_t *v, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh );
  316. void SanityCheckAgainstStudioHDR( studiohdr_t *phdr );
  317. void WriteStringTable( int stringTableOffset );
  318. void WriteMaterialReplacements( int materialReplacementsOffset );
  319. void WriteMaterialReplacementLists( int materialReplacementsOffset, int materialReplacementListOffset );
  320. void WriteHeader( int vertCacheSize, int maxBonesPerVert,
  321. int maxBonesPerTri, int maxBonesPerStrip, int numBodyParts, long checkSum );
  322. void WriteBodyPart( int bodyPartID, mstudiobodyparts_t *pBodyPart, int modelID );
  323. void WriteModel( int modelID, mstudiomodel_t *pModel, int lodID );
  324. void WriteModelLOD( int lodID, ModelLOD_t *pLOD, int meshID );
  325. void WriteMesh( int meshID, Mesh_t *pMesh, int stripGroupID );
  326. void WriteStripGroup( int stripGroupID, StripGroup_t *pStripGroup,
  327. int vertID, int indexID, int stripID );
  328. int WriteVerts( int vertID, StripGroup_t *pStripGroup );
  329. int WriteIndices( int indexID, StripGroup_t *pStripGroup );
  330. void WriteStrip( int stripID, Strip_t *pStrip, int indexID, int vertID, int boneID );
  331. void WriteBoneStateChange( int boneID, BoneStateChange_t *boneStateChange );
  332. void DrawGLViewTriangle( FILE *fp, Vector& pos1, Vector& pos2, Vector& pos3,
  333. Vector& color1, Vector& color2, Vector& color3 );
  334. void WriteGLViewFile( studiohdr_t *phdr, const char *pFileName, unsigned int flags, float shrinkFactor );
  335. void ShrinkVerts( float shrinkFactor );
  336. void GLViewDrawBegin( int mode );
  337. void CheckVertBoneWeights( Vertex_t *pVert, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh );
  338. void GLViewVert( FILE *fp, Vertex_t vert, int index, Vector& color, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, bool showSubStrips, float shrinkFraction );
  339. void GLViewDrawEnd( void );
  340. void SetMeshPropsColor( unsigned int meshFlags, Vector& color );
  341. void SetFlexedAndSkinColor( unsigned int glViewFlags, unsigned int stripGroupFlags, Vector& color );
  342. void SetColorFromNumVertexBones( int numBones, Vector& color );
  343. Vector& GetOrigVertPosition( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert );
  344. float GetOrigVertBoneWeightValue( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert, int boneID );
  345. mstudioboneweight_t &GetOrigVertBoneWeight( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert );
  346. int GetOrigVertBoneIndex( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert, int boneID );
  347. void ShowStats( void );
  348. void MapGlobalBonesToHardwareBoneIDsAndSortBones( studiohdr_t *phdr );
  349. void RemoveRedundantBoneStateChanges( void );
  350. void CheckVert( Vertex_t *pVert, int maxBonesPerTri, int maxBonesPerVert );
  351. void CheckAllVerts( int maxBonesPerTri, int maxBonesPerVert );
  352. void SortBonesWithinVertex( bool flexed, Vertex_t *vert, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, int *globalToHardwareBoneIndex, int *hardwareToGlobalBoneIndex, int maxBonesPerTri, int maxBonesPerVert );
  353. int GetTotalVertsForMesh( Mesh_t *pMesh );
  354. int GetTotalIndicesForMesh( Mesh_t *pMesh );
  355. int GetTotalStripsForMesh( Mesh_t *pMesh );
  356. int GetTotalStripGroupsForMesh( Mesh_t *pMesh );
  357. int GetTotalBoneStateChangesForMesh( Mesh_t *pMesh );
  358. bool MeshNeedsRemoval( studiohdr_t *pHdr, mstudiomesh_t *pStudioMesh,
  359. LodScriptData_t& scriptLOD );
  360. void PrintBoneStateChanges( studiohdr_t *pHdr, int lod );
  361. void PrintVerts( studiohdr_t *phdr, int lod );
  362. void SanityCheckVertexBoneLODFlags( studiohdr_t *pStudioHdr, FileHeader_t *pVtxHeader );
  363. //
  364. // SOURCE DATA
  365. //
  366. // These are all the built models
  367. CUtlVector<Model_t> m_Models;
  368. // total number of bones in the studio model
  369. int m_NumBones;
  370. // information about the hardware
  371. int m_VertexCacheSize;
  372. int m_MaxBonesPerTri;
  373. int m_MaxBonesPerVert;
  374. int m_MaxBonesPerStrip;
  375. bool m_UsesFixedFunction;
  376. // stats
  377. int m_NumSkinnedAndFlexedVerts;
  378. CHardwareMatrixState m_HardwareMatrixState;
  379. // a place to stick file output.
  380. CFileBuffer *m_FileBuffer;
  381. // offset for different items in the output file.
  382. int m_BodyPartsOffset;
  383. int m_ModelsOffset;
  384. int m_ModelLODsOffset;
  385. int m_MeshesOffset;
  386. int m_StripGroupsOffset;
  387. int m_StripsOffset;
  388. int m_VertsOffset;
  389. int m_IndicesOffset;
  390. int m_BoneStageChangesOffset;
  391. int m_StringTableOffset;
  392. int m_MaterialReplacementsOffset;
  393. int m_MaterialReplacementsListOffset;
  394. int m_EndOfFileOffset;
  395. };
  396. //-----------------------------------------------------------------------------
  397. // Singleton instance
  398. //-----------------------------------------------------------------------------
  399. static COptimizedModel s_OptimizedModel;
  400. //-----------------------------------------------------------------------------
  401. // Cleanup method
  402. //-----------------------------------------------------------------------------
  403. void COptimizedModel::CleanupEverything()
  404. {
  405. }
  406. void COptimizedModel::OutputMemoryUsage( void )
  407. {
  408. printf( "body parts: %7d bytes\n", ( int )( m_ModelsOffset - m_BodyPartsOffset ) );
  409. printf( "models: %7d bytes\n", ( int )( m_MeshesOffset - m_ModelsOffset ) );
  410. printf( "model LODs: %7d bytes\n", ( int )( m_MeshesOffset - m_ModelLODsOffset ) );
  411. printf( "meshes: %7d bytes\n", ( int )( m_StripGroupsOffset - m_MeshesOffset ) );
  412. printf( "strip groups: %7d bytes\n", ( int )( m_StripsOffset - m_StripGroupsOffset ) );
  413. printf( "strips: %7d bytes\n", ( int )( m_VertsOffset - m_StripsOffset ) );
  414. printf( "verts: %7d bytes\n", ( int )( m_IndicesOffset - m_VertsOffset ) );
  415. printf( "indices: %7d bytes\n", ( int )( m_BoneStageChangesOffset - m_IndicesOffset ) );
  416. printf( "bone changes: %7d bytes\n", ( int )( m_EndOfFileOffset - m_BoneStageChangesOffset ) );
  417. printf( "everything: %7d bytes\n", ( int )( m_EndOfFileOffset ) );
  418. }
  419. void COptimizedModel::SanityCheckAgainstStudioHDR( studiohdr_t *phdr )
  420. {
  421. #if 0 // garymcthack
  422. printf( "SanityCheckAgainstStudioHDR\n" );
  423. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  424. Assert( header->numBodyParts == phdr->numbodyparts );
  425. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  426. {
  427. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  428. mstudiobodyparts_t *pStudioBodyPart = phdr->pBodypart( bodyPartID );
  429. Assert( bodyPart->numModels == pStudioBodyPart->nummodels );
  430. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  431. {
  432. ModelHeader_t *model = bodyPart->pModel( modelID );
  433. mstudiomodel_t *pStudioModel = pStudioBodyPart->pModel( modelID );
  434. Assert( model->numMeshes == pStudioModel->nummeshes );
  435. for( int meshID = 0; meshID < model->numMeshes; meshID++ )
  436. {
  437. MeshHeader_t *mesh = model->pMesh( meshID );
  438. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  439. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  440. {
  441. StripGroupHeader_t *stripGroup = mesh->pStripGroup( stripGroupID );
  442. for( int stripID = 0; stripID < stripGroup->numStrips; stripID++ )
  443. {
  444. StripHeader_t *strip = stripGroup->pStrip( stripID );
  445. }
  446. }
  447. }
  448. }
  449. }
  450. #endif
  451. }
  452. //-----------------------------------------------------------------------------
  453. //
  454. // The following methods are all related to creating groups of meshes to strip
  455. //
  456. //-----------------------------------------------------------------------------
  457. //-----------------------------------------------------------------------------
  458. // pick any triangle that hasn't been used yet to start with.
  459. //-----------------------------------------------------------------------------
  460. static Triangle_t *GetNextUntouched( TriangleList_t& triangles )
  461. {
  462. int i;
  463. for( i = 0; i < triangles.Size(); i++ )
  464. {
  465. if( !triangles[i].touched )
  466. return &triangles[i];
  467. }
  468. return 0;
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Returns the number of bones that are not represented in the current hardware state
  472. //-----------------------------------------------------------------------------
  473. inline int COptimizedModel::ComputeNewBonesNeeded( Triangle_t const& triangle ) const
  474. {
  475. int numNewBones = 0;
  476. for( int i = 0; i < triangle.numBones; ++i )
  477. {
  478. if( !m_HardwareMatrixState.IsMatrixAllocated( triangle.boneID[i] ) )
  479. ++numNewBones;
  480. }
  481. return numNewBones;
  482. }
  483. //-----------------------------------------------------------------------------
  484. // returns triangle index
  485. // Find the next triangle without a bone state change
  486. // May add new hardware bones if there is space and is necessary.
  487. //-----------------------------------------------------------------------------
  488. Triangle_t* COptimizedModel::GetNextUntouchedWithoutBoneStateChange( TriangleList_t& triangles )
  489. {
  490. Triangle_t *bestTriangle = 0;
  491. int bestNumNewBones = MAX_NUM_BONES_PER_TRI + 1;
  492. int i;
  493. for( i = 0; i < triangles.Size(); i++ )
  494. {
  495. // We haven't processed this one, so let's try it
  496. if( !triangles[i].touched )
  497. {
  498. // How many bones are not represented in the current state?
  499. int numNewBones = ComputeNewBonesNeeded( triangles[i] );
  500. // if this triangle fit and if it's the best so far, save it.
  501. if ( (numNewBones <= m_HardwareMatrixState.FreeMatrixCount()) &&
  502. (numNewBones < bestNumNewBones ) )
  503. {
  504. bestNumNewBones = numNewBones;
  505. bestTriangle = &triangles[i];
  506. // Can't get any better than this!
  507. if (bestNumNewBones == 0)
  508. break;
  509. }
  510. }
  511. }
  512. return bestTriangle;
  513. }
  514. //-----------------------------------------------------------------------------
  515. // This will returns the triangle that requires the last number of bone
  516. // state changes
  517. //-----------------------------------------------------------------------------
  518. Triangle_t *COptimizedModel::GetNextUntouchedWithLeastBoneStateChanges( TriangleList_t& triangles )
  519. {
  520. Triangle_t *bestTriangle = 0;
  521. int bestNumNewBones = MAX_NUM_BONES_PER_TRI + 1;
  522. int i;
  523. // For this one, just find the triangle that needs the least number
  524. // of new bones. That way, we'll not have to change too many states
  525. for( i = 0; i < triangles.Size(); i++ )
  526. {
  527. if( !triangles[i].touched )
  528. {
  529. int numNewBones = ComputeNewBonesNeeded( triangles[i] );
  530. if( numNewBones < bestNumNewBones )
  531. {
  532. bestNumNewBones = numNewBones;
  533. bestTriangle = &triangles[i];
  534. }
  535. }
  536. }
  537. // This only happens if there are no triangles untouched
  538. if( !bestTriangle )
  539. return 0;
  540. #ifdef USE_FLUSH
  541. m_HardwareMatrixState.DeallocateAll();
  542. #else
  543. // Remove bones until we have enough space...
  544. int numToRemove = bestNumNewBones - m_HardwareMatrixState.FreeMatrixCount();
  545. Assert( numToRemove > 0 );
  546. m_HardwareMatrixState.DeallocateLRU(numToRemove);
  547. #endif
  548. return bestTriangle;
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Allocate bones for a triangle from the hardware matrix state
  552. //-----------------------------------------------------------------------------
  553. bool COptimizedModel::AllocateHardwareBonesForTriangle( Triangle_t *tri )
  554. {
  555. for( int i = 0; i < tri->numBones; ++i )
  556. {
  557. int bone = tri->boneID[i];
  558. if( !m_HardwareMatrixState.IsMatrixAllocated( bone ) )
  559. {
  560. if( !m_HardwareMatrixState.AllocateMatrix( bone ) )
  561. return false;
  562. }
  563. }
  564. return true;
  565. }
  566. //-----------------------------------------------------------------------------
  567. // Get the next triangle.
  568. // Try to find one that doesn't cause a bone state change by jumping to
  569. // a new location in the model that works with the bones that we have allocated.
  570. // If we can't find one, and allowNewStrip is true, then flush and pick the next
  571. // best triangle that is close to the current hardware bone state.
  572. //-----------------------------------------------------------------------------
  573. Triangle_t *COptimizedModel::GetNextTriangle( TriangleList_t& tris, bool allowNewStrip )
  574. {
  575. // First try to get a triangle that doesn't involve changing matrix state
  576. Triangle_t *tri;
  577. tri = GetNextUntouchedWithoutBoneStateChange( tris );
  578. // If that didn't work, pick the triangle that changes the state the least
  579. if( !tri && allowNewStrip )
  580. {
  581. tri = GetNextUntouchedWithLeastBoneStateChanges( tris );
  582. }
  583. // Return the triangle we found
  584. return tri;
  585. }
  586. //-----------------------------------------------------------------------------
  587. // Make sure all vertices we've added up to now use bones in the matrix list
  588. //-----------------------------------------------------------------------------
  589. void COptimizedModel::SanityCheckVertBones( VertexIndexList_t const& list, VertexList_t const& vertices )
  590. {
  591. #ifdef _DEBUG
  592. Vertex_t const *pVert;
  593. int i;
  594. for( i = 0; i < list.Size(); i++ )
  595. {
  596. pVert = &vertices[list[i]];
  597. if( !g_staticprop )
  598. {
  599. Assert( pVert->numBones != 0 );
  600. }
  601. int j;
  602. for( j = 0; j < pVert->numBones; j++ )
  603. {
  604. if( pVert->boneID[j] == -1 )
  605. {
  606. continue;
  607. }
  608. if( !m_HardwareMatrixState.IsMatrixAllocated( pVert->boneID[j] ) )
  609. {
  610. Assert( 0 );
  611. }
  612. }
  613. }
  614. #endif
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Make sure all vertices we've added up to now use bones in the matrix list
  618. //-----------------------------------------------------------------------------
  619. void COptimizedModel::Stripify( VertexIndexList_t const& sourceIndices, bool isHWSkinned,
  620. int* pNumIndices, unsigned short** ppIndices )
  621. {
  622. if( sourceIndices.Size() == 0 )
  623. {
  624. *ppIndices = 0;
  625. *pNumIndices = 0;
  626. return;
  627. }
  628. // Skip the tristripping phase if we're building in preview mode
  629. if ( g_bBuildPreview || isHWSkinned == false )
  630. {
  631. *pNumIndices = sourceIndices.Count();
  632. *ppIndices = new unsigned short[*pNumIndices];
  633. memcpy( *ppIndices, sourceIndices.Base(), (*pNumIndices) * sizeof(unsigned short) );
  634. return;
  635. }
  636. /*
  637. printf( "Stripify\n" );
  638. int i;
  639. for( i = 0; i < sourceIndices.Size(); i++ )
  640. {
  641. printf( "stripindex: %d\n", sourceIndices[i] );
  642. }
  643. */
  644. #ifdef NVTRISTRIP
  645. PrimitiveGroup *primGroups;
  646. unsigned short numPrimGroups;
  647. // Be sure to call delete[] on the returned primGroups to avoid leaking mem
  648. GenerateStrips( &sourceIndices[0], sourceIndices.Size(),
  649. &primGroups, &numPrimGroups );
  650. Assert( numPrimGroups == 1 );
  651. *pNumIndices = primGroups->numIndices;
  652. *ppIndices = new unsigned short[*pNumIndices];
  653. memcpy( *ppIndices, primGroups->indices, sizeof( unsigned short ) * *pNumIndices );
  654. delete [] primGroups;
  655. #endif
  656. }
  657. //-----------------------------------------------------------------------------
  658. // eat up triangle recursively by flood-filling around the model until
  659. // we run out of bones on the hardware.
  660. //-----------------------------------------------------------------------------
  661. void COptimizedModel::BuildStripsRecursive( VertexIndexList_t& indices,
  662. TriangleList_t& tris, Triangle_t *triangle )
  663. {
  664. Assert( triangle );
  665. // Don't process the triangle if it's already been processed
  666. if( triangle->touched )
  667. return;
  668. // Only suck in triangles that need no state change
  669. if ( ComputeNewBonesNeeded( *triangle ) )
  670. return;
  671. // We've got enough hardware bones. Lets add this triangle's vertices, and
  672. // then add the vertices of all the neighboring triangles.
  673. triangle->touched = true;
  674. indices.AddToTail( ( unsigned short )triangle->vertID[0] );
  675. indices.AddToTail( ( unsigned short )triangle->vertID[1] );
  676. indices.AddToTail( ( unsigned short )triangle->vertID[2] );
  677. // Try to add our neighbors
  678. if( triangle->neighborTriID[0] != -1 )
  679. {
  680. BuildStripsRecursive( indices, tris, &tris[triangle->neighborTriID[0]] );
  681. }
  682. if( triangle->neighborTriID[1] != -1 )
  683. {
  684. BuildStripsRecursive( indices, tris, &tris[triangle->neighborTriID[1]] );
  685. }
  686. if( triangle->neighborTriID[2] != -1 )
  687. {
  688. BuildStripsRecursive( indices, tris, &tris[triangle->neighborTriID[2]] );
  689. }
  690. }
  691. //-----------------------------------------------------------------------------
  692. // Processes a HW-skinned strip group
  693. //-----------------------------------------------------------------------------
  694. void COptimizedModel::BuildHWSkinnedStrips( TriangleList_t& triangles,
  695. VertexList_t& vertices, StripGroup_t *pStripGroup, int maxBonesPerStrip )
  696. {
  697. // Set up the hardware matrix state
  698. m_HardwareMatrixState.Init( maxBonesPerStrip );
  699. // Empty out the list of triangles to be stripified.
  700. VertexIndexList_t trianglesToStrip;
  701. trianglesToStrip.EnsureCapacity( triangles.Size() * 3 );
  702. // pick any old unused triangle to start with.
  703. Triangle_t *pSeedTri = GetNextUntouched( triangles );
  704. while( pSeedTri )
  705. {
  706. // Make sure we've got out transforms allocated
  707. #ifdef DBGFLAG_ASSERT
  708. bool ok =
  709. #endif
  710. AllocateHardwareBonesForTriangle( pSeedTri );
  711. Assert( ok );
  712. // eat up triangle recursively by flood-filling around the model until
  713. // we run out of bones on the hardware.
  714. BuildStripsRecursive( trianglesToStrip, triangles, pSeedTri );
  715. // Try to jump to a new location in the mesh without
  716. // causing a hardware bone state overflow or flush.
  717. pSeedTri = GetNextTriangle( triangles, false );
  718. if( pSeedTri )
  719. continue;
  720. // Save the results of the generated strip.
  721. int stripIdx = pStripGroup->strips.AddToTail( );
  722. Strip_t& newStrip = pStripGroup->strips[stripIdx];
  723. // Compute the strip flags
  724. newStrip.flags = 0;
  725. #ifdef EMIT_TRILISTS
  726. newStrip.flags |= STRIP_IS_TRILIST;
  727. #else
  728. newStrip.flags |= STRIP_IS_TRISTRIP;
  729. #endif
  730. // Sanity check the indices of the bones.
  731. SanityCheckVertBones( trianglesToStrip, vertices );
  732. // There are no more triangles to eat up without causing a flush, so
  733. // go ahead and stripify what we have and flush.
  734. // NOTE: This allocates space for stripIndices.pIndices.
  735. Stripify( trianglesToStrip, true, &newStrip.numIndices, &newStrip.pIndices );
  736. // hack - should just build directly into newStrip.verts instead of using a global.
  737. int i;
  738. for( i = 0; i < vertices.Size(); i++ )
  739. {
  740. newStrip.verts.AddToTail( vertices[i] );
  741. }
  742. // Compute the number of bones in this strip
  743. newStrip.numBoneStateChanges = m_HardwareMatrixState.AllocatedMatrixCount();
  744. Assert( newStrip.numBoneStateChanges <= maxBonesPerStrip );
  745. // Save off the bones used for this strip.
  746. for( i = 0; i < m_HardwareMatrixState.AllocatedMatrixCount(); i++ )
  747. {
  748. newStrip.boneStateChanges[i].hardwareID = i;
  749. newStrip.boneStateChanges[i].newBoneID = m_HardwareMatrixState.GetNthBoneGlobalID( i );
  750. }
  751. // Empty out the triangles to strip so that we can start again with a new strip.
  752. trianglesToStrip.RemoveAll();
  753. // Get the next best triangle, allowing for a bone state flushes.
  754. pSeedTri = GetNextTriangle( triangles, true );
  755. }
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Processes a SW-skinned strip group
  759. //-----------------------------------------------------------------------------
  760. void COptimizedModel::BuildSWSkinnedStrips( TriangleList_t& triangles,
  761. VertexList_t const& vertices, StripGroup_t *pStripGroup )
  762. {
  763. // Save the results of the generated strip.
  764. int stripIdx = pStripGroup->strips.AddToTail( );
  765. Strip_t& newStrip = pStripGroup->strips[stripIdx];
  766. // Set the strip flags
  767. newStrip.flags = 0;
  768. #ifdef EMIT_TRILISTS
  769. newStrip.flags |= STRIP_IS_TRILIST;
  770. #else
  771. newStrip.flags |= STRIP_IS_TRISTRIP;
  772. #endif
  773. int nTriangleCount = triangles.Count();
  774. VertexIndexList_t indices;
  775. indices.EnsureCapacity( nTriangleCount * 3 );
  776. for( int i = 0; i < nTriangleCount; i++ )
  777. {
  778. Triangle_t* triangle = &triangles[i];
  779. triangle->touched = true;
  780. indices.AddToTail( ( unsigned short )triangle->vertID[0] );
  781. indices.AddToTail( ( unsigned short )triangle->vertID[1] );
  782. indices.AddToTail( ( unsigned short )triangle->vertID[2] );
  783. }
  784. Stripify( indices, false, &newStrip.numIndices, &newStrip.pIndices );
  785. // hack - should just build directly into newStrip.verts instead of using a global.
  786. for( int i = 0; i < vertices.Size(); i++ )
  787. {
  788. newStrip.verts.AddToTail( vertices[i] );
  789. }
  790. newStrip.numBoneStateChanges = 0;
  791. for( int i = 0; i < MAX_NUM_BONES_PER_STRIP; i++ )
  792. {
  793. newStrip.boneStateChanges[i].hardwareID = -1;
  794. newStrip.boneStateChanges[i].newBoneID = -1;
  795. }
  796. }
  797. //-----------------------------------------------------------------------------
  798. // Returns true if a particular vertex is part of a flex
  799. //-----------------------------------------------------------------------------
  800. bool COptimizedModel::IsVertexFlexed( mstudiomesh_t *pStudioMesh, int vertID ) const
  801. {
  802. mstudioflex_t *pflex = pStudioMesh->pFlex( 0 );
  803. int i, j, n;
  804. // Iterate through all the flexes
  805. // figure out if the vertex is part of the flex
  806. for (i = 0; i < pStudioMesh->numflexes; i++)
  807. {
  808. byte *pvanim = pflex[i].pBaseVertanim();
  809. int nVAnimSizeBytes = pflex[i].VertAnimSizeBytes();
  810. for (j = 0; j < pflex[i].numverts; j++, pvanim += nVAnimSizeBytes )
  811. {
  812. mstudiovertanim_t *pAnim = (mstudiovertanim_t*)( pvanim );
  813. n = pAnim->index;
  814. if ( n == vertID )
  815. return true;
  816. }
  817. }
  818. return false;
  819. }
  820. //-----------------------------------------------------------------------------
  821. // Computes flags for the strip group
  822. //-----------------------------------------------------------------------------
  823. void COptimizedModel::ComputeStripGroupFlags( StripGroup_t *pStripGroup,
  824. bool isHWSkinned, bool isFlexed )
  825. {
  826. pStripGroup->flags = 0;
  827. if( isFlexed )
  828. {
  829. pStripGroup->flags |= STRIPGROUP_IS_FLEXED;
  830. pStripGroup->flags |= STRIPGROUP_IS_DELTA_FLEXED; // Going forward, DX9 models are delta flexed
  831. }
  832. if( isHWSkinned )
  833. {
  834. pStripGroup->flags |= STRIPGROUP_IS_HWSKINNED;
  835. }
  836. }
  837. //-----------------------------------------------------------------------------
  838. // Try to reduce the number of bones affecting this vert so hardware can deal
  839. //-----------------------------------------------------------------------------
  840. #define MIN_BONE_INFLUENCE 1.0f
  841. static void TryToReduceBoneInfluence( Vertex_t& stripGroupVert,
  842. mstudioboneweight_t const& boneWeights, int maxBones )
  843. {
  844. int i;
  845. while ( stripGroupVert.numBones > maxBones)
  846. {
  847. // Find the minimum bone weight...
  848. float minWeight = 2.0;
  849. int minIndex = -1;
  850. for( i = 0; i < MAX_NUM_BONES_PER_VERT; ++i )
  851. {
  852. if (stripGroupVert.boneID[i] != -1)
  853. {
  854. float weight = boneWeights.weight[ stripGroupVert.boneWeightIndex[i] ];
  855. if (weight < minWeight)
  856. {
  857. minWeight = weight;
  858. minIndex = i;
  859. }
  860. }
  861. }
  862. Assert( minIndex >= 0 );
  863. // Now that we got it, remove that bone influence if it's small enough
  864. if (minWeight >= MIN_BONE_INFLUENCE)
  865. return;
  866. // Blat out that bone!
  867. for( i = minIndex; i < MAX_NUM_BONES_PER_VERT - 1; ++i )
  868. {
  869. stripGroupVert.boneID[i] = stripGroupVert.boneID[i+1];
  870. stripGroupVert.boneWeightIndex[i] = stripGroupVert.boneWeightIndex[i+1];
  871. }
  872. stripGroupVert.boneID[ MAX_NUM_BONES_PER_VERT - 1] = -1;
  873. stripGroupVert.boneWeightIndex[ MAX_NUM_BONES_PER_VERT - 1] = 0;
  874. --stripGroupVert.numBones;
  875. }
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Generate three strip-group ready vertices from source mesh data
  879. // returns true if the face contants a flexed vertex
  880. //-----------------------------------------------------------------------------
  881. bool COptimizedModel::GenerateStripGroupVerticesFromFace( mstudioiface_t* pFace,
  882. mstudiomesh_t *pStudioMesh,
  883. int maxPreferredBones,
  884. Vertex_t* pStripGroupVert )
  885. {
  886. int vertIDs[3];
  887. vertIDs[0] = pFace->a;
  888. vertIDs[1] = pFace->b;
  889. vertIDs[2] = pFace->c;
  890. const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData();
  891. Assert( vertData ); // This can only return NULL on X360 for now
  892. bool triangleIsFlexed = false;
  893. for( int faceIndex = 0; faceIndex < 3; ++faceIndex )
  894. {
  895. // Get the original source vertex id
  896. int vertex = vertIDs[faceIndex];
  897. // Check the verts of the triangle to see if they are flexed
  898. triangleIsFlexed = triangleIsFlexed || IsVertexFlexed( pStudioMesh, vertex );
  899. // How many bones affect this vertex
  900. mstudioboneweight_t *pBoneWeight = vertData->BoneWeights(vertex);
  901. int bonesAffectingVertex = pBoneWeight->numbones;
  902. if( !g_staticprop && bonesAffectingVertex <= 0 )
  903. {
  904. MdlWarning( "too few bones/vert (%d) : it has no bones!\n", bonesAffectingVertex );
  905. }
  906. else if( bonesAffectingVertex > MAX_NUM_BONES_PER_VERT )
  907. {
  908. MdlError( "too many bones/vert (%d) : MAX_NUM_BONES_PER_VERT needs to be upped\n", bonesAffectingVertex );
  909. }
  910. // Set the fields of the strip group's vert
  911. pStripGroupVert[faceIndex].origMeshVertID = vertex;
  912. pStripGroupVert[faceIndex].numBones = bonesAffectingVertex;
  913. int boneID;
  914. #ifndef IGNORE_BONES
  915. for( boneID = 0; boneID < bonesAffectingVertex; boneID++ )
  916. {
  917. pStripGroupVert[faceIndex].boneID[boneID] = pBoneWeight->bone[boneID];
  918. pStripGroupVert[faceIndex].boneWeightIndex[boneID] = boneID;
  919. }
  920. for( ; boneID < MAX_NUM_BONES_PER_VERT; boneID++ )
  921. {
  922. pStripGroupVert[faceIndex].boneID[boneID] = -1;
  923. pStripGroupVert[faceIndex].boneWeightIndex[boneID] = boneID;
  924. }
  925. #else
  926. // don't let bones have any influence.
  927. for( boneID = 0; boneID < MAX_NUM_BONES_PER_VERT; boneID++ )
  928. {
  929. pStripGroupVert[faceIndex].boneID[boneID] = -1;
  930. pStripGroupVert[faceIndex].boneWeights[boneID] = 0;
  931. }
  932. #endif
  933. // For hardware skinning, we want to try to reduce the number of
  934. // bone influences, which we'll do here if we can.
  935. // At the moment, we can't do this for fixed function because
  936. // we get seam problems at transitions between SW and HW rendered
  937. if (!m_UsesFixedFunction)
  938. {
  939. if ((maxPreferredBones > 0) && (bonesAffectingVertex > maxPreferredBones))
  940. {
  941. TryToReduceBoneInfluence( pStripGroupVert[faceIndex], *pBoneWeight,
  942. maxPreferredBones );
  943. }
  944. }
  945. }
  946. return triangleIsFlexed;
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Computes the unique number of bones in a specified set of vertices
  950. //-----------------------------------------------------------------------------
  951. int COptimizedModel::CountUniqueBones( int count, Vertex_t *pVertex ) const
  952. {
  953. int uniqueBoneCount = 0;
  954. int uniqueBoneList[MAX_NUM_BONES_PER_STRIP];
  955. while ( --count >= 0 )
  956. {
  957. for (int i = 0; i < pVertex[count].numBones; ++i)
  958. {
  959. int boneID = pVertex[count].boneID[i];
  960. int j = uniqueBoneCount;
  961. while ( --j >= 0 )
  962. {
  963. if (uniqueBoneList[j] == boneID)
  964. break;
  965. }
  966. // Didn't find a match!
  967. if (j < 0)
  968. {
  969. Assert( uniqueBoneCount < MAX_NUM_BONES_PER_STRIP );
  970. uniqueBoneList[uniqueBoneCount++] = boneID;
  971. }
  972. }
  973. }
  974. return uniqueBoneCount;
  975. }
  976. int COptimizedModel::CountMaxVertBones( int count, Vertex_t *pVertex ) const
  977. {
  978. int maxBones = 0;
  979. while ( --count >= 0 )
  980. {
  981. if (maxBones < pVertex[count].numBones)
  982. maxBones = pVertex[count].numBones;
  983. }
  984. return maxBones;
  985. }
  986. //-----------------------------------------------------------------------------
  987. // Adds a vertex to the list of vertices to be added to the strip group
  988. //-----------------------------------------------------------------------------
  989. static int FindOrCreateVertex( VertexList_t& list, Vertex_t const& vert )
  990. {
  991. int i;
  992. for( i = 0; i < list.Size(); i++ )
  993. {
  994. if (list[i].origMeshVertID == vert.origMeshVertID)
  995. {
  996. // If this is the case, then everything else should be too!
  997. Assert( !memcmp( &list[i], &vert, sizeof( vert )) );
  998. return i;
  999. }
  1000. }
  1001. list.AddToTail( vert );
  1002. return i;
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // Computes the bones used by the triangle
  1006. //-----------------------------------------------------------------------------
  1007. void COptimizedModel::BuildTriangleBoneData( VertexList_t& list, Triangle_t& tri )
  1008. {
  1009. int j, k, l;
  1010. // Blat out the bone ID state
  1011. tri.numBones = 0;
  1012. for( j = 0; j < MAX_NUM_BONES_PER_TRI; j++ )
  1013. {
  1014. tri.boneID[j] = -1;
  1015. }
  1016. // Iterate through the vertices in the triangle
  1017. for( j = 0; j < 3; j++ )
  1018. {
  1019. // Iterate over the bones influencing the vertex
  1020. Vertex_t& vert = list[tri.vertID[j]];
  1021. for( k = 0; k < vert.numBones; ++k )
  1022. {
  1023. int bone = vert.boneID[k];
  1024. Assert( (bone >= 0) && (bone < m_NumBones) );
  1025. // Look for matches with previously found bones
  1026. for ( l = tri.numBones; --l >= 0; )
  1027. {
  1028. if ( bone == tri.boneID[l] )
  1029. break;
  1030. }
  1031. // No match, add it to our list
  1032. if ( l < 0 )
  1033. tri.boneID[tri.numBones++] = bone;
  1034. }
  1035. }
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Computes neighboring triangles along each face of a triangle
  1039. //-----------------------------------------------------------------------------
  1040. struct EdgeInfo_t
  1041. {
  1042. int m_nConnectedVertId;
  1043. int m_nEdgeIndex;
  1044. int m_nTriangleId;
  1045. };
  1046. static void FindMatchingEdge( TriangleList_t& list, int nTriangleId, int nEdgeIndex,
  1047. const int *pVertIds, CUtlVector< int >& vertexToEdges, CUtlFixedLinkedList< EdgeInfo_t >& edges )
  1048. {
  1049. // Have we already attached this edge to something?
  1050. Triangle_t &tri = list[nTriangleId];
  1051. if ( tri.neighborTriID[nEdgeIndex] != -1 )
  1052. return;
  1053. int nVertIndex = ( pVertIds[0] < pVertIds[1] ) ? 0 : 1;
  1054. int nConnectedVertId = pVertIds[ 1-nVertIndex ];
  1055. int hFirstEdge = vertexToEdges[nVertIndex];
  1056. for ( int hEdge = hFirstEdge; hEdge != edges.InvalidIndex(); hEdge = edges.Next(hEdge) )
  1057. {
  1058. EdgeInfo_t &edge = edges[hEdge];
  1059. if ( edge.m_nConnectedVertId != nConnectedVertId )
  1060. continue;
  1061. // Can't attach triangles to themselves
  1062. if ( edge.m_nTriangleId == nTriangleId )
  1063. continue;
  1064. // Found a match! Mark the two triangles as sharing an edge
  1065. tri.neighborTriID[nEdgeIndex] = edge.m_nTriangleId;
  1066. list[ edge.m_nTriangleId ].neighborTriID[ edge.m_nEdgeIndex ] = nTriangleId;
  1067. if ( hEdge == hFirstEdge )
  1068. {
  1069. vertexToEdges[nVertIndex] = edges.Next( hFirstEdge );
  1070. }
  1071. edges.Free( hEdge );
  1072. return;
  1073. }
  1074. // No match! Insert the disconnected edge into the edge list
  1075. int hNewEdge = edges.Alloc( true );
  1076. EdgeInfo_t &newEdge = edges[hNewEdge];
  1077. newEdge.m_nConnectedVertId = nConnectedVertId;
  1078. newEdge.m_nEdgeIndex = nEdgeIndex;
  1079. newEdge.m_nTriangleId = nTriangleId;
  1080. edges.LinkBefore( hFirstEdge, hNewEdge );
  1081. vertexToEdges[nVertIndex] = hNewEdge;
  1082. }
  1083. void COptimizedModel::BuildNeighborInfo( TriangleList_t& list, int nMaxVertexId )
  1084. {
  1085. // NOTE: vertexToEdges[vertId] contains the index of the head of a linked list stored in edges
  1086. CUtlFixedLinkedList< EdgeInfo_t > edges;
  1087. CUtlVector< int > vertexToEdges;
  1088. vertexToEdges.SetCount( nMaxVertexId );
  1089. memset( vertexToEdges.Base(), 0, nMaxVertexId * sizeof(int) );
  1090. int pEdgeVertIds[2];
  1091. int nTriCount = list.Count();
  1092. for ( int i = 0; i < nTriCount; ++i )
  1093. {
  1094. Triangle_t &tri = list[i];
  1095. // Add the three edges for this tri into a lookup table indexed by the lower vertID
  1096. pEdgeVertIds[0] = tri.vertID[0]; pEdgeVertIds[1] = tri.vertID[1];
  1097. FindMatchingEdge( list, i, 0, pEdgeVertIds, vertexToEdges, edges );
  1098. pEdgeVertIds[0] = tri.vertID[1]; pEdgeVertIds[1] = tri.vertID[2];
  1099. FindMatchingEdge( list, i, 1, pEdgeVertIds, vertexToEdges, edges );
  1100. pEdgeVertIds[0] = tri.vertID[2]; pEdgeVertIds[1] = tri.vertID[0];
  1101. FindMatchingEdge( list, i, 2, pEdgeVertIds, vertexToEdges, edges );
  1102. }
  1103. }
  1104. //-----------------------------------------------------------------------------
  1105. // Processes a single strip group
  1106. //-----------------------------------------------------------------------------
  1107. void COptimizedModel::ProcessStripGroup( StripGroup_t *pStripGroup, bool isHWSkinned,
  1108. bool isFlexed, mstudiomodel_t *pStudioModel,
  1109. mstudiomesh_t *pStudioMesh,
  1110. CUtlVector<mstudioiface_t> &srcFaces,
  1111. TriangleProcessedList_t& trianglesProcessed,
  1112. int maxBonesPerVert, int maxBonesPerTri,
  1113. int maxBonesPerStrip, bool forceNoFlex, bool bHWFlex )
  1114. {
  1115. ComputeStripGroupFlags( pStripGroup, isHWSkinned, isFlexed );
  1116. // all of the triangles before stripping for the current stripgroup.
  1117. TriangleList_t stripGroupSourceTriangles;
  1118. VertexList_t stripGroupVertices;
  1119. // FIXME: Flexed/HWSkinned state of faces don't change with each pass.
  1120. // We could precompute those flags just once (instead of doing it 4 times)
  1121. // Add each face to the stripgroup, if it's appropriate
  1122. for( int n=0; n < srcFaces.Size(); ++n )
  1123. {
  1124. // Don't bother processing a triangle that's already been done
  1125. if (trianglesProcessed[n])
  1126. continue;
  1127. mstudioiface_t *pFace = &srcFaces[n];
  1128. int preferredBones = isHWSkinned && (bHWFlex || !isFlexed) ? maxBonesPerVert : 0;
  1129. // start a new strip group header.
  1130. Vertex_t stripGroupVert[3];
  1131. bool triangleIsFlexed = GenerateStripGroupVerticesFromFace(
  1132. pFace, pStudioMesh,
  1133. preferredBones,
  1134. stripGroupVert );
  1135. if( forceNoFlex )
  1136. {
  1137. triangleIsFlexed = false;
  1138. }
  1139. // Don't bother to add the vertices to our strip-group vertex list
  1140. // if we don't need to.
  1141. if ( triangleIsFlexed != isFlexed )
  1142. continue;
  1143. // If we're doing software skinning, then always accept, even if
  1144. // this triangle should have been a HW skinned triangle.
  1145. if (isHWSkinned)
  1146. {
  1147. // Check how many unique bones were in that face, and how many vertices
  1148. // maximally in a vertex
  1149. int numVertexBones = CountMaxVertBones( 3, stripGroupVert );
  1150. int numTriangleBones = CountUniqueBones( 3, stripGroupVert );
  1151. // If we have too many, we'll be skinning in software
  1152. bool triangleIsHWSkinned = ( numTriangleBones <= maxBonesPerTri ) &&
  1153. ( numVertexBones <= maxBonesPerVert );
  1154. // Don't bother to add vertices which aren't gonna be used in this pass
  1155. if (!triangleIsHWSkinned)
  1156. continue;
  1157. }
  1158. // Add a new triangle to our list of triangles
  1159. int triIndex = stripGroupSourceTriangles.AddToTail( );
  1160. Triangle_t& newTri = stripGroupSourceTriangles[triIndex];
  1161. newTri.vertID[0] = FindOrCreateVertex( stripGroupVertices, stripGroupVert[0] );
  1162. newTri.vertID[1] = FindOrCreateVertex( stripGroupVertices, stripGroupVert[1] );
  1163. newTri.vertID[2] = FindOrCreateVertex( stripGroupVertices, stripGroupVert[2] );
  1164. BuildTriangleBoneData( stripGroupVertices, newTri );
  1165. // By default, this processes a triangle
  1166. // Later on below, we may decide to un-process triangles
  1167. // if tristrips that are too small are generated
  1168. trianglesProcessed[n] = true;
  1169. }
  1170. // No mesh? bye bye
  1171. if (stripGroupSourceTriangles.Size() == 0)
  1172. return;
  1173. // Figure out neighboring triangles
  1174. BuildNeighborInfo( stripGroupSourceTriangles, stripGroupVertices.Count() );
  1175. // Build the actual strips
  1176. if( isHWSkinned )
  1177. {
  1178. BuildHWSkinnedStrips( stripGroupSourceTriangles, stripGroupVertices, pStripGroup, maxBonesPerStrip );
  1179. // Check to see if any strips were produced that are too small
  1180. // If so, remove them, and let the software pass take care of them.
  1181. //if (stripGroupSourceTriangles.Size() < m_MinimumGroupSize
  1182. // trianglesProcessed[n] = false;
  1183. }
  1184. else
  1185. {
  1186. BuildSWSkinnedStrips( stripGroupSourceTriangles, stripGroupVertices, pStripGroup );
  1187. }
  1188. }
  1189. //-----------------------------------------------------------------------------
  1190. // How many unique bones are in a strip?
  1191. //-----------------------------------------------------------------------------
  1192. int COptimizedModel::CountUniqueBonesInStrip( StripGroup_t *pStripGroup, Strip_t *pStrip )
  1193. {
  1194. int *boneUsageCounts = ( int * )_alloca( m_NumBones * sizeof( int ) );
  1195. memset( boneUsageCounts, 0, sizeof( int ) * m_NumBones );
  1196. int i;
  1197. for( i = 0; i < pStrip->numStripGroupVerts; i++ )
  1198. {
  1199. Vertex_t *pVert = &pStripGroup->verts[i+pStrip->stripGroupVertexOffset];
  1200. if( !g_staticprop )
  1201. {
  1202. Assert( pVert->numBones != 0 );
  1203. }
  1204. int j;
  1205. for( j = 0; j < pVert->numBones; j++ )
  1206. {
  1207. int boneID;
  1208. boneID = pVert->boneID[j];
  1209. Assert( boneID != -1 );
  1210. boneUsageCounts[boneID]++;
  1211. }
  1212. }
  1213. int numBones = 0;
  1214. for( i = 0; i < m_NumBones; i++ )
  1215. {
  1216. if( boneUsageCounts[i] )
  1217. {
  1218. numBones++;
  1219. }
  1220. }
  1221. return numBones;
  1222. }
  1223. //-----------------------------------------------------------------------------
  1224. // A little work to be done after we construct the strip groups
  1225. //-----------------------------------------------------------------------------
  1226. void COptimizedModel::PostProcessStripGroup( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, StripGroup_t *pStripGroup )
  1227. {
  1228. int i;
  1229. // We're gonna compile all of the vertices in the current strip into
  1230. // the current strip group's vertex list
  1231. for( i = 0; i < pStripGroup->strips.Size(); i++ )
  1232. {
  1233. // create sorted strip verts and indices in the stripgroup
  1234. Strip_t *pStrip = &pStripGroup->strips[i];
  1235. int vertOffset = pStripGroup->verts.Size();
  1236. pStrip->stripGroupVertexOffset = vertOffset;
  1237. pStrip->stripGroupIndexOffset = pStripGroup->indices.Size();
  1238. // make sure we have enough memory allocated
  1239. pStripGroup->indices.EnsureCapacity( pStripGroup->indices.Size() + pStrip->numIndices );
  1240. // Try to find each of the strip's vertices in the strip group
  1241. int maxNumBones = 0;
  1242. int j;
  1243. for( j = 0; j < pStrip->numIndices; j++ )
  1244. {
  1245. int newIndex = -1;
  1246. int index = pStrip->pIndices[j];
  1247. Vertex_t *pVert = &pStrip->verts[index];
  1248. // Does this vertex exist in the strip group?
  1249. int k;
  1250. for( k = vertOffset; k < pStripGroup->verts.Size(); k++ )
  1251. {
  1252. if( pVert->origMeshVertID == pStripGroup->verts[k].origMeshVertID )
  1253. {
  1254. newIndex = k;
  1255. break;
  1256. }
  1257. }
  1258. // Didn't find it? Add the vertex to the list
  1259. if( newIndex == -1 )
  1260. {
  1261. newIndex = pStripGroup->verts.AddToTail( *pVert );
  1262. }
  1263. pStripGroup->indices.AddToTail( newIndex );
  1264. #ifdef _DEBUG
  1265. // float GetOrigVertBoneWeightValue( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert, int boneID );
  1266. int i;
  1267. float *pWeight = ( float * )_alloca( sizeof( float ) * pVert->numBones );
  1268. for( i = 0; i < pVert->numBones; i++ )
  1269. {
  1270. pWeight[i] = GetOrigVertBoneWeightValue( pStudioModel, pStudioMesh, pVert, i );
  1271. }
  1272. #endif
  1273. // Keep track of the max # of bones in a vert
  1274. if (pVert->numBones > maxNumBones)
  1275. maxNumBones = pVert->numBones;
  1276. }
  1277. pStrip->numStripGroupIndices = pStripGroup->indices.Size() - pStrip->stripGroupIndexOffset;
  1278. pStrip->numStripGroupVerts = pStripGroup->verts.Size() - pStrip->stripGroupVertexOffset;
  1279. // The number of bones in a strip is the max number of
  1280. // bones in a vertex in this strip for vertex shaders, and it's the
  1281. // number of unique bones in the strip for fixed-function
  1282. if (!m_UsesFixedFunction)
  1283. pStrip->numBones = maxNumBones;
  1284. else
  1285. pStrip->numBones = CountUniqueBonesInStrip(pStripGroup, pStrip);
  1286. }
  1287. }
  1288. //-----------------------------------------------------------------------------
  1289. // Returns true if a particular mesh is teeth
  1290. //-----------------------------------------------------------------------------
  1291. bool COptimizedModel::MeshIsTeeth( studiohdr_t *pStudioHeader, mstudiomesh_t *pStudioMesh )
  1292. {
  1293. // The mesh is teeth if it's got a skin whose material has a non-zero flags
  1294. int i;
  1295. for( i = 0; i < pStudioHeader->numskinfamilies; i++ )
  1296. {
  1297. short *pskinref = pStudioHeader->pSkinref( 0 );
  1298. pskinref += ( i * pStudioHeader->numskinref );
  1299. mstudiotexture_t *ptexture;
  1300. ptexture = pStudioHeader->pTexture( pskinref[ pStudioMesh->material ] );
  1301. if( ptexture->flags )
  1302. {
  1303. return true;
  1304. }
  1305. }
  1306. return false;
  1307. }
  1308. //-----------------------------------------------------------------------------
  1309. // Computes the flags for a mesh
  1310. //-----------------------------------------------------------------------------
  1311. void COptimizedModel::ComputeMeshFlags( Mesh_t *pMesh, studiohdr_t *pStudioHeader,
  1312. mstudiomesh_t *pStudioMesh )
  1313. {
  1314. // eyeball?
  1315. pMesh->flags = 0;
  1316. if( pStudioMesh->materialtype != 0 )
  1317. {
  1318. pMesh->flags |= MESH_IS_EYES;
  1319. }
  1320. // teeth?
  1321. if( MeshIsTeeth( pStudioHeader, pStudioMesh ) )
  1322. {
  1323. pMesh->flags |= MESH_IS_TEETH;
  1324. }
  1325. }
  1326. //-----------------------------------------------------------------------------
  1327. // Creates 4 strip groups for a mesh, combinations of flexed + hwskinned
  1328. // A mesh has a single material
  1329. //-----------------------------------------------------------------------------
  1330. void COptimizedModel::ProcessMesh( Mesh_t *pMesh, studiohdr_t *pStudioHeader,
  1331. CUtlVector<mstudioiface_t> &srcFaces,
  1332. mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh,
  1333. bool forceNoFlex, bool bForceSoftwareSkin, bool bHWFlex )
  1334. {
  1335. // Compute the mesh flags
  1336. ComputeMeshFlags( pMesh, pStudioHeader, pStudioMesh );
  1337. // We're gonna keep track of which ones we haven't processed
  1338. // because we're gonna add all unprocessed faces to the software
  1339. // lists if for some reason they don't get added to the hardware lists
  1340. TriangleProcessedList_t trianglesProcessed;
  1341. trianglesProcessed.AddMultipleToTail( srcFaces.Size() );
  1342. memset( trianglesProcessed.Base(), 0, trianglesProcessed.Size() );
  1343. // there are up to 4 stripgroups per mesh
  1344. // Note that we're gonna do the HW skinned versions first
  1345. // because they have the option of deciding not to be hardware after all
  1346. int isHWSkinned, isFlexed;
  1347. for( isHWSkinned = bForceSoftwareSkin ? 0 : 1; isHWSkinned >= 0; --isHWSkinned )
  1348. {
  1349. for( isFlexed = 1; isFlexed >= 0; --isFlexed )
  1350. {
  1351. int realMaxBonesPerTri, realMaxBonesPerVert, realMaxBonesPerStrip;
  1352. if( isFlexed && !bHWFlex )
  1353. {
  1354. realMaxBonesPerTri = 1;
  1355. realMaxBonesPerVert = 1;
  1356. realMaxBonesPerStrip = 1;
  1357. }
  1358. else
  1359. {
  1360. realMaxBonesPerTri = m_MaxBonesPerTri;
  1361. realMaxBonesPerVert = m_MaxBonesPerVert;
  1362. realMaxBonesPerStrip = m_MaxBonesPerStrip;
  1363. }
  1364. int newStripGroupIndex = pMesh->stripGroups.AddToTail( );
  1365. StripGroup_t& newStripGroup = pMesh->stripGroups[newStripGroupIndex];
  1366. ProcessStripGroup( &newStripGroup,
  1367. isHWSkinned ? true : false,
  1368. isFlexed ? true : false,
  1369. pStudioModel, pStudioMesh, srcFaces, trianglesProcessed,
  1370. realMaxBonesPerVert, realMaxBonesPerTri, realMaxBonesPerStrip, forceNoFlex, bHWFlex );
  1371. PostProcessStripGroup( pStudioModel, pStudioMesh, &newStripGroup );
  1372. // Clear out the strip group if there wasn't anything in it
  1373. if( !newStripGroup.indices.Size() )
  1374. pMesh->stripGroups.FastRemove( newStripGroupIndex );
  1375. }
  1376. }
  1377. }
  1378. //-----------------------------------------------------------------------------
  1379. // some setup required before we really get into it
  1380. //-----------------------------------------------------------------------------
  1381. void COptimizedModel::SetupMeshProcessing( studiohdr_t *pHdr, int vertexCacheSize,
  1382. bool usesFixedFunction, int maxBonesPerVert, int maxBonesPerTri,
  1383. int maxBonesPerStrip, const char *fileName )
  1384. {
  1385. #ifdef NVTRISTRIP
  1386. // tell nvtristrip all of it's params
  1387. SetCacheSize( vertexCacheSize );
  1388. SetStitchStrips( true );
  1389. SetMinStripSize( 0 );
  1390. # ifdef EMIT_TRILISTS
  1391. SetListsOnly( true );
  1392. # else
  1393. SetListsOnly( false );
  1394. # endif
  1395. #endif // NVTRISTRIP
  1396. if( !g_quiet )
  1397. {
  1398. printf( "---------------------\n" );
  1399. printf( "Generating optimized mesh \"%s\":\n", fileName );
  1400. #ifdef _DEBUG
  1401. printf( "\tvertex cache size: %d\n", vertexCacheSize );
  1402. printf( "\tmax bones/tri: %d\n", maxBonesPerTri );
  1403. printf( "\tmax bones/vert: %d\n", maxBonesPerVert );
  1404. printf( "\tmax bones/strip: %d\n", maxBonesPerStrip );
  1405. #endif
  1406. }
  1407. CleanupEverything();
  1408. // Total number of bones in the original model
  1409. m_NumBones = pHdr->numbones;
  1410. // Hardware limitations
  1411. m_MaxBonesPerVert = maxBonesPerVert;
  1412. m_MaxBonesPerTri = maxBonesPerTri;
  1413. m_MaxBonesPerStrip = maxBonesPerStrip;
  1414. m_UsesFixedFunction = usesFixedFunction;
  1415. m_VertexCacheSize = vertexCacheSize;
  1416. // stats
  1417. m_NumSkinnedAndFlexedVerts = 0;
  1418. }
  1419. //-----------------------------------------------------------------------------
  1420. // Process the entire model, return stats...
  1421. //-----------------------------------------------------------------------------
  1422. int COptimizedModel::GetTotalVertsForMesh( Mesh_t *pMesh )
  1423. {
  1424. int numVerts = 0;
  1425. int i;
  1426. for( i = 0; i < pMesh->stripGroups.Size(); i++ )
  1427. {
  1428. StripGroup_t *pStripGroup = &pMesh->stripGroups[i];
  1429. numVerts += pStripGroup->verts.Size();
  1430. }
  1431. return numVerts;
  1432. }
  1433. int COptimizedModel::GetTotalIndicesForMesh( Mesh_t *pMesh )
  1434. {
  1435. int numIndices = 0;
  1436. int i;
  1437. for( i = 0; i < pMesh->stripGroups.Size(); i++ )
  1438. {
  1439. StripGroup_t *pStripGroup = &pMesh->stripGroups[i];
  1440. numIndices += pStripGroup->indices.Size();
  1441. }
  1442. return numIndices;
  1443. }
  1444. int COptimizedModel::GetTotalStripsForMesh( Mesh_t *pMesh )
  1445. {
  1446. int numStrips = 0;
  1447. int i;
  1448. for( i = 0; i < pMesh->stripGroups.Size(); i++ )
  1449. {
  1450. StripGroup_t *pStripGroup = &pMesh->stripGroups[i];
  1451. numStrips += pStripGroup->strips.Size();
  1452. }
  1453. return numStrips;
  1454. }
  1455. int COptimizedModel::GetTotalStripGroupsForMesh( Mesh_t *pMesh )
  1456. {
  1457. return pMesh->stripGroups.Size();
  1458. }
  1459. int COptimizedModel::GetTotalBoneStateChangesForMesh( Mesh_t *pMesh )
  1460. {
  1461. int numBoneStateChanges = 0;
  1462. int i;
  1463. for( i = 0; i < pMesh->stripGroups.Size(); i++ )
  1464. {
  1465. StripGroup_t *pStripGroup = &pMesh->stripGroups[i];
  1466. int j;
  1467. for( j = 0; j < pStripGroup->strips.Size(); j++ )
  1468. {
  1469. Strip_t *pStrip = &pStripGroup->strips[j];
  1470. numBoneStateChanges += pStrip->numBoneStateChanges;
  1471. }
  1472. }
  1473. return numBoneStateChanges;
  1474. }
  1475. /*
  1476. static void WriteDebugFile( const char *fileName, const char *outFileName, float red, float grn, float blu )
  1477. {
  1478. char *tmpName = ( char * )_alloca( strlen( fileName ) + 1 );
  1479. strcpy( tmpName, fileName );
  1480. s_source_t *pSrc = Load_Source( tmpName, "SMD" );
  1481. Assert( pSrc );
  1482. int i, j;
  1483. FILE *fp;
  1484. fp = fopen( outFileName, "w" );
  1485. Assert( fp );
  1486. for( i = 0; i < pSrc->nummeshes; i++ )
  1487. {
  1488. s_mesh_t *pMesh = &pSrc->mesh[i];
  1489. for( j = 0; j < pMesh->numfaces; j++ )
  1490. {
  1491. s_face_t *pFace = &pSrc->face[pMesh->faceoffset + j];
  1492. Vector &a = pSrc->vertex[pMesh->vertexoffset + pFace->a];
  1493. Vector &b = pSrc->vertex[pMesh->vertexoffset + pFace->b];
  1494. Vector &c = pSrc->vertex[pMesh->vertexoffset + pFace->c];
  1495. fprintf( fp, "3\n" );
  1496. fprintf( fp, "%f %f %f %f %f %f\n", ( float )( a[0] ), ( float )( a[1] ), ( float )( a[2] ),
  1497. ( float )red, ( float )grn, ( float )blu );
  1498. fprintf( fp, "%f %f %f %f %f %f\n", ( float )( b[0] ), ( float )( b[1] ), ( float )( b[2] ),
  1499. ( float )red, ( float )grn, ( float )blu );
  1500. fprintf( fp, "%f %f %f %f %f %f\n", ( float )( c[0] ), ( float )( c[1] ), ( float )( c[2] ),
  1501. ( float )red, ( float )grn, ( float )blu );
  1502. }
  1503. }
  1504. fclose( fp );
  1505. }
  1506. */
  1507. void COptimizedModel::SourceMeshToTriangleList( s_model_t *pSrcModel, s_mesh_t *pSrcMesh,
  1508. CUtlVector<mstudioiface_t> &meshTriangleList )
  1509. {
  1510. s_face_t *pFaces = pSrcModel->source->face + pSrcMesh->faceoffset;
  1511. int i;
  1512. for ( i = 0; i < pSrcMesh->numfaces; i++ )
  1513. {
  1514. const s_face_t &pFace = pFaces[i];
  1515. int j = meshTriangleList.AddToTail();
  1516. mstudioiface_t &newTriangle = meshTriangleList[j];
  1517. newTriangle.a = pFace.a;
  1518. newTriangle.b = pFace.b;
  1519. newTriangle.c = pFace.c;
  1520. }
  1521. }
  1522. /*
  1523. static void WriteSourceMesh( s_model_t *pSrcModel, s_mesh_t *pSrcMesh, int red_, int grn_, int blu_ )
  1524. {
  1525. FILE *fp;
  1526. fp = fopen( "blah.glv", "a+" );
  1527. float red, grn, blu;
  1528. red = ( float )rand() / ( float )VALVE_RAND_MAX;
  1529. grn = ( float )rand() / ( float )VALVE_RAND_MAX;
  1530. blu = ( float )rand() / ( float )VALVE_RAND_MAX;
  1531. float len = red * red + grn * grn + blu * blu;
  1532. len = sqrt( len );
  1533. red *= 255.0f / len;
  1534. grn *= 255.0f / len;
  1535. blu *= 255.0f / len;
  1536. s_face_t *pFaces = pSrcModel->source->face + pSrcMesh->faceoffset;
  1537. int i;
  1538. for( i = 0; i < pSrcMesh->numfaces; i++ )
  1539. {
  1540. const s_face_t &face = pFaces[i];
  1541. Vector a, b, c;
  1542. a = pSrcModel->source->vertex[pSrcMesh->vertexoffset + face.a];
  1543. b = pSrcModel->source->vertex[pSrcMesh->vertexoffset + face.b];
  1544. c = pSrcModel->source->vertex[pSrcMesh->vertexoffset + face.c];
  1545. fprintf( fp, "3\n" );
  1546. fprintf( fp, "%f %f %f %f %f %f\n", a[0], a[1], a[2], red, grn, blu );
  1547. fprintf( fp, "%f %f %f %f %f %f\n", c[0], c[1], c[2], red, grn, blu );
  1548. fprintf( fp, "%f %f %f %f %f %f\n", b[0], b[1], b[2], red, grn, blu );
  1549. }
  1550. fclose( fp );
  1551. }
  1552. */
  1553. static void RandomColor( Vector& color )
  1554. {
  1555. color[0] = ( ( float )rand() ) / ( float )VALVE_RAND_MAX;
  1556. color[1] = ( ( float )rand() ) / ( float )VALVE_RAND_MAX;
  1557. color[2] = ( ( float )rand() ) / ( float )VALVE_RAND_MAX;
  1558. VectorNormalize( color );
  1559. }
  1560. /*
  1561. static void GLViewCube( Vector pos, float size, FILE *fp )
  1562. {
  1563. fprintf( fp, "4\n" );
  1564. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] + size, pos[2] + size );
  1565. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] + size, pos[2] + size );
  1566. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] - size, pos[2] + size );
  1567. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] - size, pos[2] + size );
  1568. fprintf( fp, "4\n" );
  1569. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] + size, pos[2] - size );
  1570. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] + size, pos[2] - size );
  1571. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] - size, pos[2] - size );
  1572. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] - size, pos[2] - size );
  1573. fprintf( fp, "4\n" );
  1574. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] - size, pos[2] - size );
  1575. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] - size, pos[2] - size );
  1576. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] - size, pos[2] + size );
  1577. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] - size, pos[2] + size );
  1578. fprintf( fp, "4\n" );
  1579. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] + size, pos[2] - size );
  1580. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] + size, pos[2] - size );
  1581. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] + size, pos[2] + size );
  1582. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] + size, pos[2] + size );
  1583. fprintf( fp, "4\n" );
  1584. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] + size, pos[2] - size );
  1585. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] - size, pos[2] - size );
  1586. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] + size, pos[2] + size );
  1587. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] - size, pos[1] - size, pos[2] + size );
  1588. fprintf( fp, "4\n" );
  1589. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] + size, pos[2] - size );
  1590. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] - size, pos[2] - size );
  1591. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] + size, pos[2] + size );
  1592. fprintf( fp, "%f %f %f 255 0 0\n", pos[0] + size, pos[1] - size, pos[2] + size );
  1593. }
  1594. */
  1595. #if 0
  1596. static void MStudioBoneWeightToSBoneWeight( s_boneweight_t &sbone, const mstudioboneweight_t &mbone,
  1597. const s_source_t *pSrc )
  1598. {
  1599. int i;
  1600. for( i = 0; i < mbone.numbones; i++ )
  1601. {
  1602. sbone.bone[i] = pSrc->boneimap[mbone.bone[i]];
  1603. // sbone.bone[i] = mbone.bone[i];
  1604. sbone.weight[i] = mbone.weight[i];
  1605. }
  1606. sbone.numbones = mbone.numbones;
  1607. }
  1608. #endif
  1609. void COptimizedModel::CreateLODTriangleList( s_model_t *pSrcModel, int nLodID, s_source_t* pSrc,
  1610. mstudiomodel_t *pStudioModel,
  1611. mstudiomesh_t *pStudioMesh,
  1612. CUtlVector<mstudioiface_t> &meshTriangleList,
  1613. bool writeDebug )
  1614. {
  1615. if ( !pSrc || !pSrcModel )
  1616. return;
  1617. #ifdef _DEBUG
  1618. // const mstudio_modelvertexdata_t *vertData = pStudioModel->GetVertexData();
  1619. // Assert( vertData ); // This can only return NULL on X360 for now
  1620. // mstudiovertex_t *modelFirstVert = vertData->Vertex( 0 );
  1621. // mstudiovertex_t *modelLastVert = vertData->Vertex( pStudioModel->numvertices - 1 );
  1622. // mstudiovertex_t *meshFirstVert = vertData->Vertex( 0 );
  1623. // mstudiovertex_t *meshLastVert = vertData->Vertex( pStudioMesh->numvertices - 1 );
  1624. #endif
  1625. /*
  1626. if( writeDebug )
  1627. {
  1628. printf( "MODEL VERTS:\n" );
  1629. int i;
  1630. for( i = 0; i < pStudioModel->numvertices; i++ )
  1631. {
  1632. Vector &v = *pStudioModel->pVertex( i );
  1633. Vector &n = *pStudioModel->pNormal( i );
  1634. Vector2D &t = *pStudioModel->pTexcoord( i );
  1635. printf( "model %d: p %f %f %f n: %f %f %f t: %f %f\n",
  1636. i, v[0], v[1], v[2], n[0], n[1], n[2], t[0], t[1] );
  1637. }
  1638. printf( "MESH VERTS:\n" );
  1639. for( i = 0; i < pStudioMesh->numvertices; i++ )
  1640. {
  1641. Vector &v = *pStudioMesh->pVertex( i );
  1642. Vector &n = *pStudioMesh->pNormal( i );
  1643. Vector2D &t = *pStudioMesh->pTexcoord( i );
  1644. printf( "mesh %d: p %f %f %f n: %f %f %f t: %f %f\n",
  1645. i, v[0], v[1], v[2], n[0], n[1], n[2], t[0], t[1] );
  1646. }
  1647. }
  1648. */
  1649. // need to find the mesh in the lod model that matches the original model.
  1650. int i;
  1651. int textureSearchID = MaterialToTexture( pStudioMesh->material );
  1652. // this is icky.. . pSrc->nummeshes really refers to the total number of non-empty meshes
  1653. // in the model. In pSrc->meshes, there are some empty meshes in the middle if they
  1654. // don't exist for the material, so you have to go through all of the meshes to find
  1655. // the non-empty ones.
  1656. s_mesh_t *pSrcMesh = NULL;
  1657. for ( i = 0; i < pStudioModel->nummeshes; i++ )
  1658. {
  1659. if ( pSrc->texmap[pSrc->meshindex[i]] == textureSearchID )
  1660. {
  1661. // woo hoo! found it.
  1662. pSrcMesh = &pSrc->mesh[pSrc->meshindex[i]];
  1663. break;
  1664. }
  1665. }
  1666. if ( !pSrcMesh )
  1667. {
  1668. //printf( "%s doesn't have material %d\n", lodSMDName, textureSearchID );
  1669. // There aren't any triangles in this lower lod with this material on it.
  1670. return;
  1671. }
  1672. CUtlVector<int> indexMapping;
  1673. indexMapping.AddMultipleToTail( pSrcMesh->numvertices );
  1674. for ( i = 0; i < pSrcMesh->numvertices; i++ )
  1675. {
  1676. // get the mapping between indices in the lod and their real pool location.
  1677. indexMapping[i] = pSrcModel->m_pLodData->pMeshVertIndexMaps[nLodID][pSrcMesh->vertexoffset + i];
  1678. }
  1679. // build the lod's faces so indexes map to remapped vertexes
  1680. for ( i = 0; i < pSrcMesh->numfaces; i++ )
  1681. {
  1682. int index = meshTriangleList.AddToTail();
  1683. mstudioiface_t& newFace = meshTriangleList[index];
  1684. const s_face_t &srcFace = pSrc->face[pSrcMesh->faceoffset + i];
  1685. newFace.a = indexMapping[srcFace.a];
  1686. newFace.b = indexMapping[srcFace.b];
  1687. newFace.c = indexMapping[srcFace.c];
  1688. }
  1689. }
  1690. bool ComparePath( const char *a, const char *b )
  1691. {
  1692. if ( strlen( a ) != strlen( b ) )
  1693. {
  1694. return false;
  1695. }
  1696. // case and seperator invariant
  1697. for ( ; *a; a++, b++ )
  1698. {
  1699. if ( *a == *b )
  1700. {
  1701. continue;
  1702. }
  1703. if ( tolower( *a ) == tolower( *b ) )
  1704. {
  1705. continue;
  1706. }
  1707. if ( ( *a == '/' || *a == '\\' ) &&
  1708. ( *b == '/' || *b == '\\' ) )
  1709. {
  1710. continue;
  1711. }
  1712. return false;
  1713. }
  1714. return true;
  1715. }
  1716. bool COptimizedModel::MeshNeedsRemoval( studiohdr_t *pHdr, mstudiomesh_t *pStudioMesh,
  1717. LodScriptData_t& scriptLOD )
  1718. {
  1719. mstudiotexture_t *ptexture;
  1720. short *pskinref = pHdr->pSkinref( 0 );
  1721. ptexture = pHdr->pTexture( pskinref[ pStudioMesh->material ] );
  1722. const char *meshName = ptexture->material->GetName();
  1723. int i;
  1724. for( i = 0; i < scriptLOD.meshRemovals.Size(); i++ )
  1725. {
  1726. const char *meshRemovalName = scriptLOD.meshRemovals[i].GetSrcName();
  1727. if ( ComparePath( meshName, meshRemovalName ) )
  1728. {
  1729. return true;
  1730. }
  1731. }
  1732. return false;
  1733. }
  1734. //-----------------------------------------------------------------------------
  1735. // Process the entire model, return stats...
  1736. //-----------------------------------------------------------------------------
  1737. void COptimizedModel::ProcessModel( studiohdr_t *pHdr, s_bodypart_t *pSrcBodyParts,
  1738. TotalMeshStats_t& stats, bool bForceSoftwareSkin, bool bHWFlex )
  1739. {
  1740. memset( &stats, 0, sizeof(stats) );
  1741. m_Models.RemoveAll();
  1742. int bodyPartID, modelID, meshID, lodID;
  1743. for ( bodyPartID = 0; bodyPartID < pHdr->numbodyparts; bodyPartID++, stats.m_TotalBodyParts++ )
  1744. {
  1745. mstudiobodyparts_t *pBodyPart = pHdr->pBodypart( bodyPartID );
  1746. s_bodypart_t *pSrcBodyPart = &pSrcBodyParts[bodyPartID];
  1747. for ( modelID = 0; modelID < pBodyPart->nummodels; modelID++, stats.m_TotalModels++ )
  1748. {
  1749. int i = m_Models.AddToTail();
  1750. Model_t& newModel = m_Models[i];
  1751. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  1752. s_model_t *pSrcModel = pSrcBodyPart->pmodel[modelID];
  1753. for ( lodID = 0; lodID < g_ScriptLODs.Count(); lodID++, stats.m_TotalModelLODs++ )
  1754. {
  1755. LodScriptData_t& scriptLOD = g_ScriptLODs[lodID];
  1756. s_source_t *pLODSource = pSrcModel->m_LodSources[lodID];
  1757. int i = newModel.modelLODs.AddToTail();
  1758. Assert( i == lodID );
  1759. ModelLOD_t& newLOD = newModel.modelLODs[i];
  1760. newLOD.switchPoint = scriptLOD.switchValue;
  1761. // In this case, we've been told to remove the model
  1762. if ( !pLODSource )
  1763. {
  1764. if ( pSrcModel && stricmp( pSrcModel->name, "blank" ) != 0)
  1765. {
  1766. // This is nonsensical
  1767. Assert( lodID != 0 );
  1768. }
  1769. continue;
  1770. }
  1771. for ( meshID = 0; meshID < pStudioModel->nummeshes; meshID++, stats.m_TotalMeshes++ )
  1772. {
  1773. #ifdef _DEBUG
  1774. // printf( "bodyPart: %d model: %d modellod: %d mesh: %d\n", bodyPartID, modelID, lodID, meshID );
  1775. #endif
  1776. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  1777. s_mesh_t *pSrcMesh = &pSrcModel->source->mesh[pSrcModel->source->meshindex[meshID]];
  1778. int i = newLOD.meshes.AddToTail();
  1779. Assert( i == meshID );
  1780. Mesh_t& newMesh = newLOD.meshes[i];
  1781. if ( MeshNeedsRemoval( pHdr, pStudioMesh, scriptLOD ) )
  1782. continue;
  1783. #ifdef _DEBUG
  1784. // int textureSearchID = material_to_texture( pStudioMesh->material );
  1785. // const char *pDebugName = pHdr->pTexture( textureSearchID )->pszName( );
  1786. #endif
  1787. CUtlVector<mstudioiface_t> meshTriangleList;
  1788. if ( pLODSource )
  1789. {
  1790. // map the lod data to triangles
  1791. // uses the original mesh redirected through a mapping table
  1792. // this expects built per lod-to-root mapping tables to generate faces
  1793. CreateLODTriangleList( pSrcModel, lodID, pLODSource, pStudioModel, pStudioMesh, meshTriangleList, false );
  1794. }
  1795. else
  1796. {
  1797. // build the triangle list from the unmapped source
  1798. SourceMeshToTriangleList( pSrcModel, pSrcMesh, meshTriangleList );
  1799. }
  1800. ProcessMesh( &newMesh, pHdr, meshTriangleList, pStudioModel, pStudioMesh,
  1801. !scriptLOD.GetFacialAnimationEnabled(), bForceSoftwareSkin, bHWFlex );
  1802. stats.m_TotalVerts += GetTotalVertsForMesh( &newMesh );
  1803. stats.m_TotalIndices += GetTotalIndicesForMesh( &newMesh );
  1804. stats.m_TotalStrips += GetTotalStripsForMesh( &newMesh );
  1805. stats.m_TotalStripGroups += GetTotalStripGroupsForMesh( &newMesh );
  1806. stats.m_TotalBoneStateChanges += GetTotalBoneStateChangesForMesh( &newMesh );
  1807. }
  1808. }
  1809. }
  1810. }
  1811. }
  1812. //-----------------------------------------------------------------------------
  1813. // Some processing that happens at the end
  1814. //-----------------------------------------------------------------------------
  1815. void COptimizedModel::MapGlobalBonesToHardwareBoneIDsAndSortBones( studiohdr_t *phdr )
  1816. {
  1817. // garymcthack: holy jesus is this function long!
  1818. #ifdef IGNORE_BONES
  1819. return;
  1820. #endif
  1821. int *globalToHardwareBoneIndex;
  1822. int hardwareToGlobalBoneIndex[MAX_NUM_BONES_PER_STRIP];
  1823. globalToHardwareBoneIndex = ( int * )_alloca( m_NumBones * sizeof( int ) );
  1824. Assert( globalToHardwareBoneIndex );
  1825. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  1826. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  1827. {
  1828. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  1829. mstudiobodyparts_t *pStudioBodyPart = phdr->pBodypart( bodyPartID );
  1830. for( int lodID = 0; lodID < header->numLODs; lodID++ )
  1831. {
  1832. // Dear LORD! This needs to be in another function! (garymcthack)
  1833. int i;
  1834. for( i = 0; i < m_NumBones; i++ )
  1835. {
  1836. globalToHardwareBoneIndex[i] = -1;
  1837. }
  1838. for( i = 0; i < MAX_NUM_BONES_PER_STRIP; i++ )
  1839. {
  1840. hardwareToGlobalBoneIndex[i] = -1;
  1841. }
  1842. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  1843. {
  1844. ModelHeader_t *model = bodyPart->pModel( modelID );
  1845. mstudiomodel_t *pStudioModel = pStudioBodyPart->pModel( modelID );
  1846. ModelLODHeader_t *pLOD = model->pLOD( lodID );
  1847. for( int meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  1848. {
  1849. MeshHeader_t *mesh = pLOD->pMesh( meshID );
  1850. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  1851. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  1852. {
  1853. // Only do this to HW-skinned meshes
  1854. StripGroupHeader_t *pStripGroup = mesh->pStripGroup( stripGroupID );
  1855. if( !( pStripGroup->flags & STRIPGROUP_IS_HWSKINNED ) )
  1856. {
  1857. continue;
  1858. }
  1859. for( int stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  1860. {
  1861. // printf( "UPDATING BONE STATE\n" );
  1862. StripHeader_t *pStrip = pStripGroup->pStrip( stripID );
  1863. // Generate mapping back and forth between hardware IDs and original bone IDs
  1864. int boneStateChangeID;
  1865. for( boneStateChangeID = 0; boneStateChangeID < pStrip->numBoneStateChanges; boneStateChangeID++ )
  1866. {
  1867. BoneStateChangeHeader_t *pBoneStateChange = pStrip->pBoneStateChange( boneStateChangeID );
  1868. globalToHardwareBoneIndex[pBoneStateChange->newBoneID] = pBoneStateChange->hardwareID;
  1869. if( pBoneStateChange->hardwareID != -1 )
  1870. {
  1871. hardwareToGlobalBoneIndex[pBoneStateChange->hardwareID] = pBoneStateChange->newBoneID;
  1872. }
  1873. }
  1874. int vertID;
  1875. for( vertID = 0; vertID < pStrip->numVerts; vertID++ )
  1876. {
  1877. Vertex_t *vert = pStripGroup->pVertex( vertID + pStrip->vertOffset );
  1878. int boneID;
  1879. for( boneID = 0; boneID < header->maxBonesPerVert; boneID++ )
  1880. {
  1881. int globalBoneID = vert->boneID[boneID];
  1882. if( globalBoneID == -1 )
  1883. {
  1884. // this index isn't used.
  1885. vert->boneID[boneID] = 0;
  1886. // make sure it's associated weight is zero.
  1887. // Assert( vert->boneWeights[boneID] == 0 );
  1888. continue;
  1889. }
  1890. Assert( globalBoneID >= 0 && globalBoneID < m_NumBones );
  1891. vert->boneID[boneID] = globalToHardwareBoneIndex[globalBoneID];
  1892. Assert( vert->boneID[boneID] >= 0 && vert->boneID[boneID] < header->maxBonesPerStrip );
  1893. }
  1894. // We only do index palette skinning when we're not doing fixed function
  1895. if (m_UsesFixedFunction)
  1896. {
  1897. // bool flexed = pStripGroup->flags & STRIPGROUP_IS_FLEXED;
  1898. SortBonesWithinVertex( false /* flexed */, vert, pStudioModel,
  1899. pStudioMesh, globalToHardwareBoneIndex, hardwareToGlobalBoneIndex,
  1900. m_MaxBonesPerTri, m_MaxBonesPerVert );
  1901. }
  1902. }
  1903. }
  1904. }
  1905. }
  1906. }
  1907. }
  1908. }
  1909. }
  1910. //-----------------------------------------------------------------------------
  1911. //
  1912. // The following methods are associated with writing a .vtx file
  1913. //
  1914. //-----------------------------------------------------------------------------
  1915. void COptimizedModel::WriteHeader( int vertCacheSize, int maxBonesPerVert,
  1916. int maxBonesPerTri, int maxBonesPerStrip, int numBodyParts, long checkSum )
  1917. {
  1918. FileHeader_t fileHeader;
  1919. fileHeader.version = OPTIMIZED_MODEL_FILE_VERSION;
  1920. fileHeader.vertCacheSize = vertCacheSize;
  1921. fileHeader.maxBonesPerTri = IsUShort( maxBonesPerTri );
  1922. fileHeader.maxBonesPerVert = maxBonesPerVert;
  1923. fileHeader.maxBonesPerStrip = IsUShort( maxBonesPerStrip );
  1924. fileHeader.numBodyParts = numBodyParts;
  1925. fileHeader.bodyPartOffset = sizeof( FileHeader_t );
  1926. fileHeader.checkSum = checkSum;
  1927. fileHeader.numLODs = g_ScriptLODs.Size();
  1928. fileHeader.materialReplacementListOffset = m_MaterialReplacementsListOffset;
  1929. m_FileBuffer->WriteAt( 0, &fileHeader, sizeof( FileHeader_t ), "header" );
  1930. #ifdef _DEBUG
  1931. // FileHeader_t *debug = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  1932. // BodyPartHeader_t *pBodyPart = debug->pBodyPart( 0 );
  1933. #endif
  1934. }
  1935. void COptimizedModel::WriteBodyPart( int bodyPartID, mstudiobodyparts_t *pBodyPart, int modelID )
  1936. {
  1937. BodyPartHeader_t bodyPart;
  1938. bodyPart.numModels = pBodyPart->nummodels;
  1939. int bodyPartOffset = m_BodyPartsOffset + bodyPartID * sizeof( BodyPartHeader_t );
  1940. int modelFileOffset = m_ModelsOffset + modelID * sizeof( ModelHeader_t );
  1941. bodyPart.modelOffset = modelFileOffset - bodyPartOffset;
  1942. m_FileBuffer->WriteAt( bodyPartOffset, &bodyPart, sizeof( BodyPartHeader_t ), "bodypart" );
  1943. #ifdef _DEBUG
  1944. // BodyPartHeader_t *debug = ( BodyPartHeader_t * )m_FileBuffer->GetPointer( bodyPartOffset );
  1945. // ModelHeader_t *pModel = debug->pModel( 0 );
  1946. #endif
  1947. }
  1948. void COptimizedModel::WriteModel( int modelID, mstudiomodel_t *pModel, int lodID )
  1949. {
  1950. ModelHeader_t model;
  1951. model.numLODs = IsChar( g_ScriptLODs.Size() );
  1952. int modelFileOffset = m_ModelsOffset + modelID * sizeof( ModelHeader_t );
  1953. int lodFileOffset = m_ModelLODsOffset + lodID * sizeof( ModelLODHeader_t );
  1954. model.lodOffset = lodFileOffset - modelFileOffset;
  1955. m_FileBuffer->WriteAt( modelFileOffset, &model, sizeof( ModelHeader_t ), "model" );
  1956. #ifdef _DEBUG
  1957. // ModelHeader_t *debug = ( ModelHeader_t * )m_FileBuffer->GetPointer( modelFileOffset );
  1958. // ModelLODHeader_t *pLOD = debug->pLOD( 0 );
  1959. #endif
  1960. }
  1961. void COptimizedModel::WriteModelLOD( int lodID, ModelLOD_t *pLOD, int meshID )
  1962. {
  1963. ModelLODHeader_t lod;
  1964. int lodFileOffset = m_ModelLODsOffset + lodID * sizeof( ModelLODHeader_t );
  1965. int meshFileOffset = m_MeshesOffset + meshID * sizeof( MeshHeader_t );
  1966. lod.meshOffset = meshFileOffset - lodFileOffset;
  1967. lod.numMeshes = pLOD->meshes.Size();
  1968. lod.switchPoint = pLOD->switchPoint;
  1969. m_FileBuffer->WriteAt( lodFileOffset, &lod, sizeof( ModelLODHeader_t ), "modellod" );
  1970. #ifdef _DEBUG
  1971. // ModelLODHeader_t *debug = ( ModelLODHeader_t * )m_FileBuffer->GetPointer( lodFileOffset );
  1972. // MeshHeader_t *pMesh = debug->pMesh( 0 );
  1973. #endif
  1974. }
  1975. void COptimizedModel::WriteMesh( int meshID, Mesh_t *pMesh, int stripGroupID )
  1976. {
  1977. MeshHeader_t mesh;
  1978. mesh.numStripGroups = IsChar( pMesh->stripGroups.Size() );
  1979. int meshFileOffset = m_MeshesOffset + meshID * sizeof( MeshHeader_t );
  1980. int stripGroupFileOffset = m_StripGroupsOffset + stripGroupID * sizeof( StripGroupHeader_t );
  1981. mesh.stripGroupHeaderOffset = stripGroupFileOffset - meshFileOffset;
  1982. mesh.flags = pMesh->flags;
  1983. m_FileBuffer->WriteAt( meshFileOffset, &mesh, sizeof( MeshHeader_t ), "mesh" );
  1984. #ifdef _DEBUG
  1985. // MeshHeader_t *debug = ( MeshHeader_t * )m_FileBuffer->GetPointer( meshFileOffset );
  1986. // StripGroupHeader_t *pStripGroup = debug->pStripGroup( 0 );
  1987. #endif
  1988. }
  1989. void COptimizedModel::WriteStripGroup( int stripGroupID, StripGroup_t *pStripGroup,
  1990. int vertID, int indexID, int stripID )
  1991. {
  1992. StripGroupHeader_t stripGroup;
  1993. stripGroup.numVerts = pStripGroup->verts.Size();
  1994. stripGroup.numIndices = pStripGroup->indices.Size();
  1995. stripGroup.numStrips = pStripGroup->strips.Size();
  1996. stripGroup.flags = IsByte( pStripGroup->flags );
  1997. int stripGroupFileOffset = m_StripGroupsOffset + stripGroupID * sizeof( StripGroupHeader_t );
  1998. int vertsFileOffset = m_VertsOffset + vertID * sizeof( Vertex_t );
  1999. int indicesFileOffset = m_IndicesOffset + indexID * sizeof( unsigned short );
  2000. int stripsFileOffset = m_StripsOffset + stripID * sizeof( StripHeader_t );
  2001. stripGroup.vertOffset = vertsFileOffset - stripGroupFileOffset;
  2002. stripGroup.indexOffset = indicesFileOffset - stripGroupFileOffset;
  2003. stripGroup.stripOffset = stripsFileOffset - stripGroupFileOffset;
  2004. m_FileBuffer->WriteAt( stripGroupFileOffset, &stripGroup, sizeof( StripGroupHeader_t), "strip group" );
  2005. #ifdef _DEBUG
  2006. // StripGroupHeader_t *debug = ( StripGroupHeader_t * )m_FileBuffer->GetPointer( stripGroupFileOffset );
  2007. // unsigned short *pIndex = debug->pIndex( 0 );
  2008. // Vertex_t *pVert = debug->pVertex( 0 );
  2009. // StripHeader_t *pStripHeader = debug->pStrip( 0 );
  2010. #endif
  2011. }
  2012. int COptimizedModel::WriteVerts( int vertID, StripGroup_t *pStripGroup )
  2013. {
  2014. int vertFileOffset = m_VertsOffset + vertID * sizeof( Vertex_t );
  2015. int numVerts = pStripGroup->verts.Size();
  2016. m_FileBuffer->WriteAt( vertFileOffset, &pStripGroup->verts[0], sizeof( Vertex_t ) * numVerts, "verts" );
  2017. #ifdef _DEBUG
  2018. // Vertex_t *debug = ( Vertex_t * )m_FileBuffer->GetPointer( vertFileOffset );
  2019. #endif
  2020. return numVerts;
  2021. }
  2022. int COptimizedModel::WriteIndices( int indexID, StripGroup_t *pStripGroup )
  2023. {
  2024. int indexFileOffset = m_IndicesOffset + indexID * sizeof( unsigned short );
  2025. int numIndices = pStripGroup->indices.Size();
  2026. m_FileBuffer->WriteAt( indexFileOffset, &pStripGroup->indices[0], sizeof( unsigned short ) * numIndices, "indices" );
  2027. #ifdef _DEBUG
  2028. // unsigned short *debug = ( unsigned short * )m_FileBuffer->GetPointer( indexFileOffset );
  2029. #endif
  2030. return numIndices;
  2031. }
  2032. void COptimizedModel::WriteStrip( int stripID, Strip_t *pStrip, int indexID, int vertID, int boneID )
  2033. {
  2034. StripHeader_t stripHeader;
  2035. stripHeader.numIndices = pStrip->numStripGroupIndices;
  2036. stripHeader.indexOffset = pStrip->stripGroupIndexOffset;
  2037. stripHeader.numVerts = pStrip->numStripGroupVerts;
  2038. stripHeader.vertOffset = pStrip->stripGroupVertexOffset;
  2039. stripHeader.numBoneStateChanges = pStrip->numBoneStateChanges;
  2040. stripHeader.numBones = IsShort( pStrip->numBones );
  2041. stripHeader.flags = IsByte( pStrip->flags );
  2042. int boneFileOffset = m_BoneStageChangesOffset + boneID * sizeof( BoneStateChangeHeader_t );
  2043. int stripFileOffset = m_StripsOffset + stripID * sizeof( StripHeader_t );
  2044. stripHeader.boneStateChangeOffset = IsInt24( boneFileOffset - stripFileOffset );
  2045. m_FileBuffer->WriteAt( stripFileOffset, &stripHeader, sizeof( StripHeader_t ), "strip" );
  2046. #ifdef _DEBUG
  2047. // StripHeader_t *debug = ( StripHeader_t* )m_FileBuffer->GetPointer( stripFileOffset );
  2048. // BoneStateChangeHeader_t *pBoneStateChange = debug->pBoneStateChange( 0 );
  2049. #endif
  2050. }
  2051. void COptimizedModel::WriteBoneStateChange( int boneID, BoneStateChange_t *boneStateChange )
  2052. {
  2053. BoneStateChangeHeader_t boneHeader;
  2054. boneHeader.hardwareID = boneStateChange->hardwareID;
  2055. boneHeader.newBoneID = boneStateChange->newBoneID;
  2056. int boneFileOffset = m_BoneStageChangesOffset + boneID * sizeof( BoneStateChangeHeader_t );
  2057. #if 0
  2058. printf( "\tboneStateChange: hwid: %d boneID %d\n", ( int )boneHeader.hardwareID,
  2059. ( int )boneHeader.newBoneID );
  2060. #endif
  2061. m_FileBuffer->WriteAt( boneFileOffset, &boneHeader, sizeof( BoneStateChangeHeader_t ), "bone" );
  2062. #ifdef _DEBUG
  2063. // BoneStateChangeHeader_t *debug = ( BoneStateChangeHeader_t * )m_FileBuffer->GetPointer( boneFileOffset );
  2064. #endif
  2065. }
  2066. void COptimizedModel::ZeroNumBones( void )
  2067. {
  2068. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  2069. int bodyPartID, modelID, lodID, meshID, stripGroupID, vertID, stripID;
  2070. for( bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  2071. {
  2072. BodyPartHeader_t *pBodyPart = header->pBodyPart( bodyPartID );
  2073. for( modelID = 0; modelID < pBodyPart->numModels; modelID++ )
  2074. {
  2075. ModelHeader_t *pModel = pBodyPart->pModel( modelID );
  2076. for( lodID = 0; lodID < pModel->numLODs; lodID++ )
  2077. {
  2078. ModelLODHeader_t *pLOD = pModel->pLOD( lodID );
  2079. for( meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  2080. {
  2081. MeshHeader_t *pMesh = pLOD->pMesh( meshID );
  2082. for( stripGroupID = 0; stripGroupID < pMesh->numStripGroups; stripGroupID++ )
  2083. {
  2084. StripGroupHeader_t *pStripGroup = pMesh->pStripGroup( stripGroupID );
  2085. for( vertID = 0; vertID < pStripGroup->numVerts; vertID++ )
  2086. {
  2087. Vertex_t *pVert = pStripGroup->pVertex( vertID );
  2088. pVert->numBones = 0;
  2089. }
  2090. for( stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  2091. {
  2092. StripHeader_t *pStrip = pStripGroup->pStrip( stripID );
  2093. pStrip->numBones = 0;
  2094. pStrip->numBoneStateChanges = 0;
  2095. }
  2096. }
  2097. }
  2098. }
  2099. }
  2100. }
  2101. }
  2102. void COptimizedModel::WriteStringTable( int stringTableOffset )
  2103. {
  2104. int stringTableSize = s_StringTable.CalcSize();
  2105. if( stringTableSize == 0 )
  2106. {
  2107. return;
  2108. }
  2109. char *pTmp = new char[stringTableSize];
  2110. s_StringTable.WriteToMem( pTmp );
  2111. m_FileBuffer->WriteAt( stringTableOffset, pTmp, stringTableSize, "string table" );
  2112. // char *pDebug = ( char * )m_FileBuffer->GetPointer( stringTableOffset );
  2113. delete [] pTmp;
  2114. }
  2115. void COptimizedModel::WriteMaterialReplacements( int materialReplacementsOffset )
  2116. {
  2117. int offset = materialReplacementsOffset;
  2118. int i, j;
  2119. int numLODs = g_ScriptLODs.Size();
  2120. for( i = 0; i < numLODs; i++ )
  2121. {
  2122. LodScriptData_t &scriptLOD = g_ScriptLODs[i];
  2123. for( j = 0; j < scriptLOD.materialReplacements.Size(); j++ )
  2124. {
  2125. CLodScriptReplacement_t &materialReplacement = scriptLOD.materialReplacements[j];
  2126. MaterialReplacementHeader_t tmpHeader;
  2127. tmpHeader.materialID = FindMaterialByName( materialReplacement.GetSrcName() );
  2128. tmpHeader.replacementMaterialNameOffset = m_StringTableOffset +
  2129. s_StringTable.StringTableOffset( materialReplacement.GetDstName() ) - offset;
  2130. m_FileBuffer->WriteAt( offset, &tmpHeader, sizeof( tmpHeader ), "material replacements" );
  2131. offset += sizeof( MaterialReplacementHeader_t );
  2132. }
  2133. }
  2134. }
  2135. void COptimizedModel::WriteMaterialReplacementLists( int materialReplacementsOffset, int materialReplacementListOffset )
  2136. {
  2137. int replacementOffset = materialReplacementsOffset;
  2138. int offset = materialReplacementListOffset;
  2139. int i;
  2140. int numLODs = g_ScriptLODs.Size();
  2141. for( i = 0; i < numLODs; i++ )
  2142. {
  2143. LodScriptData_t &scriptLOD = g_ScriptLODs[i];
  2144. MaterialReplacementListHeader_t tmpHeader;
  2145. tmpHeader.numReplacements = IsChar( scriptLOD.materialReplacements.Size() );
  2146. tmpHeader.replacementOffset = IsInt24( replacementOffset - offset );
  2147. m_FileBuffer->WriteAt( offset, &tmpHeader, sizeof( tmpHeader ), "material replacement headers" );
  2148. MaterialReplacementListHeader_t *pDebugList =
  2149. ( MaterialReplacementListHeader_t * )m_FileBuffer->GetPointer( offset );
  2150. if( pDebugList->numReplacements )
  2151. {
  2152. // MaterialReplacementHeader_t *pDebug = pDebugList->pMaterialReplacement( 0 );
  2153. // const char *string = pDebug->pMaterialReplacementName();
  2154. }
  2155. replacementOffset += tmpHeader.numReplacements * sizeof( MaterialReplacementHeader_t );
  2156. offset += sizeof( MaterialReplacementListHeader_t );
  2157. }
  2158. }
  2159. void COptimizedModel::SanityCheckVertexBoneLODFlags( studiohdr_t *pStudioHdr, FileHeader_t *pVtxHeader )
  2160. {
  2161. int bodyPartID;
  2162. for( bodyPartID = 0; bodyPartID < pStudioHdr->numbodyparts; bodyPartID++ )
  2163. {
  2164. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyPartID );
  2165. BodyPartHeader_t *pVtxBodyPart = pVtxHeader->pBodyPart( bodyPartID );
  2166. int modelID;
  2167. for( modelID = 0; modelID < pBodyPart->nummodels; modelID++ )
  2168. {
  2169. mstudiomodel_t *pModel = pBodyPart->pModel( modelID );
  2170. ModelHeader_t *pVtxModel = pVtxBodyPart->pModel( modelID );
  2171. int lodID;
  2172. for( lodID = 0; lodID < pVtxModel->numLODs; lodID++ )
  2173. {
  2174. ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( lodID );
  2175. int meshID;
  2176. Assert( pVtxLOD->numMeshes == pModel->nummeshes );
  2177. for( meshID = 0; meshID < pVtxLOD->numMeshes; meshID++ )
  2178. {
  2179. MeshHeader_t *pVtxMesh = pVtxLOD->pMesh( meshID );
  2180. mstudiomesh_t *pMesh = pModel->pMesh( meshID );
  2181. int stripGroupID;
  2182. for( stripGroupID = 0; stripGroupID < pVtxMesh->numStripGroups; stripGroupID++ )
  2183. {
  2184. StripGroupHeader_t *pStripGroup = pVtxMesh->pStripGroup( stripGroupID );
  2185. int vertID;
  2186. for( vertID = 0; vertID < pStripGroup->numVerts; vertID++ )
  2187. {
  2188. Vertex_t *pVertex = pStripGroup->pVertex( vertID );
  2189. Vector pos = GetOrigVertPosition( pModel, pMesh, pVertex );
  2190. const mstudioboneweight_t &boneWeight = GetOrigVertBoneWeight( pModel, pMesh, pVertex );
  2191. int i;
  2192. for( i = 0; i < boneWeight.numbones; i++ )
  2193. {
  2194. mstudiobone_t *pBone = pStudioHdr->pBone( boneWeight.bone[i] );
  2195. // const char *pBoneName = pBone->pszName();
  2196. if (!( pBone->flags & ( BONE_USED_BY_VERTEX_LOD0 << lodID ) ))
  2197. {
  2198. MdlError("Mismarked Bone flag");
  2199. }
  2200. }
  2201. }
  2202. }
  2203. }
  2204. }
  2205. }
  2206. }
  2207. }
  2208. void COptimizedModel::WriteVTXFile( studiohdr_t *pHdr, const char *pFileName,
  2209. TotalMeshStats_t const& stats )
  2210. {
  2211. // calculate file offsets
  2212. m_FileBuffer = new CFileBuffer( FILEBUFFER_SIZE );
  2213. m_BodyPartsOffset = sizeof( FileHeader_t );
  2214. m_ModelsOffset = m_BodyPartsOffset + sizeof( BodyPartHeader_t ) * stats.m_TotalBodyParts;
  2215. m_ModelLODsOffset = m_ModelsOffset + sizeof( ModelHeader_t ) * stats.m_TotalModels;
  2216. m_MeshesOffset = m_ModelLODsOffset + sizeof( ModelLODHeader_t ) * stats.m_TotalModelLODs;
  2217. m_StripGroupsOffset = m_MeshesOffset + sizeof( MeshHeader_t ) * stats.m_TotalMeshes;
  2218. m_StripsOffset = m_StripGroupsOffset + sizeof( StripGroupHeader_t ) * stats.m_TotalStripGroups;
  2219. m_VertsOffset = m_StripsOffset + sizeof( StripHeader_t ) * stats.m_TotalStrips;
  2220. m_IndicesOffset = m_VertsOffset + sizeof( Vertex_t ) * stats.m_TotalVerts;
  2221. m_BoneStageChangesOffset = m_IndicesOffset + sizeof( unsigned short ) * stats.m_TotalIndices;
  2222. m_StringTableOffset = m_BoneStageChangesOffset + sizeof( BoneStateChangeHeader_t ) * stats.m_TotalBoneStateChanges;
  2223. m_MaterialReplacementsOffset = m_StringTableOffset + s_StringTable.CalcSize();
  2224. m_MaterialReplacementsListOffset = m_MaterialReplacementsOffset + stats.m_TotalMaterialReplacements * sizeof( MaterialReplacementHeader_t );
  2225. m_EndOfFileOffset = m_MaterialReplacementsListOffset + g_ScriptLODs.Count() * sizeof( MaterialReplacementListHeader_t );
  2226. int curModel = 0;
  2227. int curLOD = 0;
  2228. int curMesh = 0;
  2229. int curStrip = 0;
  2230. int curStripGroup = 0;
  2231. int curVert = 0;
  2232. int curIndex = 0;
  2233. int curBoneStateChange = 0;
  2234. int deltaModel = 0;
  2235. int deltaLOD = 0;
  2236. int deltaMesh = 0;
  2237. int deltaStrip = 0;
  2238. int deltaStripGroup = 0;
  2239. int deltaVert = 0;
  2240. int deltaIndex = 0;
  2241. int deltaBoneStateChange = 0;
  2242. WriteStringTable( m_StringTableOffset );
  2243. WriteMaterialReplacements( m_MaterialReplacementsOffset );
  2244. WriteMaterialReplacementLists( m_MaterialReplacementsOffset, m_MaterialReplacementsListOffset );
  2245. for( int bodyPartID = 0; bodyPartID < pHdr->numbodyparts; bodyPartID++ )
  2246. {
  2247. mstudiobodyparts_t *pBodyPart = pHdr->pBodypart( bodyPartID );
  2248. for( int modelID = 0; modelID < pBodyPart->nummodels; modelID++ )
  2249. {
  2250. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  2251. Model_t *pModel = &m_Models[curModel + deltaModel];
  2252. for( int lodID = 0; lodID < g_ScriptLODs.Count(); lodID++ )
  2253. {
  2254. // printf( "lod: %d\n", lodID );
  2255. ModelLOD_t *pLOD = &pModel->modelLODs[lodID];
  2256. for( int meshID = 0; meshID < pStudioModel->nummeshes; meshID++ )
  2257. {
  2258. Mesh_t *pMesh = &pLOD->meshes[meshID];
  2259. for( int stripGroupID = 0; stripGroupID < pMesh->stripGroups.Count(); stripGroupID++ )
  2260. {
  2261. StripGroup_t *pStripGroup = &pMesh->stripGroups[stripGroupID];
  2262. deltaVert += WriteVerts( curVert + deltaVert, pStripGroup );
  2263. deltaIndex += WriteIndices( curIndex + deltaIndex, pStripGroup );
  2264. int nStripCount = pStripGroup->strips.Count();
  2265. for( int stripID = 0; stripID < nStripCount; stripID++ )
  2266. {
  2267. Strip_t *pStrip = &pStripGroup->strips[stripID];
  2268. for( int boneStateChangeID = 0; boneStateChangeID < pStrip->numBoneStateChanges; boneStateChangeID++ )
  2269. {
  2270. WriteBoneStateChange( curBoneStateChange + deltaBoneStateChange, &pStrip->boneStateChanges[boneStateChangeID] );
  2271. deltaBoneStateChange++;
  2272. }
  2273. WriteStrip( curStrip + deltaStrip, pStrip, curIndex, curVert, curBoneStateChange );
  2274. deltaStrip++;
  2275. curBoneStateChange += deltaBoneStateChange;
  2276. deltaBoneStateChange = 0;
  2277. }
  2278. WriteStripGroup( curStripGroup + deltaStripGroup, pStripGroup, curVert, curIndex, curStrip );
  2279. deltaStripGroup++;
  2280. curStrip += deltaStrip;
  2281. deltaStrip = 0;
  2282. curVert += deltaVert;
  2283. deltaVert = 0;
  2284. curIndex += deltaIndex;
  2285. deltaIndex = 0;
  2286. }
  2287. WriteMesh( curMesh + deltaMesh, pMesh, curStripGroup );
  2288. deltaMesh++;
  2289. curStripGroup += deltaStripGroup;
  2290. deltaStripGroup = 0;
  2291. }
  2292. WriteModelLOD( curLOD + deltaLOD, pLOD, curMesh );
  2293. deltaLOD++;
  2294. curMesh += deltaMesh;
  2295. deltaMesh = 0;
  2296. }
  2297. WriteModel( curModel + deltaModel, pStudioModel, curLOD );
  2298. deltaModel++;
  2299. curLOD += deltaLOD;
  2300. deltaLOD = 0;
  2301. }
  2302. WriteBodyPart( bodyPartID, pBodyPart, curModel );
  2303. curModel += deltaModel;
  2304. deltaModel = 0;
  2305. }
  2306. WriteHeader( m_VertexCacheSize, m_MaxBonesPerVert, m_MaxBonesPerTri,
  2307. m_MaxBonesPerStrip, pHdr->numbodyparts, pHdr->checksum );
  2308. #ifdef _DEBUG
  2309. m_FileBuffer->TestWritten( m_EndOfFileOffset );
  2310. #endif
  2311. MapGlobalBonesToHardwareBoneIDsAndSortBones( pHdr );
  2312. // DebugCompareVerts( phdr );
  2313. SanityCheckAgainstStudioHDR( pHdr );
  2314. if ( !g_quiet )
  2315. {
  2316. OutputMemoryUsage();
  2317. }
  2318. RemoveRedundantBoneStateChanges();
  2319. if( g_staticprop )
  2320. {
  2321. ZeroNumBones();
  2322. }
  2323. // Show statistics
  2324. #ifdef _DEBUG
  2325. // ShowStats();
  2326. #endif
  2327. m_FileBuffer->WriteToFile( pFileName, m_EndOfFileOffset );
  2328. FileHeader_t *pVtxHeader = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  2329. SanityCheckVertexBoneLODFlags( pHdr, pVtxHeader );
  2330. }
  2331. static void MergeLikeBoneIndicesWithinVert( mstudioboneweight_t *pBoneWeight )
  2332. {
  2333. if( pBoneWeight->numbones == 1 )
  2334. {
  2335. return;
  2336. }
  2337. int i, j;
  2338. int realNumBones = pBoneWeight->numbones;
  2339. for( i = 0; i < pBoneWeight->numbones; i++ )
  2340. {
  2341. for( j = i+1; j < pBoneWeight->numbones; j++ )
  2342. {
  2343. if( ( pBoneWeight->bone[i] == pBoneWeight->bone[j] ) && ( pBoneWeight->weight[i] != 0.0f ) )
  2344. {
  2345. pBoneWeight->weight[i] += pBoneWeight->weight[j];
  2346. pBoneWeight->weight[j] = 0.0f;
  2347. realNumBones--;
  2348. }
  2349. }
  2350. }
  2351. // force all of the -1's at the end with a bubble sort
  2352. float tmpWeight;
  2353. int tmpIndex;
  2354. // bubble sort the bones.
  2355. for( j = pBoneWeight->numbones; j > 1; j-- )
  2356. {
  2357. int k;
  2358. for( k = 0; k < j - 1; k++ )
  2359. {
  2360. if( ( pBoneWeight->weight[k] == 0.0f ) && ( pBoneWeight->weight[k+1] != 0.0f ) )
  2361. {
  2362. // swap
  2363. tmpIndex = pBoneWeight->bone[k];
  2364. tmpWeight = pBoneWeight->weight[k];
  2365. pBoneWeight->bone[k] = pBoneWeight->bone[k+1];
  2366. pBoneWeight->weight[k] = pBoneWeight->weight[k+1];
  2367. pBoneWeight->bone[k+1] = tmpIndex;
  2368. pBoneWeight->weight[k+1] = tmpWeight;
  2369. }
  2370. }
  2371. }
  2372. pBoneWeight->numbones = realNumBones;
  2373. }
  2374. static void MergeLikeBoneIndicesWithinVerts( studiohdr_t *pHdr )
  2375. {
  2376. int bodyPartID, modelID, vertID;
  2377. for( bodyPartID = 0; bodyPartID < pHdr->numbodyparts; bodyPartID++ )
  2378. {
  2379. mstudiobodyparts_t *pBodyPart = pHdr->pBodypart( bodyPartID );
  2380. for( modelID = 0; modelID < pBodyPart->nummodels; modelID++ )
  2381. {
  2382. mstudiomodel_t *pModel = pBodyPart->pModel( modelID );
  2383. for( vertID = 0; vertID < pModel->numvertices; vertID++ )
  2384. {
  2385. const mstudio_modelvertexdata_t *vertData = pModel->GetVertexData();
  2386. Assert( vertData ); // This can only return NULL on X360 for now
  2387. mstudioboneweight_t *pBoneWeight = vertData->BoneWeights( vertID );
  2388. MergeLikeBoneIndicesWithinVert( pBoneWeight );
  2389. }
  2390. }
  2391. }
  2392. }
  2393. void COptimizedModel::PrintBoneStateChanges( studiohdr_t *phdr, int lod )
  2394. {
  2395. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  2396. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  2397. {
  2398. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  2399. // mstudiobodyparts_t *pStudioBodyPart = phdr->pBodypart( bodyPartID );
  2400. // for( int lodID = 0; lodID < header->numLODs; lodID++ )
  2401. int lodID = lod;
  2402. {
  2403. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  2404. {
  2405. ModelHeader_t *model = bodyPart->pModel( modelID );
  2406. // mstudiomodel_t *pStudioModel = pStudioBodyPart->pModel( modelID );
  2407. ModelLODHeader_t *pLOD = model->pLOD( lodID );
  2408. for( int meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  2409. {
  2410. MeshHeader_t *mesh = pLOD->pMesh( meshID );
  2411. // mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  2412. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  2413. {
  2414. StripGroupHeader_t *pStripGroup = mesh->pStripGroup( stripGroupID );
  2415. for( int stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  2416. {
  2417. StripHeader_t *pStrip = pStripGroup->pStrip( stripID );
  2418. for( int boneStateChangeID = 0; boneStateChangeID < pStrip->numBoneStateChanges; boneStateChangeID++ )
  2419. {
  2420. BoneStateChangeHeader_t *pBoneStateChange = pStrip->pBoneStateChange( boneStateChangeID );
  2421. printf( "bone change: hwid: %d boneid: %d (%s)\n",
  2422. ( int )pBoneStateChange->hardwareID,
  2423. ( int )pBoneStateChange->newBoneID,
  2424. g_bonetable[pBoneStateChange->newBoneID].name);
  2425. }
  2426. }
  2427. }
  2428. }
  2429. }
  2430. }
  2431. }
  2432. }
  2433. void COptimizedModel::PrintVerts( studiohdr_t *phdr, int lod )
  2434. {
  2435. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  2436. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  2437. {
  2438. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  2439. mstudiobodyparts_t *pStudioBodyPart = phdr->pBodypart( bodyPartID );
  2440. // for( int lodID = 0; lodID < header->numLODs; lodID++ )
  2441. int lodID = lod;
  2442. {
  2443. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  2444. {
  2445. ModelHeader_t *model = bodyPart->pModel( modelID );
  2446. mstudiomodel_t *pStudioModel = pStudioBodyPart->pModel( modelID );
  2447. ModelLODHeader_t *pLOD = model->pLOD( lodID );
  2448. for( int meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  2449. {
  2450. MeshHeader_t *mesh = pLOD->pMesh( meshID );
  2451. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  2452. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  2453. {
  2454. StripGroupHeader_t *pStripGroup = mesh->pStripGroup( stripGroupID );
  2455. for( int vertID = 0; vertID < pStripGroup->numVerts; vertID++ )
  2456. {
  2457. PrintVert( pStripGroup->pVertex( vertID ), pStudioModel, pStudioMesh );
  2458. }
  2459. }
  2460. }
  2461. }
  2462. }
  2463. }
  2464. }
  2465. static int CalcNumMaterialReplacements()
  2466. {
  2467. int i;
  2468. int numReplacements = 0;
  2469. int numLODs = g_ScriptLODs.Size();
  2470. for( i = 0; i < numLODs; i++ )
  2471. {
  2472. LodScriptData_t &scriptLOD = g_ScriptLODs[i];
  2473. numReplacements += scriptLOD.materialReplacements.Size();
  2474. }
  2475. return numReplacements;
  2476. }
  2477. //-----------------------------------------------------------------------------
  2478. //
  2479. // Main entry point
  2480. //
  2481. //-----------------------------------------------------------------------------
  2482. bool COptimizedModel::OptimizeFromStudioHdr( studiohdr_t *pHdr, s_bodypart_t *pSrcBodyParts,
  2483. int vertCacheSize,
  2484. bool usesFixedFunction, bool bForceSoftwareSkin, bool bHWFlex, int maxBonesPerVert, int maxBonesPerTri,
  2485. int maxBonesPerStrip, const char *pFileName, const char *glViewFileName )
  2486. {
  2487. Assert( maxBonesPerVert <= MAX_NUM_BONES_PER_VERT );
  2488. Assert( maxBonesPerTri <= MAX_NUM_BONES_PER_TRI );
  2489. Assert( maxBonesPerStrip <= MAX_NUM_BONES_PER_STRIP );
  2490. MergeLikeBoneIndicesWithinVerts( pHdr );
  2491. // Some initialization shite
  2492. SetupMeshProcessing( pHdr, vertCacheSize, usesFixedFunction, maxBonesPerVert,
  2493. maxBonesPerTri, maxBonesPerStrip, pFileName );
  2494. // The dude that does it all
  2495. TotalMeshStats_t stats;
  2496. ProcessModel( pHdr, pSrcBodyParts, stats, bForceSoftwareSkin, bHWFlex );
  2497. stats.m_TotalMaterialReplacements = CalcNumMaterialReplacements();
  2498. // Write it out to disk
  2499. WriteVTXFile( pHdr, pFileName, stats );
  2500. // Write out debugging files....
  2501. WriteGLViewFiles( pHdr, glViewFileName );
  2502. // DebugCrap( pHdr );
  2503. // PrintBoneStateChanges( pHdr, 1 );
  2504. // PrintVerts( pHdr, 1 );
  2505. delete m_FileBuffer;
  2506. m_FileBuffer = NULL;
  2507. if( m_NumSkinnedAndFlexedVerts != 0 )
  2508. {
  2509. MdlWarning( "!!!!WARNING!!!!: %d flexed verts had more than one bone influence. . will use SLOW path in engine\n", m_NumSkinnedAndFlexedVerts );
  2510. }
  2511. CleanupEverything();
  2512. return true;
  2513. }
  2514. static int numGLViewTrangles = 0;
  2515. static int numGLViewSWDegenerates = 0;
  2516. static int numGLViewHWDegenerates = 0;
  2517. enum
  2518. {
  2519. GLVIEWDRAW_TRILIST,
  2520. GLVIEWDRAW_TRISTRIP,
  2521. GLVIEWDRAW_NONE
  2522. };
  2523. static int s_DrawMode;
  2524. static int s_LastThreeIndices[3];
  2525. static Vertex_t s_LastThreeVerts[3];
  2526. static Vector s_LastThreePositions[3];
  2527. static int s_ListID = 0;
  2528. static bool s_Shrunk[3];
  2529. //-----------------------------------------------------------------------------
  2530. //
  2531. // The following methods are used in writing out GL View files
  2532. //
  2533. //-----------------------------------------------------------------------------
  2534. void COptimizedModel::PrintVert( Vertex_t *v, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh )
  2535. {
  2536. printf( "vert:\n" );
  2537. #if 0
  2538. printf( "\tposition: %f %f %f\n",
  2539. v->position[0], v->position[1], v->position[2] );
  2540. printf( "\tnormal: %f %f %f\n",
  2541. v->normal[0], v->normal[1], v->normal[2] );
  2542. printf( "\ttexcoord: %f %f\n",
  2543. v->texCoord[0], v->texCoord[1] );
  2544. #endif
  2545. printf( "\torigMeshVertID: %d\n", v->origMeshVertID );
  2546. printf( "\tnumBones: %d\n", v->numBones );
  2547. int i;
  2548. // for( i = 0; i < MAX_NUM_BONES_PER_VERT; i++ )
  2549. for( i = 0; i < v->numBones; i++ )
  2550. {
  2551. float boneWeight = GetOrigVertBoneWeightValue( pStudioModel, pStudioMesh, v, i );
  2552. printf( "\tboneID[%d]: %d weight: %f (%s)\n", i, ( int )v->boneID[i], boneWeight,
  2553. g_bonetable[v->boneID[i]].name );
  2554. }
  2555. }
  2556. static float RandomFloat( float min, float max )
  2557. {
  2558. float ret;
  2559. ret = ( ( float )rand() ) / ( float )VALVE_RAND_MAX;
  2560. ret *= max - min;
  2561. ret += min;
  2562. return ret;
  2563. }
  2564. Vector& COptimizedModel::GetOrigVertPosition( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert )
  2565. {
  2566. Assert( pStudioMesh->pModel() == pStudioModel );
  2567. const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData();
  2568. Assert( vertData ); // This can only return NULL on X360 for now
  2569. return *vertData->Position( pVert->origMeshVertID );
  2570. }
  2571. float COptimizedModel::GetOrigVertBoneWeightValue( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert, int boneID )
  2572. {
  2573. Assert( pStudioMesh->pModel() == pStudioModel );
  2574. const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData();
  2575. Assert( vertData ); // This can only return NULL on X360 for now
  2576. return vertData->BoneWeights( pVert->origMeshVertID )->weight[pVert->boneWeightIndex[boneID]];
  2577. }
  2578. mstudioboneweight_t &COptimizedModel::GetOrigVertBoneWeight( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert )
  2579. {
  2580. Assert( pStudioMesh->pModel() == pStudioModel );
  2581. const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData();
  2582. Assert( vertData ); // This can only return NULL on X360 for now
  2583. return *vertData->BoneWeights( pVert->origMeshVertID );
  2584. }
  2585. int COptimizedModel::GetOrigVertBoneIndex( mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, Vertex_t *pVert, int boneID )
  2586. {
  2587. Assert( pStudioMesh->pModel() == pStudioModel );
  2588. const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData();
  2589. Assert( vertData ); // This can only return NULL on X360 for now
  2590. return vertData->BoneWeights( pVert->origMeshVertID )->bone[pVert->boneWeightIndex[boneID]];
  2591. }
  2592. void COptimizedModel::SetMeshPropsColor( unsigned int meshFlags, Vector& color )
  2593. {
  2594. if( meshFlags & MESH_IS_TEETH )
  2595. {
  2596. color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f;
  2597. }
  2598. else if( meshFlags & MESH_IS_EYES )
  2599. {
  2600. color[0] = 1.0f; color[1] = 1.0f; color[2] = 0.0f;
  2601. }
  2602. else
  2603. {
  2604. color[0] = 0.0f; color[1] = 1.0f; color[2] = 0.0f;
  2605. }
  2606. }
  2607. void COptimizedModel::SetFlexedAndSkinColor( unsigned int glViewFlags, unsigned int stripGroupFlags, Vector& color )
  2608. {
  2609. if( ( glViewFlags & WRITEGLVIEW_SHOWFLEXED ) && ( glViewFlags & WRITEGLVIEW_SHOWSW ) )
  2610. {
  2611. if( ( stripGroupFlags & STRIPGROUP_IS_FLEXED ) &&
  2612. ( stripGroupFlags & STRIPGROUP_IS_HWSKINNED ) )
  2613. {
  2614. // flexed and hw skinned = yellow
  2615. color[0] = 1.0f; color[1] = 1.0f; color[2] = 0.0f;
  2616. }
  2617. else if( !( stripGroupFlags & STRIPGROUP_IS_FLEXED ) &&
  2618. ( stripGroupFlags & STRIPGROUP_IS_HWSKINNED ) )
  2619. {
  2620. // not flexed and hw skinned = green
  2621. color[0] = 0.0f; color[1] = 1.0f; color[2] = 0.0f;
  2622. }
  2623. else if( !( stripGroupFlags & STRIPGROUP_IS_FLEXED ) &&
  2624. !( stripGroupFlags & STRIPGROUP_IS_HWSKINNED ) )
  2625. {
  2626. // not flexed and sw skinned = blue
  2627. color[0] = 0.0f; color[1] = 0.0f; color[2] = 1.0f;
  2628. }
  2629. else if( ( stripGroupFlags & STRIPGROUP_IS_FLEXED ) &&
  2630. !( stripGroupFlags & STRIPGROUP_IS_HWSKINNED ) )
  2631. {
  2632. // flexed and sw skinned = red
  2633. color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f;
  2634. }
  2635. else
  2636. {
  2637. Assert( 0 );
  2638. }
  2639. }
  2640. else if( glViewFlags & WRITEGLVIEW_SHOWFLEXED )
  2641. {
  2642. if( stripGroupFlags & STRIPGROUP_IS_FLEXED )
  2643. {
  2644. color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f;
  2645. }
  2646. else
  2647. {
  2648. color[0] = 0.0f; color[1] = 1.0f; color[2] = 0.0f;
  2649. }
  2650. }
  2651. else if( glViewFlags & WRITEGLVIEW_SHOWSW )
  2652. {
  2653. if( stripGroupFlags & STRIPGROUP_IS_HWSKINNED )
  2654. {
  2655. color[0] = 0.0f; color[1] = 1.0f; color[2] = 0.0f;
  2656. }
  2657. else
  2658. {
  2659. color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f;
  2660. }
  2661. }
  2662. }
  2663. void COptimizedModel::SetColorFromNumVertexBones( int numBones, Vector& color )
  2664. {
  2665. Vector numBonesColor[5] = {
  2666. Vector( 0.0f, 0.0f, 0.0f ), // 0 bones = black
  2667. Vector( 0.0f, 1.0f, 0.0f ), // 1 bone = green
  2668. Vector( 1.0f, 1.0f, 0.0f ), // 2 bones = yellow
  2669. Vector( 0.0f, 0.0f, 1.0f ), // 3 bones = blue
  2670. Vector( 1.0f, 0.0f, 0.0f ) // 4 bones = red
  2671. };
  2672. Assert( numBones >= 0 && numBones <= 4 );
  2673. VectorCopy( numBonesColor[numBones], color );
  2674. }
  2675. void COptimizedModel::DrawGLViewTriangle( FILE *fp, Vector& pos1, Vector& pos2, Vector& pos3,
  2676. Vector& color1, Vector& color2, Vector& color3 )
  2677. {
  2678. numGLViewTrangles++;
  2679. fprintf( fp, "3\n" );
  2680. fprintf( fp, "%f %f %f %f %f %f\n", pos1[0], pos1[1], pos1[2], color1[0], color1[1], color1[2] );
  2681. fprintf( fp, "%f %f %f %f %f %f\n", pos2[0], pos2[1], pos2[2], color2[0], color2[1], color2[2] );
  2682. fprintf( fp, "%f %f %f %f %f %f\n", pos3[0], pos3[1], pos3[2], color3[0], color3[1], color3[2] );
  2683. }
  2684. //-----------------------------------------------------------------------------
  2685. // use index to test for degenerates. . isn't used for anything else.
  2686. //-----------------------------------------------------------------------------
  2687. void COptimizedModel::GLViewVert( FILE *fp, Vertex_t vert, int index,
  2688. Vector& color, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh,
  2689. bool showSubStrips, float shrinkFactor )
  2690. {
  2691. // CheckVertBoneWeights( &vert, pStudioModel, pStudioMesh );
  2692. Assert( s_DrawMode != GLVIEWDRAW_NONE );
  2693. int id = s_ListID % 3;
  2694. s_LastThreeIndices[id] = index;
  2695. s_LastThreeVerts[id] = vert;
  2696. s_Shrunk[id] = false;
  2697. VectorCopy( GetOrigVertPosition( pStudioModel, pStudioMesh, &s_LastThreeVerts[id] ), s_LastThreePositions[id] );
  2698. if( s_DrawMode == GLVIEWDRAW_TRILIST )
  2699. {
  2700. // trilist
  2701. if( id == 2 )
  2702. {
  2703. ShrinkVerts( shrinkFactor );
  2704. DrawGLViewTriangle( fp,
  2705. s_LastThreePositions[0],
  2706. s_LastThreePositions[1],
  2707. s_LastThreePositions[2],
  2708. color, color, color );
  2709. }
  2710. }
  2711. else
  2712. {
  2713. // tristrip
  2714. if( s_ListID >= 2 )
  2715. {
  2716. // spit out the triangle with both facings. . doesn't matter if we
  2717. // get the facing right for glview
  2718. if( s_LastThreeIndices[0] == s_LastThreeIndices[1] ||
  2719. s_LastThreeIndices[1] == s_LastThreeIndices[2] ||
  2720. s_LastThreeIndices[0] == s_LastThreeIndices[2] )
  2721. {
  2722. // skip degenerate triangles
  2723. numGLViewHWDegenerates++;
  2724. if( showSubStrips )
  2725. {
  2726. RandomColor( color );
  2727. }
  2728. }
  2729. else if( !( s_ListID & 1 ) )
  2730. {
  2731. ShrinkVerts( shrinkFactor );
  2732. DrawGLViewTriangle( fp,
  2733. s_LastThreePositions[(id+0-2+3)%3],
  2734. s_LastThreePositions[(id+1-2+3)%3],
  2735. s_LastThreePositions[(id+2-2+3)%3],
  2736. color, color, color );
  2737. }
  2738. else
  2739. {
  2740. ShrinkVerts( shrinkFactor );
  2741. DrawGLViewTriangle( fp,
  2742. s_LastThreePositions[(id+2-2+3)%3],
  2743. s_LastThreePositions[(id+1-2+3)%3],
  2744. s_LastThreePositions[(id+0-2+3)%3],
  2745. color, color, color );
  2746. }
  2747. }
  2748. }
  2749. s_ListID++;
  2750. }
  2751. void COptimizedModel::GLViewDrawEnd( void )
  2752. {
  2753. s_DrawMode = GLVIEWDRAW_NONE;
  2754. }
  2755. /*
  2756. void COptimizedModel::DebugCrap( studiohdr_t *phdr )
  2757. {
  2758. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  2759. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  2760. {
  2761. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  2762. mstudiobodyparts_t *pStudioBodyPart = phdr->pBodypart( bodyPartID );
  2763. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  2764. {
  2765. ModelHeader_t *model = bodyPart->pModel( modelID );
  2766. mstudiomodel_t *pStudioModel = pStudioBodyPart->pModel( modelID );
  2767. for( int lodID = 0; lodID < model->numLODs; lodID++ )
  2768. {
  2769. char tmp[256];
  2770. sprintf( tmp, "crap.lod%d", lodID );
  2771. printf( "writing %s\n", tmp );
  2772. FILE *fp = fopen( tmp, "w" );
  2773. if( !fp )
  2774. {
  2775. printf( "can't write crap file %s\n", tmp );
  2776. return;
  2777. }
  2778. ModelLODHeader_t *pLOD = model->pLOD( lodID );
  2779. for( int meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  2780. {
  2781. MeshHeader_t *mesh = pLOD->pMesh( meshID );
  2782. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  2783. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  2784. {
  2785. StripGroupHeader_t *pStripGroup = mesh->pStripGroup( stripGroupID );
  2786. for( int stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  2787. {
  2788. StripHeader_t *pStrip = pStripGroup->pStrip( stripID );
  2789. for( int indexID = 0; indexID < pStrip->numIndices; indexID++ )
  2790. {
  2791. int id = *pStripGroup->pIndex( indexID + pStrip->indexOffset );
  2792. Vertex_t& vert = *pStripGroup->pVertex( id );
  2793. Vector& vertPos = GetOrigVertPosition( pStudioModel, pStudioMesh, &vert );
  2794. fprintf( fp, "mesh: %04d origvertid: %04d pos: %0.2f %0.2f %0.2f ", meshID, vert.origMeshVertID, vertPos[0], vertPos[1], vertPos[2] );
  2795. int i;
  2796. for( i = 0; i < vert.numBones; i++ )
  2797. {
  2798. float boneWeight;
  2799. boneWeight = GetOrigVertBoneWeightValue( pStudioModel, pStudioMesh, &vert, i );
  2800. int boneID;
  2801. boneID = GetOrigVertBoneIndex( pStudioModel, pStudioMesh, &vert, i );
  2802. fprintf( fp, "bone: %d %0.1f ", boneID, boneWeight );
  2803. }
  2804. fprintf( fp, "\n" );
  2805. }
  2806. }
  2807. }
  2808. }
  2809. fclose( fp );
  2810. }
  2811. }
  2812. }
  2813. }
  2814. */
  2815. void COptimizedModel::WriteGLViewFile( studiohdr_t *phdr, const char *pFileName, unsigned int flags, float shrinkFactor )
  2816. {
  2817. Vector color;
  2818. RandomColor( color );
  2819. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  2820. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  2821. {
  2822. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  2823. mstudiobodyparts_t *pStudioBodyPart = phdr->pBodypart( bodyPartID );
  2824. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  2825. {
  2826. ModelHeader_t *model = bodyPart->pModel( modelID );
  2827. mstudiomodel_t *pStudioModel = pStudioBodyPart->pModel( modelID );
  2828. for( int lodID = 0; lodID < model->numLODs; lodID++ )
  2829. {
  2830. char tmp[256];
  2831. sprintf( tmp, "%s.lod%d", pFileName, lodID );
  2832. printf( "writing %s\n", tmp );
  2833. CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( tmp ) );
  2834. spFile->Edit();
  2835. FILE *fp = fopen( tmp, "w" );
  2836. if( !fp )
  2837. {
  2838. printf( "can't write glview file %s\n", tmp );
  2839. return;
  2840. }
  2841. /*
  2842. // write out tangent space vectors
  2843. int vertID;
  2844. for( vertID = 0; vertID < pStudioModel->numvertices; vertID++ )
  2845. {
  2846. const Vector &pos = *pStudioModel->pVertex( vertID );
  2847. const Vector &norm = *pStudioModel->pNormal( vertID );
  2848. const Vector4D &sVect = *pStudioModel->pTangentS( vertID );
  2849. Vector tmpVect;
  2850. tmpVect = pos + norm * .15f;
  2851. fprintf( fp, "2\n" );
  2852. fprintf( fp, "%f %f %f 0.0 0.0 1.0\n", pos[0], pos[1], pos[2] );
  2853. fprintf( fp, "%f %f %f 0.0 0.0 1.0\n", tmpVect[0], tmpVect[1], tmpVect[2] );
  2854. Vector tmpSVect( sVect[0], sVect[1], sVect[2] );
  2855. tmpVect = pos + tmpSVect * .15f;
  2856. fprintf( fp, "2\n" );
  2857. fprintf( fp, "%f %f %f 1.0 0.0 0.0\n", pos[0], pos[1], pos[2] );
  2858. fprintf( fp, "%f %f %f 1.0 0.0 0.0\n", tmpVect[0], tmpVect[1], tmpVect[2] );
  2859. Vector tmpTVect;
  2860. CrossProduct( norm, tmpSVect, tmpTVect );
  2861. tmpTVect *= sVect[3];
  2862. tmpVect = pos + tmpTVect * .15f;
  2863. fprintf( fp, "2\n" );
  2864. fprintf( fp, "%f %f %f 0.0 1.0 0.0\n", pos[0], pos[1], pos[2] );
  2865. fprintf( fp, "%f %f %f 0.0 1.0 0.0\n", tmpVect[0], tmpVect[1], tmpVect[2] );
  2866. }
  2867. continue;
  2868. */
  2869. ModelLODHeader_t *pLOD = model->pLOD( lodID );
  2870. for( int meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  2871. {
  2872. MeshHeader_t *mesh = pLOD->pMesh( meshID );
  2873. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  2874. if( flags & WRITEGLVIEW_SHOWMESH )
  2875. {
  2876. RandomColor( color );
  2877. }
  2878. if( flags & WRITEGLVIEW_SHOWMESHPROPS )
  2879. {
  2880. SetMeshPropsColor( mesh->flags, color );
  2881. }
  2882. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  2883. {
  2884. if( flags & WRITEGLVIEW_SHOWSTRIPGROUP )
  2885. {
  2886. RandomColor( color );
  2887. }
  2888. StripGroupHeader_t *pStripGroup = mesh->pStripGroup( stripGroupID );
  2889. SetFlexedAndSkinColor( flags, pStripGroup->flags, color );
  2890. for( int stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  2891. {
  2892. StripHeader_t *pStrip = pStripGroup->pStrip( stripID );
  2893. if( flags & WRITEGLVIEW_SHOWSTRIP )
  2894. {
  2895. RandomColor( color );
  2896. }
  2897. if( flags & WRITEGLVIEW_SHOWSTRIPNUMBONES )
  2898. {
  2899. switch (pStrip->numBones)
  2900. {
  2901. case 0:
  2902. case 1:
  2903. color.Init( 0, 0, 255 );
  2904. break;
  2905. case 2:
  2906. color.Init( 0, 255, 0 );
  2907. break;
  2908. case 3:
  2909. color.Init( 255, 255, 0 );
  2910. break;
  2911. case 4:
  2912. color.Init( 255, 0, 0 );
  2913. break;
  2914. }
  2915. }
  2916. GLViewDrawBegin( ( pStrip->flags & STRIP_IS_TRILIST ) ? GLVIEWDRAW_TRILIST : GLVIEWDRAW_TRISTRIP );
  2917. for( int indexID = 0; indexID < pStrip->numIndices; indexID++ )
  2918. {
  2919. int id = *pStripGroup->pIndex( indexID + pStrip->indexOffset );
  2920. Vertex_t& vert = *pStripGroup->pVertex( id );
  2921. if( flags & WRITEGLVIEW_SHOWVERTNUMBONES )
  2922. {
  2923. switch (vert.numBones)
  2924. {
  2925. case 0:
  2926. case 1:
  2927. color.Init( 0, 0, 255 );
  2928. break;
  2929. case 2:
  2930. color.Init( 0, 255, 0 );
  2931. break;
  2932. case 3:
  2933. color.Init( 255, 255, 0 );
  2934. break;
  2935. case 4:
  2936. color.Init( 255, 0, 0 );
  2937. break;
  2938. }
  2939. }
  2940. GLViewVert( fp, vert, id, color, pStudioModel, pStudioMesh,
  2941. ( flags & WRITEGLVIEW_SHOWSUBSTRIP ) ? true : false, shrinkFactor );
  2942. }
  2943. GLViewDrawEnd();
  2944. }
  2945. }
  2946. }
  2947. fclose( fp );
  2948. spFile->Add();
  2949. }
  2950. }
  2951. }
  2952. }
  2953. //-----------------------------------------------------------------------------
  2954. // Write out all GL View files
  2955. //-----------------------------------------------------------------------------
  2956. void COptimizedModel::WriteGLViewFiles( studiohdr_t *pHdr, char const* glViewFileName )
  2957. {
  2958. if( !g_bDumpGLViewFiles )
  2959. return;
  2960. char tmpFileName[128];
  2961. strcpy( tmpFileName, glViewFileName );
  2962. strcat( tmpFileName, ".mesh" );
  2963. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWMESH, .8f );
  2964. strcpy( tmpFileName, glViewFileName );
  2965. strcat( tmpFileName, ".stripgroup" );
  2966. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWSTRIPGROUP, .8f );
  2967. strcpy( tmpFileName, glViewFileName );
  2968. strcat( tmpFileName, ".strip" );
  2969. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWSTRIP, .8f );
  2970. strcpy( tmpFileName, glViewFileName );
  2971. strcat( tmpFileName, ".substrip" );
  2972. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWSUBSTRIP, .97f );
  2973. strcpy( tmpFileName, glViewFileName );
  2974. strcat( tmpFileName, ".flexed" );
  2975. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWFLEXED, .8f );
  2976. strcpy( tmpFileName, glViewFileName );
  2977. strcat( tmpFileName, ".sw" );
  2978. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWSW, .8f );
  2979. strcpy( tmpFileName, glViewFileName );
  2980. strcat( tmpFileName, ".flexedandsw" );
  2981. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWSW | WRITEGLVIEW_SHOWFLEXED, .8f );
  2982. strcpy( tmpFileName, glViewFileName );
  2983. strcat( tmpFileName, ".meshprops" );
  2984. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWMESHPROPS, .8f );
  2985. strcpy( tmpFileName, glViewFileName );
  2986. strcat( tmpFileName, ".vertnumbones" );
  2987. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWVERTNUMBONES, 1.0f );
  2988. strcpy( tmpFileName, glViewFileName );
  2989. strcat( tmpFileName, ".stripnumbones" );
  2990. WriteGLViewFile( pHdr, tmpFileName, WRITEGLVIEW_SHOWSTRIPNUMBONES, 1.0f );
  2991. }
  2992. void COptimizedModel::GLViewDrawBegin( int mode )
  2993. {
  2994. s_DrawMode = mode;
  2995. s_ListID = 0;
  2996. }
  2997. void COptimizedModel::ShrinkVerts( float shrinkFactor )
  2998. {
  2999. Vector center;
  3000. Vector delta;
  3001. VectorCopy( s_LastThreePositions[0], center );
  3002. VectorAdd( center, s_LastThreePositions[1], center );
  3003. VectorAdd( center, s_LastThreePositions[2], center );
  3004. VectorScale( center, 1.0f / 3.0f, center );
  3005. int i;
  3006. for( i = 0; i < 3; i++ )
  3007. {
  3008. if( s_Shrunk[i] )
  3009. {
  3010. continue;
  3011. }
  3012. VectorSubtract( s_LastThreePositions[i], center, delta );
  3013. VectorScale( delta, shrinkFactor, delta );
  3014. VectorAdd( center, delta, s_LastThreePositions[i] );
  3015. s_Shrunk[i] = true;
  3016. }
  3017. }
  3018. void COptimizedModel::CheckVertBoneWeights( Vertex_t *pVert, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh )
  3019. {
  3020. int i;
  3021. float sum = 0;
  3022. for( i = 0; i < MAX_NUM_BONES_PER_VERT; i++ )
  3023. {
  3024. float boneWeight = GetOrigVertBoneWeightValue( pStudioModel, pStudioMesh, pVert, i );
  3025. sum += boneWeight;
  3026. }
  3027. Assert( sum > 0.95f && sum < 1.1f );
  3028. }
  3029. #define CACHE_INEFFICIENCY 6
  3030. void COptimizedModel::ShowStats( void )
  3031. {
  3032. int totalHWTriangles = 0;
  3033. int totalHWDegenerates = 0;
  3034. int totalHWIndices = 0;
  3035. int totalHWVertexCacheHits = 0;
  3036. int totalHWVertexCacheMisses = 0;
  3037. int totalSWTriangles = 0;
  3038. int totalSWDegenerates = 0;
  3039. int totalSWIndices = 0;
  3040. int totalSWVertexCacheHits = 0;
  3041. int totalSWVertexCacheMisses = 0;
  3042. CHardwareVertexCache hardwareVertexCache;
  3043. hardwareVertexCache.Init( m_VertexCacheSize - CACHE_INEFFICIENCY );
  3044. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  3045. printf( "header: %d body parts\n", header->numBodyParts );
  3046. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  3047. {
  3048. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  3049. printf( " bodyPart %d: %d models\n", bodyPartID, bodyPart->numModels );
  3050. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  3051. {
  3052. ModelHeader_t *model = bodyPart->pModel( modelID );
  3053. printf( " model: %d lods\n", model->numLODs );
  3054. for( int lodID = 0; lodID < 1; lodID++ )
  3055. {
  3056. ModelLODHeader_t *pLOD = model->pLOD( lodID );
  3057. printf( " lod: %d meshes\n", pLOD->numMeshes );
  3058. for( int meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  3059. {
  3060. MeshHeader_t *mesh = pLOD->pMesh( meshID );
  3061. printf( " mesh %d: %d stripsgroups\n", meshID, mesh->numStripGroups );
  3062. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  3063. {
  3064. StripGroupHeader_t *pStripGroup = mesh->pStripGroup( stripGroupID );
  3065. for( int stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  3066. {
  3067. for( int stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  3068. {
  3069. int lastThreeIndices[3];
  3070. StripHeader_t *pStrip = pStripGroup->pStrip( stripID );
  3071. printf( " strip: %d numIndices: %d indexOffset: %d\n", stripID, pStrip->numIndices, pStrip->indexOffset );
  3072. hardwareVertexCache.Flush();
  3073. if( pStrip->flags & STRIP_IS_TRISTRIP )
  3074. {
  3075. for( int indexID = 0; indexID < pStrip->numIndices; indexID++ )
  3076. {
  3077. totalHWIndices++;
  3078. int newVertOffset = indexID % 3;
  3079. lastThreeIndices[newVertOffset] = *pStripGroup->pIndex( indexID + pStrip->indexOffset );
  3080. if( !hardwareVertexCache.IsPresent( lastThreeIndices[newVertOffset] ) )
  3081. {
  3082. totalHWVertexCacheMisses++;
  3083. hardwareVertexCache.Insert( lastThreeIndices[newVertOffset] );
  3084. }
  3085. else
  3086. {
  3087. totalHWVertexCacheHits++;
  3088. }
  3089. if( indexID >= 2 )
  3090. {
  3091. totalHWTriangles++;
  3092. if( lastThreeIndices[0] != lastThreeIndices[1] &&
  3093. lastThreeIndices[1] != lastThreeIndices[2] )
  3094. {
  3095. }
  3096. else
  3097. {
  3098. totalHWDegenerates++;
  3099. }
  3100. }
  3101. }
  3102. }
  3103. else
  3104. {
  3105. Assert( pStrip->numIndices % 3 == 0 );
  3106. totalHWTriangles += pStrip->numIndices / 3;
  3107. for( int indexID = 0; indexID < pStrip->numIndices; indexID++ )
  3108. {
  3109. int newVertOffset = indexID % 3;
  3110. int index = *pStripGroup->pIndex( indexID + pStrip->indexOffset );
  3111. // printf( "%d\n", index );
  3112. lastThreeIndices[newVertOffset] = index;
  3113. if( newVertOffset == 2 )
  3114. {
  3115. if( lastThreeIndices[0] == lastThreeIndices[1] ||
  3116. lastThreeIndices[1] == lastThreeIndices[2] ||
  3117. lastThreeIndices[0] == lastThreeIndices[2] )
  3118. {
  3119. // printf( "degenerate triangle!!!! %d %d %d\n", lastThreeIndices[0], lastThreeIndices[1], lastThreeIndices[2] );
  3120. totalHWDegenerates++;
  3121. }
  3122. }
  3123. totalHWIndices++;
  3124. if( !hardwareVertexCache.IsPresent( index ) )
  3125. {
  3126. totalHWVertexCacheMisses++;
  3127. hardwareVertexCache.Insert( index );
  3128. }
  3129. else
  3130. {
  3131. totalHWVertexCacheHits++;
  3132. }
  3133. }
  3134. }
  3135. }
  3136. }
  3137. }
  3138. }
  3139. }
  3140. }
  3141. }
  3142. int totalRealHWTriangles = totalHWTriangles - totalHWDegenerates;
  3143. int totalRealSWTriangles = totalSWTriangles - totalSWDegenerates;
  3144. printf( "TotalHWTriangles: %d\n", totalHWTriangles );
  3145. printf( "TotalHWDegenerates: %d\n", totalHWDegenerates );
  3146. printf( "TotalRealHWTriangles: %d\n", totalRealHWTriangles );
  3147. printf( "TotalHWIndices: %d\n", totalHWIndices );
  3148. printf( "HW real tris/index: %f\n", ( float )totalRealHWTriangles / ( float )totalHWIndices );
  3149. printf( "totalHWVertexCacheHits: %d\n", totalHWVertexCacheHits );
  3150. printf( "totalHWVertexCacheMisses: %d\n", totalHWVertexCacheMisses );
  3151. printf( "HW vertex cache hit/miss ratio: %f\n", ( float )totalHWVertexCacheHits / ( float )totalHWVertexCacheMisses );
  3152. printf( "TotalSWTriangles: %d\n", totalSWTriangles );
  3153. printf( "TotalSWDegenerates: %d\n", totalSWDegenerates );
  3154. printf( "TotalRealSWTriangles: %d\n", totalRealSWTriangles );
  3155. printf( "TotalSWIndices: %d\n", totalSWIndices );
  3156. printf( "SW real tris/index: %f\n", ( float )totalRealSWTriangles / ( float )totalSWIndices );
  3157. printf( "totalSWVertexCacheHits: %d\n", totalSWVertexCacheHits );
  3158. printf( "totalSWVertexCacheMisses: %d\n", totalSWVertexCacheMisses );
  3159. printf( "SW vertex cache hit/miss ratio: %f\n", ( float )totalSWVertexCacheHits / ( float )totalSWVertexCacheMisses );
  3160. }
  3161. void COptimizedModel::CheckVert( Vertex_t *pVert, int maxBonesPerTri, int maxBonesPerVert )
  3162. {
  3163. #ifndef IGNORE_BONES
  3164. #ifdef _DEBUG
  3165. int offset = ( int )( ( unsigned char * )pVert - ( unsigned char * )m_FileBuffer->GetPointer( 0 ) );
  3166. Assert( offset >= m_VertsOffset && offset < m_IndicesOffset );
  3167. Assert( ( ( offset - m_VertsOffset ) % sizeof( Vertex_t ) ) == 0 );
  3168. #endif
  3169. int j;
  3170. for( j = 0; j < maxBonesPerVert; j++ )
  3171. {
  3172. if( pVert->boneID[j] != -1 )
  3173. {
  3174. Assert( pVert->boneID[j] >= 0 && pVert->boneID[j] < maxBonesPerTri );
  3175. }
  3176. #if 0
  3177. if( pVert->boneWeights[j] != 0 )
  3178. {
  3179. Assert( pVert->boneID[j] != -1 );
  3180. }
  3181. #endif
  3182. }
  3183. // Test to make sure we are sorted.
  3184. for( j = 0; j < maxBonesPerVert-1; j++ )
  3185. {
  3186. #if 1
  3187. // if( pVert->boneWeights[j] != 0 && pVert->boneWeights[j+1] != 0 )
  3188. {
  3189. Assert( pVert->boneID[j] < pVert->boneID[j+1] );
  3190. }
  3191. #endif
  3192. }
  3193. #if 0
  3194. // Make sure that all the non-zero weights are first.
  3195. bool foundZero = false;
  3196. for( j = 0; j < maxBonesPerVert; j++ )
  3197. {
  3198. if( !foundZero )
  3199. {
  3200. if( pVert->boneWeights[j] == 0.0f )
  3201. {
  3202. foundZero = true;
  3203. }
  3204. }
  3205. else
  3206. {
  3207. Assert( pVert->boneWeights[j] == 0.0f );
  3208. }
  3209. }
  3210. #endif
  3211. #endif
  3212. }
  3213. void COptimizedModel::CheckAllVerts( int maxBonesPerTri, int maxBonesPerVert )
  3214. {
  3215. int i;
  3216. for( i = m_VertsOffset; i < m_IndicesOffset; i += sizeof( Vertex_t ) )
  3217. {
  3218. Vertex_t *vert = ( Vertex_t * )m_FileBuffer->GetPointer( i );
  3219. CheckVert( vert, maxBonesPerTri, maxBonesPerVert );
  3220. }
  3221. }
  3222. void COptimizedModel::SortBonesWithinVertex( bool flexed, Vertex_t *vert, mstudiomodel_t *pStudioModel, mstudiomesh_t *pStudioMesh, int *globalToHardwareBoneIndex, int *hardwareToGlobalBoneIndex, int maxBonesPerTri, int maxBonesPerVert )
  3223. {
  3224. int i;
  3225. /*
  3226. for( i = 0; i < m_NumBones; i++ )
  3227. {
  3228. if( globalToHardwareBoneIndex[i] != -1 )
  3229. {
  3230. if( flexed )
  3231. {
  3232. printf( "global bone id: %d hardware bone id: %d\n",
  3233. i, globalToHardwareBoneIndex[i] );
  3234. }
  3235. }
  3236. }
  3237. */
  3238. #if 0
  3239. unsigned char tmpWeightIndex;
  3240. int tmpIndex;
  3241. int j;
  3242. // bubble sort the bones.
  3243. for( j = m_MaxBonesPerVert; j > 1; j-- )
  3244. {
  3245. int k;
  3246. for( k = 0; k < j - 1; k++ )
  3247. {
  3248. if( vert->boneID[k] > vert->boneID[k+1] )
  3249. {
  3250. // swap
  3251. tmpIndex = vert->boneID[k];
  3252. tmpWeightIndex = vert->boneWeightIndex[k];
  3253. vert->boneID[k] = vert->boneID[k+1];
  3254. vert->boneWeightIndex[k] = vert->boneWeightIndex[k+1];
  3255. vert->boneID[k+1] = tmpIndex;
  3256. vert->boneWeightIndex[k+1] = tmpWeightIndex;
  3257. }
  3258. }
  3259. }
  3260. #else
  3261. int origBoneWeightIndex[MAX_NUM_BONES_PER_VERT];
  3262. int zeroWeightIndex = -1;
  3263. // find a orig vert bone index that has a zero weight
  3264. for( i = 0; i < MAX_NUM_BONES_PER_VERT; i++ )
  3265. {
  3266. float boneWeight = GetOrigVertBoneWeightValue( pStudioModel, pStudioMesh, vert, i );
  3267. if( boneWeight == 0.0f )
  3268. {
  3269. zeroWeightIndex = i;
  3270. break;
  3271. }
  3272. }
  3273. for( i = 0; i < MAX_NUM_BONES_PER_VERT; i++ )
  3274. {
  3275. origBoneWeightIndex[i] = zeroWeightIndex;
  3276. }
  3277. for( i = 0; i < vert->numBones; i++ )
  3278. {
  3279. float boneWeight = GetOrigVertBoneWeightValue( pStudioModel, pStudioMesh, vert, i );
  3280. int globalBoneIndex = GetOrigVertBoneIndex( pStudioModel, pStudioMesh, vert, i );
  3281. // if( vert->numBones > 1 )
  3282. {
  3283. if( flexed )
  3284. {
  3285. printf( "boneWeight: %f\n", boneWeight );
  3286. printf( "globalBoneIndex: %d\n", globalBoneIndex );
  3287. }
  3288. }
  3289. if( boneWeight > 0.0f )
  3290. {
  3291. int hardwareBoneIndex = globalToHardwareBoneIndex[globalBoneIndex];
  3292. Assert( globalBoneIndex != -1 );
  3293. origBoneWeightIndex[hardwareBoneIndex] = vert->boneWeightIndex[i];
  3294. }
  3295. else
  3296. {
  3297. int hardwareBoneIndex = globalToHardwareBoneIndex[globalBoneIndex];
  3298. origBoneWeightIndex[hardwareBoneIndex] = zeroWeightIndex;
  3299. Assert( zeroWeightIndex != -1 );
  3300. Assert( globalBoneIndex == -1 );
  3301. }
  3302. }
  3303. // if( vert->numBones > 1 )
  3304. for( i = 0; i < maxBonesPerTri; i++ )
  3305. {
  3306. vert->boneID[i] = i;
  3307. vert->boneWeightIndex[i] = origBoneWeightIndex[i];
  3308. float boneWeight = GetOrigVertBoneWeightValue( pStudioModel, pStudioMesh, vert, i );
  3309. int globalBoneIndex = GetOrigVertBoneIndex( pStudioModel, pStudioMesh, vert, i );
  3310. if( flexed )
  3311. {
  3312. Assert( boneWeight >= 0.0f && boneWeight <= 1.0f );
  3313. printf( "boneWeight: %f ", boneWeight );
  3314. printf( "globalBoneIndex: %d ", globalBoneIndex );
  3315. printf( "hardwareBoneID: %d\n", i );
  3316. }
  3317. }
  3318. vert->numBones = maxBonesPerTri; // this may be different for software t&l stuff
  3319. #endif
  3320. }
  3321. void COptimizedModel::RemoveRedundantBoneStateChanges( void )
  3322. {
  3323. FileHeader_t *header = ( FileHeader_t * )m_FileBuffer->GetPointer( 0 );
  3324. for( int bodyPartID = 0; bodyPartID < header->numBodyParts; bodyPartID++ )
  3325. {
  3326. BodyPartHeader_t *bodyPart = header->pBodyPart( bodyPartID );
  3327. bool allocated[MAX_NUM_BONES_PER_STRIP];
  3328. int hardwareBoneState[MAX_NUM_BONES_PER_STRIP];
  3329. bool changed[MAX_NUM_BONES_PER_STRIP];
  3330. // start anew with each body part
  3331. // printf( "START BODY PARTY - RESETTING BONE MATRIX STATE\n" );
  3332. int i;
  3333. for( i = 0; i < MAX_NUM_BONES_PER_STRIP; i++ )
  3334. {
  3335. hardwareBoneState[i] = -1;
  3336. allocated[i] = false;
  3337. }
  3338. for( int modelID = 0; modelID < bodyPart->numModels; modelID++ )
  3339. {
  3340. ModelHeader_t *model = bodyPart->pModel( modelID );
  3341. for( int lodID = 0; lodID < model->numLODs; lodID++ )
  3342. {
  3343. ModelLODHeader_t *pLOD = model->pLOD( lodID );
  3344. for( int meshID = 0; meshID < pLOD->numMeshes; meshID++ )
  3345. {
  3346. MeshHeader_t *mesh = pLOD->pMesh( meshID );
  3347. for( int stripGroupID = 0; stripGroupID < mesh->numStripGroups; stripGroupID++ )
  3348. {
  3349. StripGroupHeader_t *pStripGroup = mesh->pStripGroup( stripGroupID );
  3350. if( !( pStripGroup->flags & STRIPGROUP_IS_HWSKINNED ) )
  3351. {
  3352. // printf( "skipping! software skinned stripgroup\n" );
  3353. continue;
  3354. }
  3355. for( int stripID = 0; stripID < pStripGroup->numStrips; stripID++ )
  3356. {
  3357. StripHeader_t *pStrip = pStripGroup->pStrip( stripID );
  3358. // int startNumBoneChanges = pStrip->numBoneStateChanges;
  3359. /*
  3360. printf( "HARDWARE BONE STATE\n" );
  3361. for( i = 0; i < MAX_NUM_BONES_PER_STRIP; i++ )
  3362. {
  3363. if( allocated[i] )
  3364. {
  3365. printf( "\thw: %d global: %d\n", i, hardwareBoneState[i] );
  3366. }
  3367. }
  3368. printf( "before optimization\n" );
  3369. for( i = 0; i < pStrip->numBoneStateChanges; i++ )
  3370. {
  3371. printf( "\thw: %d global: %d\n",
  3372. ( int )pStrip->pBoneStateChange( i )->hardwareID,
  3373. ( int )pStrip->pBoneStateChange( i )->newBoneID );
  3374. }
  3375. */
  3376. for( i = 0; i < MAX_NUM_BONES_PER_STRIP; i++ )
  3377. {
  3378. changed[i] = false;
  3379. }
  3380. for( int boneStateChangeID = 0; boneStateChangeID < pStrip->numBoneStateChanges; boneStateChangeID++ )
  3381. {
  3382. BoneStateChangeHeader_t *boneStateChange = pStrip->pBoneStateChange( boneStateChangeID );
  3383. Assert( boneStateChange->hardwareID >= 0 && boneStateChange->hardwareID < MAX_NUM_BONES_PER_STRIP );
  3384. if( allocated[boneStateChange->hardwareID] &&
  3385. hardwareBoneState[boneStateChange->hardwareID] == boneStateChange->newBoneID )
  3386. {
  3387. // already got this one!
  3388. }
  3389. else
  3390. {
  3391. changed[boneStateChange->hardwareID] = true;
  3392. allocated[boneStateChange->hardwareID] = true;
  3393. hardwareBoneState[boneStateChange->hardwareID] = boneStateChange->newBoneID;
  3394. }
  3395. }
  3396. /*
  3397. // now "changed" should tell us which ones we care about.
  3398. int curOutBoneID = 0;
  3399. for( i = 0; i < pStrip->numBoneStateChanges; i++ )
  3400. {
  3401. // hack . . going to stomp over what is already there with new data.
  3402. if( changed[i] )
  3403. {
  3404. BoneStateChangeHeader_t *boneStateChange = pStrip->pBoneStateChange( curOutBoneID );
  3405. boneStateChange->hardwareID = i;
  3406. boneStateChange->newBoneID = hardwareBoneState[i];
  3407. curOutBoneID++;
  3408. }
  3409. }
  3410. pStrip->numBoneStateChanges = curOutBoneID;
  3411. printf( "start bone changes: %d end bone changes: %d\n", startNumBoneChanges, pStrip->numBoneStateChanges );
  3412. printf( "after optimization\n" );
  3413. for( i = 0; i < pStrip->numBoneStateChanges; i++ )
  3414. {
  3415. printf( "\thw: %d global: %d\n",
  3416. ( int )pStrip->pBoneStateChange( i )->hardwareID,
  3417. ( int )pStrip->pBoneStateChange( i )->newBoneID );
  3418. }
  3419. */
  3420. }
  3421. }
  3422. }
  3423. }
  3424. }
  3425. }
  3426. }
  3427. static void AddMaterialReplacementsToStringTable( void )
  3428. {
  3429. int i, j;
  3430. int numLODs = g_ScriptLODs.Size();
  3431. for( i = 0; i < numLODs; i++ )
  3432. {
  3433. LodScriptData_t &scriptLOD = g_ScriptLODs[i];
  3434. for( j = 0; j < scriptLOD.materialReplacements.Size(); j++ )
  3435. {
  3436. CLodScriptReplacement_t &materialReplacement = scriptLOD.materialReplacements[j];
  3437. s_StringTable.AddString( materialReplacement.GetDstName() );
  3438. }
  3439. }
  3440. }
  3441. // Check that all replacematerial/removemesh commands map to valid source materials
  3442. void ValidateLODReplacements( studiohdr_t *pHdr )
  3443. {
  3444. bool failed = false;
  3445. int lodID;
  3446. for( lodID = 0; lodID < g_ScriptLODs.Size(); lodID++ )
  3447. {
  3448. LodScriptData_t& scriptLOD = g_ScriptLODs[lodID];
  3449. int j;
  3450. for( j = 0; j < scriptLOD.meshRemovals.Count(); j++ )
  3451. {
  3452. const char *pName1 = scriptLOD.meshRemovals[j].GetSrcName();
  3453. int i;
  3454. for( i = 0; i < pHdr->numtextures; i++ )
  3455. {
  3456. const char *pName2 = pHdr->pTexture( i )->material->GetName();
  3457. if( ComparePath( pName1, pName2 ) )
  3458. {
  3459. goto got_one;
  3460. }
  3461. }
  3462. // no match
  3463. MdlWarning( "\"%s\" doesn't match any of the materals in the model\n", pName1 );
  3464. failed = true;
  3465. got_one:
  3466. ;
  3467. }
  3468. }
  3469. if( failed )
  3470. {
  3471. MdlWarning( "possible materials in model:\n" );
  3472. int i;
  3473. for( i = 0; i < pHdr->numtextures; i++ )
  3474. {
  3475. MdlWarning( "\t\"%s\"\n", pHdr->pTexture( i )->material->GetName() );
  3476. }
  3477. MdlError( "Exiting due to errors\n" );
  3478. }
  3479. }
  3480. void WriteOptimizedFiles( studiohdr_t *phdr, s_bodypart_t *pSrcBodyParts )
  3481. {
  3482. char filename[MAX_PATH];
  3483. char tmpFileName[MAX_PATH];
  3484. char glViewFilename[MAX_PATH];
  3485. ValidateLODReplacements( phdr );
  3486. s_StringTable.Purge();
  3487. // hack! This should really go in the mdl file since it's common to all LODs.
  3488. AddMaterialReplacementsToStringTable();
  3489. V_strcpy_safe( filename, gamedir );
  3490. // if( *g_pPlatformName )
  3491. // {
  3492. // strcat( filename, "platform_" );
  3493. // strcat( filename, g_pPlatformName );
  3494. // strcat( filename, "/" );
  3495. // }
  3496. V_strcat_safe( filename, "models/" );
  3497. V_strcat_safe( filename, outname );
  3498. Q_StripExtension( filename, filename, sizeof( filename ) );
  3499. V_strcpy_safe( tmpFileName, filename );
  3500. V_strcat_safe( tmpFileName, ".sw.vtx" );
  3501. V_strcpy_safe( glViewFilename, filename );
  3502. V_strcat_safe( glViewFilename, ".sw.glview" );
  3503. bool bForceSoftwareSkinning = phdr->numbones > 0 && !g_staticprop;
  3504. s_OptimizedModel.OptimizeFromStudioHdr( phdr, pSrcBodyParts,
  3505. 512, //vert cache size FIXME: figure out the correct size for L1
  3506. false, /* doesn't use fixed function */
  3507. bForceSoftwareSkinning, // force software skinning if not static prop
  3508. false, // No hardware flex
  3509. 3, // bones/vert
  3510. 3*3, // bones/tri
  3511. 512, // bones/strip
  3512. tmpFileName, glViewFilename );
  3513. V_strcpy_safe( tmpFileName, filename );
  3514. V_strcat_safe( tmpFileName, ".dx80.vtx" );
  3515. V_strcpy_safe( glViewFilename, filename );
  3516. V_strcat_safe( glViewFilename, ".dx80.glview" );
  3517. s_OptimizedModel.OptimizeFromStudioHdr( phdr, pSrcBodyParts,
  3518. 24 /* vert cache size (real size, not effective!)*/,
  3519. false, /* doesn't use fixed function */
  3520. false, // don't force software skinning
  3521. false, // No hardware flex
  3522. 3 /* bones/vert */,
  3523. 9 /* bones/tri */,
  3524. 16 /* bones/strip */,
  3525. tmpFileName, glViewFilename );
  3526. V_strcpy_safe( tmpFileName, filename );
  3527. V_strcat_safe( tmpFileName, ".dx90.vtx" );
  3528. V_strcpy_safe( glViewFilename, filename );
  3529. V_strcat_safe( glViewFilename, ".dx90.glview" );
  3530. s_OptimizedModel.OptimizeFromStudioHdr( phdr, pSrcBodyParts,
  3531. 24 /* vert cache size (real size, not effective!)*/,
  3532. false, /* doesn't use fixed function */
  3533. false, // don't force software skinning
  3534. true, // Hardware flex on DX9 parts
  3535. 3 /* bones/vert */,
  3536. 9 /* bones/tri */,
  3537. 53 /* bones/strip */,
  3538. tmpFileName, glViewFilename );
  3539. s_StringTable.Purge();
  3540. }
  3541. }; // namespace OptimizedModel
  3542. #pragma optimize( "", on )