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.

350 lines
9.5 KiB

  1. //========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Builds physics2 collision models from studio model source
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "studiomdl.h"
  10. #include "collisionmodelsource.h"
  11. //-----------------------------------------------------------------------------
  12. // Purpose: Transforms the source's verts into "world" space
  13. // Input : *psource -
  14. // *worldVerts -
  15. //-----------------------------------------------------------------------------
  16. void CCollisionModelSource::ConvertToWorldSpace( CUtlVector<Vector> &worldVerts, s_source_t *pmodel )
  17. {
  18. int i, n;
  19. if (!m_bAssumeWorldspace)
  20. {
  21. matrix3x4_t boneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
  22. CalcBoneTransforms( g_panimation[0], 0, boneToWorld );
  23. for (i = 0; i < pmodel->numvertices; i++)
  24. {
  25. Vector tmp,tmp2;
  26. worldVerts[i].Init(0,0,0 );
  27. int nBoneCount = pmodel->vertex[i].boneweight.numbones;
  28. for (n = 0; n < nBoneCount; n++)
  29. {
  30. // convert to Half-Life world space
  31. // convert vertex into original models' bone local space
  32. int localBone = pmodel->vertex[i].boneweight.bone[n];
  33. int globalBone = pmodel->boneLocalToGlobal[localBone];
  34. Assert( localBone >= 0 );
  35. Assert( globalBone >= 0 );
  36. matrix3x4_t boneToPose;
  37. ConcatTransforms( pmodel->boneToPose[localBone], g_bonetable[globalBone].srcRealign, boneToPose );
  38. VectorITransform( pmodel->vertex[i].position, boneToPose, tmp2 );
  39. // now transform to that bone's world-space position in this animation
  40. VectorTransform(tmp2, boneToWorld[globalBone], tmp );
  41. VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
  42. }
  43. }
  44. }
  45. else
  46. {
  47. matrix3x4_t srcBoneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
  48. BuildRawTransforms( pmodel, "BindPose", 0, pmodel->scale, pmodel->adjust, pmodel->rotation, 0, srcBoneToWorld );
  49. for (i = 0; i < pmodel->numvertices; i++)
  50. {
  51. Vector tmp;
  52. worldVerts[i].Init( 0, 0, 0 );
  53. int nBoneCount = pmodel->vertex[i].boneweight.numbones;
  54. for (n = 0; n < nBoneCount; n++)
  55. {
  56. int localBone = pmodel->vertex[i].boneweight.bone[n];
  57. Assert( localBone >= 0 );
  58. // convert vertex into world space
  59. VectorTransform( pmodel->vertex[i].position, srcBoneToWorld[localBone], tmp );
  60. // just assume the model is in identity space
  61. // FIXME: shouldn't this do an inverse xform of the default boneToWorld?
  62. VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
  63. }
  64. }
  65. }
  66. if ( g_flCollisionPrecision > 0 )
  67. {
  68. #ifdef DEBUG
  69. printf("Applying collision precision truncation: %f\n", g_flCollisionPrecision );
  70. #endif
  71. for ( int i = 0; i < worldVerts.Count(); i++ )
  72. {
  73. worldVerts[i].x -= fmod( worldVerts[i].x, g_flCollisionPrecision );
  74. worldVerts[i].y -= fmod( worldVerts[i].y, g_flCollisionPrecision );
  75. worldVerts[i].z -= fmod( worldVerts[i].z, g_flCollisionPrecision );
  76. }
  77. }
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose: Transforms the set of verts into the space of a particular bone
  81. // Input : *psource -
  82. // boneIndex -
  83. // *boneVerts -
  84. //-----------------------------------------------------------------------------
  85. void CCollisionModelSource::ConvertToBoneSpace( int boneIndex, CUtlVector<Vector> &boneVerts )
  86. {
  87. int i;
  88. int remapIndex = m_pModel->boneLocalToGlobal[boneIndex];
  89. matrix3x4_t boneToPose;
  90. if ( remapIndex < 0 )
  91. {
  92. MdlWarning("Error! physics for unused bone %s\n", m_pModel->localBone[boneIndex].name );
  93. MatrixCopy( m_pModel->boneToPose[boneIndex], boneToPose );
  94. }
  95. else
  96. {
  97. ConcatTransforms( m_pModel->boneToPose[boneIndex], g_bonetable[remapIndex].srcRealign, boneToPose );
  98. }
  99. for (i = 0; i < m_pModel->numvertices; i++)
  100. {
  101. VectorITransform(m_pModel->vertex[i].position, boneToPose, boneVerts[i] );
  102. }
  103. }
  104. bool CCollisionModelSource::ShouldProcessBone( int boneIndex )
  105. {
  106. if ( boneIndex >= 0 )
  107. {
  108. if ( m_bonemap[boneIndex] == boneIndex )
  109. return true;
  110. }
  111. return false;
  112. }
  113. // called before processing, after the model has been simplified.
  114. // Update internal state due to simplification
  115. void CCollisionModelSource::Simplify()
  116. {
  117. if ( m_pModel )
  118. {
  119. for ( int i = 0; i < m_pModel->numbones; i++ )
  120. {
  121. if ( m_pModel->boneLocalToGlobal[i] < 0 )
  122. {
  123. SkipBone(i);
  124. }
  125. // Walk the parents of this bone, if they map to the same global bone then go ahead and
  126. // merge them now so we can aggregate the collision models
  127. int nMatchingParent = i;
  128. int nParentCheck = m_pModel->localBone[nMatchingParent].parent;
  129. int nGlobalMatch = m_pModel->boneLocalToGlobal[i];
  130. while ( nParentCheck >= 0 && m_pModel->boneLocalToGlobal[nParentCheck] == nGlobalMatch )
  131. {
  132. nMatchingParent = nParentCheck;
  133. nParentCheck = m_pModel->localBone[nParentCheck].parent;
  134. }
  135. if ( nMatchingParent != i )
  136. {
  137. MergeBones( nMatchingParent, i );
  138. }
  139. }
  140. }
  141. extern int g_rootIndex;
  142. const char *pAnimationRootBone = g_bonetable[g_rootIndex].name;
  143. // merge this root bone with the root of animation
  144. MergeBones( pAnimationRootBone, m_rootName );
  145. }
  146. void CCollisionModelSource::SkipBone( int boneIndex )
  147. {
  148. if ( boneIndex >= 0 )
  149. m_bonemap[boneIndex] = -1;
  150. }
  151. void CCollisionModelSource::InitBoneMap( void )
  152. {
  153. m_bonemap.SetSize(m_pModel->numbones);
  154. for ( int i = 0; i < m_pModel->numbones; i++ )
  155. {
  156. m_bonemap[i] = i;
  157. }
  158. }
  159. void CCollisionModelSource::MergeBones( int parent, int child )
  160. {
  161. if ( parent < 0 || child < 0 )
  162. return;
  163. int map = parent;
  164. int safety = 0;
  165. while ( m_bonemap[map] != map )
  166. {
  167. map = m_bonemap[map];
  168. safety++;
  169. // infinite loop?
  170. if ( safety > m_pModel->numbones )
  171. break;
  172. if ( map < 0 )
  173. break;
  174. }
  175. m_bonemap[child] = map;
  176. }
  177. void CCollisionModelSource::MergeBones(const char *parent, const char *child)
  178. {
  179. MergeBones(FindLocalBoneNamed( parent ), FindLocalBoneNamed( child ));
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose: Search a source for a bone with a specified name
  183. // Input : *pSource -
  184. // *pName -
  185. // Output : int boneIndex, -1 if none
  186. //-----------------------------------------------------------------------------
  187. int FindLocalBoneNamed( const s_source_t *pSource, const char *pName )
  188. {
  189. if ( pName && pSource )
  190. {
  191. int i;
  192. for ( i = 0; i < pSource->numbones; i++ )
  193. {
  194. if ( !stricmp( pName, pSource->localBone[i].name ) )
  195. return i;
  196. }
  197. pName = RenameBone( pName );
  198. for ( i = 0; i < pSource->numbones; i++ )
  199. {
  200. if ( !stricmp( pName, pSource->localBone[i].name ) )
  201. return i;
  202. }
  203. }
  204. return -1;
  205. }
  206. int CCollisionModelSource::FindLocalBoneNamed( const char *pName )
  207. {
  208. return ::FindLocalBoneNamed(m_pModel, pName);
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose: Test this face to see if any of its verts are assigned to a particular bone
  212. // *pmodel -
  213. // *face -
  214. // boneIndex -
  215. // Output : Returns true if this face has a vert assigned to boneIndex
  216. //-----------------------------------------------------------------------------
  217. bool CCollisionModelSource::FaceHasVertOnBone( const s_face_t &face, int boneIndex )
  218. {
  219. if ( boneIndex < 0 )
  220. return true;
  221. int j;
  222. s_boneweight_t *pweight;
  223. pweight = &m_pModel->vertex[ face.a ].boneweight;
  224. for ( j = 0; j < pweight->numbones; j++ )
  225. {
  226. // assigned to boneIndex?
  227. if ( RemapBone( pweight->bone[j] ) == boneIndex )
  228. return true;
  229. }
  230. pweight = &m_pModel->vertex[ face.b ].boneweight;
  231. for ( j = 0; j < pweight->numbones; j++ )
  232. {
  233. // assigned to boneIndex?
  234. if ( RemapBone( pweight->bone[j] ) == boneIndex )
  235. return true;
  236. }
  237. pweight = &m_pModel->vertex[ face.c ].boneweight;
  238. for ( j = 0; j < pweight->numbones; j++ )
  239. {
  240. // assigned to boneIndex?
  241. if ( RemapBone( pweight->bone[j] ) == boneIndex )
  242. return true;
  243. }
  244. return false;
  245. }
  246. int CCollisionModelSource::RemapBone( int boneIndex ) const
  247. {
  248. if ( boneIndex >= 0 )
  249. return m_bonemap[boneIndex];
  250. return boneIndex;
  251. }
  252. s_face_t CCollisionModelSource::GetGlobalFace( s_mesh_t *pMesh, int nFace )
  253. {
  254. s_face_t output;
  255. GlobalFace(&output, pMesh, m_pModel->face + pMesh->faceoffset + nFace);
  256. return output;
  257. }
  258. void CCollisionModelSource::FindBoundBones(s_mesh_t *pMesh, CUtlVector<int>&setBones)
  259. {
  260. s_face_t *pFaces = m_pModel->face + pMesh->faceoffset;
  261. s_vertexinfo_t *pVertices = m_pModel->vertex + pMesh->vertexoffset;
  262. for ( int nFace = 0; nFace < pMesh->numfaces; nFace++ )
  263. {
  264. FindBoundBones(pVertices[pFaces[nFace].a].boneweight, setBones);
  265. FindBoundBones(pVertices[pFaces[nFace].b].boneweight, setBones);
  266. FindBoundBones(pVertices[pFaces[nFace].c].boneweight, setBones);
  267. }
  268. }
  269. void CCollisionModelSource::FindBoundBones(s_boneweight_t &weights, CUtlVector<int>&setBones)
  270. {
  271. for(int nBoundBone = 0; nBoundBone < weights.numbones; ++nBoundBone)
  272. { int boneIndex = RemapBone(weights.bone[nBoundBone]);
  273. if(!setBones.HasElement(boneIndex))
  274. setBones.AddToTail(boneIndex);
  275. }
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose: Fixup the pointers in this face to reference the mesh globally (source relative)
  279. // (faces are mesh relative, each source has several meshes)
  280. // Input : *pout -
  281. // *pmesh -
  282. // *pin -
  283. //-----------------------------------------------------------------------------
  284. void GlobalFace( s_face_t *pout, s_mesh_t *pmesh, s_face_t *pin )
  285. {
  286. pout->a = pmesh->vertexoffset + pin->a;
  287. pout->b = pmesh->vertexoffset + pin->b;
  288. pout->c = pmesh->vertexoffset + pin->c;
  289. }