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.

1693 lines
49 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <sys/stat.h>
  11. #include <math.h>
  12. #include <float.h>
  13. #include "cmdlib.h"
  14. #include "scriplib.h"
  15. #include "mathlib/mathlib.h"
  16. #include "studio.h"
  17. #include "studiomdl.h"
  18. #include "bone_setup.h"
  19. #include "tier1/strtools.h"
  20. #include "mathlib/vmatrix.h"
  21. #include "optimize.h"
  22. // debugging only - enabling turns off remapping to create all lod vertexes as unique
  23. // to ensure remapping logic does not introduce collapse anomalies
  24. //#define UNIQUE_VERTEXES_FOR_LOD
  25. //-----------------------------------------------------------------------------
  26. // Forward declarations local to this file
  27. //-----------------------------------------------------------------------------
  28. class CVertexDictionary;
  29. struct VertexInfo_t;
  30. static void BuildBoneLODMapping( CUtlVector<int> &boneMap, int lodID );
  31. //-----------------------------------------------------------------------------
  32. // Globals
  33. //-----------------------------------------------------------------------------
  34. static int g_NumBonesInLOD[MAX_NUM_LODS];
  35. //-----------------------------------------------------------------------------
  36. // Makes sure all boneweights in a s_boneweight_t are valid
  37. //-----------------------------------------------------------------------------
  38. static void ValidateBoneWeight( const s_boneweight_t &boneWeight )
  39. {
  40. #ifdef _DEBUG
  41. int i;
  42. if( boneWeight.weight[0] == 1.0f )
  43. {
  44. Assert( boneWeight.numbones == 1 );
  45. }
  46. for( i = 0; i < boneWeight.numbones; i++ )
  47. {
  48. Assert( boneWeight.bone[i] >= 0 && boneWeight.bone[i] < g_numbones );
  49. }
  50. float weight = 0.0f;
  51. for( i = 0; i < boneWeight.numbones; i++ )
  52. {
  53. weight += boneWeight.weight[i] ;
  54. }
  55. Assert( fabs( weight - 1.0f ) < 1e-3 );
  56. #endif
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Swap bones
  60. //-----------------------------------------------------------------------------
  61. static inline void SwapBones( s_boneweight_t &boneWeight, int nBone1, int nBone2 )
  62. {
  63. // swap
  64. int nTmpBone = boneWeight.bone[nBone1];
  65. float flTmpWeight = boneWeight.weight[nBone1];
  66. boneWeight.bone[nBone1] = boneWeight.bone[nBone2];
  67. boneWeight.weight[nBone1] = boneWeight.weight[nBone2];
  68. boneWeight.bone[nBone2] = nTmpBone;
  69. boneWeight.weight[nBone2] = flTmpWeight;
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Sort the bone weight structure to be sorted by bone weight
  73. //-----------------------------------------------------------------------------
  74. static void SortBoneWeightByWeight( s_boneweight_t &boneWeight )
  75. {
  76. // bubble sort the bones by weight. . .put the largest weight first.
  77. for( int j = boneWeight.numbones; j > 1; j-- )
  78. {
  79. for( int k = 0; k < j - 1; k++ )
  80. {
  81. if( boneWeight.weight[k] >= boneWeight.weight[k+1] )
  82. continue;
  83. SwapBones( boneWeight, k, k+1 );
  84. }
  85. }
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Sort the bone weight structure to be sorted by bone index
  89. //-----------------------------------------------------------------------------
  90. static void SortBoneWeightByIndex( s_boneweight_t &boneWeight )
  91. {
  92. // bubble sort the bones by index. . .put the smallest index first.
  93. for ( int j = boneWeight.numbones; j > 1; j-- )
  94. {
  95. for( int k = 0; k < j - 1; k++ )
  96. {
  97. if( boneWeight.bone[k] <= boneWeight.bone[k+1] )
  98. continue;
  99. SwapBones( boneWeight, k, k+1 );
  100. }
  101. }
  102. }
  103. //-----------------------------------------------------------------------------
  104. // A vertex format
  105. //-----------------------------------------------------------------------------
  106. struct VertexInfo_t
  107. {
  108. Vector m_Position;
  109. Vector m_Normal;
  110. int m_numTexCoords;
  111. Vector2D m_TexCoord[MAXSTUDIOTEXCOORDS];
  112. Vector4D m_TangentS;
  113. s_boneweight_t m_BoneWeight;
  114. int m_nLodFlag;
  115. };
  116. //-----------------------------------------------------------------------------
  117. // Stores all vertices in the vertex dictionary
  118. //-----------------------------------------------------------------------------
  119. class CVertexDictionary
  120. {
  121. public:
  122. CVertexDictionary();
  123. // Adds a vertex to the dictionary
  124. int AddVertex( const VertexInfo_t &srcVertex );
  125. int AddVertexFromSource( const s_source_t *pSrc, int nVertexIndex, int nLod );
  126. // Iteration
  127. int VertexCount() const;
  128. VertexInfo_t &Vertex( int i );
  129. const VertexInfo_t &Vertex( int i ) const;
  130. int RootLODVertexStart() const;
  131. int RootLODVertexEnd() const;
  132. // Gets the vertex count for the previous LOD
  133. int PrevLODVertexCount() const;
  134. // Marks the dictionary as starting defining vertices for a new LOD
  135. void StartNewLOD();
  136. void SetRootVertexRange( int start, int end );
  137. private:
  138. CUtlVector<VertexInfo_t> m_Verts;
  139. int m_nPrevLODCount;
  140. int m_nRootLODStart;
  141. int m_nRootLODEnd;
  142. };
  143. //-----------------------------------------------------------------------------
  144. // Copies in a particular vertex from the s_source_t
  145. //-----------------------------------------------------------------------------
  146. CVertexDictionary::CVertexDictionary()
  147. {
  148. m_nPrevLODCount = 0;
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Accessor
  152. //-----------------------------------------------------------------------------
  153. inline VertexInfo_t &CVertexDictionary::Vertex( int i )
  154. {
  155. return m_Verts[i];
  156. }
  157. inline const VertexInfo_t &CVertexDictionary::Vertex( int i ) const
  158. {
  159. return m_Verts[i];
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Gets the vertex count for the previous LOD
  163. //-----------------------------------------------------------------------------
  164. inline int CVertexDictionary::PrevLODVertexCount() const
  165. {
  166. return m_nPrevLODCount;
  167. }
  168. inline int CVertexDictionary::RootLODVertexStart() const
  169. {
  170. return m_nRootLODStart;
  171. }
  172. inline int CVertexDictionary::RootLODVertexEnd() const
  173. {
  174. return m_nRootLODEnd;
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Marks the dictionary as starting defining vertices for a new LOD
  178. //-----------------------------------------------------------------------------
  179. void CVertexDictionary::StartNewLOD()
  180. {
  181. m_nPrevLODCount = VertexCount();
  182. }
  183. void CVertexDictionary::SetRootVertexRange( int start, int end )
  184. {
  185. m_nRootLODStart = start;
  186. m_nRootLODEnd = end;
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Adds a vertex to the dictionary
  190. //-----------------------------------------------------------------------------
  191. int CVertexDictionary::AddVertex( const VertexInfo_t &srcVertex )
  192. {
  193. int nDstVertID = m_Verts.AddToTail( srcVertex );
  194. VertexInfo_t &vertex = m_Verts[ nDstVertID ];
  195. ValidateBoneWeight( vertex.m_BoneWeight );
  196. SortBoneWeightByIndex( vertex.m_BoneWeight );
  197. ValidateBoneWeight( vertex.m_BoneWeight );
  198. return nDstVertID;
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Copies in a particular vertex from the s_source_t
  202. //-----------------------------------------------------------------------------
  203. int CVertexDictionary::AddVertexFromSource( const s_source_t *pSrc, int nVertexIndex, int nLod )
  204. {
  205. int nDstVertID = m_Verts.AddToTail( );
  206. VertexInfo_t &vertex = m_Verts[ nDstVertID ];
  207. const s_vertexinfo_t &srcVertex = pSrc->m_GlobalVertices[nVertexIndex];
  208. vertex.m_Position = srcVertex.position;
  209. vertex.m_Normal = srcVertex.normal;
  210. vertex.m_TangentS = srcVertex.tangentS;
  211. vertex.m_BoneWeight = srcVertex.boneweight;
  212. vertex.m_nLodFlag = 1 << nLod;
  213. for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
  214. {
  215. vertex.m_TexCoord[i] = srcVertex.texcoord[i];
  216. }
  217. vertex.m_numTexCoords = srcVertex.numTexcoord;
  218. ValidateBoneWeight( vertex.m_BoneWeight );
  219. SortBoneWeightByIndex( vertex.m_BoneWeight );
  220. ValidateBoneWeight( vertex.m_BoneWeight );
  221. return nDstVertID;
  222. }
  223. //-----------------------------------------------------------------------------
  224. // How many vertices in the dictionary?
  225. //-----------------------------------------------------------------------------
  226. int CVertexDictionary::VertexCount() const
  227. {
  228. return m_Verts.Count();
  229. }
  230. s_source_t* GetModelLODSource( const char *pModelName,
  231. const LodScriptData_t& scriptLOD, bool* pFound )
  232. {
  233. // When doing LOD replacement, ignore all path + extension information
  234. char* pTempBuf = (char*)_alloca( Q_strlen(pModelName) + 1 );
  235. // Strip off extensions for the source...
  236. strcpy( pTempBuf, pModelName );
  237. char* pDot = strrchr( pTempBuf, '.' );
  238. if (pDot)
  239. {
  240. *pDot = 0;
  241. }
  242. for( int i = 0; i < scriptLOD.modelReplacements.Count(); i++ )
  243. {
  244. // FIXME: Should we strip off path information?
  245. // char* pSlash = strrchr( pTempBuf1, '\\' );
  246. // char* pSlash2 = strrchr( pTempBuf1, '/' );
  247. // if (pSlash2 > pSlash)
  248. // pSlash = pSlash2;
  249. // if (!pSlash)
  250. // pSlash = pTempBuf1;
  251. if( !Q_stricmp( pTempBuf, scriptLOD.modelReplacements[i].GetSrcName() ) )
  252. {
  253. *pFound = true;
  254. return scriptLOD.modelReplacements[i].m_pSource;
  255. }
  256. }
  257. *pFound = false;
  258. return 0;
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Tolerances for all fields of the vertex
  262. //-----------------------------------------------------------------------------
  263. #define POSITION_EPSILON 0.05f
  264. #define TEXCOORD_EPSILON 0.01f
  265. #define NORMAL_EPSILON 10.0f // in degrees
  266. #define TANGENT_EPSILON 10.0f // in degrees
  267. #define BONEWEIGHT_EPSILON 0.05f
  268. #define EXTRADATA_EPSILON 0.01f
  269. #define UNMATCHED_BONE_WEIGHT 1.0f
  270. //-----------------------------------------------------------------------------
  271. // Computes error between two positions; returns false if the error is too great
  272. //-----------------------------------------------------------------------------
  273. bool ComparePositionFuzzy( const Vector &p1, const Vector &p2, float &flError )
  274. {
  275. Vector vecDelta;
  276. VectorSubtract( p1, p2, vecDelta );
  277. flError = DotProduct( vecDelta, vecDelta );
  278. return ( flError <= (POSITION_EPSILON * POSITION_EPSILON) );
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Computes error between two normals; returns false if the error is too great
  282. //-----------------------------------------------------------------------------
  283. bool CompareNormalFuzzy( const Vector &n1, const Vector &n2, float &flError )
  284. {
  285. static float flEpsilon = cos( DEG2RAD( NORMAL_EPSILON ) );
  286. Vector v1, v2;
  287. v1 = n1;
  288. v2 = n2;
  289. VectorNormalize( v1 );
  290. VectorNormalize( v2 );
  291. float flDot = DotProduct( v1, v2 );
  292. flError = 1.0F - flDot;
  293. return ( flDot >= flEpsilon );
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Computes error between two tangentS vectors; returns false if the error is too great
  297. //-----------------------------------------------------------------------------
  298. bool CompareTangentSFuzzy( const Vector4D &n1, const Vector4D &n2, float &flError )
  299. {
  300. static float flEpsilon = cos( DEG2RAD( TANGENT_EPSILON ) );
  301. Vector4D v1, v2;
  302. v1 = n1;
  303. v2 = n2;
  304. if (v1.w != v2.w)
  305. {
  306. // must match as -1 or 1
  307. flError = 2;
  308. return false;
  309. }
  310. VectorNormalize( v1.AsVector3D() );
  311. VectorNormalize( v2.AsVector3D() );
  312. float flDot = DotProduct( v1.AsVector3D(), v2.AsVector3D() );
  313. // error ranges from [0..2]
  314. flError = 1.0F - flDot;
  315. return ( flDot >= flEpsilon );
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Computes error between two texcoords; returns false if the error is too great
  319. //-----------------------------------------------------------------------------
  320. bool CompareTexCoordsFuzzy( const Vector2D *t1, const Vector2D *t2, float &flError )
  321. {
  322. Vector2D vecError;
  323. flError = 0.0f;
  324. for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
  325. {
  326. vecError[0] = fabs(t2[i][0] - t1[i][0]);
  327. vecError[1] = fabs(t2[i][1] - t1[i][1]);
  328. flError += vecError.LengthSqr();
  329. }
  330. return ( flError <= (TEXCOORD_EPSILON * TEXCOORD_EPSILON) );
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Computes the error between two bone weights, returns false if they are too far
  334. //-----------------------------------------------------------------------------
  335. bool CompareBoneWeightsFuzzy( const s_boneweight_t &b1, const s_boneweight_t &b2, float &flError )
  336. {
  337. // This is a list of which bones that exist in b1 also exist in b2.
  338. // Use the index to figure out where in the array for b2 that the corresponding bone in b1 is.
  339. int nMatchingBones = 0;
  340. int pBoneIndexMap1[MAX_NUM_BONES_PER_VERT];
  341. int pBoneIndexMap2[MAX_NUM_BONES_PER_VERT];
  342. int i;
  343. for ( i = 0; i < b2.numbones; ++i )
  344. {
  345. pBoneIndexMap2[i] = -1;
  346. }
  347. for ( i = 0; i < b1.numbones; ++i )
  348. {
  349. pBoneIndexMap1[i] = -1;
  350. for ( int j = 0; j < b2.numbones; ++j )
  351. {
  352. if ( b2.bone[j] == b1.bone[i] )
  353. {
  354. pBoneIndexMap1[i] = j;
  355. pBoneIndexMap2[j] = i;
  356. ++nMatchingBones;
  357. break;
  358. }
  359. }
  360. }
  361. // If no bones match, we're done
  362. if ( !nMatchingBones )
  363. {
  364. flError = FLT_MAX;
  365. return false;
  366. }
  367. // At least one bone matches, so we're going to consider this vertex as a potential match
  368. // This loop will take care of figuring out the error for all bones that exist in
  369. // b1 alone, and all bones that exist in b1 and b2
  370. flError = 0;
  371. for ( i = 0; i < b1.numbones; ++i )
  372. {
  373. // If we didn't find a match for this bone, compute a more expensive weight
  374. if ( pBoneIndexMap1[i] == -1 )
  375. {
  376. flError += b1.weight[i] * b1.weight[i] * UNMATCHED_BONE_WEIGHT;
  377. continue;
  378. }
  379. float flDeltaWeight = fabs( b1.weight[i] - b2.weight[ pBoneIndexMap1[i] ] );
  380. flError += flDeltaWeight * flDeltaWeight;
  381. }
  382. // This loop will take care of figuring out the error for all bones that exist in b2 alone
  383. for ( i = 0; i < b2.numbones; ++i )
  384. {
  385. // If we didn't find a match for this bone, compute a more expensive weight
  386. if ( pBoneIndexMap2[i] == -1 )
  387. {
  388. flError += b2.weight[i] * b2.weight[i] * UNMATCHED_BONE_WEIGHT;
  389. }
  390. }
  391. // This renormalizes the error. The error will become greater with the total
  392. // number of bones in the two vertices.
  393. flError /= sqrt( (float) (b1.numbones + b2.numbones));
  394. return ( flError <= BONEWEIGHT_EPSILON );
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Searches for a material in the texture list
  398. //-----------------------------------------------------------------------------
  399. int FindMaterialByName( const char *pMaterialName )
  400. {
  401. int i;
  402. int allocLen = strlen( pMaterialName ) + 1;
  403. char *pBaseName = ( char * )_alloca( allocLen );
  404. Q_FileBase( ( char * )pMaterialName, pBaseName, allocLen );
  405. for( i = 0; i < g_numtextures; i++ )
  406. {
  407. if( stricmp( pMaterialName, g_texture[i].name ) == 0 )
  408. {
  409. return i;
  410. }
  411. }
  412. return -1;
  413. }
  414. static s_mesh_t *FindMeshByMaterial( s_source_t *pSrc, int nMaterialID )
  415. {
  416. for ( int m = 0; m < pSrc->nummeshes; m++ )
  417. {
  418. if ( pSrc->meshindex[m] == nMaterialID )
  419. return &pSrc->mesh[ pSrc->meshindex[m] ];
  420. }
  421. // this mesh/material doesn't exist at this lod.
  422. return NULL;
  423. }
  424. static s_mesh_t *FindOrCullMesh( int nLodID, s_source_t *pSrc, int nMaterialID )
  425. {
  426. char baseMeshName[MAX_PATH];
  427. char baseRemovalName[MAX_PATH];
  428. // possibly marked for removal via $removemesh
  429. // determine mesh name
  430. int nTextureID = MaterialToTexture( nMaterialID );
  431. if (nTextureID == -1)
  432. {
  433. MdlError( "Unknown Texture for Material %d\n", nMaterialID );
  434. }
  435. Q_FileBase(g_texture[nTextureID].name, baseMeshName, sizeof(baseMeshName)-1);
  436. for ( int i = 0; i < g_ScriptLODs[nLodID].meshRemovals.Count(); i++ )
  437. {
  438. const char *pMeshRemovalName = g_ScriptLODs[nLodID].meshRemovals[i].GetSrcName();
  439. Q_FileBase( pMeshRemovalName, baseRemovalName, sizeof(baseRemovalName)-1);
  440. if (!stricmp( baseRemovalName, baseMeshName ))
  441. {
  442. // mesh has been marked for removal
  443. return NULL;
  444. }
  445. }
  446. s_mesh_t *pMesh = FindMeshByMaterial( pSrc, nMaterialID );
  447. return pMesh;
  448. }
  449. static void CopyVerts( int nLodID, const s_source_t *pSrc, const s_mesh_t *pSrcMesh, CVertexDictionary &vertexDict, s_mesh_t *pDstMesh, int *pMeshVertIndexMap )
  450. {
  451. // populate the dictionary with the verts
  452. for( int srcVertID = 0; srcVertID < pSrcMesh->numvertices; srcVertID++ )
  453. {
  454. int nVertexIndex = pSrcMesh->vertexoffset + srcVertID;
  455. pMeshVertIndexMap[ nVertexIndex ] = vertexDict.AddVertexFromSource( pSrc, nVertexIndex, nLodID ) - pDstMesh->vertexoffset;
  456. }
  457. pDstMesh->numvertices = pSrcMesh->numvertices;
  458. }
  459. static void CopyFaces( const s_source_t *pSrc, const s_mesh_t *pSrcMesh, CUtlVector<s_face_t> &faces, s_mesh_t *pDstMesh )
  460. {
  461. int srcFaceID;
  462. for( srcFaceID = 0; srcFaceID < pSrcMesh->numfaces; srcFaceID++ )
  463. {
  464. int srcID = srcFaceID + pSrcMesh->faceoffset;
  465. s_face_t *pSrcFace = &pSrc->face[srcID];
  466. s_face_t *pDstFace = &faces[faces.AddToTail()];
  467. pDstFace->a = pSrcFace->a;
  468. pDstFace->b = pSrcFace->b;
  469. pDstFace->c = pSrcFace->c;
  470. pDstFace->d = pSrcFace->d;
  471. pDstMesh->numfaces++;
  472. }
  473. }
  474. #define IGNORE_POSITION 0x01
  475. #define IGNORE_TEXCOORD 0x02
  476. #define IGNORE_BONEWEIGHT 0x04
  477. #define IGNORE_NORMAL 0x08
  478. #define IGNORE_TANGENTS 0x10
  479. //-----------------------------------------------------------------------------
  480. // return -1 if there is no match. The index returned is used to index into vertexDict.
  481. //-----------------------------------------------------------------------------
  482. static int FindVertexWithinVertexDictionary( const VertexInfo_t &find,
  483. const CVertexDictionary &vertexDict, int nStartVert, int nEndVert, int fIgnore )
  484. {
  485. int nBestIndex = -1;
  486. float flPositionError = 0.0f;
  487. float flNormalError = 0.0f;
  488. float flTangentSError = 0.0f;
  489. float flTexcoordError = 0.0f;
  490. float flBoneWeightError = 0.0f;
  491. float flMinPositionError = FLT_MAX;
  492. float flMinNormalError = FLT_MAX;
  493. float flMinTangentSError = FLT_MAX;
  494. float flMinTexcoordError = FLT_MAX;
  495. float flMinBoneWeightError = FLT_MAX;
  496. bool bFound;
  497. if (fIgnore & IGNORE_POSITION)
  498. {
  499. flMinPositionError = 0;
  500. flPositionError = 0;
  501. }
  502. if (fIgnore & IGNORE_TEXCOORD)
  503. {
  504. flMinTexcoordError = 0;
  505. flTexcoordError = 0;
  506. }
  507. if (fIgnore & IGNORE_BONEWEIGHT)
  508. {
  509. flMinBoneWeightError = 0;
  510. flBoneWeightError = 0;
  511. }
  512. if (fIgnore & IGNORE_NORMAL)
  513. {
  514. flMinNormalError = 0;
  515. flNormalError = 0;
  516. }
  517. if (fIgnore & IGNORE_TANGENTS)
  518. {
  519. flMinTangentSError = 0;
  520. flTangentSError = 0;
  521. }
  522. for (int nVertexIndex = nStartVert; nVertexIndex < nEndVert; ++nVertexIndex)
  523. {
  524. // see if the position is reasonable
  525. if ( !(fIgnore & IGNORE_POSITION) && !ComparePositionFuzzy( find.m_Position, vertexDict.Vertex(nVertexIndex).m_Position, flPositionError ) )
  526. continue;
  527. if ( !(fIgnore & IGNORE_TEXCOORD) && !CompareTexCoordsFuzzy( find.m_TexCoord, vertexDict.Vertex(nVertexIndex).m_TexCoord, flTexcoordError ) )
  528. continue;
  529. if ( !(fIgnore & IGNORE_BONEWEIGHT) && !CompareBoneWeightsFuzzy( find.m_BoneWeight, vertexDict.Vertex(nVertexIndex).m_BoneWeight, flBoneWeightError ) )
  530. continue;
  531. if ( !(fIgnore & IGNORE_NORMAL) && !CompareNormalFuzzy( find.m_Normal, vertexDict.Vertex(nVertexIndex).m_Normal, flNormalError ) )
  532. continue;
  533. if ( !(fIgnore & IGNORE_TANGENTS) && !CompareTangentSFuzzy( find.m_TangentS, vertexDict.Vertex(nVertexIndex).m_TangentS, flTangentSError ) )
  534. continue;
  535. // the vert with minimum error is the best or exact candidate
  536. bFound = false;
  537. if (flMinPositionError > flPositionError)
  538. {
  539. bFound = true;
  540. }
  541. else if (flMinPositionError == flPositionError)
  542. {
  543. if (flMinTexcoordError > flTexcoordError)
  544. {
  545. bFound = true;
  546. }
  547. else if (flMinTexcoordError == flTexcoordError)
  548. {
  549. if (flMinBoneWeightError > flBoneWeightError)
  550. {
  551. bFound = true;
  552. }
  553. else if (flMinBoneWeightError == flBoneWeightError)
  554. {
  555. if (flMinNormalError > flNormalError)
  556. {
  557. bFound = true;
  558. }
  559. else if (flMinNormalError == flNormalError)
  560. {
  561. if (flMinTangentSError >= flTangentSError)
  562. {
  563. bFound = true;
  564. }
  565. }
  566. }
  567. }
  568. }
  569. if (!bFound)
  570. continue;
  571. flMinPositionError = flPositionError;
  572. flMinTexcoordError = flTexcoordError;
  573. flMinBoneWeightError = flBoneWeightError;
  574. flMinNormalError = flNormalError;
  575. flMinTangentSError = flTangentSError;
  576. nBestIndex = nVertexIndex;
  577. }
  578. return nBestIndex;
  579. }
  580. //-----------------------------------------------------------------------------
  581. // Use position, normal, and texcoord checks across the entire model to find a boneweight
  582. //-----------------------------------------------------------------------------
  583. static void FindBoneWeightWithinModel( const VertexInfo_t &searchVertex, const s_source_t *pSrc, s_boneweight_t &boneWeight, int fIgnore )
  584. {
  585. int nBestIndex = -1;
  586. float flPositionError = 0.0f;
  587. float flNormalError = 0.0f;
  588. float flTangentSError = 0.0f;
  589. float flTexcoordError = 0.0f;
  590. float flMinPositionError = FLT_MAX;
  591. float flMinNormalError = FLT_MAX;
  592. float flMinTangentSError = FLT_MAX;
  593. float flMinTexcoordError = FLT_MAX;
  594. bool bFound;
  595. if (fIgnore & IGNORE_NORMAL)
  596. {
  597. flMinNormalError = 0;
  598. flNormalError = 0;
  599. }
  600. if (fIgnore & IGNORE_TEXCOORD)
  601. {
  602. flMinTexcoordError = 0;
  603. flTexcoordError = 0;
  604. }
  605. if (fIgnore & IGNORE_TANGENTS)
  606. {
  607. flMinTangentSError = 0;
  608. flTangentSError = 0;
  609. }
  610. int nVertexCount = pSrc->m_GlobalVertices.Count();
  611. for ( int i = 0; i < nVertexCount; i++ )
  612. {
  613. const s_vertexinfo_t &srcVertex = pSrc->m_GlobalVertices[i];
  614. // Compute error metrics
  615. ComparePositionFuzzy( searchVertex.m_Position, srcVertex.position, flPositionError );
  616. if (!(fIgnore & IGNORE_NORMAL))
  617. {
  618. CompareNormalFuzzy( searchVertex.m_Normal, srcVertex.normal, flNormalError );
  619. }
  620. if (!(fIgnore & IGNORE_TEXCOORD))
  621. {
  622. CompareTexCoordsFuzzy( searchVertex.m_TexCoord, srcVertex.texcoord, flTexcoordError );
  623. }
  624. if (!(fIgnore & IGNORE_TANGENTS))
  625. {
  626. CompareTangentSFuzzy( searchVertex.m_TangentS, srcVertex.tangentS, flTangentSError );
  627. }
  628. // the vert with minimum error is the best or exact candidate
  629. bFound = false;
  630. if (flMinPositionError > flPositionError)
  631. {
  632. bFound = true;
  633. }
  634. else if (flMinPositionError == flPositionError)
  635. {
  636. if (flMinTexcoordError > flTexcoordError)
  637. {
  638. bFound = true;
  639. }
  640. else if (flMinTexcoordError == flTexcoordError)
  641. {
  642. if (flMinNormalError > flNormalError)
  643. {
  644. bFound = true;
  645. }
  646. else if (flMinNormalError == flNormalError)
  647. {
  648. if (flMinTangentSError >= flTangentSError)
  649. {
  650. bFound = true;
  651. }
  652. }
  653. }
  654. }
  655. if (bFound)
  656. {
  657. flMinPositionError = flPositionError;
  658. flMinTexcoordError = flTexcoordError;
  659. flMinNormalError = flNormalError;
  660. flMinTangentSError = flTangentSError;
  661. nBestIndex = i;
  662. }
  663. }
  664. if ( nBestIndex == -1 )
  665. {
  666. MdlError( "Encountered a mesh with no vertices!\n" );
  667. }
  668. memcpy( &boneWeight, &pSrc->m_GlobalVertices[ nBestIndex ].boneweight, sizeof(s_boneweight_t) );
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Modify the bone weights in all of the vertices....
  672. //-----------------------------------------------------------------------------
  673. static void RemapBoneWeights( const CUtlVector<int> &boneMap, s_boneweight_t &boneWeight )
  674. {
  675. for( int i = 0; i < boneWeight.numbones; i++ )
  676. {
  677. Assert( boneWeight.bone[i] >= 0 && boneWeight.bone[i] < boneMap.Count() );
  678. boneWeight.bone[i] = boneMap[ boneWeight.bone[i] ];
  679. }
  680. }
  681. //-----------------------------------------------------------------------------
  682. // After the remapping, we may get multiple instances of the same bone
  683. // which we want to collapse into a single bone
  684. //-----------------------------------------------------------------------------
  685. static void CollapseBoneWeights( s_boneweight_t &boneWeight )
  686. {
  687. // We need the bones to be sorted by bone index for the loop right below
  688. SortBoneWeightByIndex( boneWeight );
  689. for( int i = 0; i < boneWeight.numbones-1; i++ )
  690. {
  691. if( boneWeight.bone[i] != boneWeight.bone[i+1] )
  692. continue;
  693. // add i+1's weight to i since they have the same bone index
  694. boneWeight.weight[i] += boneWeight.weight[i+1];
  695. // remove i+1
  696. for( int j = i+1; j < boneWeight.numbones-1; j++ )
  697. {
  698. boneWeight.bone[j] = boneWeight.bone[j+1];
  699. boneWeight.weight[j] = boneWeight.weight[j+1];
  700. }
  701. --boneWeight.numbones;
  702. // Gotta step back one, may have many bones collapsing into one
  703. --i;
  704. }
  705. ValidateBoneWeight( boneWeight );
  706. }
  707. //-----------------------------------------------------------------------------
  708. // Find a matching vertex within the root lod
  709. //-----------------------------------------------------------------------------
  710. static void CalculateBoneWeightFromRootLod( const VertexInfo_t &searchVertex, CVertexDictionary &vertexDict,
  711. const s_source_t *pRootLODSrc, VertexInfo_t &idealVertex )
  712. {
  713. idealVertex = searchVertex;
  714. // Look through the part of the vertex dictionary associated with the root LODs for a match
  715. // bone weights are not defined properly in SMDs for lower LODs, so don't consider
  716. // we can only accept the boneweight from the root LOD
  717. int nFlags = g_bSkinnedLODs ? IGNORE_TANGENTS : IGNORE_BONEWEIGHT|IGNORE_TANGENTS;
  718. int nVertexDictID = FindVertexWithinVertexDictionary( searchVertex, vertexDict,
  719. vertexDict.RootLODVertexStart(), vertexDict.RootLODVertexEnd(), nFlags );
  720. if ( nVertexDictID != -1 )
  721. {
  722. Assert( nVertexDictID >= vertexDict.RootLODVertexStart() && nVertexDictID < vertexDict.RootLODVertexEnd() );
  723. Assert( nVertexDictID >= 0 && nVertexDictID < vertexDict.VertexCount() );
  724. // found vertex in dictionary
  725. #ifdef UNIQUE_VERTEXES_FOR_LOD
  726. if ( !g_bSkinnedLODs )
  727. {
  728. // keep entry vertex and fill in the missing bone weight attribute
  729. idealVertex.m_BoneWeight = vertexDict.Vertex( nVertexDictID ).m_BoneWeight;
  730. }
  731. else
  732. #else
  733. // discard entry vertex in favor of best match
  734. // this ensures all the attributes, including bone weight are correct for that vertex
  735. // the worst case is that the vertex is not an *exact* match for entry attributes just a "close" match
  736. idealVertex = vertexDict.Vertex( nVertexDictID );
  737. #endif
  738. return;
  739. }
  740. // In this case, we didn't find anything within the tolerance, so we need to
  741. // do a *positional check only* to give us a bone weight to assign to this vertex.
  742. if ( !g_bSkinnedLODs )
  743. {
  744. FindBoneWeightWithinModel( searchVertex, pRootLODSrc, idealVertex.m_BoneWeight, IGNORE_BONEWEIGHT|IGNORE_TANGENTS );
  745. }
  746. }
  747. //-----------------------------------------------------------------------------
  748. // Find a matching vertex
  749. //-----------------------------------------------------------------------------
  750. static void CalculateIdealVert( const VertexInfo_t &searchVertex, CVertexDictionary &vertexDict,
  751. const s_mesh_t *pVertexDictMesh, const s_source_t *pRootLODSrc, VertexInfo_t &idealVertex )
  752. {
  753. #ifndef UNIQUE_VERTEXES_FOR_LOD
  754. // Only look through the part of the vertex dictionary associated with all *higher* LODs for a match
  755. int nVertexDictID = FindVertexWithinVertexDictionary( searchVertex, vertexDict,
  756. pVertexDictMesh->vertexoffset, vertexDict.PrevLODVertexCount(), 0 );
  757. if ( nVertexDictID != -1 )
  758. {
  759. Assert( nVertexDictID >= pVertexDictMesh->vertexoffset && nVertexDictID < vertexDict.PrevLODVertexCount() );
  760. Assert( nVertexDictID >= 0 && nVertexDictID < vertexDict.VertexCount() );
  761. // found vertex in dictionary
  762. idealVertex = vertexDict.Vertex( nVertexDictID );
  763. return;
  764. }
  765. #endif
  766. // could not find a tolerant match
  767. // the search vertex is unique
  768. idealVertex = searchVertex;
  769. }
  770. static bool FuzzyFloatCompare( float f1, float f2, float epsilon )
  771. {
  772. if( fabs( f1 - f2 ) < epsilon )
  773. {
  774. return true;
  775. }
  776. else
  777. {
  778. return false;
  779. }
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Is this bone weight structure sorted by bone?
  783. //-----------------------------------------------------------------------------
  784. static bool IsBoneWeightSortedByBone( const s_boneweight_t &src )
  785. {
  786. for ( int i = 1; i < src.numbones; ++i )
  787. {
  788. Assert( src.bone[i] != -1 );
  789. if ( src.bone[ i-1 ] > src.bone[ i ] )
  790. return false;
  791. }
  792. return true;
  793. }
  794. //-----------------------------------------------------------------------------
  795. // Are two bone-weight structures equal?
  796. //-----------------------------------------------------------------------------
  797. static bool AreBoneWeightsEqual( const s_boneweight_t &b1, const s_boneweight_t &b2 )
  798. {
  799. // Have to have the same number of bones
  800. if ( b1.numbones != b2.numbones )
  801. return false;
  802. // This is a list of which bones that exist in b1 also exist in b2.
  803. // Use the index to figure out where in the array for b2 that the corresponding bone in b1 is.
  804. int nMatchingBones = 0;
  805. int pBoneIndexMap[MAX_NUM_BONES_PER_VERT];
  806. int i;
  807. for ( i = 0; i < b1.numbones; ++i )
  808. {
  809. pBoneIndexMap[i] = -1;
  810. for ( int j = 0; j < b2.numbones; ++j )
  811. {
  812. if ( b2.bone[j] == b1.bone[i] )
  813. {
  814. pBoneIndexMap[i] = j;
  815. ++nMatchingBones;
  816. break;
  817. }
  818. }
  819. }
  820. // If we aren't using the same bone indices, we're done
  821. if ( nMatchingBones != b1.numbones )
  822. return false;
  823. // Check to see if the weights are the same
  824. for ( i = 0; i < b1.numbones; ++i )
  825. {
  826. Assert( pBoneIndexMap[i] != -1 );
  827. if ( b1.weight[i] != b2.weight[ pBoneIndexMap[i] ] )
  828. return false;
  829. }
  830. return true;
  831. }
  832. //-----------------------------------------------------------------------------
  833. // Finds an *exact* requested vertex in the dictionary
  834. //-----------------------------------------------------------------------------
  835. static int FindVertexInDictionaryExact( CVertexDictionary &vertexDict, int nStartVert, int nEndVert, const VertexInfo_t &vertex )
  836. {
  837. for ( int nVertID = nStartVert; nVertID < nEndVert; ++nVertID )
  838. {
  839. if ( vertexDict.Vertex( nVertID ).m_Position != vertex.m_Position )
  840. continue;
  841. if ( !AreBoneWeightsEqual( vertexDict.Vertex( nVertID ).m_BoneWeight, vertex.m_BoneWeight ) )
  842. continue;
  843. bool bMatch = true;
  844. for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
  845. {
  846. if (vertexDict.Vertex(nVertID).m_TexCoord[i] != vertex.m_TexCoord[i])
  847. {
  848. bMatch = false;
  849. break;
  850. }
  851. }
  852. if (!bMatch)
  853. continue;
  854. if ( vertexDict.Vertex( nVertID ).m_Normal != vertex.m_Normal )
  855. continue;
  856. if ( vertexDict.Vertex( nVertID ).m_TangentS != vertex.m_TangentS )
  857. continue;
  858. return nVertID;
  859. }
  860. return -1;
  861. }
  862. //-----------------------------------------------------------------------------
  863. // Finds the *exact* requested vertex in the dictionary or creates it
  864. //-----------------------------------------------------------------------------
  865. static int FindOrCreateExactVertexInDictionary( CVertexDictionary &vertexDict,
  866. const VertexInfo_t &vertex, s_mesh_t *pDstMesh )
  867. {
  868. int nMeshVertID = FindVertexInDictionaryExact( vertexDict, pDstMesh->vertexoffset, pDstMesh->vertexoffset+pDstMesh->numvertices, vertex );
  869. if ( nMeshVertID != -1 )
  870. {
  871. // flag vertex for what LoD's are using it
  872. vertexDict.Vertex( nMeshVertID ).m_nLodFlag |= vertex.m_nLodFlag;
  873. return nMeshVertID - pDstMesh->vertexoffset;
  874. }
  875. nMeshVertID = vertexDict.AddVertex( vertex );
  876. ++pDstMesh->numvertices;
  877. return nMeshVertID - pDstMesh->vertexoffset;
  878. }
  879. static void PrintBonesUsedInLOD( s_source_t *pSrc )
  880. {
  881. printf( "PrintBonesUsedInLOD\n" );
  882. int nVertexCount = pSrc->m_GlobalVertices.Count();
  883. for( int i = 0; i <nVertexCount; i++ )
  884. {
  885. Vector &pos = pSrc->m_GlobalVertices[i].position;
  886. Vector &norm = pSrc->m_GlobalVertices[i].normal;
  887. Vector2D &texcoord = pSrc->m_GlobalVertices[i].texcoord[0];
  888. printf( "pos: %f %f %f norm: %f %f %f texcoord: %f %f\n",
  889. pos[0], pos[1], pos[2], norm[0], norm[1], norm[2], texcoord[0], texcoord[1] );
  890. s_boneweight_t *pBoneWeight = &pSrc->m_GlobalVertices[i].boneweight;
  891. int j;
  892. for( j = 0; j < pBoneWeight->numbones; j++ )
  893. {
  894. int globalBoneID = pBoneWeight->bone[j];
  895. const char *pBoneName = g_bonetable[globalBoneID].name;
  896. printf( "vert: %d bone: %d boneid: %d weight: %f name: \"%s\"\n", i, ( int )j, ( int )pBoneWeight->bone[j],
  897. ( float )pBoneWeight->weight[j], pBoneName );
  898. }
  899. printf( "\n" );
  900. fflush( stdout );
  901. }
  902. }
  903. //-----------------------------------------------------------------------------
  904. // Indicates a particular set of bones is used by a particular LOD
  905. //-----------------------------------------------------------------------------
  906. static void MarkBonesUsedByLod( const s_boneweight_t &boneWeight, int nLodID )
  907. {
  908. for( int j = 0; j < boneWeight.numbones; ++j )
  909. {
  910. int nGlobalBoneID = boneWeight.bone[j];
  911. s_bonetable_t *pBone = &g_bonetable[nGlobalBoneID];
  912. pBone->flags |= ( BONE_USED_BY_VERTEX_LOD0 << nLodID );
  913. }
  914. }
  915. static void PrintSBoneWeight( s_boneweight_t *pBoneWeight, const s_source_t *pSrc )
  916. {
  917. int j;
  918. for( j = 0; j < pBoneWeight->numbones; j++ )
  919. {
  920. int globalBoneID;
  921. globalBoneID = pBoneWeight->bone[j];
  922. const char *pBoneName = g_bonetable[globalBoneID].name;
  923. printf( "bone: %d boneid: %d weight: %f name: \"%s\"\n", ( int )j, ( int )pBoneWeight->bone[j],
  924. ( float )pBoneWeight->weight[j], pBoneName );
  925. }
  926. }
  927. //-----------------------------------------------------------------------------
  928. // In the non-top LOD, look for vertices that would be appropriate from the
  929. // vertex dictionary, and use them if you find them, or add new vertices to the
  930. // vertex dictionary if not and use those new vertices.
  931. //-----------------------------------------------------------------------------
  932. static void CreateLODVertsInDictionary( int nLodID, const s_source_t *pRootLODSrc, s_source_t *pCurrentLODSrc,
  933. const s_mesh_t *pCurrLODMesh, s_mesh_t *pVertexDictMesh, CVertexDictionary &vertexDict, int *pMeshVertIndexMap )
  934. {
  935. // this function is specific to lods and not the root
  936. Assert( nLodID );
  937. int nNumCurrentVerts = vertexDict.VertexCount();
  938. // Used to control where we look for vertices + merging rules
  939. vertexDict.StartNewLOD();
  940. CUtlVector<int> boneMap;
  941. BuildBoneLODMapping( boneMap, nLodID );
  942. for( int nSrcVertID = 0; nSrcVertID < pCurrLODMesh->numvertices; ++nSrcVertID )
  943. {
  944. int nSrcID = nSrcVertID + pCurrLODMesh->vertexoffset;
  945. // candidate vertex
  946. // vertices at lower LODs have bogus boneweights assigned
  947. // must get the boneweight from the nearest or exact vertex at root lod
  948. const s_vertexinfo_t& srcVertex = pCurrentLODSrc->m_GlobalVertices[nSrcID];
  949. VertexInfo_t vertex;
  950. vertex.m_Position = srcVertex.position;
  951. vertex.m_Normal = srcVertex.normal;
  952. vertex.m_TangentS = srcVertex.tangentS;
  953. for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
  954. {
  955. vertex.m_TexCoord[i] = srcVertex.texcoord[i];
  956. }
  957. vertex.m_numTexCoords = srcVertex.numTexcoord;
  958. if ( g_bSkinnedLODs )
  959. {
  960. vertex.m_BoneWeight = srcVertex.boneweight;
  961. }
  962. else
  963. {
  964. #ifdef _DEBUG
  965. memset( &vertex.m_BoneWeight, 0xDD, sizeof( s_boneweight_t ) );
  966. #endif
  967. }
  968. // determine the best bone weight for the desired vertex within the root lod only
  969. // the root lod contains no bone remappings
  970. // this ensures we get a vertex with its matched proper boneweight assignment
  971. VertexInfo_t idealVertex;
  972. CalculateBoneWeightFromRootLod( vertex, vertexDict, pRootLODSrc, idealVertex );
  973. // try again to match the candidate vertex
  974. // determine the ideal vertex with desired remapped boneweight
  975. vertex = idealVertex;
  976. CalculateIdealVert( vertex, vertexDict, pVertexDictMesh, pRootLODSrc, idealVertex);
  977. // remap bone
  978. RemapBoneWeights( boneMap, idealVertex.m_BoneWeight );
  979. CollapseBoneWeights( idealVertex.m_BoneWeight );
  980. SortBoneWeightByWeight( idealVertex.m_BoneWeight );
  981. // FIXME: this is marking bones based on the slammed vertex data
  982. MarkBonesUsedByLod( idealVertex.m_BoneWeight, nLodID );
  983. // tag ideal vertex as being part of the current lod
  984. idealVertex.m_nLodFlag = 1 << nLodID;
  985. // Find the exact vertex or create it in the dictionary
  986. int nMeshVertID = FindOrCreateExactVertexInDictionary( vertexDict, idealVertex, pVertexDictMesh );
  987. // Indicate where in the higher LODs the vertex we selected resides
  988. pMeshVertIndexMap[nSrcID] = nMeshVertID;
  989. }
  990. int nNewVertsCreated = vertexDict.VertexCount() - nNumCurrentVerts;
  991. if ( !g_quiet && nNewVertsCreated )
  992. {
  993. printf( "Lod %d: vertexes: %d (%d new)\n", nLodID, vertexDict.VertexCount(), nNewVertsCreated);
  994. }
  995. }
  996. static void PrintSourceVerts( s_source_t *pSrc )
  997. {
  998. int nVertexCount = pSrc->m_GlobalVertices.Count();
  999. for( int i = 0; i < nVertexCount; i++ )
  1000. {
  1001. const s_vertexinfo_t &srcVertex = pSrc->m_GlobalVertices[i];
  1002. printf( "v %d ", i );
  1003. printf( "pos: %f %f %f ", srcVertex.position[0], srcVertex.position[1], srcVertex.position[2] );
  1004. printf( "norm: %f %f %f ", srcVertex.normal[0], srcVertex.normal[1], srcVertex.normal[2] );
  1005. printf( "texcoord: %f %f\n", srcVertex.texcoord[0], srcVertex.texcoord[1] );
  1006. int j;
  1007. for( j = 0; j < srcVertex.boneweight.numbones; j++ )
  1008. {
  1009. printf( "\t%d: %d %f\n", j, ( int )srcVertex.boneweight.bone[j],
  1010. srcVertex.boneweight.weight[j] );
  1011. }
  1012. fflush( stdout );
  1013. }
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Copy the vertex dictionary to the finalized processed data
  1017. // Leaves the source data intact, necessary for later processes.
  1018. // Routines can then choose which data they operate on
  1019. //-----------------------------------------------------------------------------
  1020. static void SetProcessedWithDictionary( s_model_t* pSrcModel, CVertexDictionary &vertexDict,
  1021. CUtlVector<s_face_t> &faces, CUtlVector<s_mesh_t> &meshes, int *pMeshVertIndexMaps[MAX_NUM_LODS] )
  1022. {
  1023. int i;
  1024. s_loddata_t *pLodData = new s_loddata_t;
  1025. memset( pLodData, 0, sizeof(s_loddata_t) );
  1026. pSrcModel->m_pLodData = pLodData;
  1027. int nVertexCount = vertexDict.VertexCount();
  1028. pLodData->vertex = (s_lodvertexinfo_t *)calloc( nVertexCount, sizeof( s_lodvertexinfo_t ) );
  1029. pLodData->numvertices = nVertexCount;
  1030. pLodData->face = (s_face_t *)calloc( faces.Count(), sizeof( s_face_t ));
  1031. pLodData->numfaces = faces.Count();
  1032. for ( i = 0; i < nVertexCount; ++i )
  1033. {
  1034. const VertexInfo_t &srcVertex = vertexDict.Vertex( i );
  1035. s_lodvertexinfo_t &dstVertex = pLodData->vertex[i];
  1036. dstVertex.boneweight = srcVertex.m_BoneWeight;
  1037. Assert( dstVertex.boneweight.numbones <= 4 );
  1038. dstVertex.position = srcVertex.m_Position;
  1039. dstVertex.normal = srcVertex.m_Normal;
  1040. dstVertex.tangentS = srcVertex.m_TangentS;
  1041. dstVertex.lodFlag = srcVertex.m_nLodFlag;
  1042. for (int j = 0; j < MAXSTUDIOTEXCOORDS; ++j)
  1043. {
  1044. dstVertex.texcoord[j] = srcVertex.m_TexCoord[j];
  1045. }
  1046. dstVertex.numTexcoord = srcVertex.m_numTexCoords;
  1047. }
  1048. memcpy( pLodData->face, faces.Base(), faces.Count() * sizeof( s_face_t ) );
  1049. memcpy( pLodData->mesh, meshes.Base(), meshes.Count() * sizeof( s_mesh_t ) );
  1050. for (i=0; i<MAX_NUM_LODS; i++)
  1051. {
  1052. pLodData->pMeshVertIndexMaps[i] = pMeshVertIndexMaps[i];
  1053. }
  1054. }
  1055. //-----------------------------------------------------------------------------
  1056. // This fills out boneMap, which is a mapping from src bone to src bone replacement (or to itself
  1057. // if there is no bone replacement.
  1058. //-----------------------------------------------------------------------------
  1059. static void BuildBoneLODMapping( CUtlVector<int> &boneMap, int lodID )
  1060. {
  1061. boneMap.AddMultipleToTail( g_numbones );
  1062. Assert( lodID < g_ScriptLODs.Count() );
  1063. LodScriptData_t& scriptLOD = g_ScriptLODs[lodID];
  1064. // First, create a direct mapping where no bones are collapsed
  1065. int i;
  1066. for( i = 0; i < g_numbones; i++ )
  1067. {
  1068. boneMap[i] = i;
  1069. }
  1070. for( i = 0; i < scriptLOD.boneReplacements.Count(); i++ )
  1071. {
  1072. const char *src, *dst;
  1073. src = scriptLOD.boneReplacements[i].GetSrcName();
  1074. dst = scriptLOD.boneReplacements[i].GetDstName();
  1075. int j = findGlobalBone( src );
  1076. int k = findGlobalBone( dst );
  1077. if ( j != -1 && k != -1)
  1078. {
  1079. boneMap[j] = k;
  1080. }
  1081. else if ( j == -1)
  1082. {
  1083. // FIXME: is this really an error? It could just be replacement command for bone that doesnt' exist anymore.
  1084. if (g_verbose)
  1085. {
  1086. MdlWarning( "Couldn't replace unknown bone \"%s\" with \"%s\"\n", src, dst );
  1087. }
  1088. }
  1089. else
  1090. {
  1091. // FIXME: is this really an error? It could just be replacement command for bone that doesnt' exist anymore.
  1092. if (g_verbose)
  1093. {
  1094. MdlWarning( "Couldn't replace bone \"%s\" with unknown \"%s\"\n", src, dst );
  1095. }
  1096. }
  1097. }
  1098. }
  1099. static void MarkRootLODBones( CVertexDictionary &vertexDictionary )
  1100. {
  1101. // should result in an identity mapping
  1102. // because their are no bone remaps at the root lod
  1103. CUtlVector<int> boneMap;
  1104. BuildBoneLODMapping( boneMap, 0 );
  1105. // iterate and mark bones
  1106. for (int nVertDictID=vertexDictionary.RootLODVertexStart(); nVertDictID<vertexDictionary.RootLODVertexEnd(); nVertDictID++)
  1107. {
  1108. s_boneweight_t &boneWeight = vertexDictionary.Vertex( nVertDictID ).m_BoneWeight;
  1109. RemapBoneWeights( boneMap, boneWeight );
  1110. CollapseBoneWeights( boneWeight );
  1111. SortBoneWeightByWeight( boneWeight );
  1112. MarkBonesUsedByLod( boneWeight, 0 );
  1113. }
  1114. }
  1115. //-----------------------------------------------------------------------------
  1116. // Computes LOD vertices for a model piece.
  1117. //-----------------------------------------------------------------------------
  1118. static void UnifyModelLODs( s_model_t *pSrcModel )
  1119. {
  1120. if ( !Q_stricmp( pSrcModel->name, "blank" ) )
  1121. return;
  1122. // each lod has a unique vertex mapping table
  1123. int nNumLODs = pSrcModel->m_LodSources.Count();
  1124. int nLodID;
  1125. int *pMeshVertIndexMaps[MAX_NUM_LODS];
  1126. for ( nLodID = 0; nLodID < MAX_NUM_LODS; nLodID++ )
  1127. {
  1128. if ( nLodID < nNumLODs && pSrcModel->m_LodSources[nLodID] )
  1129. {
  1130. int nVertexCount = pSrcModel->m_LodSources[nLodID]->m_GlobalVertices.Count();
  1131. pMeshVertIndexMaps[nLodID] = new int[ nVertexCount ];
  1132. #ifdef _DEBUG
  1133. memset( pMeshVertIndexMaps[nLodID], 0xDD, nVertexCount * sizeof(int) );
  1134. #endif
  1135. }
  1136. else
  1137. {
  1138. pMeshVertIndexMaps[nLodID] = NULL;
  1139. }
  1140. }
  1141. // These hold the aggregate data for the model that grows as lods are processed
  1142. CVertexDictionary vertexDictionary;
  1143. CUtlVector<s_face_t> faces;
  1144. CUtlVector<s_mesh_t> meshes;
  1145. meshes.AddMultipleToTail( MAXSTUDIOSKINS );
  1146. Assert( meshes.Count() == MAXSTUDIOSKINS );
  1147. memset( meshes.Base(), 0, meshes.Count() * sizeof( s_mesh_t ) );
  1148. int nMeshID;
  1149. for( nMeshID = 0; nMeshID < pSrcModel->source->nummeshes; nMeshID++ )
  1150. {
  1151. s_mesh_t *pVertexDictMesh = &meshes[pSrcModel->source->meshindex[nMeshID]];
  1152. pVertexDictMesh->numvertices = 0;
  1153. pVertexDictMesh->vertexoffset = vertexDictionary.VertexCount();
  1154. pVertexDictMesh->numfaces = 0;
  1155. pVertexDictMesh->faceoffset = faces.Count();
  1156. // First build up information for LOD 0
  1157. if ( !pSrcModel->m_LodSources[0] )
  1158. continue;
  1159. s_source_t *pLOD0Source = pSrcModel->m_LodSources[0];
  1160. // lookup the material used by this mesh
  1161. int nMaterialID = pLOD0Source->meshindex[nMeshID];
  1162. const char *pName = g_texture[nMaterialID].name;
  1163. if ( !g_quiet )
  1164. {
  1165. printf( "Processing LOD for material: %s\n", pName );
  1166. }
  1167. s_mesh_t *pLOD0Mesh = FindMeshByMaterial( pLOD0Source, nMaterialID );
  1168. if ( !pLOD0Mesh )
  1169. continue;
  1170. // populate with all vertices from LOD 0
  1171. int nStart = vertexDictionary.VertexCount();
  1172. CopyVerts( 0, pLOD0Source, pLOD0Mesh, vertexDictionary, pVertexDictMesh, pMeshVertIndexMaps[0] );
  1173. vertexDictionary.SetRootVertexRange( nStart, vertexDictionary.VertexCount() );
  1174. MarkRootLODBones( vertexDictionary );
  1175. // only fix up the faces for the highest lod since the lowest ones are going
  1176. // to be reprocessed later.
  1177. CopyFaces( pLOD0Source, pLOD0Mesh, faces, pVertexDictMesh );
  1178. // Now, for each LOD, try to build meshes using the vertices in LOD 0.
  1179. // Ideally, vertices used in an LOD would be in LOD 0 for the benefit of shared vertices.
  1180. // If we don't find vertices in LOD 0, this code will add vertices into LOD 0's list
  1181. // of vertices for the next LOD to find
  1182. for ( nLodID = 1; nLodID < nNumLODs; ++nLodID )
  1183. {
  1184. s_source_t *pCurrLOD = pSrcModel->m_LodSources[nLodID];
  1185. if ( !pCurrLOD )
  1186. continue;
  1187. // Find the mesh that matches the material
  1188. // mesh may not be present or could be culled due to $removemesh commands
  1189. s_mesh_t *pCurrLODMesh = FindOrCullMesh( nLodID, pCurrLOD, nMaterialID );
  1190. if ( !pCurrLODMesh )
  1191. continue;
  1192. CreateLODVertsInDictionary( nLodID, pLOD0Source, pCurrLOD, pCurrLODMesh, pVertexDictMesh, vertexDictionary, pMeshVertIndexMaps[nLodID]);
  1193. }
  1194. }
  1195. #ifdef _DEBUG
  1196. Msg( "Total vertex count: %d\n", vertexDictionary.VertexCount() );
  1197. #endif
  1198. // save the data we just built into the processed data section
  1199. // The processed data has all of the verts that are needed for all LODs.
  1200. SetProcessedWithDictionary( pSrcModel, vertexDictionary, faces, meshes, pMeshVertIndexMaps );
  1201. // PrintSourceVerts( pSrcModel->m_LodModels[0] );
  1202. }
  1203. //-----------------------------------------------------------------------------
  1204. // Force the vertex array for a model to have all of the vertices that are needed
  1205. // for all of the LODs of the model.
  1206. //-----------------------------------------------------------------------------
  1207. void UnifyLODs( void )
  1208. {
  1209. // todo: need to fixup the firstref/lastref stuff . . do we really need it anymore?
  1210. for( int modelID = 0; modelID < g_nummodelsbeforeLOD; modelID++ )
  1211. {
  1212. UnifyModelLODs( g_model[modelID] );
  1213. }
  1214. }
  1215. static void PrintSpaces( int numSpaces )
  1216. {
  1217. int i;
  1218. for( i = 0; i < numSpaces; i++ )
  1219. {
  1220. printf( " " );
  1221. }
  1222. }
  1223. static void SpewBoneInfo( int globalBoneID, int depth )
  1224. {
  1225. s_bonetable_t *pBone = &g_bonetable[globalBoneID];
  1226. if( g_bPrintBones )
  1227. {
  1228. PrintSpaces( depth * 2 );
  1229. printf( "%d \"%s\" ", depth, pBone->name );
  1230. }
  1231. int i;
  1232. for( i = 0; i < 8; i++ )
  1233. {
  1234. if( pBone->flags & ( BONE_USED_BY_VERTEX_LOD0 << i ) )
  1235. {
  1236. if( g_bPrintBones )
  1237. {
  1238. printf( "lod%d ", i );
  1239. }
  1240. g_NumBonesInLOD[i]++;
  1241. }
  1242. }
  1243. if( g_bPrintBones )
  1244. {
  1245. if( pBone->flags & BONE_USED_BY_HITBOX )
  1246. printf( "hitbox " );
  1247. if( pBone->flags & BONE_USED_BY_ATTACHMENT )
  1248. printf( "attachment " );
  1249. if( pBone->flags & BONE_USED_BY_BONE_MERGE )
  1250. printf( "merge " );
  1251. printf( "\n" );
  1252. }
  1253. int j;
  1254. for( j = 0; j < g_numbones; j++ )
  1255. {
  1256. s_bonetable_t *pBone = &g_bonetable[j];
  1257. if( pBone->parent == globalBoneID )
  1258. {
  1259. SpewBoneInfo( j, depth + 1 );
  1260. }
  1261. }
  1262. }
  1263. void SpewBoneUsageStats( void )
  1264. {
  1265. memset( g_NumBonesInLOD, 0, sizeof( int ) * MAX_NUM_LODS );
  1266. if( g_numbones == 0 )
  1267. {
  1268. return;
  1269. }
  1270. SpewBoneInfo( 0, 0 );
  1271. if( g_bPrintBones )
  1272. {
  1273. int i;
  1274. for( i = 0; i < g_ScriptLODs.Count(); i++ )
  1275. {
  1276. printf( "\t%d bones used in lod %d\n", g_NumBonesInLOD[i], i );
  1277. }
  1278. }
  1279. }
  1280. void MarkParentBoneLODs( void )
  1281. {
  1282. int i;
  1283. for( i = 0; i < g_numbones; i++ )
  1284. {
  1285. int flags = g_bonetable[i].flags;
  1286. flags &= BONE_USED_BY_VERTEX_MASK;
  1287. int globalBoneID = g_bonetable[i].parent;
  1288. while( globalBoneID != -1 )
  1289. {
  1290. g_bonetable[globalBoneID].flags |= flags;
  1291. globalBoneID = g_bonetable[globalBoneID].parent;
  1292. }
  1293. }
  1294. }
  1295. //-----------------------------------------------------------------------------
  1296. // Returns the sources associated with the various LODs based on the script commands
  1297. //-----------------------------------------------------------------------------
  1298. static void GetLODSources( CUtlVector< s_source_t * > &lods, const s_model_t *pSrcModel )
  1299. {
  1300. int nNumLODs = g_ScriptLODs.Count();
  1301. lods.EnsureCount( nNumLODs );
  1302. for( int lodID = 0; lodID < nNumLODs; lodID++ )
  1303. {
  1304. LodScriptData_t& scriptLOD = g_ScriptLODs[lodID];
  1305. bool bFound;
  1306. s_source_t* pSource = GetModelLODSource( pSrcModel->filename, scriptLOD, &bFound );
  1307. if ( !pSource && !bFound )
  1308. {
  1309. pSource = pSrcModel->source;
  1310. }
  1311. lods[lodID] = pSource;
  1312. }
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Creates models to store converted data for the various LODs
  1316. //-----------------------------------------------------------------------------
  1317. void LoadLODSources( void )
  1318. {
  1319. g_nummodelsbeforeLOD = g_nummodels;
  1320. for( int modelID = 0; modelID < g_nummodelsbeforeLOD; modelID++ )
  1321. {
  1322. if ( !Q_stricmp( g_model[modelID]->name, "blank" ) )
  1323. {
  1324. int nNumLODs = g_ScriptLODs.Count();
  1325. g_model[modelID]->m_LodSources.SetCount( nNumLODs );
  1326. for ( int i = 0; i < nNumLODs; ++i )
  1327. {
  1328. g_model[modelID]->m_LodSources[i] = NULL;
  1329. }
  1330. continue;
  1331. }
  1332. GetLODSources( g_model[modelID]->m_LodSources, g_model[modelID] );
  1333. }
  1334. }
  1335. static void ReplaceBonesRecursive( int globalBoneID, bool replaceThis,
  1336. CUtlVector<CLodScriptReplacement_t> &boneReplacements,
  1337. const char *replacementName )
  1338. {
  1339. if( replaceThis )
  1340. {
  1341. CLodScriptReplacement_t &boneReplacement = boneReplacements[boneReplacements.AddToTail()];
  1342. boneReplacement.SetSrcName( g_bonetable[globalBoneID].name );
  1343. boneReplacement.SetDstName( replacementName );
  1344. }
  1345. // find children and recurse.
  1346. int i;
  1347. for( i = 0; i < g_numbones; i++ )
  1348. {
  1349. if( g_bonetable[i].parent == globalBoneID )
  1350. {
  1351. ReplaceBonesRecursive( i, true, boneReplacements, replacementName );
  1352. }
  1353. }
  1354. }
  1355. static void ConvertSingleBoneTreeCollapseToReplaceBones( CLodScriptReplacement_t &boneTreeCollapse,
  1356. CUtlVector<CLodScriptReplacement_t> &boneReplacements )
  1357. {
  1358. // find the bone that we are starting with.
  1359. int i = findGlobalBone( boneTreeCollapse.GetSrcName() );
  1360. if (i != -1)
  1361. {
  1362. ReplaceBonesRecursive( i, false, boneReplacements, g_bonetable[i].name );
  1363. return;
  1364. }
  1365. MdlWarning( "Couldn't find bone %s for bonetreecollapse, skipping\n", boneTreeCollapse.GetSrcName() );
  1366. }
  1367. void ConvertBoneTreeCollapsesToReplaceBones( void )
  1368. {
  1369. int i;
  1370. for( i = 0; i < g_ScriptLODs.Count(); i++ )
  1371. {
  1372. LodScriptData_t& lod = g_ScriptLODs[i];
  1373. int j;
  1374. for( j = 0; j < lod.boneTreeCollapses.Count(); j++ )
  1375. {
  1376. ConvertSingleBoneTreeCollapseToReplaceBones( lod.boneTreeCollapses[j],
  1377. lod.boneReplacements );
  1378. }
  1379. }
  1380. }
  1381. /*
  1382. static void PrintReplacedBones( LodScriptData_t &lod )
  1383. {
  1384. int i;
  1385. for( i = 0; i < lod.boneReplacements.Count(); i++ )
  1386. {
  1387. printf( "%s -> %s\n",
  1388. lod.boneReplacements[i].GetSrcName(),
  1389. lod.boneReplacements[i].GetDstName() );
  1390. }
  1391. }
  1392. */
  1393. void FixupReplacedBonesForLOD( LodScriptData_t &lod )
  1394. {
  1395. /*
  1396. printf( "before:\n" );
  1397. PrintReplacedBones( lod );
  1398. */
  1399. bool changed;
  1400. int i;
  1401. int j;
  1402. do
  1403. {
  1404. changed = false;
  1405. for( i = 0; i < lod.boneReplacements.Count(); i++ )
  1406. {
  1407. for( j = 0; j < lod.boneReplacements.Count(); j++ )
  1408. {
  1409. if( i == j )
  1410. {
  1411. continue;
  1412. }
  1413. if( Q_stricmp( lod.boneReplacements[i].GetSrcName(), lod.boneReplacements[j].GetDstName() ) == 0 )
  1414. {
  1415. lod.boneReplacements[j].SetDstName( lod.boneReplacements[i].GetDstName() );
  1416. changed = true;
  1417. }
  1418. }
  1419. }
  1420. } while( changed );
  1421. /*
  1422. printf( "after:\n" );
  1423. PrintReplacedBones( lod );
  1424. */
  1425. }
  1426. void FixupReplacedBones( void )
  1427. {
  1428. int i;
  1429. for( i = 0; i < g_ScriptLODs.Count(); i++ )
  1430. {
  1431. FixupReplacedBonesForLOD( g_ScriptLODs[i] );
  1432. }
  1433. }