summaryrefslogtreecommitdiff
path: root/utils/hlmv/physmesh.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/hlmv/physmesh.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/hlmv/physmesh.cpp')
-rw-r--r--utils/hlmv/physmesh.cpp619
1 files changed, 619 insertions, 0 deletions
diff --git a/utils/hlmv/physmesh.cpp b/utils/hlmv/physmesh.cpp
new file mode 100644
index 0000000..957bd8f
--- /dev/null
+++ b/utils/hlmv/physmesh.cpp
@@ -0,0 +1,619 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+/***
+*
+* Copyright (c) 1998, Valve LLC. All rights reserved.
+*
+* This product contains software technology licensed from Id
+* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
+* All Rights Reserved.
+*
+****/
+
+#include "filesystem.h"
+#include "vphysics/constraints.h"
+#include "phyfile.h"
+#include "physdll.h"
+#include "physmesh.h"
+#include "mathlib/mathlib.h"
+#include <stddef.h>
+#include "utlvector.h"
+#include "commonmacros.h"
+#include "studiomodel.h"
+#include "tier1/strtools.h"
+#include "bone_setup.h"
+#include "fmtstr.h"
+#include "vcollide_parse.h"
+
+int FindPhysprop( const char *pPropname );
+
+bool LoadPhysicsProperties( void );
+extern int FindBoneIndex( CStudioHdr *pstudiohdr, const char *pName );
+
+
+struct collisionpair_t
+{
+ int object0;
+ int object1;
+ collisionpair_t *pNext;
+};
+
+class CStudioPhysics : public IStudioPhysics
+{
+public:
+ CStudioPhysics( void )
+ {
+ m_pList = NULL;
+ m_listCount = 0;
+ m_mass = 0;
+ m_noselfCollisions = false;
+ m_pCollisionPairs = NULL;
+ memset( &m_edit, 0, sizeof(editparams_t) );
+ }
+
+ ~CStudioPhysics( void )
+ {
+ if ( physcollision )
+ {
+ for ( int i = 0; i < m_listCount; i++ )
+ {
+ physcollision->DestroyDebugMesh( m_pList[i].m_vertCount, m_pList[i].m_pVerts );
+ physcollision->DestroyQueryModel( m_pList[i].m_pCollisionModel );
+ }
+ }
+ delete[] m_pList;
+ }
+
+ int Count( void )
+ {
+ return m_listCount;
+ }
+
+ CPhysmesh *GetMesh( int index )
+ {
+ if ( index < m_listCount )
+ return m_pList + index;
+
+ return NULL;
+ }
+
+ float GetMass( void ) { return m_mass; }
+
+ void AddCollisionPair( int index0, int index1 )
+ {
+ collisionpair_t *pPair = new collisionpair_t;
+ pPair->object0 = index0;
+ pPair->object1 = index1;
+ pPair->pNext = m_pCollisionPairs;
+ m_pCollisionPairs = pPair;
+
+ }
+
+ void Load( MDLHandle_t handle );
+ char *DumpQC( void );
+ void ParseKeydata( void );
+
+ vcollide_t *GetVCollide()
+ {
+ return g_pMDLCache->GetVCollide( m_MDLHandle );
+ }
+
+ CPhysmesh *m_pList;
+ MDLHandle_t m_MDLHandle;
+ int m_listCount;
+
+ float m_mass;
+ editparams_t m_edit;
+ bool m_noselfCollisions;
+ collisionpair_t *m_pCollisionPairs;
+};
+
+
+void CPhysmesh::Clear( void )
+{
+ memset( this, 0, sizeof(*this) );
+ memset( &m_constraint, 0, sizeof(m_constraint) );
+ m_constraint.parentIndex = -1;
+ m_constraint.childIndex = -1;
+}
+
+
+IStudioPhysics *LoadPhysics( MDLHandle_t mdlHandle )
+{
+ CStudioPhysics *pPhysics = new CStudioPhysics;
+ pPhysics->Load( mdlHandle );
+ return pPhysics;
+}
+
+void DestroyPhysics( IStudioPhysics *pStudioPhysics )
+{
+ CStudioPhysics *pPhysics = static_cast<CStudioPhysics*>( pStudioPhysics );
+ if ( pPhysics )
+ {
+ delete pPhysics;
+ }
+}
+
+void CStudioPhysics::Load( MDLHandle_t mdlHandle )
+{
+ m_MDLHandle = mdlHandle;
+
+ LoadPhysicsProperties();
+
+ vcollide_t *pVCollide = GetVCollide( );
+ if ( !pVCollide )
+ {
+ m_pList = NULL;
+ m_listCount = 0;
+ return;
+ }
+
+ m_pList = new CPhysmesh[pVCollide->solidCount];
+ m_listCount = pVCollide->solidCount;
+
+ int i;
+
+ for ( i = 0; i < pVCollide->solidCount; i++ )
+ {
+ m_pList[i].Clear();
+ m_pList[i].m_vertCount = physcollision->CreateDebugMesh( pVCollide->solids[i], &m_pList[i].m_pVerts );
+ m_pList[i].m_pCollisionModel = physcollision->CreateQueryModel( pVCollide->solids[i] );
+ }
+
+ ParseKeydata();
+
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( mdlHandle ), g_pMDLCache );
+ for ( i = 0; i < pVCollide->solidCount; i++ )
+ {
+ CPhysmesh *pmesh = m_pList + i;
+ int boneIndex = FindBoneIndex( &studioHdr, pmesh->m_boneName );
+ if ( boneIndex < 0 )
+ continue;
+
+ if ( pmesh->m_constraint.parentIndex >= 0 )
+ {
+ CPhysmesh *pparent = m_pList + pmesh->m_constraint.parentIndex;
+ int parentIndex = FindBoneIndex( &studioHdr, pparent->m_boneName );
+ Studio_CalcBoneToBoneTransform( &studioHdr, boneIndex, parentIndex, pmesh->m_matrix );
+ }
+ else
+ {
+ MatrixInvert( studioHdr.pBone(boneIndex)->poseToBone, pmesh->m_matrix );
+ }
+ }
+
+ // doesn't have a root bone? Make it the first bone
+ if ( !m_edit.rootName[0] )
+ {
+ strcpy( m_edit.rootName, m_pList[0].m_boneName );
+ }
+}
+
+
+class CEditParse : public IVPhysicsKeyHandler
+{
+public:
+ virtual void ParseKeyValue( void *pCustom, const char *pKey, const char *pValue )
+ {
+ editparams_t *pEdit = (editparams_t *)pCustom;
+ if ( !strcmpi( pKey, "rootname" ) )
+ {
+ strncpy( pEdit->rootName, pValue, sizeof(pEdit->rootName) );
+ }
+ else if ( !strcmpi( pKey, "totalmass" ) )
+ {
+ pEdit->totalMass = atof( pValue );
+ }
+ else if ( !strcmpi( pKey, "concave" ) )
+ {
+ pEdit->concave = atoi( pValue );
+ }
+ else if ( !strcmpi( pKey, "jointmerge" ) )
+ {
+ char tmp[1024];
+ char parentName[512], childName[512];
+ Q_strncpy( tmp, pValue, 1024 );
+ char *pWord = strtok( tmp, "," );
+ Q_strncpy( parentName, pWord, sizeof(parentName) );
+ pWord = strtok( NULL, "," );
+ Q_strncpy( childName, pWord, sizeof(childName) );
+ if ( pEdit->mergeCount < ARRAYSIZE(pEdit->mergeList) )
+ {
+ merge_t *pMerge = &pEdit->mergeList[pEdit->mergeCount];
+ pEdit->mergeCount++;
+ pMerge->parent = g_pStudioModel->FindBone(parentName);
+ pMerge->child = g_pStudioModel->FindBone(childName);
+ }
+ }
+ }
+ virtual void SetDefaults( void *pCustom )
+ {
+ editparams_t *pEdit = (editparams_t *)pCustom;
+ memset( pEdit, 0, sizeof(*pEdit) );
+ }
+};
+
+class CRagdollCollisionRulesParse : public IVPhysicsKeyHandler
+{
+public:
+ CRagdollCollisionRulesParse( CStudioPhysics *pStudio ) : m_pStudio(pStudio)
+ {
+ pStudio->m_noselfCollisions = false;
+ }
+
+ virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue )
+ {
+ if ( !strcmpi( pKey, "selfcollisions" ) )
+ {
+ // keys disabled by default
+ Assert( atoi(pValue) == 0 );
+ m_pStudio->m_noselfCollisions = true;
+ }
+ else if ( !strcmpi( pKey, "collisionpair" ) )
+ {
+ if ( !m_pStudio->m_noselfCollisions )
+ {
+ char tmp[1024];
+ Q_strncpy( tmp, pValue, 1024 );
+ char *pWord = strtok( tmp, "," );
+ int index0 = atoi(pWord);
+ pWord = strtok( NULL, "," );
+ int index1 = atoi(pWord);
+ m_pStudio->AddCollisionPair( index0, index1 );
+ }
+ else
+ {
+ Assert(0);
+ }
+ }
+ }
+ virtual void SetDefaults( void *pData ) {}
+
+private:
+ CStudioPhysics *m_pStudio;
+};
+
+class CSolidParse : public IVPhysicsKeyHandler
+{
+public:
+ virtual void ParseKeyValue( void *pCustom, const char *pKey, const char *pValue )
+ {
+ hlmvsolid_t *pSolid = (hlmvsolid_t *)pCustom;
+ if ( !strcmpi( pKey, "massbias" ) )
+ {
+ pSolid->massBias = atof( pValue );
+ }
+ else
+ {
+ printf("Bad key %s!!\n", pKey);
+ }
+ }
+ virtual void SetDefaults( void *pCustom )
+ {
+ hlmvsolid_t *pSolid = (hlmvsolid_t *)pCustom;
+ pSolid->massBias = 1.0;
+ }
+};
+
+void CStudioPhysics::ParseKeydata( void )
+{
+ IVPhysicsKeyParser *pParser = physcollision->VPhysicsKeyParserCreate( GetVCollide()->pKeyValues );
+
+ while ( !pParser->Finished() )
+ {
+ const char *pBlock = pParser->GetCurrentBlockName();
+ if ( !stricmp( pBlock, "solid" ) )
+ {
+ hlmvsolid_t solid;
+ CSolidParse solidParse;
+
+ pParser->ParseSolid( &solid, &solidParse );
+ solid.surfacePropIndex = FindPhysprop( solid.surfaceprop );
+
+ if ( solid.index >= 0 && solid.index < m_listCount )
+ {
+ strcpy( m_pList[solid.index].m_boneName, solid.name );
+ memcpy( &m_pList[solid.index].m_solid, &solid, sizeof(solid) );
+ }
+ }
+ else if ( !stricmp( pBlock, "ragdollconstraint" ) )
+ {
+ constraint_ragdollparams_t constraint;
+ pParser->ParseRagdollConstraint( &constraint, NULL );
+ if ( constraint.childIndex >= 0 && constraint.childIndex < m_listCount )
+ {
+ // In the editor / qc these show up as 5X so that 1.0 is the default
+ constraint.axes[0].torque *= 5;
+ constraint.axes[1].torque *= 5;
+ constraint.axes[2].torque *= 5;
+ m_pList[constraint.childIndex].m_constraint = constraint;
+ }
+ }
+ else if ( !stricmp( pBlock, "editparams" ) )
+ {
+ CEditParse editParse;
+ pParser->ParseCustom( &m_edit, &editParse );
+ m_mass = m_edit.totalMass;
+ }
+ else if ( !strcmpi( pBlock, "collisionrules" ) )
+ {
+ CRagdollCollisionRulesParse rules(this);
+ pParser->ParseCustom( NULL, &rules );
+ }
+ else
+ {
+ pParser->SkipBlock();
+ }
+ }
+ physcollision->VPhysicsKeyParserDestroy( pParser );
+}
+
+
+int FindPhysprop( const char *pPropname )
+{
+ if ( physprop )
+ {
+ int count = physprop->SurfacePropCount();
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( !strcmpi( pPropname, physprop->GetPropName(i) ) )
+ return i;
+ }
+ }
+ return 0;
+}
+
+
+
+class CTextBuffer
+{
+public:
+ CTextBuffer( void ) {}
+ ~CTextBuffer( void ) {}
+
+ inline int GetSize( void ) { return m_buffer.Size(); }
+ inline char *GetData( void ) { return m_buffer.Base(); }
+
+ void WriteText( const char *pText )
+ {
+ int len = strlen( pText );
+ CopyData( pText, len );
+ }
+
+ void Terminate( void ) { CopyData( "\0", 1 ); }
+
+ void CopyData( const char *pData, int len )
+ {
+ int offset = m_buffer.AddMultipleToTail( len );
+ memcpy( m_buffer.Base() + offset, pData, len );
+ }
+
+private:
+ CUtlVector<char> m_buffer;
+};
+
+
+struct physdefaults_t
+{
+ int surfacePropIndex;
+ float inertia;
+ float damping;
+ float rotdamping;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Nasty little routine (that was easy to code) to find the most common
+// value in an array of structs containing that as a member
+// Input : *pStructArray - pointer to head of struct array
+// arrayCount - number of elements in the array
+// structSize - size of each element
+// fieldOffset - offset to the float we're finding
+// Output : static T - most common value
+//-----------------------------------------------------------------------------
+template< class T >
+static T FindCommonValue( void *pStructArray, int arrayCount, int structSize, int fieldOffset )
+{
+ int maxCount = 0;
+ T maxVal = 0;
+
+ // BUGBUG: This is O(n^2), but n is really small
+ for ( int i = 0; i < arrayCount; i++ )
+ {
+ // current = struct[i].offset
+ T current = *(T *)((char *)pStructArray + (i*structSize) + fieldOffset);
+ int currentCount = 0;
+
+ // if everything is set to the default, this is almost O(n)
+ if ( current == maxVal )
+ continue;
+
+ for ( int j = 0; j < arrayCount; j++ )
+ {
+ // value = struct[j].offset
+ T value = *(T *)((char *)pStructArray + (j*structSize) + fieldOffset);
+ if ( value == current )
+ currentCount++;
+ }
+
+ if ( currentCount > maxCount )
+ {
+ maxVal = current;
+ maxCount = currentCount;
+ }
+ }
+
+ return maxVal;
+}
+
+static void CalcDefaultProperties( CPhysmesh *pList, int listCount, physdefaults_t &defs )
+{
+ defs.surfacePropIndex = FindCommonValue<int>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.surfacePropIndex) );
+ defs.inertia = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.inertia) );
+ defs.damping = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.damping) );
+ defs.rotdamping = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.rotdamping) );
+}
+
+static void DumpModelProperties( CTextBuffer &out, float mass, physdefaults_t &defs )
+{
+ char tmpbuf[1024];
+ sprintf( tmpbuf, "\t$mass %.1f\r\n", mass );
+ out.WriteText( tmpbuf );
+ sprintf( tmpbuf, "\t$inertia %.2f\r\n", defs.inertia );
+ out.WriteText( tmpbuf );
+ sprintf( tmpbuf, "\t$damping %.2f\r\n", defs.damping );
+ out.WriteText( tmpbuf );
+ sprintf( tmpbuf, "\t$rotdamping %.2f\r\n", defs.rotdamping );
+ out.WriteText( tmpbuf );
+}
+
+char *CStudioPhysics::DumpQC( void )
+{
+ if ( !m_listCount )
+ return NULL;
+
+ CTextBuffer out;
+ physdefaults_t defs;
+
+ CalcDefaultProperties( m_pList, m_listCount, defs );
+
+ if ( m_listCount == 1 )
+ {
+ out.WriteText( "$collisionmodel ragdoll {\r\n\r\n" );
+ if ( m_edit.concave )
+ {
+ out.WriteText( "\t$concave\r\n" );
+ }
+ DumpModelProperties( out, m_mass, defs );
+ }
+ else
+ {
+ int i;
+
+ out.WriteText( "$collisionjoints ragdoll {\r\n\r\n" );
+ DumpModelProperties( out, m_mass, defs );
+
+ // write out the root bone
+ if ( m_edit.rootName[0] )
+ {
+ char tmp[128];
+ sprintf( tmp, "\t$rootbone \"%s\"\r\n", m_edit.rootName );
+ out.WriteText( tmp );
+ }
+
+ for ( i = 0; i < m_edit.mergeCount; i++ )
+ {
+ char tmp[1024];
+ if ( m_edit.mergeList[i].parent >= 0 && m_edit.mergeList[i].child >= 0 )
+ {
+ char const *pParentName = g_pStudioModel->GetStudioHdr()->pBone(m_edit.mergeList[i].parent)->pszName();
+ char const *pChildName = g_pStudioModel->GetStudioHdr()->pBone(m_edit.mergeList[i].child)->pszName();
+ Q_snprintf( tmp, sizeof(tmp), "\t$jointmerge \"%s\" \"%s\"\r\n", pParentName, pChildName );
+ out.WriteText( tmp );
+ }
+ }
+ char tmpbuf[1024];
+ for ( i = 0; i < m_listCount; i++ )
+ {
+ CPhysmesh *pmesh = m_pList + i;
+ char jointname[256];
+ sprintf( jointname, "\"%s\"", pmesh->m_boneName );
+ if ( pmesh->m_solid.massBias != 1.0 )
+ {
+ sprintf( tmpbuf, "\t$jointmassbias %s %.2f\r\n", jointname, pmesh->m_solid.massBias );
+ out.WriteText( tmpbuf );
+ }
+ if ( pmesh->m_solid.params.inertia != defs.inertia )
+ {
+ sprintf( tmpbuf, "\t$jointinertia %s %.2f\r\n", jointname, pmesh->m_solid.params.inertia );
+ out.WriteText( tmpbuf );
+ }
+ if ( pmesh->m_solid.params.damping != defs.damping )
+ {
+ sprintf( tmpbuf, "\t$jointdamping %s %.2f\r\n", jointname, pmesh->m_solid.params.damping );
+ out.WriteText( tmpbuf );
+ }
+ if ( pmesh->m_solid.params.rotdamping != defs.rotdamping )
+ {
+ sprintf( tmpbuf, "\t$jointrotdamping %s %.2f\r\n", jointname, pmesh->m_solid.params.rotdamping );
+ out.WriteText( tmpbuf );
+ }
+
+ if ( pmesh->m_constraint.parentIndex >= 0 )
+ {
+ for ( int j = 0; j < 3; j++ )
+ {
+ char *pAxis[] = { "x", "y", "z" };
+ sprintf( tmpbuf, "\t$jointconstrain %s %s limit %.2f %.2f %.2f\r\n", jointname, pAxis[j], pmesh->m_constraint.axes[j].minRotation, pmesh->m_constraint.axes[j].maxRotation, pmesh->m_constraint.axes[j].torque );
+ out.WriteText( tmpbuf );
+ }
+ }
+ if ( i != m_listCount-1 )
+ {
+ out.WriteText( "\r\n" );
+ }
+ }
+ }
+
+ if ( m_noselfCollisions )
+ {
+ out.WriteText( "\t$noselfcollisions\r\n" );
+ }
+ else if ( m_pCollisionPairs )
+ {
+ collisionpair_t *pPair = m_pCollisionPairs;
+ out.WriteText("\r\n");
+ while ( pPair )
+ {
+ out.WriteText( CFmtStr( "\t$jointcollide %s %s\r\n", m_pList[pPair->object0].m_boneName, m_pList[pPair->object1].m_boneName ) );
+ pPair = pPair->pNext;
+ }
+ }
+ out.WriteText( "}\r\n" );
+
+ // only need the pose for ragdolls
+ if ( m_listCount != 1 )
+ {
+ out.WriteText( "$sequence ragdoll \t\t\"ragdoll_pose\" \t\tFPS 30 \t\tactivity ACT_DIERAGDOLL 1\r\n" );
+ }
+
+ out.Terminate();
+
+ if ( out.GetSize() )
+ {
+ char *pOutput = new char[out.GetSize()];
+ memcpy( pOutput, out.GetData(), out.GetSize() );
+ return pOutput;
+ }
+
+ return NULL;
+}
+
+static const char *pMaterialFilename = "scripts/surfaceproperties.txt";
+
+bool LoadPhysicsProperties( void )
+{
+ // already loaded
+ if ( physprop->SurfacePropCount() )
+ return false;
+
+ FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb" );
+ if ( fp == FILESYSTEM_INVALID_HANDLE )
+ return false;
+
+ int len = g_pFileSystem->Size( fp );
+
+ char *pText = new char[len+1];
+ g_pFileSystem->Read( pText, len, fp );
+ g_pFileSystem->Close( fp );
+ pText[len]=0;
+
+ physprop->ParseSurfaceData( pMaterialFilename, pText );
+
+ delete[] pText;
+ return true;
+}