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
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
|