Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1631 lines
48 KiB

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