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.
 
 
 
 
 
 

437 lines
14 KiB

//============ Copyright (c) Valve Corporation, All rights reserved. ==========
#ifndef CLOTHPROXYCOMPILER_HDR
#define CLOTHPROXYCOMPILER_HDR
#include "movieobjects/dmevertexdata.h"
#include "bitvec.h"
#include "tier1/utlstringmap.h"
#include "mdlobjects/authphysfx.h"
#include "mdlobjects/clothproxymesh.h"
#include "movieobjects/dmefaceset.h"
#include "meshutils/mesh.h"
#include "movieobjects/dmemeshtypes.h"
class CDmeModel;
class CAuthPhysFx;
class CVClothProxyMesh;
class CDmeDag;
class CClothProxyCompiler
{
public:
CVClothProxyMeshOptions m_Options;
CClothProxyCompiler( CAuthPhysFx *pAuthFx );
~CClothProxyCompiler(){}
bool IsEmpty() const
{
return m_pAuthFx->m_Nodes.Count() == 0;
}
CAuthPhysFx *GetFx() { return m_pAuthFx; }
void Init( const CVClothProxyMeshOptions &clothProxyMeshList );
int GetOrCreateClothRootBone();
void Append( CDmeModel *pModel, float flClothEnableThreshold, const CVClothProxyMesh &proxy);
void AppendPlaneCollision( CDmeModel *pModel );
void Cook( );
CLockedResource< PhysFeModelDesc_t > Compile( CResourceStream *pStream )const;
void MarkFreeRotatingNodes( const CAuthPhysFx::CQuad &quad );
void AlignNodes();
int GetAuthFxBone( const char *pName );
template <typename T>
class CIndexedAttr
{
public:
CDmrArrayConst< T > m_Data;
CDmrArrayConst< int > m_IndexData;
public:
CIndexedAttr() {}
CIndexedAttr( CDmeVertexData *pBindState, FieldIndex_t nField )
{
Init( pBindState, nField );
}
CIndexedAttr( CDmeVertexData *pBindState, CDmeVertexDataBase::StandardFields_t nField )
{
Init( pBindState, nField );
}
void Init( CDmeVertexData *pBindState, CDmeVertexDataBase::StandardFields_t nField )
{
Init( pBindState, pBindState->FindFieldIndex( nField ) );
}
CIndexedAttr( CDmeVertexData *pBindState, const char *pField )
{
Init( pBindState, pBindState->FindFieldIndex( pField ) );
}
void Init( CDmeVertexData *pBindState, FieldIndex_t nField )
{
if ( nField >= 0 )
{
m_Data = pBindState->GetVertexData( nField );
m_IndexData = pBindState->GetIndexData( nField );
}
}
operator bool() const { return m_IndexData.IsValid() && m_Data.IsValid() && m_Data.Count() > 0 && m_IndexData.Count() > 0; }
const T& operator []( int i ) const { return m_Data[ m_IndexData[ i ] ]; }
int GetDataCount() const{ return m_Data.Count(); }
int GetElementCount()const { return m_IndexData.Count(); } // not really vertex count
int GetAttrCount()const { return 1; } // TODO: find the number of attributes per vertex
void Reset()
{
m_Data = CDmrArrayConst< T >();
m_IndexData = CDmrArrayConst< int >();
}
};
struct Binding_t
{
int nAuthFxBone;
float flWeight;
Binding_t() : nAuthFxBone( -1 ), flWeight( 0 ) {}
};
struct ProjItem_t
{
ProjItem_t( int i = -1, float f = 0 ) : nIndex( i ), flEnvelope( f ){}
ProjItem_t( const ProjItem_t &other ) : nIndex( other.nIndex ), flEnvelope( other.flEnvelope ){}
int nIndex;
float flEnvelope;
};
struct QuadProjection_t
{
Vector m_vContact; // projected point on the quad
Vector m_vNormal;
float m_flDistance; // negative if behind the plane
int m_nBindings;
Binding_t m_Binding[ 4 ];
bool IsEmpty() const { return m_nBindings == 0; }
Vector GetOriginalPoint()const { return m_vContact + m_vNormal * m_flDistance; }
void AddBinding( uint nIndex, float flWeight )
{
Binding_t &b = m_Binding[ m_nBindings++ ];
b.nAuthFxBone = nIndex;
b.flWeight = flWeight;
}
bool operator < ( const QuadProjection_t &other )const
{
return m_flDistance < other.m_flDistance;
}
};
class CModelContext
{
public:
CModelContext( CClothProxyCompiler *pCompiler, CDmeModel *pModel, float flClothEnableThreshold, const CVClothProxyMesh &proxy );
int MapJointToFxBone( int nJoint, bool bSimulated );
int GetJointCount()const { return m_JointToFxBone.Count(); }
public:
CDmeModel *m_pModel;
CUtlVector< UtlSymId_t > m_JointToBoneSubset;
CUtlVector< int > m_JointToFxBone;
CAuthPhysFx * m_pAuthFx;
float m_flClothEnableThreshold;
const CVClothProxyMesh &m_Proxy;
};
friend class CModelContext;
class CMeshContext
{
public:
CMeshContext( CClothProxyCompiler *pCompiler, CModelContext *pModelContext, CDmeMesh *pMesh, const matrix3x4_t &tm, int nDmeMesh );
public:
int m_nDmeMesh;
CDmeMesh *m_pDmeMesh;
matrix3x4_t m_MeshTransform;
CDmeVertexData *m_pBindState;
CIndexedAttr< Vector > m_AttrPos, m_AttrNormal, m_AttrTangent;
CAuthPhysFx *m_pAuthFx;
CModelContext *m_pModelContext;
CUtlVector< int > m_DmePosToFxBone; // position (index in the position array in DmeVertex) to FxBone (index of CBone in AuthFx) map
CClothProxyCompiler *m_pCompiler;
public:
int FindMostBoundJoint( int nDmePos, float flBonusForExisting );
int GetSkinningJointCount();
int GetOrCreateClothBoneIndex( int nDmePos, bool bSimulated );
int GetClothBoneIndex( int nDmePos );
CAuthPhysFx::CBone *GetOrCreateClothBone( int nDmePos, bool bSimulated );
CAuthPhysFx::CBone *GetClothBone( int nDmePos );
};
friend class CMeshContext;
int GetMaxBonesPerVertex()const { return Max( 1, Min( 4, m_Options.m_nMaxBonesPerVertex ) ); }
bool Project( const Vector &vPos, UtlSymId_t*pFindSubset, int nFindSubsetCount, CUtlVector<Binding_t> &outBindings, int nIslandFilter );
void ProjectAndAddToQueue( const ProjItem_t &q, const Vector & vPos, CUtlSortVector< QuadProjection_t > &bestProj, int nIslandFilter );
int NodeToIsland( int nNode );
int GetIslandCount() const { return m_nIslandCount; }
protected:
void AppendPlaneCollisionDag( CModelContext &modelContext, CDmeDag *pDmeDag );
void AppendPlaneCollisionMesh( CModelContext &modelContext, CDmeMesh *pMesh, const matrix3x4_t &tm );
void AppendDag( CModelContext &context, CDmeDag *pDmeDag );
void AppendMesh( CModelContext &modelContext, CDmeMesh *pMesh, const matrix3x4_t &tm );
void CreateClothBones( CMeshContext &context );
void AddFitWeights( CMeshContext &context );
void BindNodeOffsetParents( CMeshContext &context );
void OrientClothBones( CMeshContext &context );
void CreateClothQuads( CMeshContext &context );
void ApplyClothBoneAttributes( CMeshContext &context );
QuadProjection_t ProjectOnQuad( const Vector &vPos, const CAuthPhysFx::CQuad &quad );
QuadProjection_t ProjectOnTri( const Vector &vPos, const CAuthPhysFx::CQuad &quad );
public:
class CVertex
{
public:
Vector m_vPos; // world position
};
class CPolygon
{
public:
CUtlVectorFixedGrowable< CVertex*, 4 > m_Verts;
Vector m_vNormal;
};
class CAuthFxSubset
{
public:
CUtlVectorFixedGrowable< ProjItem_t, 4 > m_Quads;
CUtlVectorFixedGrowable< ProjItem_t, 4 > m_Rods;
void Append( const CAuthFxSubset &other );
};
class CAuthFxBoneSubset : public CAuthFxSubset
{
public:
CAuthFxBoneSubset( int nModelJoint ) : m_nModelJoint( nModelJoint ){}
int m_nModelJoint;
};
UtlSymId_t FindBoneSym( const char *pName )
{
return m_BoneSubsets.Find( pName );
}
CUtlVector< CUtlString > m_MeshNames; // names of meshes that comprise this proxy mesh, for debugging
protected:
CAuthPhysFx *m_pAuthFx;
CUtlStringMapAutoPurge< CAuthFxBoneSubset* > m_BoneSubsets; // for each AuthFx bone, a subset of quads and rods to project to
CAuthFxSubset m_DefaultSubset;
int m_nProxyMeshes;
int m_nRootFxNode;
private:
int m_nIslandCount;
CUtlVector< int > m_NodeToIslandMap;
};
template <typename Functor >
inline void EnumerateFaces( CDmeMesh *pDmeMesh, CDmeVertexData * pBindState, const CVarBitVec *pUsefulDmeVerts, Functor &fn )
{
//const CUtlVector< Vector > & arrDmeVert = pBindState->GetPositionData();// the most original and un-split position array
const CUtlVector< int > & arrVertIndex = pBindState->GetVertexIndexData( CDmeVertexDataBase::FIELD_POSITION );
// find connecting (static) bones, and the bones that will drive them (add those, too, taking care not to add them twice)
//pBindState->FindFieldIndex( CDmeVertexDataBase::FIELD_POSITION )
int nFaceSetCount = pDmeMesh->FaceSetCount();
int nSkippedDegenerate = 0, nSkippedManygons = 0;
for ( int nFaceSet = 0; nFaceSet < nFaceSetCount; ++nFaceSet )
{
CDmeFaceSet *pFaceSet = pDmeMesh->GetFaceSet( nFaceSet );
int nIndexCount = pFaceSet->NumIndices(); // for each face (N-gon), there are N indices and -1 in this array
int nFirstIndex = 0;
while ( nFirstIndex < nIndexCount )
{
int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
if ( nVertexCount < 2 )
{
nSkippedDegenerate++;
continue;
}
int nUsefulVertexCount = nVertexCount;
if ( nVertexCount > 4 )
{
nUsefulVertexCount = 4;
nSkippedManygons++;
}
int nPosVerts[ 4 ] = { -1,-1,-1,-1};
//DmeVertexIndex_t nFaceVerts[ 4 ];
CAuthPhysFx::CQuad quad;
bool bUseful = false;
for ( int nV = 0; nV < nUsefulVertexCount; ++nV )
{
DmeVertexIndex_t nFaceVertIndex = pFaceSet->GetIndex( nFirstIndex + nV );
//nFaceVerts[ nV ] = nFaceVertIndex;
int nDmeVert = arrVertIndex[ nFaceVertIndex ];
nPosVerts[ nV ] = nDmeVert;
if ( !pUsefulDmeVerts || ( nDmeVert < pUsefulDmeVerts->GetNumBits() && pUsefulDmeVerts->IsBitSet( nDmeVert ) ) )
{// it's all useful if we don't have a bitmap; otherwise, consult the bitmap - at least one vertex of the polygon must be useful
bUseful = true;
}
}
if ( bUseful ) // skip irrelevant polygons
{
for ( int nV = nUsefulVertexCount; nV < 4; ++nV )
{
nPosVerts[ nV ] = nPosVerts[ nUsefulVertexCount - 1 ];
//nFaceVerts[ nV ] = nFaceVerts[ nUsefulVertexCount - 1 ];
}
fn( nPosVerts, nUsefulVertexCount );
}
nFirstIndex += nVertexCount + 1; // skip N-gon indices and the -1 terminator
}
}
if ( nSkippedDegenerate || nSkippedManygons )
{
Warning( "Cloth: %d degenerate and %d 5+gons\n", nSkippedDegenerate, nSkippedManygons );
}
}
/*
template <typename Functor >
inline void EnumerateFaces( CDmeMesh *pDmeMesh, CDmeVertexData * pBindState, Functor &fn )
{
//const CUtlVector< Vector > & arrDmeVert = pBindState->GetPositionData();// the most original and un-split position array
const CUtlVector< int > & arrVertIndex = pBindState->GetVertexIndexData( CDmeVertexDataBase::FIELD_POSITION );
int nFaceSetCount = pDmeMesh->FaceSetCount();
for ( int nFaceCount = 0; nFaceCount < nFaceSetCount; ++nFaceCount )
{
CDmeFaceSet *pFaceSet = pDmeMesh->GetFaceSet( nFaceCount );
int nIndexCount = pFaceSet->NumIndices(); // for each face (N-gon), there are N indices and -1 in this array
int nFirstIndex = 0;
while ( nFirstIndex < nIndexCount )
{
int nTotalVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
int nPolyIndex0 = arrVertIndex[ pFaceSet->GetIndex( nFirstIndex ) ];
//Vector vApex = arrDmeVert[ nPolyIndex0 ]; // we're going around the first vertex and split the poly into quads and tris
for ( int nBase = 1; nBase < nTotalVertexCount; nBase += 2 )
{
int nPosVerts[ 4 ] = { nPolyIndex0 };
int nFoundVerts = 1;
//Vector vPrevVert = vApex;
for ( int m = Min( nTotalVertexCount - 1, nBase + 2 ); m >= nBase; m-- )
{
int nPolyIndexM = arrVertIndex[ pFaceSet->GetIndex( nFirstIndex + m ) ];
Vector vVertM = arrVertIndex[ nPolyIndexM ];
//if ( ( vPrevVert - vVertM ).Length() > flCollapseEdgesThreshold )
{
nPosVerts[ nFoundVerts++ ] = nPolyIndexM;
//vPrevVert = vVertM;
}
}
if ( nFoundVerts > 1 )
{
for ( int m = nFoundVerts; m < 4; ++nFoundVerts )
nPosVerts[ m ] = nPosVerts[ nFoundVerts - 1 ];
fn( nPosVerts, nFoundVerts );
}
}
nFirstIndex += nVertexCount + 1; // skip N-gon indices and the -1 terminator
}
}
}
*/
template < typename Attr >
class ClothAttributes
{
public:
Attr m_animation_attraction ;
Attr m_animation_force_attraction ;
Attr m_drag ;
Attr m_mass ;
Attr m_gravity ;
Attr m_collision_radius ;
Attr m_ground_collision ;
Attr m_ground_friction ;
Attr m_use_rods;
Attr m_anchor_free_rotate;
protected:
float Get( const CMesh::CSingleVertexFieldAccessor< float > &accessor )
{
return *accessor;
}
float Get( float x )
{
return x;
}
public:
template < typename Map >
ClothAttributes( Map map )
{
m_animation_attraction = map( "cloth_animation_attract" );
m_animation_force_attraction = map( "cloth_animation_force_attract" );
m_drag = map( "cloth_drag" );
m_mass = map( "cloth_mass" );
m_gravity = map( "cloth_gravity" );
m_collision_radius = map( "cloth_collision_radius" );
m_ground_collision = map( "cloth_ground_collision" );
m_ground_friction = map( "cloth_ground_friction" );
m_use_rods = map( "cloth_use_rods" );
m_anchor_free_rotate = map( "cloth_anchor_free_rotate" );
}
void Apply( int nVert, CAuthPhysFx::CBone &authFxBone )
{
if ( m_animation_attraction )
authFxBone.m_Integrator.flAnimationVertexAttraction = 30 * Get( m_animation_attraction[ nVert ] );
if ( m_animation_force_attraction )
authFxBone.m_Integrator.flAnimationForceAttraction = 30 * Get( m_animation_force_attraction[ nVert ] );
if ( m_drag )
authFxBone.m_Integrator.flPointDamping = 30 * Get( m_drag[ nVert ] );
if ( m_mass )
authFxBone.m_flMassBias = expf( Get( m_mass[ nVert ] ) );
if ( m_gravity )
authFxBone.m_Integrator.flGravity = Get( m_gravity[ nVert ] );
if ( m_collision_radius )
authFxBone.m_flCollisionRadius = Get( m_collision_radius[ nVert ] );
if ( m_ground_collision )
{
authFxBone.m_flWorldFriction = 1.0f - Get( m_ground_collision[ nVert ] ); // ground_collision 0 maps to "worldFriction(source1 misnomer)" 1
if ( authFxBone.m_flWorldFriction < 0.999f )
{
authFxBone.m_bNeedsWorldCollision = true;
}
}
if ( m_ground_friction )
{
authFxBone.m_flGroundFriction = Get( m_ground_friction[ nVert ] );
}
if ( m_use_rods )
{
authFxBone.m_bUseRods = Get( m_use_rods[ nVert ] )> 0.5f;
}
if ( m_anchor_free_rotate )
{
authFxBone.m_bFreeRotation = Get( m_anchor_free_rotate[ nVert ] )> 0.5f;
}
}
};
extern const char *g_pDefaultClothRootBoneName;
extern CClothProxyCompiler *g_pClothProxyCompiler ;
#endif // CLOTHPROXYCOMPILER_HDR