|
|
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: Builds physics2 collision models from studio model source
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "studiomdl.h"
#include "collisionmodelsource.h"
//-----------------------------------------------------------------------------
// Purpose: Transforms the source's verts into "world" space
// Input : *psource -
// *worldVerts -
//-----------------------------------------------------------------------------
void CCollisionModelSource::ConvertToWorldSpace( CUtlVector<Vector> &worldVerts, s_source_t *pmodel ) { int i, n;
if (!m_bAssumeWorldspace) { matrix3x4_t boneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
CalcBoneTransforms( g_panimation[0], 0, boneToWorld );
for (i = 0; i < pmodel->numvertices; i++) { Vector tmp,tmp2; worldVerts[i].Init(0,0,0 );
int nBoneCount = pmodel->vertex[i].boneweight.numbones; for (n = 0; n < nBoneCount; n++) { // convert to Half-Life world space
// convert vertex into original models' bone local space
int localBone = pmodel->vertex[i].boneweight.bone[n]; int globalBone = pmodel->boneLocalToGlobal[localBone]; Assert( localBone >= 0 ); Assert( globalBone >= 0 );
matrix3x4_t boneToPose; ConcatTransforms( pmodel->boneToPose[localBone], g_bonetable[globalBone].srcRealign, boneToPose ); VectorITransform( pmodel->vertex[i].position, boneToPose, tmp2 );
// now transform to that bone's world-space position in this animation
VectorTransform(tmp2, boneToWorld[globalBone], tmp ); VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] ); } } } else { matrix3x4_t srcBoneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
BuildRawTransforms( pmodel, "BindPose", 0, pmodel->scale, pmodel->adjust, pmodel->rotation, 0, srcBoneToWorld );
for (i = 0; i < pmodel->numvertices; i++) { Vector tmp; worldVerts[i].Init( 0, 0, 0 );
int nBoneCount = pmodel->vertex[i].boneweight.numbones; for (n = 0; n < nBoneCount; n++) { int localBone = pmodel->vertex[i].boneweight.bone[n]; Assert( localBone >= 0 );
// convert vertex into world space
VectorTransform( pmodel->vertex[i].position, srcBoneToWorld[localBone], tmp ); // just assume the model is in identity space
// FIXME: shouldn't this do an inverse xform of the default boneToWorld?
VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] ); } } }
if ( g_flCollisionPrecision > 0 ) { #ifdef DEBUG
printf("Applying collision precision truncation: %f\n", g_flCollisionPrecision ); #endif
for ( int i = 0; i < worldVerts.Count(); i++ ) { worldVerts[i].x -= fmod( worldVerts[i].x, g_flCollisionPrecision ); worldVerts[i].y -= fmod( worldVerts[i].y, g_flCollisionPrecision ); worldVerts[i].z -= fmod( worldVerts[i].z, g_flCollisionPrecision ); } }
}
//-----------------------------------------------------------------------------
// Purpose: Transforms the set of verts into the space of a particular bone
// Input : *psource -
// boneIndex -
// *boneVerts -
//-----------------------------------------------------------------------------
void CCollisionModelSource::ConvertToBoneSpace( int boneIndex, CUtlVector<Vector> &boneVerts ) { int i;
int remapIndex = m_pModel->boneLocalToGlobal[boneIndex]; matrix3x4_t boneToPose; if ( remapIndex < 0 ) { MdlWarning("Error! physics for unused bone %s\n", m_pModel->localBone[boneIndex].name ); MatrixCopy( m_pModel->boneToPose[boneIndex], boneToPose ); } else { ConcatTransforms( m_pModel->boneToPose[boneIndex], g_bonetable[remapIndex].srcRealign, boneToPose ); }
for (i = 0; i < m_pModel->numvertices; i++) { VectorITransform(m_pModel->vertex[i].position, boneToPose, boneVerts[i] ); } }
bool CCollisionModelSource::ShouldProcessBone( int boneIndex ) { if ( boneIndex >= 0 ) { if ( m_bonemap[boneIndex] == boneIndex ) return true; } return false; }
// called before processing, after the model has been simplified.
// Update internal state due to simplification
void CCollisionModelSource::Simplify() { if ( m_pModel ) { for ( int i = 0; i < m_pModel->numbones; i++ ) { if ( m_pModel->boneLocalToGlobal[i] < 0 ) { SkipBone(i); }
// Walk the parents of this bone, if they map to the same global bone then go ahead and
// merge them now so we can aggregate the collision models
int nMatchingParent = i; int nParentCheck = m_pModel->localBone[nMatchingParent].parent; int nGlobalMatch = m_pModel->boneLocalToGlobal[i]; while ( nParentCheck >= 0 && m_pModel->boneLocalToGlobal[nParentCheck] == nGlobalMatch ) { nMatchingParent = nParentCheck; nParentCheck = m_pModel->localBone[nParentCheck].parent; } if ( nMatchingParent != i ) { MergeBones( nMatchingParent, i ); } } }
extern int g_rootIndex; const char *pAnimationRootBone = g_bonetable[g_rootIndex].name;
// merge this root bone with the root of animation
MergeBones( pAnimationRootBone, m_rootName );
}
void CCollisionModelSource::SkipBone( int boneIndex ) { if ( boneIndex >= 0 ) m_bonemap[boneIndex] = -1; }
void CCollisionModelSource::InitBoneMap( void ) { m_bonemap.SetSize(m_pModel->numbones); for ( int i = 0; i < m_pModel->numbones; i++ ) { m_bonemap[i] = i; } }
void CCollisionModelSource::MergeBones( int parent, int child ) { if ( parent < 0 || child < 0 ) return;
int map = parent; int safety = 0; while ( m_bonemap[map] != map ) { map = m_bonemap[map]; safety++; // infinite loop?
if ( safety > m_pModel->numbones ) break;
if ( map < 0 ) break; }
m_bonemap[child] = map; }
void CCollisionModelSource::MergeBones(const char *parent, const char *child) { MergeBones(FindLocalBoneNamed( parent ), FindLocalBoneNamed( child )); }
//-----------------------------------------------------------------------------
// Purpose: Search a source for a bone with a specified name
// Input : *pSource -
// *pName -
// Output : int boneIndex, -1 if none
//-----------------------------------------------------------------------------
int FindLocalBoneNamed( const s_source_t *pSource, const char *pName ) { if ( pName && pSource ) { int i; for ( i = 0; i < pSource->numbones; i++ ) { if ( !stricmp( pName, pSource->localBone[i].name ) ) return i; }
pName = RenameBone( pName );
for ( i = 0; i < pSource->numbones; i++ ) { if ( !stricmp( pName, pSource->localBone[i].name ) ) return i; } }
return -1; }
int CCollisionModelSource::FindLocalBoneNamed( const char *pName ) { return ::FindLocalBoneNamed(m_pModel, pName); }
//-----------------------------------------------------------------------------
// Purpose: Test this face to see if any of its verts are assigned to a particular bone
// *pmodel -
// *face -
// boneIndex -
// Output : Returns true if this face has a vert assigned to boneIndex
//-----------------------------------------------------------------------------
bool CCollisionModelSource::FaceHasVertOnBone( const s_face_t &face, int boneIndex ) { if ( boneIndex < 0 ) return true;
int j; s_boneweight_t *pweight; pweight = &m_pModel->vertex[ face.a ].boneweight; for ( j = 0; j < pweight->numbones; j++ ) { // assigned to boneIndex?
if ( RemapBone( pweight->bone[j] ) == boneIndex ) return true; }
pweight = &m_pModel->vertex[ face.b ].boneweight; for ( j = 0; j < pweight->numbones; j++ ) { // assigned to boneIndex?
if ( RemapBone( pweight->bone[j] ) == boneIndex ) return true; }
pweight = &m_pModel->vertex[ face.c ].boneweight; for ( j = 0; j < pweight->numbones; j++ ) { // assigned to boneIndex?
if ( RemapBone( pweight->bone[j] ) == boneIndex ) return true; }
return false; }
int CCollisionModelSource::RemapBone( int boneIndex ) const { if ( boneIndex >= 0 ) return m_bonemap[boneIndex]; return boneIndex; }
s_face_t CCollisionModelSource::GetGlobalFace( s_mesh_t *pMesh, int nFace ) { s_face_t output; GlobalFace(&output, pMesh, m_pModel->face + pMesh->faceoffset + nFace); return output; }
void CCollisionModelSource::FindBoundBones(s_mesh_t *pMesh, CUtlVector<int>&setBones) { s_face_t *pFaces = m_pModel->face + pMesh->faceoffset; s_vertexinfo_t *pVertices = m_pModel->vertex + pMesh->vertexoffset;
for ( int nFace = 0; nFace < pMesh->numfaces; nFace++ ) { FindBoundBones(pVertices[pFaces[nFace].a].boneweight, setBones); FindBoundBones(pVertices[pFaces[nFace].b].boneweight, setBones); FindBoundBones(pVertices[pFaces[nFace].c].boneweight, setBones); } }
void CCollisionModelSource::FindBoundBones(s_boneweight_t &weights, CUtlVector<int>&setBones) { for(int nBoundBone = 0; nBoundBone < weights.numbones; ++nBoundBone) { int boneIndex = RemapBone(weights.bone[nBoundBone]); if(!setBones.HasElement(boneIndex)) setBones.AddToTail(boneIndex); } }
//-----------------------------------------------------------------------------
// Purpose: Fixup the pointers in this face to reference the mesh globally (source relative)
// (faces are mesh relative, each source has several meshes)
// Input : *pout -
// *pmesh -
// *pin -
//-----------------------------------------------------------------------------
void GlobalFace( s_face_t *pout, s_mesh_t *pmesh, s_face_t *pin ) { pout->a = pmesh->vertexoffset + pin->a; pout->b = pmesh->vertexoffset + pin->b; pout->c = pmesh->vertexoffset + pin->c; }
|