csgo-2018-source/utils/studiomdl/collisionmodelsource.cpp
2021-07-24 21:11:47 -07:00

351 lines
9.5 KiB
C++

//========= 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;
}