Counter Strike : Global Offensive Source Code
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.

4471 lines
152 KiB

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