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.
1424 lines
44 KiB
1424 lines
44 KiB
//========== Copyright (c) Valve Corporation. All Rights Reserved. ============
|
|
#ifndef AUTHPHYSMODEL_HDR
|
|
#define AUTHPHYSMODEL_HDR
|
|
|
|
#ifdef COMPILER_MSVC
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "tier1/utlincrementalvector.h"
|
|
#include "tier1/utlstringtoken.h"
|
|
#include "resourcefile/resourcestream.h"
|
|
#include "mathlib/aabb.h"
|
|
#include "mathlib/vertexcolor.h"
|
|
#include "rubikon/param_types.h"
|
|
#include "mathlib/femodeldesc.h"
|
|
#include "mathlib/femodelbuilder.h"
|
|
#include "tier1/utlhashtable.h"
|
|
#include "bitvec.h"
|
|
#include "tier1/utlstringmap.h"
|
|
#include "tier1/utlsortvector.h"
|
|
#include "mdlobjects/vpropbreakabledata.h"
|
|
|
|
struct VPhysXBodyPart_t;
|
|
struct RnSphereDesc_t;
|
|
struct RnCapsuleDesc_t;
|
|
struct RnHullDesc_t;
|
|
struct RnMeshDesc_t;
|
|
struct RnShapeDesc_t;
|
|
struct AABB_t;
|
|
struct RnHull_t;
|
|
class CToolSceneTraceEnvironment;
|
|
class CMesh;
|
|
struct GizmoTransform_t;
|
|
struct RnHullSimplificationParams_t;
|
|
struct PhysSoftbodyDesc_t;
|
|
class CUtlSymbolTable;
|
|
template <class T >class CUtlStringMap;
|
|
class CPhysModelSource;
|
|
/*schema*/ class CAuthPhysBody;
|
|
class KeyValues;
|
|
struct VPhysXAggregateData_t;
|
|
struct VPhysXJoint_t;
|
|
class CResourceStream;
|
|
struct VertexPositionNormal_t;
|
|
struct VertexPositionColor_t;
|
|
struct AuthHullSimplificationParams_t;
|
|
class CVClothProxyMeshOptions;
|
|
|
|
|
|
/*schema*/ enum AuthPhysCollisionAttributesModeEnum_t
|
|
{
|
|
AUTH_PHYS_COLL_ATTR_IGNORE, META( MPropertyFriendlyName = "Default/Derived" )
|
|
AUTH_PHYS_COLL_ATTR_OVERRIDE, META( MPropertyFriendlyName = "Override" )
|
|
AUTH_PHYS_COLL_ATTR_APPEND META( MPropertyFriendlyName = "Append" )
|
|
};
|
|
DECLARE_SCHEMA_ENUM( AuthPhysCollisionAttributesModeEnum_t );
|
|
|
|
|
|
enum AuthPhysFxCollisionTypeEnum_t
|
|
{
|
|
AUTH_PHYS_FX_SPHERE,
|
|
AUTH_PHYS_FX_PLANE
|
|
};
|
|
|
|
|
|
|
|
/*schema*/ class CAuthPhysCollisionAttributesOverride;
|
|
/*schema*/ struct VPhysXCollisionAttributes_t;
|
|
class CAuthPhysCompileContext;
|
|
|
|
|
|
|
|
/*schema*/ class CAuthPhysCollisionAttributes
|
|
{
|
|
public:
|
|
CAuthPhysCollisionAttributes()
|
|
{
|
|
m_CollisionGroup = "default";
|
|
}
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysCollisionAttributes )
|
|
void ApplyOverride( const CAuthPhysCollisionAttributesOverride &collOverride );
|
|
void Compile( CAuthPhysCompileContext &context, VPhysXCollisionAttributes_t &entry )const;
|
|
|
|
CUtlString m_CollisionGroup; META( MPropertyFriendlyName = "Collision Group"; MPropertyChoiceProviderFn = GetCollisionGroupList );
|
|
CUtlVector< CUtlString > m_InteractAs; META( MPropertyFriendlyName = "Interact As"; MPropertyChoiceProviderFn = GetInteractionLayerList );
|
|
CUtlVector< CUtlString > m_InteractWith; META( MPropertyFriendlyName = "Interact With"; MPropertyChoiceProviderFn = GetInteractionLayerList );
|
|
|
|
bool operator == ( const CAuthPhysCollisionAttributes & other )const;
|
|
};
|
|
|
|
|
|
/*schema*/ class CAuthPhysCollisionAttributesOverride: public CAuthPhysCollisionAttributes
|
|
{
|
|
public:
|
|
CAuthPhysCollisionAttributesOverride()
|
|
{
|
|
m_nMode = AUTH_PHYS_COLL_ATTR_IGNORE; // ignore by default
|
|
}
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysCollisionAttributesOverride )
|
|
AuthPhysCollisionAttributesModeEnum_t m_nMode; META( MPropertyFriendlyName = "Mode" );
|
|
};
|
|
|
|
|
|
|
|
class CLockecResourceHashFunctor
|
|
{
|
|
public:
|
|
uint operator() ( const CLockedResource<char> &res )const
|
|
{
|
|
CRC32_t nCrc;
|
|
CRC32_Init( &nCrc );
|
|
CRC32_ProcessBuffer( &nCrc, res, res.Count( ) );
|
|
CRC32_Final( &nCrc );
|
|
return nCrc;
|
|
}
|
|
};
|
|
|
|
|
|
class CLockedResourceEqualFunctor
|
|
{
|
|
public:
|
|
bool operator() ( const CLockedResource<char> &a, const CLockedResource<char> & b )const
|
|
{
|
|
return a.Count( ) == b.Count( ) && !V_memcmp( a, b, a.Count( ) );
|
|
}
|
|
};
|
|
|
|
typedef CUtlHashtable< CLockedResource< char >, empty_t, CLockecResourceHashFunctor, CLockedResourceEqualFunctor > CLockedResourceHashtable;
|
|
|
|
|
|
|
|
|
|
class CAuthPhysCompileContext
|
|
{
|
|
public:
|
|
CAuthPhysCompileContext( CResourceStream *pStream )
|
|
: m_pStream( pStream ), m_DefaultSurfaceProperty( "default" )
|
|
{}
|
|
|
|
int ResolveCollisionAttributesIndex();
|
|
|
|
const CAuthPhysCollisionAttributes &GetDefaultCollisionAttributes() { return m_DefaultCollisionAttributes; }
|
|
void ApplyOverrideToDefault( const CAuthPhysCollisionAttributesOverride &collAttr ) { m_DefaultCollisionAttributes.ApplyOverride( collAttr ); }
|
|
void SetDefaultCollisionAttributes( const CAuthPhysCollisionAttributes &attr ) { m_DefaultCollisionAttributes = attr; }
|
|
|
|
int GetCollAttrPaletteSize( )const { return m_CollAttrPalette.Count();}
|
|
const CAuthPhysCollisionAttributes &GetCollAttrPaletteEntry( int i ) { return m_CollAttrPalette[i]; }
|
|
CResourceStream *GetStream() { return m_pStream; }
|
|
|
|
CLockedResource<char> WriteString( const char *pString, uint32 *pHashOut = NULL ); // writes a non-unique string (saving space) , returns its hash
|
|
template <typename T> CLockedResource< T > FindOrWrite( const T *pData, uint nElements )
|
|
{
|
|
CLockedResource< char > res( ( char* ) pData, nElements * sizeof( T ) );
|
|
UtlHashHandle_t hFind = m_WrittenResources.Find( res );
|
|
if ( hFind == m_WrittenResources.InvalidHandle( ) )
|
|
{
|
|
CLockedResource<T> written = m_pStream->Allocate< T>( nElements );
|
|
V_memcpy( written, pData, sizeof ( T ) *nElements );
|
|
|
|
m_WrittenResources.Insert( CLockedResource<char>( ( char* ) ( T* ) written, nElements * sizeof( T ) ));
|
|
return written;
|
|
}
|
|
else
|
|
{
|
|
CLockedResource<char> found = m_WrittenResources[ hFind ];
|
|
return CLockedResource< T >( ( T* ) ( char* ) found, nElements ); // found a copy, no need to write it out again
|
|
}
|
|
}
|
|
|
|
const CUtlString &GetDefaultSurfaceProperty()const { return m_DefaultSurfaceProperty; }
|
|
void SetDefaultSurfaceProperty( const char *pSurfacePropertyOverride ){ m_DefaultSurfaceProperty = pSurfacePropertyOverride ; }
|
|
int ResolveSurfacePropertyIndex();
|
|
|
|
int GetSurfacePropertyPaletteSize() { return m_SurfacePropPalette.Count(); }
|
|
const CUtlString &GetSurfacePropertyPaletteEntry( int i ) { return m_SurfacePropPalette[i]; }
|
|
protected:
|
|
CAuthPhysCollisionAttributes m_DefaultCollisionAttributes;
|
|
CResourceStream *m_pStream;
|
|
CUtlVector< CAuthPhysCollisionAttributes > m_CollAttrPalette;
|
|
|
|
CLockedResourceHashtable m_WrittenResources;
|
|
CUtlVector< CUtlString > m_SurfacePropPalette;
|
|
CUtlString m_DefaultSurfaceProperty;
|
|
};
|
|
|
|
|
|
class CAuthPhysCollisionAttributeScope
|
|
{
|
|
public:
|
|
CAuthPhysCollisionAttributeScope( CAuthPhysCompileContext* pContext, CAuthPhysCollisionAttributesOverride &collOverride )
|
|
{
|
|
m_pContext = pContext;
|
|
m_SavedDefaults = pContext->GetDefaultCollisionAttributes();
|
|
pContext->ApplyOverrideToDefault( collOverride );
|
|
}
|
|
~CAuthPhysCollisionAttributeScope()
|
|
{
|
|
m_pContext->SetDefaultCollisionAttributes( m_SavedDefaults );
|
|
}
|
|
protected:
|
|
CAuthPhysCompileContext* m_pContext;
|
|
CAuthPhysCollisionAttributes m_SavedDefaults;
|
|
};
|
|
|
|
class CAuthPhysSurfacePropertyScope
|
|
{
|
|
public:
|
|
CAuthPhysSurfacePropertyScope( CAuthPhysCompileContext* pContext, const CUtlString &surfacePropOverride )
|
|
{
|
|
m_pContext = pContext;
|
|
m_SavedDefault = pContext->GetDefaultSurfaceProperty();
|
|
if( !surfacePropOverride.IsEmpty() )
|
|
{
|
|
pContext->SetDefaultSurfaceProperty( surfacePropOverride );
|
|
}
|
|
}
|
|
~CAuthPhysSurfacePropertyScope()
|
|
{
|
|
m_pContext->SetDefaultSurfaceProperty( m_SavedDefault );
|
|
}
|
|
protected:
|
|
CAuthPhysCompileContext* m_pContext;
|
|
CUtlString m_SavedDefault;
|
|
};
|
|
|
|
|
|
enum PhysicsShapeType_t;
|
|
|
|
|
|
/*schema*/ enum AuthPhysShapeTypeEnum_t
|
|
{
|
|
AUTH_PHYS_SHAPE_SPHERE = SHAPE_SPHERE, META( MPropertyFriendlyName = "Sphere" )
|
|
AUTH_PHYS_SHAPE_CAPSULE = SHAPE_CAPSULE, META( MPropertyFriendlyName = "Capsule" )
|
|
AUTH_PHYS_SHAPE_HULL = SHAPE_HULL, META( MPropertyFriendlyName = "Convex Hull" )
|
|
AUTH_PHYS_SHAPE_MESH = SHAPE_MESH, META( MPropertyFriendlyName = "Triangle Mesh" )
|
|
};
|
|
DECLARE_SCHEMA_ENUM( AuthPhysShapeTypeEnum_t );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*schema*/ class CPhysPartBreakableData
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CPhysPartBreakableData )
|
|
CPhysPartBreakableData()
|
|
{
|
|
m_bMotionDisabled = false;
|
|
m_nHealth = 1;
|
|
m_flBurstScale = 1.0;
|
|
m_flBurstRandomize = 0.0f;
|
|
m_nFadeTime = 20.0;
|
|
m_nFadeMin = 0;
|
|
m_nFadeMax = 0.0;
|
|
m_bNoShadows = false;
|
|
}
|
|
|
|
CUtlString m_CollisionGroup; META( MPropertyFriendlyName = "Collision Group"; MPropertyChoiceProviderFn = GetCollisionGroupList; );
|
|
bool m_bMotionDisabled; META( MPropertyFriendlyName = "Motion Disabled" );
|
|
int m_nHealth;META( MPropertyFriendlyName = "Health" );
|
|
int m_nFadeTime;META( MPropertyFriendlyName = "Fade Time" );
|
|
int m_nFadeMin;META( MPropertyFriendlyName = "Fade Min Distance" );
|
|
int m_nFadeMax;META( MPropertyFriendlyName = "Fade Max Distance" );
|
|
float m_flBurstScale; META( MPropertyFriendlyName = "Burst Scale" );
|
|
float m_flBurstRandomize; META( MPropertyFriendlyName = "Burst Randomize" );
|
|
bool m_bNoShadows; META( MPropertyFriendlyName = "Do Not Cast Shadows" );
|
|
CUtlString m_SurfaceProp; META( MPropertyFriendlyName = "Surface Prop"; MPropertyChoiceProviderFn = GetSurfacePropertyList; );
|
|
|
|
CLockedResource< VpropBreakablePartData_t > Compile( CResourceStream *pStream )
|
|
{
|
|
CLockedResource< VpropBreakablePartData_t > pData = pStream->Allocate< VpropBreakablePartData_t >();
|
|
pData->m_nCollisionGroupHash = m_CollisionGroup.IsEmpty() ? 0 : MakeStringToken( m_CollisionGroup.Get() ).GetHashCode();
|
|
pData->m_bMotionDisabled = m_bMotionDisabled;
|
|
pData->m_nHealth = m_nHealth;
|
|
pData->m_nFadeTime = m_nFadeTime;
|
|
pData->m_nFadeMin = m_nFadeMin;
|
|
pData->m_nFadeMax = m_nFadeMax;
|
|
pData->m_flBurstScale = m_flBurstScale;
|
|
pData->m_flBurstRandomize = m_flBurstRandomize;
|
|
pData->m_bNoShadows = m_bNoShadows;
|
|
pData->m_nSurfaceProp = m_SurfaceProp.IsEmpty() ? 0 : MakeStringToken( m_SurfaceProp ).GetHashCode();
|
|
|
|
return pData;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
/*schema*/ enum AuthPhysJointTypeEnum_t
|
|
{
|
|
AUTH_PHYS_SPHERICAL_JOINT = SPHERICAL_JOINT, META( MPropertyFriendlyName = "Spherical Joint" )
|
|
AUTH_PHYS_REVOLUTE_JOINT = REVOLUTE_JOINT, META( MPropertyFriendlyName = "Revolute Joint" )
|
|
AUTH_PHYS_PRISMATIC_JOINT = PRISMATIC_JOINT, META( MPropertyFriendlyName = "Prismatic Joint" )
|
|
AUTH_PHYS_ORTHOTWIST_JOINT = QUAT_ORTHOTWIST_JOINT, META( MPropertyFriendlyName = "Ragdoll Joint" )
|
|
AUTH_PHYS_WELD_JOINT = WELD_JOINT, META( MPropertyFriendlyName = "Weld Joint" )
|
|
AUTH_PHYS_NULL_JOINT = NULL_JOINT META( MPropertyFriendlyName = "Null Joint" )
|
|
};
|
|
DECLARE_SCHEMA_ENUM( AuthPhysJointTypeEnum_t );
|
|
|
|
|
|
struct AuthPhysRange_t
|
|
{
|
|
TYPEMETA( MNoScatter )
|
|
//DECLARE_SCHEMA_DATA_CLASS( AuthPhysRange_t );
|
|
float32 m_flMin;
|
|
float32 m_flMax;
|
|
|
|
AuthPhysRange_t() {}
|
|
AuthPhysRange_t( float flMin, float flMax )
|
|
: m_flMin( flMin )
|
|
, m_flMax( flMax )
|
|
{}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CBoneParseParams;
|
|
|
|
|
|
/*schema*/ struct AuthPhysSphereCollision_t
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( AuthPhysSphereCollision_t );
|
|
public:
|
|
AuthPhysSphereCollision_t()
|
|
{
|
|
m_bInclusive = false;
|
|
m_flRadius = 16;
|
|
m_vOrigin = vec3_origin;
|
|
m_flStickiness = 0;
|
|
}
|
|
|
|
bool m_bInclusive;
|
|
float m_flRadius;
|
|
Vector m_vOrigin;
|
|
float m_flStickiness;
|
|
};
|
|
|
|
|
|
class CAuthPhysFxRodMap : public CUtlVector < CUtlSortVector< int > * >
|
|
{
|
|
public:
|
|
CAuthPhysFxRodMap(){ }
|
|
~CAuthPhysFxRodMap() { PurgeAndDeleteElements(); }
|
|
|
|
void Init( int nNodes )
|
|
{
|
|
PurgeAndDeleteElements();
|
|
SetCount( nNodes );
|
|
FillWithValue( NULL );
|
|
}
|
|
|
|
void Append( int nNodeA, int nNodeB )
|
|
{
|
|
CUtlSortVector< int > * &list = ( *this )[ nNodeA ];
|
|
if ( !list )
|
|
{
|
|
list = new CUtlSortVector < int > ;
|
|
list->Insert( nNodeB );
|
|
}
|
|
else
|
|
{
|
|
list->InsertIfNotFound( nNodeB );
|
|
}
|
|
}
|
|
};
|
|
|
|
/*schema*/ class CAuthPhysFx
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx );
|
|
public:
|
|
CAuthPhysFx( )
|
|
{
|
|
InitDefaults( );
|
|
}
|
|
~CAuthPhysFx(){}
|
|
|
|
int Cleanup();
|
|
template <typename TBitVec>
|
|
void AlignNodes( const TBitVec &nodes );
|
|
void AlignNodes()
|
|
{
|
|
struct IdentityMap_t { bool operator[]( int i ) const { return true; } } identityMap;
|
|
AlignNodes( identityMap );
|
|
}
|
|
public:
|
|
|
|
/*schema*/ class CDagEdge
|
|
{
|
|
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CDagEdge );
|
|
public:
|
|
CDagEdge( )
|
|
{
|
|
m_nParentBone = -1;
|
|
m_nChildBone = -1;
|
|
}
|
|
|
|
bool UsesNode( int nNode ) const
|
|
{
|
|
return m_nParentBone == nNode || m_nChildBone == nNode;
|
|
}
|
|
|
|
void ChangeNode( int nOldNode, int nNewNode )
|
|
{
|
|
if ( m_nParentBone == nOldNode )
|
|
m_nParentBone = nNewNode;
|
|
if ( m_nChildBone == nOldNode )
|
|
m_nChildBone = nNewNode;
|
|
}
|
|
void RebaseNodes( int nBaseNode )
|
|
{
|
|
m_nParentBone += nBaseNode;
|
|
m_nChildBone += nBaseNode;
|
|
}
|
|
|
|
void RemapNodes( const CUtlVector< int > &remap )
|
|
{
|
|
RemapNode( m_nParentBone, remap );
|
|
RemapNode( m_nChildBone, remap );
|
|
}
|
|
|
|
bool IsValid( int nNodeCount )const
|
|
{
|
|
return m_nParentBone < nNodeCount && m_nChildBone < nNodeCount;
|
|
}
|
|
|
|
bool operator == ( const CDagEdge &other ) const { return m_nParentBone == other.m_nParentBone && m_nChildBone == other.m_nChildBone; }
|
|
|
|
int m_nParentBone;
|
|
int m_nChildBone;
|
|
};
|
|
|
|
/*schema*/ class CCollisionSphere: public CDagEdge
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CCollisionSphere );
|
|
public:
|
|
CCollisionSphere( )
|
|
{
|
|
m_bInclusive = false;
|
|
m_flRadius = 0;
|
|
m_vOrigin = vec3_origin;
|
|
m_flStickiness = 0;
|
|
}
|
|
|
|
void Assign( const AuthPhysSphereCollision_t &that )
|
|
{
|
|
m_bInclusive = that.m_bInclusive;
|
|
m_flRadius = that.m_flRadius;
|
|
m_vOrigin = that.m_vOrigin;
|
|
m_flStickiness = that.m_flStickiness;
|
|
}
|
|
public:
|
|
bool m_bInclusive;
|
|
float m_flRadius;
|
|
Vector m_vOrigin;
|
|
float m_flStickiness;
|
|
};
|
|
|
|
/*schema*/ class CCollisionPlane: public CDagEdge
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CCollisionPlane );
|
|
public:
|
|
CCollisionPlane( )
|
|
{
|
|
m_Plane.m_vNormal = Vector( 0, 0, 1 );
|
|
m_Plane.m_flOffset = 0;
|
|
m_flStickiness = 0;
|
|
}
|
|
|
|
bool operator == ( const CCollisionPlane &other )const
|
|
{
|
|
return static_cast< const CDagEdge& >( *this ) == static_cast< const CDagEdge& >( other )
|
|
&& m_Plane == other.m_Plane;
|
|
|
|
}
|
|
|
|
RnPlane_t m_Plane;
|
|
float m_flStickiness;
|
|
};
|
|
|
|
/*schema*/ class CCtrlOffset : public CDagEdge
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CCtrlOffset );
|
|
public:
|
|
CCtrlOffset()
|
|
{
|
|
m_vOffset = Vector( 0, 8, 0 ); // zero offset isn't really useful
|
|
}
|
|
bool operator == ( const CCtrlOffset &other )const
|
|
{
|
|
return static_cast< const CDagEdge& >( *this ) == static_cast< const CDagEdge& >( other ) && m_vOffset == other.m_vOffset;
|
|
}
|
|
operator const FeCtrlOffset_t()const
|
|
{
|
|
FeCtrlOffset_t offset;
|
|
offset.nCtrlChild = m_nChildBone;
|
|
offset.nCtrlParent = m_nParentBone;
|
|
offset.vOffset = m_vOffset;
|
|
return offset;
|
|
}
|
|
|
|
Vector m_vOffset;
|
|
};
|
|
|
|
|
|
/*schema*/ class CBone
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CBone )
|
|
public:
|
|
CBone( )
|
|
{
|
|
m_nParent = -1;
|
|
m_bVirtual = false;
|
|
m_bNeedNodeBase = false;
|
|
m_bHasMassOverride = false;
|
|
m_bSimulated = true;
|
|
m_bForceSimulated = false;
|
|
m_bFreeRotation = true;
|
|
m_bAnimRotation = false;
|
|
m_bOsOffset = false;
|
|
m_Transform = g_TransformIdentity;
|
|
m_flMass = 1.0f;
|
|
m_flMassBias = 0.0f;
|
|
m_bNeedsWorldCollision = false;
|
|
m_Integrator.Init( );
|
|
m_nFollowParent = -1;
|
|
m_flFollowWeight = 0;
|
|
m_flWorldFriction = 0;
|
|
m_flGroundFriction = 0;
|
|
m_flLegacyStretchForce = 0;
|
|
m_bUseRods = false;
|
|
m_flCollisionRadius = 0;
|
|
m_nCollisionMask = 0xFFFF;
|
|
m_flLocalForce = 1.0f;
|
|
m_flLocalRotation = 0.0f;
|
|
m_flVolumetricSolveAmount = 0.0f;
|
|
}
|
|
|
|
CFeModelBuilder::BuildNode_t AsBuildNode()const;
|
|
|
|
uint32 GetNameHash() { return MakeStringToken( m_Name.Get() ).GetHashCode(); }
|
|
bool ApplyDotaFlags( const char *pFlags, CBoneParseParams *pParamsOut );
|
|
bool ChangeNode( int nOldNode, int nNewNode )
|
|
{
|
|
bool bChanged = false;
|
|
if ( m_nParent == nOldNode )
|
|
{
|
|
m_nParent = nNewNode;
|
|
bChanged = true;
|
|
}
|
|
if ( m_nFollowParent == nOldNode )
|
|
{
|
|
m_nFollowParent = nNewNode;
|
|
bChanged = true;
|
|
}
|
|
return bChanged;
|
|
}
|
|
CTransform GetOffsetTransform( const Vector &vOffset )
|
|
{
|
|
return CTransform( m_Transform.TransformVector( vOffset ), m_Transform.m_orientation );
|
|
}
|
|
void RebaseNodes( int nBaseNode )
|
|
{
|
|
if( m_nFollowParent >= 0 )
|
|
m_nFollowParent += nBaseNode;
|
|
if ( m_nParent >= 0 )
|
|
m_nParent += nBaseNode;
|
|
}
|
|
void RemapNodes( const CUtlVector< int > &remap )
|
|
{
|
|
RemapNode( m_nFollowParent, remap );
|
|
RemapNode( m_nParent, remap );
|
|
}
|
|
public:
|
|
bool m_bSimulated; // this is an fx particle comprising jiggle bones, chains, cloths, or any other soft body
|
|
bool m_bForceSimulated; // if true, this node may not be deduced to be non-simulated
|
|
bool m_bFreeRotation;
|
|
bool m_bAnimRotation;
|
|
bool m_bVirtual;
|
|
bool m_bNeedNodeBase;
|
|
bool m_bNeedsWorldCollision;
|
|
bool m_bOsOffset; // object-space offset, used for compatibility with S1 cloth
|
|
float m_flWorldFriction; META( MPropertyFriendlyName = "Ground NOT-Collide" );// this is not a friction coefficient! this is the "WorldFriction" parameter from source1 cloth
|
|
float m_flGroundFriction;
|
|
float m_flLegacyStretchForce; // premultiplied by invMass here
|
|
float m_flMass;
|
|
float m_flMassBias;
|
|
int m_nFollowParent;
|
|
float m_flFollowWeight;
|
|
float m_flCollisionRadius;
|
|
FeNodeIntegrator_t m_Integrator;
|
|
bool m_bHasMassOverride;
|
|
uint32 m_nCollisionMask;
|
|
int m_nParent; // parent bone index
|
|
CUtlString m_Name; // bone name, the same as in model, the same as used in animation
|
|
CTransform m_Transform; META( MPropertySuppressField );// bind pose, relaxed pose
|
|
bool m_bUseRods; // UI convenience: when checked, affects generation of quads or rods around this node
|
|
|
|
float m_flLocalForce;
|
|
float m_flLocalRotation;
|
|
float m_flVolumetricSolveAmount;
|
|
};
|
|
|
|
|
|
/*schema*/ class CConstraint
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CConstraint )
|
|
public:
|
|
CConstraint( int nBone0 = -1, int nBone1 = -1 ) { m_nBones[0] = nBone0; m_nBones[1] = nBone1; }
|
|
|
|
bool Equals( int nBone0, int nBone1 ) const { return ( m_nBones[0] == nBone0 && m_nBones[1] == nBone1 ) || ( m_nBones[1] == nBone0 && m_nBones[0] == nBone1 ); }
|
|
|
|
int m_nBones[2]; // bone indices of the connected particles
|
|
|
|
public:
|
|
bool UsesNode( int nNode ) const
|
|
{
|
|
return m_nBones[ 0 ] == nNode || m_nBones[ 1 ] == nNode;
|
|
}
|
|
void ChangeNode( int nOldNode, int nNewNode )
|
|
{
|
|
if ( m_nBones[ 0 ] == nOldNode )
|
|
m_nBones[ 0 ] = nNewNode;
|
|
if ( m_nBones[ 1 ] == nOldNode )
|
|
m_nBones[ 1 ] = nNewNode;
|
|
}
|
|
void RebaseNodes( int nBaseNode )
|
|
{
|
|
m_nBones[ 0 ] += nBaseNode;
|
|
m_nBones[ 1 ] += nBaseNode;
|
|
}
|
|
void RemapNodes( const CUtlVector< int > &remap )
|
|
{
|
|
RemapNode( m_nBones[ 0 ], remap );
|
|
RemapNode( m_nBones[ 1 ], remap );
|
|
}
|
|
};
|
|
|
|
/*schema*/ class CCapsule
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CCapsule )
|
|
public:
|
|
int m_nBone[2];
|
|
Vector m_vCenter[2]; // each capsule center in the corresponding bone's local coordinates
|
|
float m_flRadius;
|
|
public:
|
|
bool UsesNode( int nNode ) const
|
|
{
|
|
return m_nBone[ 0 ] == nNode || m_nBone[ 1 ] == nNode;
|
|
}
|
|
void ChangeNode( int nOldNode, int nNewNode )
|
|
{
|
|
if ( m_nBone[ 0 ] == nOldNode )
|
|
m_nBone[ 0 ] = nNewNode;
|
|
if ( m_nBone[ 1 ] == nOldNode )
|
|
m_nBone[ 1 ] = nNewNode;
|
|
}
|
|
void RebaseNodes( int nBaseNode )
|
|
{
|
|
m_nBone[ 0 ] += nBaseNode;
|
|
m_nBone[ 1 ] += nBaseNode;
|
|
}
|
|
void RemapNodes( const CUtlVector< int > &remap )
|
|
{
|
|
RemapNode( m_nBone[ 0 ], remap );
|
|
RemapNode( m_nBone[ 1 ], remap );
|
|
}
|
|
};
|
|
|
|
/*schema*/ class CQuad
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CQuad )
|
|
public:
|
|
uint m_nNodes[ 4 ];
|
|
bool m_bUseRods;
|
|
CQuad() { m_bUseRods = false; }
|
|
|
|
CQuad( uint nNode0, uint nNode1, uint nNode2, uint nNode3 )
|
|
{
|
|
m_nNodes[ 0 ] = nNode0;
|
|
m_nNodes[ 1 ] = nNode1;
|
|
m_nNodes[ 2 ] = nNode2;
|
|
m_nNodes[ 3 ] = nNode3;
|
|
m_bUseRods = false;
|
|
}
|
|
|
|
CFeModelBuilder::BuildElem_t AsBuildElem()const;
|
|
|
|
bool operator == ( const CQuad &right ) const
|
|
{
|
|
return m_nNodes[ 0 ] == right.m_nNodes[ 0 ] && m_nNodes[ 1 ] == right.m_nNodes[ 1 ] && m_nNodes[ 2 ] == right.m_nNodes[ 2 ] && m_nNodes[ 3 ] == right.m_nNodes[ 3 ];
|
|
}
|
|
bool UsesNode( uint nNode ) const
|
|
{
|
|
return m_nNodes[ 0 ] == nNode || m_nNodes[ 1 ] == nNode || m_nNodes[ 2 ] == nNode || m_nNodes[ 3 ] == nNode;
|
|
}
|
|
template <typename TBitVec >
|
|
bool UsesAnyNode( const TBitVec &nodes )
|
|
{
|
|
return nodes[ m_nNodes[ 0 ] ] || nodes[ m_nNodes[ 1 ] ] || nodes[ m_nNodes[ 2 ] ] || nodes[ m_nNodes[ 3 ] ] ;
|
|
}
|
|
void ChangeNode( uint nOldNode, uint nNewNode )
|
|
{
|
|
if ( m_nNodes[ 0 ] == nOldNode )
|
|
m_nNodes[ 0 ] = nNewNode;
|
|
if ( m_nNodes[ 1 ] == nOldNode )
|
|
m_nNodes[ 1 ] = nNewNode;
|
|
if ( m_nNodes[ 2 ] == nOldNode )
|
|
m_nNodes[ 2 ] = nNewNode;
|
|
if ( m_nNodes[ 3 ] == nOldNode )
|
|
m_nNodes[ 3 ] = nNewNode;
|
|
}
|
|
bool IsValid( uint nNodeCount )const
|
|
{
|
|
return m_nNodes[ 0 ] < nNodeCount && m_nNodes[ 1 ] < nNodeCount && m_nNodes[ 2 ] < nNodeCount && m_nNodes[ 3 ] < nNodeCount;
|
|
}
|
|
void RebaseNodes( int nBaseNode )
|
|
{
|
|
m_nNodes[ 0 ] += nBaseNode;
|
|
m_nNodes[ 1 ] += nBaseNode;
|
|
m_nNodes[ 2 ] += nBaseNode;
|
|
m_nNodes[ 3 ] += nBaseNode;
|
|
}
|
|
void RemapNodes( const CUtlVector< int > &remap )
|
|
{
|
|
RemapNode( m_nNodes[ 0 ], remap );
|
|
RemapNode( m_nNodes[ 1 ], remap );
|
|
RemapNode( m_nNodes[ 2 ], remap );
|
|
RemapNode( m_nNodes[ 3 ], remap );
|
|
}
|
|
};
|
|
|
|
/*schema*/ class CRod
|
|
{
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CRod )
|
|
public:
|
|
uint m_nNodes[ 2 ];
|
|
float32 m_flMotionBias[ 2 ]; //
|
|
float32 m_flRelaxationFactor;
|
|
|
|
bool m_bExplicitLength;
|
|
float32 m_flLength;
|
|
float32 m_flContractionFactor;
|
|
|
|
CRod( )
|
|
{
|
|
m_bExplicitLength = false;
|
|
m_flContractionFactor = 0.05f; // in Source1, rods may contract to 0
|
|
m_flLength = 0;
|
|
m_nNodes[ 0 ] = m_nNodes[ 1 ] = 0;
|
|
m_flRelaxationFactor = 1.0f;
|
|
m_flMotionBias[ 0 ] = m_flMotionBias[ 1 ] = 1.0f;
|
|
}
|
|
CRod( uint nNode0, uint nNode1, float flRelaxationFactor = 1.0f )
|
|
{
|
|
m_nNodes[ 0 ] = nNode0;
|
|
m_nNodes[ 1 ] = nNode1;
|
|
m_flRelaxationFactor = flRelaxationFactor;
|
|
m_flMotionBias[ 0 ] = m_flMotionBias[ 1 ] = 1.0f;
|
|
}
|
|
bool operator < ( const CRod &other )const
|
|
{
|
|
return m_nNodes[ 0 ] == other.m_nNodes[ 0 ] ? m_nNodes[ 1 ] < other.m_nNodes[ 1 ] : m_nNodes[ 0 ] < other.m_nNodes[ 0 ];
|
|
}
|
|
|
|
bool operator == ( const CRod &other )const
|
|
{
|
|
return ( m_nNodes[ 0 ] == other.m_nNodes[ 0 ] && m_nNodes[ 1 ] == other.m_nNodes[ 1 ] ) || ( m_nNodes[ 0 ] == other.m_nNodes[ 1 ] && m_nNodes[ 1 ] == other.m_nNodes[ 0 ] );
|
|
}
|
|
bool Equals( uint nNode0, uint nNode1 )const
|
|
{
|
|
return ( m_nNodes[ 0 ] == nNode0 && m_nNodes[ 1 ] == nNode1 ) || ( m_nNodes[ 1 ] == nNode0 && m_nNodes[ 0 ] == nNode1 );
|
|
}
|
|
bool UsesNode( uint nNode ) const
|
|
{
|
|
return m_nNodes[ 0 ] == nNode || m_nNodes[ 1 ] == nNode;
|
|
}
|
|
void ChangeNode( uint nOldNode, uint nNewNode )
|
|
{
|
|
if ( m_nNodes[ 0 ] == nOldNode )
|
|
m_nNodes[ 0 ] = nNewNode;
|
|
if ( m_nNodes[ 1 ] == nOldNode )
|
|
m_nNodes[ 1 ] = nNewNode;
|
|
}
|
|
void RebaseNodes( int nBaseNode )
|
|
{
|
|
m_nNodes[ 0 ] += nBaseNode;
|
|
m_nNodes[ 1 ] += nBaseNode;
|
|
}
|
|
void RemapNodes( const CUtlVector< int > &remap )
|
|
{
|
|
RemapNode( m_nNodes[ 0 ], remap );
|
|
RemapNode( m_nNodes[ 1 ], remap );
|
|
}
|
|
void Reverse()
|
|
{
|
|
::Swap( m_nNodes[ 0 ], m_nNodes[ 1 ] );
|
|
::Swap( m_flMotionBias[ 0 ], m_flMotionBias[ 1 ] );
|
|
}
|
|
uint GetOtherNode( uint nNode )const
|
|
{
|
|
if ( m_nNodes[ 1 ] == nNode )
|
|
{
|
|
return m_nNodes[ 0 ];
|
|
}
|
|
else
|
|
{
|
|
Assert( m_nNodes[ 0 ] == nNode );
|
|
return m_nNodes[ 1 ];
|
|
}
|
|
}
|
|
bool IsValid( uint nNodeCount )const
|
|
{
|
|
return m_nNodes[ 0 ] < nNodeCount && m_nNodes[ 1 ] < nNodeCount;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/*schema*/ class CSpring
|
|
{
|
|
public:
|
|
//DECLARE_SCHEMA_DATA_CLASS( CAuthPhysFx::CSpring )
|
|
uint m_nNodes[ 2 ];
|
|
float32 m_flSpringConstant;
|
|
float32 m_flSpringDamping;
|
|
float32 m_flStretchiness; // Not Implemented
|
|
CSpring( ){}
|
|
CSpring( uint nNode0, uint nNode1, float flSpringConstant, float flSpringDamping, float flStretchForce, float flStretchiness )
|
|
{
|
|
m_nNodes[ 0 ] = nNode0;
|
|
m_nNodes[ 1 ] = nNode1;
|
|
m_flSpringConstant = flSpringConstant + 60 * flStretchForce * flStretchiness; // I think this may be approximately how it effectively works in Source1;;
|
|
m_flSpringDamping = flSpringDamping;
|
|
m_flStretchiness = flStretchiness; // Note: so far this is not used, saving mainly for documentation
|
|
}
|
|
bool operator < ( const CRod &other )
|
|
{
|
|
return m_nNodes[ 0 ] == other.m_nNodes[ 0 ] ? m_nNodes[ 1 ] < other.m_nNodes[ 1 ] : m_nNodes[ 0 ] < other.m_nNodes[ 0 ];
|
|
}
|
|
bool UsesNode( uint nNode ) const { return m_nNodes[ 0 ] == nNode || m_nNodes[ 1 ] == nNode; }
|
|
void ChangeNode( uint nOldNode, uint nNewNode )
|
|
{
|
|
if ( m_nNodes[ 0 ] == nOldNode )
|
|
m_nNodes[ 0 ] = nNewNode;
|
|
if ( m_nNodes[ 1 ] == nOldNode )
|
|
m_nNodes[ 1 ] = nNewNode;
|
|
}
|
|
void RebaseNodes( int nBaseNode )
|
|
{
|
|
m_nNodes[ 0 ] += nBaseNode;
|
|
m_nNodes[ 1 ] += nBaseNode;
|
|
}
|
|
void RemapNodes( const CUtlVector< int > &remap )
|
|
{
|
|
RemapNode( m_nNodes[ 0 ], remap );
|
|
RemapNode( m_nNodes[ 1 ], remap );
|
|
}
|
|
};
|
|
|
|
public:
|
|
bool IsEmpty() const { return m_Quads.IsEmpty() && m_Rods.IsEmpty() && m_Nodes.IsEmpty() && m_CollisionSpheres.IsEmpty() && m_CollisionPlanes.IsEmpty() && m_SphereRigids.IsEmpty() && m_TaperedCapsuleStretches.IsEmpty() && m_TaperedCapsuleRigids.IsEmpty() && m_Capsules.IsEmpty() && m_Constraints.IsEmpty() && m_Springs.IsEmpty(); }
|
|
bool ImportDotaCloth( const char *pClothFile, CPhysModelSource &physicsModel/*CUtlStringMap< int, CUtlSymbolTable > *pBoneToIndex*/ );
|
|
void Load( const CFeModel *pFeModel );
|
|
void AddRod( const CUtlVector< CBone > &nodes, uint nNode0, uint nNode1, float flRelaxationFactor );
|
|
|
|
void SetBones( const CUtlVector< CBone > &bones );
|
|
int AddConstraint( int nBone0, int nBone1 );
|
|
const CBone *GetBone( int nBone ) const { return &m_Nodes[nBone]; }
|
|
CBone *GetBone( int nBone ) { return &m_Nodes[nBone]; }
|
|
int FindNodeIndex( const char *pName );
|
|
CBone *GetOrCreateBone( const char *pName );
|
|
int GetBoneCount() const { return m_Nodes.Count(); }
|
|
int GetSimParticleCount()const;
|
|
int GetStaticParticleCount( )const { return m_Nodes.Count( ) - GetSimParticleCount( ); }
|
|
CLockedResource< PhysFeModelDesc_t > Compile( CResourceStream *pStream, const CVClothProxyMeshOptions *pOptions = NULL )const;
|
|
bool IsSimilarTo( const CFeModel *pFeModel )const;
|
|
bool IsConstraintSimulated( int nConstraint ) const ;
|
|
bool IsSpringSimulated( int nSpring ) const;
|
|
|
|
int GetRodCount( ) const { return m_Rods.Count( ); }
|
|
CRod *GetRod( int i ) { return &m_Rods[ i ]; }
|
|
float GetRodLength( int nRod )const;
|
|
int GetQuadCount( ) const { return m_Quads.Count( ); }
|
|
CQuad *GetQuad( int i ) { return &m_Quads[ i ]; }
|
|
|
|
int GetConstraintCount() const { return m_Constraints.Count(); }
|
|
CConstraint* GetConstraint( int i ) { return &m_Constraints[i]; }
|
|
|
|
int GetSpringCount( ) const { return m_Springs.Count( ); }
|
|
CSpring* GetSpring( int i ) { return &m_Springs[ i ]; }
|
|
|
|
int GetCapsuleCount() const { return m_Capsules.Count(); }
|
|
CCapsule* GetCapsule( int i ) { return &m_Capsules[i]; }
|
|
|
|
bool IsNewSpringAllowed( int nBone0, int nBone1 );
|
|
bool IsNewRodAllowed( int nBone0, int nBone1 );
|
|
|
|
void SortAndRemoveDuplicates( );
|
|
void RemoveRodsConnecting( const CVarBitVec &nodes );
|
|
void RemoveQuadsConnecting( const CVarBitVec &nodes );
|
|
|
|
int BuildIslandMap( CUtlVector< int > &nodeToIsland )const;
|
|
|
|
void Swap( CAuthPhysFx &other )
|
|
{
|
|
m_Quads.Swap( other.m_Quads );
|
|
m_Rods.Swap( other.m_Rods );
|
|
m_Springs.Swap( other.m_Springs );
|
|
m_Constraints.Swap( other.m_Constraints ); // springs and constraints between fx particles
|
|
m_Capsules.Swap( other.m_Capsules ); // capsules repelling the particles
|
|
m_Nodes.Swap( other.m_Nodes ); // bones, including dynamic particles
|
|
m_TaperedCapsuleRigids.Swap( other.m_TaperedCapsuleRigids );
|
|
m_TaperedCapsuleStretches.Swap( other.m_TaperedCapsuleStretches );
|
|
m_SphereRigids.Swap( other.m_SphereRigids );
|
|
m_CollisionSpheres.Swap( other.m_CollisionSpheres );
|
|
m_CollisionPlanes.Swap( other.m_CollisionPlanes );
|
|
m_PresetNodeBases.Swap( other.m_PresetNodeBases );
|
|
m_FitInfluences.Swap( other.m_FitInfluences );
|
|
|
|
::Swap( m_flDefaultSurfaceStretch, other.m_flDefaultSurfaceStretch );
|
|
::Swap( m_flDefaultThreadStretch, other.m_flDefaultThreadStretch );
|
|
|
|
::Swap( m_flDefaultGravityScale, other.m_flDefaultGravityScale );
|
|
::Swap( m_flDefaultVelAirDrag, other.m_flDefaultVelAirDrag );
|
|
::Swap( m_flDefaultExpAirDrag, other.m_flDefaultExpAirDrag );
|
|
::Swap( m_flDefaultVelQuadAirDrag, other.m_flDefaultVelQuadAirDrag );
|
|
::Swap( m_flDefaultExpQuadAirDrag, other.m_flDefaultExpQuadAirDrag );
|
|
::Swap( m_flDefaultVelRodAirDrag, other.m_flDefaultVelRodAirDrag );
|
|
::Swap( m_flDefaultExpRodAirDrag, other.m_flDefaultExpRodAirDrag );
|
|
::Swap( m_flQuadVelocitySmoothRate, other.m_flQuadVelocitySmoothRate );
|
|
::Swap( m_flRodVelocitySmoothRate, other.m_flRodVelocitySmoothRate );
|
|
::Swap( m_flWindage, other.m_flWindage );
|
|
::Swap( m_flWindDrag, other.m_flWindDrag );
|
|
::Swap( m_nQuadVelocitySmoothIterations, other.m_nQuadVelocitySmoothIterations );
|
|
::Swap( m_nRodVelocitySmoothIterations, other.m_nRodVelocitySmoothIterations );
|
|
::Swap( m_flDefaultGroundFriction, other.m_flDefaultGroundFriction );
|
|
::Swap( m_flDefaultWorldCollisionPenetration, other.m_flDefaultWorldCollisionPenetration );
|
|
::Swap( m_bForceWorldCollisionOnAllNodes, other.m_bForceWorldCollisionOnAllNodes );
|
|
::Swap( m_flAddWorldCollisionRadius, other.m_flAddWorldCollisionRadius );
|
|
|
|
::Swap( m_flAddCurvature, other.m_flAddCurvature );
|
|
::Swap( m_flQuadBendTolerance, other.m_flQuadBendTolerance );
|
|
::Swap( m_flLocalForce, other.m_flLocalForce );
|
|
::Swap( m_flLocalRotation, other.m_flLocalRotation );
|
|
::Swap( m_flVolumetricSolveAmount, other.m_flVolumetricSolveAmount );
|
|
::Swap( m_bFollowTheLead, other.m_bFollowTheLead );
|
|
::Swap( m_bUsePerNodeLocalForceAndRotation, other.m_bUsePerNodeLocalForceAndRotation );
|
|
|
|
::Swap( m_bUninertialRods, other.m_bUninertialRods );
|
|
::Swap( m_bExplicitMasses, other.m_bExplicitMasses );
|
|
::Swap( m_bCanCollideWithWorldCapsulesAndSpheres, other.m_bCanCollideWithWorldCapsulesAndSpheres );
|
|
::Swap( m_bCanCollideWithWorldHulls, other.m_bCanCollideWithWorldHulls );
|
|
::Swap( m_bCanCollideWithWorldMeshes, other.m_bCanCollideWithWorldMeshes );
|
|
::Swap( m_bUnitlessDamping, other.m_bUnitlessDamping );
|
|
::Swap( m_nMergePriority, other.m_nMergePriority );
|
|
::Swap( m_bNewStyle, other.m_bNewStyle );
|
|
::Swap( m_bAddStiffnessRods, other.m_bAddStiffnessRods );
|
|
::Swap( m_bRigidEdgeHinges, other.m_bRigidEdgeHinges );
|
|
}
|
|
|
|
template <typename T >
|
|
static void AppendAndRebaseNodes( CUtlVector< T > &arrThis, const CUtlVector< T > &arrThat, int nNodeBase )
|
|
{
|
|
int nScan = arrThis.Count();
|
|
arrThis.AddMultipleToTail( arrThat.Count(), arrThat.Base() );
|
|
for ( ; nScan < arrThis.Count(); ++nScan )
|
|
{
|
|
arrThis[ nScan ].RebaseNodes( nNodeBase );
|
|
}
|
|
}
|
|
template <typename T >
|
|
static void AppendAndRemapNodes( CUtlVector< T > &arrThis, const CUtlVector< T > &arrThat, const CUtlVector< int > &remap )
|
|
{
|
|
int nScan = arrThis.Count();
|
|
arrThis.AddMultipleToTail( arrThat.Count(), arrThat.Base() );
|
|
for ( ; nScan < arrThis.Count(); ++nScan )
|
|
{
|
|
arrThis[ nScan ].RemapNodes( remap );
|
|
}
|
|
}
|
|
|
|
|
|
void Append( const CAuthPhysFx &other )
|
|
{
|
|
int nBaseNodes = m_Nodes.Count();
|
|
|
|
if ( m_nMergePriority < other.m_nMergePriority )
|
|
{
|
|
m_flDefaultSurfaceStretch = other.m_flDefaultSurfaceStretch;
|
|
m_flDefaultThreadStretch = other.m_flDefaultThreadStretch;
|
|
|
|
m_flDefaultGravityScale = other.m_flDefaultGravityScale;
|
|
m_flDefaultVelAirDrag = other.m_flDefaultVelAirDrag;
|
|
m_flDefaultExpAirDrag = other.m_flDefaultExpAirDrag;
|
|
m_flDefaultVelQuadAirDrag = other.m_flDefaultVelQuadAirDrag;
|
|
m_flDefaultExpQuadAirDrag = other.m_flDefaultExpQuadAirDrag;
|
|
m_flDefaultVelRodAirDrag = other.m_flDefaultVelRodAirDrag;
|
|
m_flDefaultExpRodAirDrag = other.m_flDefaultExpRodAirDrag;
|
|
m_flQuadVelocitySmoothRate = other.m_flQuadVelocitySmoothRate;
|
|
m_flRodVelocitySmoothRate = other.m_flRodVelocitySmoothRate;
|
|
m_flWindage = other.m_flWindage;
|
|
m_flWindDrag = other.m_flWindage;
|
|
|
|
m_nQuadVelocitySmoothIterations = other.m_nQuadVelocitySmoothIterations;
|
|
m_nRodVelocitySmoothIterations = other.m_nRodVelocitySmoothIterations;
|
|
m_flDefaultGroundFriction = other.m_flDefaultGroundFriction;
|
|
m_flDefaultWorldCollisionPenetration = other.m_flDefaultWorldCollisionPenetration;
|
|
m_bForceWorldCollisionOnAllNodes = other.m_bForceWorldCollisionOnAllNodes;
|
|
m_flAddWorldCollisionRadius = other.m_flAddWorldCollisionRadius;
|
|
|
|
m_flAddCurvature = other.m_flAddCurvature;
|
|
m_flQuadBendTolerance = other.m_flQuadBendTolerance;
|
|
m_flLocalForce = other.m_flLocalForce;
|
|
m_flLocalRotation = other.m_flLocalRotation;
|
|
m_flVolumetricSolveAmount = other.m_flVolumetricSolveAmount;
|
|
m_bFollowTheLead = other.m_bFollowTheLead;
|
|
m_bUsePerNodeLocalForceAndRotation = other.m_bUsePerNodeLocalForceAndRotation;
|
|
m_bUninertialRods = other.m_bUninertialRods;
|
|
m_bExplicitMasses = other.m_bExplicitMasses;
|
|
m_bCanCollideWithWorldHulls = other.m_bCanCollideWithWorldHulls;
|
|
m_bCanCollideWithWorldMeshes = other.m_bCanCollideWithWorldMeshes;
|
|
m_bCanCollideWithWorldCapsulesAndSpheres = other.m_bCanCollideWithWorldCapsulesAndSpheres;
|
|
m_bUnitlessDamping = other.m_bUnitlessDamping;
|
|
m_nMergePriority = other.m_nMergePriority;
|
|
m_bNewStyle = other.m_bNewStyle;
|
|
m_bAddStiffnessRods = other.m_bAddStiffnessRods;
|
|
m_bRigidEdgeHinges = other.m_bRigidEdgeHinges;
|
|
}
|
|
|
|
CUtlVector< int > remap;
|
|
remap.SetCount( other.m_Nodes.Count() );
|
|
// merge the Nodes
|
|
m_Nodes.EnsureCapacity( m_Nodes.Count() + other.m_Nodes.Count() );
|
|
for ( int nOtherNode = 0; nOtherNode < other.m_Nodes.Count(); ++nOtherNode )
|
|
{
|
|
const CBone &otherNode = other.m_Nodes[ nOtherNode ];
|
|
int nMapNode = -1;
|
|
if ( !otherNode.m_Name.IsEmpty() )
|
|
{
|
|
nMapNode = FindNodeIndex( otherNode.m_Name );
|
|
}
|
|
if ( nMapNode < 0 )
|
|
{
|
|
// couldn't find node to map, create a new one
|
|
remap[ nOtherNode ] = m_Nodes.AddToTail( otherNode );
|
|
}
|
|
else
|
|
{
|
|
remap[ nOtherNode ] = nMapNode;
|
|
}
|
|
}
|
|
// the remap array is ready; use it to remap all the node indices
|
|
for ( int nNewNode = nBaseNodes; nNewNode < m_Nodes.Count(); ++nNewNode )
|
|
{
|
|
m_Nodes[ nNewNode ].RemapNodes( remap );
|
|
}
|
|
AppendAndRemapNodes( m_Quads, other.m_Quads, remap );
|
|
AppendAndRemapNodes( m_Rods, other.m_Rods, remap );
|
|
AppendAndRemapNodes( m_Springs, other.m_Springs, remap );
|
|
AppendAndRemapNodes( m_Constraints, other.m_Constraints, remap ); // springs and constraints between fx particles
|
|
AppendAndRemapNodes( m_Capsules, other.m_Capsules, remap ); // capsules repelling the particles
|
|
AppendAndRemapNodes( m_TaperedCapsuleRigids, other.m_TaperedCapsuleRigids, remap );
|
|
AppendAndRemapNodes( m_TaperedCapsuleStretches, other.m_TaperedCapsuleStretches, remap );
|
|
AppendAndRemapNodes( m_SphereRigids, other.m_SphereRigids, remap );
|
|
AppendAndRemapNodes( m_CollisionSpheres, other.m_CollisionSpheres, remap );
|
|
AppendAndRemapNodes( m_CollisionPlanes, other.m_CollisionPlanes, remap );
|
|
AppendAndRemapNodes( m_PresetNodeBases, other.m_PresetNodeBases, remap );
|
|
AppendAndRemapNodes( m_FitInfluences, other.m_FitInfluences, remap );
|
|
}
|
|
|
|
|
|
void Purge( )
|
|
{
|
|
m_Quads.Purge();
|
|
m_Rods.Purge();
|
|
m_Springs.Purge();
|
|
m_Constraints.Purge(); // springs and constraints between fx particles
|
|
m_Capsules.Purge(); // capsules repelling the particles
|
|
m_Nodes.Purge(); // bones, including dynamic particles
|
|
m_TaperedCapsuleRigids.Purge();
|
|
m_TaperedCapsuleStretches.Purge();
|
|
m_SphereRigids.Purge();
|
|
m_CollisionSpheres.Purge();
|
|
m_CollisionPlanes.Purge();
|
|
m_PresetNodeBases.Purge();
|
|
m_FitInfluences.Purge();
|
|
InitDefaults();
|
|
}
|
|
|
|
void InitDefaults()
|
|
{
|
|
m_flDefaultSurfaceStretch = 0;
|
|
m_flDefaultThreadStretch = 0;
|
|
m_flLocalForce = 1.0f;
|
|
m_flLocalRotation = 0.0f;
|
|
m_flVolumetricSolveAmount = 0.0f;
|
|
m_bUninertialRods = false;
|
|
m_bExplicitMasses = false;
|
|
m_bCanCollideWithWorldHulls = false;
|
|
m_bCanCollideWithWorldMeshes = false;
|
|
m_bCanCollideWithWorldCapsulesAndSpheres = false;
|
|
m_bUnitlessDamping = true;
|
|
m_bFollowTheLead = false;
|
|
m_bUsePerNodeLocalForceAndRotation = false;
|
|
m_flAddCurvature = 0;
|
|
m_flQuadBendTolerance = 0.05f;
|
|
|
|
m_flDefaultGravityScale = 1.0f;
|
|
m_flDefaultVelAirDrag = 0;
|
|
m_flDefaultExpAirDrag = 0;
|
|
m_flDefaultVelQuadAirDrag = 0;
|
|
m_flDefaultExpQuadAirDrag = 0;
|
|
m_flDefaultVelRodAirDrag = 0;
|
|
m_flDefaultExpRodAirDrag = 0;
|
|
m_flQuadVelocitySmoothRate = 0;
|
|
m_flRodVelocitySmoothRate = 0;
|
|
m_flWindage = 1.0f;
|
|
m_flWindDrag = 0;
|
|
|
|
m_nQuadVelocitySmoothIterations = 0;
|
|
m_nRodVelocitySmoothIterations = 0;
|
|
m_bForceWorldCollisionOnAllNodes = false;
|
|
m_flDefaultGroundFriction = 0;
|
|
m_flDefaultWorldCollisionPenetration = 0;
|
|
m_flAddWorldCollisionRadius = 2.0f;
|
|
|
|
m_nMergePriority = 0;
|
|
m_bNewStyle = true;
|
|
m_bAddStiffnessRods = true;
|
|
m_bRigidEdgeHinges = false;
|
|
}
|
|
|
|
void CreateRodMap( CAuthPhysFxRodMap &rodMap )
|
|
{
|
|
rodMap.Init( GetBoneCount() );
|
|
for ( int nRod = 0; nRod < m_Rods.Count(); ++nRod )
|
|
{
|
|
rodMap.Append( m_Rods[ nRod ].m_nNodes[ 0 ], nRod );
|
|
rodMap.Append( m_Rods[ nRod ].m_nNodes[ 1 ], nRod );
|
|
}
|
|
}
|
|
|
|
void CreateConnMap( CAuthPhysFxRodMap &connMap )
|
|
{
|
|
connMap.Init( GetBoneCount() );
|
|
for ( int nRod = 0; nRod < m_Rods.Count(); ++nRod )
|
|
{
|
|
const uint *n = m_Rods[ nRod ].m_nNodes;
|
|
//if ( filter( n[ 0 ] ) && filter( n[ 1 ] ) )
|
|
{
|
|
connMap.Append( n[ 0 ], n[ 1 ] );
|
|
connMap.Append( n[ 1 ], n[ 0 ] );
|
|
}
|
|
}
|
|
for ( const CQuad &quad : m_Quads )
|
|
{
|
|
for ( int i = 0; i < 3; ++i )
|
|
{
|
|
uint n0 = quad.m_nNodes[ i ];
|
|
for ( int j = i + 1; j < 4; ++j )
|
|
{
|
|
uint n1 = quad.m_nNodes[ j ];
|
|
if ( n0 != n1 )
|
|
{
|
|
connMap.Append( n0, n1 );
|
|
connMap.Append( n1, n0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
CBone *GetDriverBone( int fxBone )
|
|
{
|
|
CAuthPhysFx::CBone *pFxBone;
|
|
for ( ;; )
|
|
{
|
|
AssertDbg( fxBone >= 0 && fxBone < GetBoneCount() );
|
|
pFxBone = GetBone( fxBone );
|
|
|
|
// bones that move by themselves are driving themselves
|
|
if ( pFxBone->m_bSimulated )
|
|
break;
|
|
if ( pFxBone->m_bFreeRotation )
|
|
break;
|
|
|
|
// try to find the true parent that controls all movements of this bone
|
|
if ( pFxBone->m_nParent >= 0 )
|
|
{
|
|
fxBone = pFxBone->m_nParent;
|
|
}
|
|
else if ( pFxBone->m_nFollowParent >= 0 && pFxBone->m_flFollowWeight >= 1.0f )
|
|
{
|
|
fxBone = pFxBone->m_nFollowParent;
|
|
}
|
|
else
|
|
break; // didn't find true parent
|
|
}
|
|
return pFxBone;
|
|
}
|
|
|
|
|
|
public:
|
|
CUtlVector< CQuad > m_Quads;
|
|
CUtlVector< CRod > m_Rods;
|
|
CUtlVector< CSpring > m_Springs;
|
|
CUtlVector< CConstraint > m_Constraints; // springs and constraints between fx particles
|
|
CUtlVector< CCapsule > m_Capsules; // capsules repelling the particles
|
|
CUtlVector< CBone > m_Nodes; // bones, including dynamic particles
|
|
CUtlVector< CCollisionSphere > m_CollisionSpheres;
|
|
CUtlVector< CCollisionPlane > m_CollisionPlanes;
|
|
CUtlVector< FeNodeBase_t > m_PresetNodeBases;
|
|
CUtlVector< FeTaperedCapsuleStretch_t > m_TaperedCapsuleStretches;
|
|
CUtlVector< FeTaperedCapsuleRigid_t > m_TaperedCapsuleRigids;
|
|
CUtlVector< FeSphereRigid_t > m_SphereRigids;
|
|
CUtlVector< CCtrlOffset > m_CtrlOffsets;
|
|
CUtlVector< FeFitInfluence_t > m_FitInfluences;
|
|
float m_flDefaultSurfaceStretch;
|
|
float m_flDefaultThreadStretch;
|
|
|
|
float m_flDefaultGravityScale;
|
|
float m_flDefaultVelAirDrag;
|
|
float m_flDefaultExpAirDrag;
|
|
float m_flDefaultVelQuadAirDrag;
|
|
float m_flDefaultExpQuadAirDrag;
|
|
float m_flDefaultVelRodAirDrag;
|
|
float m_flDefaultExpRodAirDrag;
|
|
float m_flQuadVelocitySmoothRate;
|
|
float m_flRodVelocitySmoothRate;
|
|
float m_flWindage;
|
|
float m_flWindDrag;
|
|
int m_nQuadVelocitySmoothIterations;
|
|
int m_nRodVelocitySmoothIterations;
|
|
float m_flDefaultGroundFriction;
|
|
float m_flDefaultWorldCollisionPenetration; META( MPropertyFriendlyName = "World Velocity Penetration (Source1) 0.0 = velocity goes to 0 on contact" );
|
|
float m_flAddWorldCollisionRadius;
|
|
|
|
float m_flLocalForce;
|
|
float m_flLocalRotation;
|
|
float m_flVolumetricSolveAmount;
|
|
float m_flAddCurvature;
|
|
float m_flQuadBendTolerance; META( MPropertyFriendlyName = "Triangulate Quads bent more than" );
|
|
bool m_bFollowTheLead;
|
|
bool m_bUsePerNodeLocalForceAndRotation;
|
|
bool m_bUninertialRods;
|
|
bool m_bExplicitMasses;
|
|
bool m_bUnitlessDamping;
|
|
bool m_bForceWorldCollisionOnAllNodes; // convenience function: just slam all nodes' world collision flag, useful for quick testing
|
|
int m_nMergePriority;
|
|
bool m_bNewStyle;
|
|
|
|
bool m_bCanCollideWithWorldMeshes;
|
|
bool m_bCanCollideWithWorldCapsulesAndSpheres;
|
|
bool m_bCanCollideWithWorldHulls;
|
|
|
|
bool m_bAddStiffnessRods;
|
|
bool m_bRigidEdgeHinges;
|
|
};
|
|
|
|
|
|
|
|
|
|
// typedef CAuthPhysFx::CQuad CAuthPhysFxQuad;
|
|
// typedef CAuthPhysFx::CRod CAuthPhysFxRod;
|
|
// typedef CAuthPhysFx::CSpring CAuthPhysFxSpring ;
|
|
// typedef CAuthPhysFx::CConstraint CAuthPhysFxConstraint ;
|
|
// typedef CAuthPhysFx::CCapsule CAuthPhysFxCapsule ;
|
|
// typedef CAuthPhysFx::CBone CAuthPhysFxBone ;
|
|
// typedef CAuthPhysFx::CCollisionSphere CAuthPhysFxCollisionSphere ;
|
|
// typedef CAuthPhysFx::CCollisionPlane CAuthPhysFxCollisionPlane ;
|
|
|
|
|
|
class CBoneParseParams
|
|
{
|
|
public:
|
|
CBoneParseParams( KeyValues *pSubKey, int nCompatibilityMode );
|
|
typedef CAuthPhysFx::CBone CBone;
|
|
void ApplyDefaultParams( CBone &node );
|
|
bool ApplyDotaFlags( CAuthPhysFx::CBone &bone, const char *pszParms );
|
|
public:
|
|
float m_flWorldFriction;
|
|
float m_flAnimationForceAttraction;
|
|
float m_flAnimationVertexAttraction;
|
|
float m_flDamping;
|
|
float m_flFixedPointDamping;
|
|
float m_flSpringStretchiness;
|
|
float m_flRelaxationFactor;
|
|
float m_flStretchForce;
|
|
float m_flStructSpringConstant;
|
|
float m_flStructSpringDamping;
|
|
float m_flGravityScale;
|
|
public:
|
|
bool m_bPrevColumnParent;
|
|
Vector m_vOffset; // offset relative to the animation transform
|
|
|
|
const char *m_pBonePrefix;
|
|
int m_nNominalColumnCount;
|
|
int m_nRowCount;
|
|
int m_nVirtColumnCount;
|
|
|
|
float m_flFollowRootBegin;
|
|
float m_flFollowRootEnd;
|
|
bool m_bIsRopeS1;
|
|
int m_nBoneIndex;
|
|
|
|
typedef CAuthPhysFx::CCollisionPlane CCollisionPlane;
|
|
typedef CAuthPhysFx::CCollisionSphere CCollisionSphere;
|
|
CUtlVector< CCollisionSphere > *m_pCollisionSpheres;
|
|
CUtlVector< CCollisionPlane > *m_pCollisionPlanes;
|
|
};
|
|
|
|
|
|
class CNodeIdx
|
|
{
|
|
public:
|
|
CNodeIdx( uint nBase, uint nRows, uint nColumns ): m_nBase( nBase ), m_nRows( nRows ), m_nColumns( nColumns ) {}
|
|
uint operator() ( uint nRow, uint nColumn )const
|
|
{
|
|
AssertDbg( nRow < m_nRows && nColumn < m_nColumns );
|
|
return m_nBase + nRow * m_nColumns + nColumn;
|
|
}
|
|
protected:
|
|
uint m_nBase;
|
|
uint m_nRows;
|
|
uint m_nColumns;
|
|
};
|
|
|
|
|
|
|
|
class CAuthClothParser: public CAuthPhysFx
|
|
{
|
|
public:
|
|
void SetBones( CPhysModelSource &physicsModel );
|
|
bool Parse( KeyValues *kv );
|
|
|
|
bool ParseDefaults( KeyValues *pSubkey );
|
|
bool ParsePiece( KeyValues *pSubkey );
|
|
void ParseExplicitDefinitions( KeyValues *pSubkey, CBoneParseParams &parseParm );
|
|
bool ParseLegacyDotaNodeGrid( KeyValues *pSubKey, CBoneParseParams &parseParms, const CNodeIdx &nodeIdx );
|
|
bool CreateLegacyDotaRodGrid( CBoneParseParams &parseParms, const CNodeIdx &nodeIdx, int );
|
|
bool TieModelToNewNode( int nNewNode );
|
|
|
|
void ReconstructHierarchy( );
|
|
|
|
void ParseExplicitNode( KeyValues *kv, CBoneParseParams &parseParm );
|
|
void ParseExplicitElem( KeyValues *kv, CBoneParseParams &parseParm );
|
|
template <typename T>
|
|
void ParseExplicitColl( KeyValues *kv, CBoneParseParams &parseParm, CUtlVector< T > &collArray );
|
|
|
|
int FindNodeByName( const char *pName );
|
|
void AddDotaRod( uint nNode0, uint nNode1, CBoneParseParams &parseParm );
|
|
protected:
|
|
int m_nDefaultCompatibilityMode;
|
|
int m_nCompatibilityMode;
|
|
|
|
CUtlStringMap< int > m_NodeNameMap;
|
|
int m_nNodeNameMapNodes;
|
|
|
|
CUtlStringMap< int > m_BoneToIndex;
|
|
CUtlVector< int > m_BoneToParent;
|
|
CUtlVector< int > m_ModelBoneToNode;
|
|
CUtlVector< CTransform >m_BoneTransforms;
|
|
};
|
|
|
|
|
|
|
|
|
|
struct AuthHullSimplificationParams_t: public RnHullSimplificationParams_t
|
|
{
|
|
float m_flMinSkinWeight;
|
|
bool m_bIncludSubtree;
|
|
|
|
AuthHullSimplificationParams_t( )
|
|
{
|
|
m_bIncludSubtree = false;
|
|
m_flMinSkinWeight = 0.5f;
|
|
}
|
|
};
|
|
|
|
|
|
template <typename TBitVec>
|
|
void CAuthPhysFx::AlignNodes( const TBitVec &useNodes )
|
|
{
|
|
CUtlVectorAligned< CFeModelBuilder::BuildNode_t > nodes;
|
|
nodes.SetCount( m_Nodes.Count() );
|
|
for ( int nNode = 0; nNode < nodes.Count(); ++nNode )
|
|
{
|
|
nodes[ nNode ] = m_Nodes[ nNode ].AsBuildNode();
|
|
}
|
|
CUtlVector< CFeModelBuilder::BuildElem_t > elems;
|
|
elems.EnsureCapacity( m_Quads.Count() );
|
|
for ( int nQuad = 0; nQuad < m_Quads.Count(); ++nQuad )
|
|
{
|
|
if ( m_Quads[ nQuad ].UsesAnyNode( useNodes ) )
|
|
{
|
|
elems.AddToTail( m_Quads[ nQuad ].AsBuildElem() );
|
|
}
|
|
}
|
|
if ( !elems.IsEmpty() )
|
|
{
|
|
CUtlVector < FeNodeBase_t > presets, bases;
|
|
CUtlVectorOfPointers< CUtlSortVector< int > > neighbors;
|
|
CFeModelBuilder::BuildNodeBases( nodes, elems, presets, bases, neighbors );
|
|
for ( const FeNodeBase_t &base : bases )
|
|
{
|
|
if ( useNodes[ base.nNode ] )
|
|
{
|
|
CBone &bone = m_Nodes[ base.nNode ];
|
|
bone.m_Transform.m_orientation = bone.m_Transform.m_orientation * Conjugate( base.qAdjust ); // qAdjust = ~predict * old_orient; so, to make qAdjust = idenity we need to make new_orient = predict = old_orient * ~(~predict * old_orient )
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|