csgo-2018-source/mdlobjects/dmeskinner.cpp

874 lines
20 KiB
C++
Raw Permalink Normal View History

2021-07-25 12:11:47 +08:00
//====== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. =======
//
// DmeEyeball
//
//=============================================================================
// Valve includes
#include "datamodel/dmelementfactoryhelper.h"
#include "mdlobjects/dmeskinner.h"
#include "movieobjects/dmedag.h"
#include "movieobjects/dmemodel.h"
#include "mathlib/mathlib.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Class for a unit Super Ellipsoid at the origin
//-----------------------------------------------------------------------------
class CSuperEllipsoid
{
public:
CSuperEllipsoid(
double powerXZ = 1.0,
double powerY = 1.0 );
bool IntersectFromOriginThroughPoint(
Vector point,
double dDistance ) const;
// Evaluate for root finding: [ | x | ^ ( 2 / powerXZ ) + | z | ^ ( 2 / powerXZ ) ] ^ ( powerXZ / powerY ) + | y | ^ powerY - 1 = 0
double RootEvaluate( const Vector &p ) const;
protected:
static void EvaluateRayFromOrigin(
const Vector &rayDirection,
double dDistance,
Vector &pointOnRay );
static bool IntersectUnitBoxFromOrigin(
const Vector &rayDirection,
double &dMin,
double &dMax );
void SolveHit(
double v0,
const Vector &intPoint0,
double v1,
const Vector &intPoint1,
Vector &intPoint ) const;
static const double s_dEpsilon;
double m_powerXZ;
double m_powerY;
int m_nMaxIterations;
};
//-----------------------------------------------------------------------------
// Expose this class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeSkinnerVolume, CDmeSkinnerVolume );
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDmeSkinnerVolume::OnConstruction()
{
m_mMatrix.Init( this, "matrix" );
m_flStrength.Init( this, "strength", 1.0f );
m_flFalloff.Init( this, "falloff", 0.0f );
m_nFalloffType.Init( this, "falloffType", FT_LINEAR );
m_flPowerY.InitAndSet( this, "powerY", 1.0f );
m_flPowerXZ.InitAndSet( this, "powerXZ", 1.0f );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDmeSkinnerVolume::OnDestruction()
{
}
//-----------------------------------------------------------------------------
// Expose this class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeSkinnerJoint, CDmeSkinnerJoint );
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDmeSkinnerJoint::OnConstruction()
{
m_mBindWorldMatrix.Init( this, "bindWorldMatrix" );
m_eVolumeList.Init( this, "volumeList" );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDmeSkinnerJoint::OnDestruction()
{
}
//-----------------------------------------------------------------------------
// Expose this class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeSkinner, CDmeSkinner );
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDmeSkinner::OnConstruction()
{
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDmeSkinner::OnDestruction()
{
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CDmeSkinner::ReskinMeshes( CDmeModel *pDmeModel, int nJointPerVertexCount )
{
CUtlStack< CDmeDag * > depthFirstStack;
depthFirstStack.Push( pDmeModel );
CDmeDag *pDmeDag;
CDmeMesh *pDmeMesh;
while ( depthFirstStack.Count() > 0 )
{
depthFirstStack.Pop( pDmeDag );
if ( !pDmeDag )
continue;
pDmeMesh = CastElement< CDmeMesh >( pDmeDag->GetShape() );
if ( pDmeMesh )
{
ReskinMesh( pDmeModel, pDmeMesh, nJointPerVertexCount );
}
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
{
depthFirstStack.Push( pDmeDag->GetChild( i ) );
}
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
class CJointWeight
{
public:
int m_nJointDataIndex;
float m_flWeight;
};
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
class CVertexWeight
{
public:
CVertexWeight( int nWeightsPerVertex )
: m_nWeightPerVertex( MAX( 0, nWeightsPerVertex ) )
{
}
int m_nWeightPerVertex;
CUtlVector< CJointWeight > m_weights; // Sorted Weight List
static int SortWeights( const CJointWeight *pLhs, const CJointWeight *pRhs );
int Count() const {
return MIN( m_nWeightPerVertex, m_weights.Count() );
}
void Reset()
{
m_weights.Purge();
}
void AddWeight(
int nJointDataIndex,
float flWeight );
void Sort();
float ComputeTotalWeight() const
{
float flTotalWeight = 0.0f;
const int nWeightCount = Count();
for ( int i = 0; i < nWeightCount; ++i )
{
flTotalWeight += m_weights[ i ].m_flWeight;
}
return flTotalWeight;
}
float GetWeight( int nWeightIndex ) const
{
if ( nWeightIndex < m_weights.Count() )
return m_weights[ nWeightIndex ].m_flWeight;
return 0.0f;
}
int GetJointIndex( int nWeightIndex ) const
{
if ( nWeightIndex < m_weights.Count() )
return m_weights[ nWeightIndex ].m_nJointDataIndex;
return 0;
}
};
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CVertexWeight::AddWeight(
int nJointDataIndex,
float flWeight )
{
CJointWeight &jointWeight = m_weights[ m_weights.AddToTail() ];
jointWeight.m_nJointDataIndex = nJointDataIndex;
jointWeight.m_flWeight = flWeight;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CVertexWeight::Sort()
{
m_weights.Sort( SortWeights );
}
//-----------------------------------------------------------------------------
// Sort them from highest to lowest
//-----------------------------------------------------------------------------
int CVertexWeight::SortWeights( const CJointWeight *pLhs, const CJointWeight *pRhs )
{
if ( pLhs->m_flWeight < pRhs->m_flWeight )
return 1;
if ( pLhs->m_flWeight > pRhs->m_flWeight )
return -1;
return 0;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CDmeSkinner::ReskinMesh( CDmeModel *pDmeModel, CDmeMesh *pDmeMesh, int nJointPerVertexCount )
{
if ( !pDmeMesh )
return false;
CDmeDag *pDmeDagMeshParent = FindReferringElement< CDmeDag >( pDmeMesh, "shape" );
if ( !pDmeDagMeshParent )
return false;
CDmeVertexData *pDmeVertexData = pDmeMesh->GetBindBaseState();
if ( !pDmeVertexData )
{
Error( "CDmeSkinner: No \"bind\" base state on DmeMesh \"%s\"\n", pDmeMesh->GetName() );
return false;
}
FieldIndex_t nJointWeightsField = -1;
FieldIndex_t nJointIndicesField = -1;
pDmeVertexData->CreateJointWeightsAndIndices( nJointPerVertexCount, &nJointWeightsField, &nJointIndicesField );
if ( nJointWeightsField < 0 || nJointIndicesField < 0 )
{
Error( "CDmeSkinner: Couldn't create jointWeights & jointIndices fields on DmeMesh \"%s\"\n", pDmeMesh->GetName() );
return false;
}
pDmeVertexData->RemoveAllVertexData( nJointWeightsField );
pDmeVertexData->RemoveAllVertexData( nJointIndicesField );
const CUtlVector< Vector > &positions = pDmeVertexData->GetPositionData();
CUtlVector< float > jointWeights;
CUtlVector< int > jointIndices;
struct s_VolumeStruct
{
CDmeSkinnerVolume *m_pDmeSkinnerVolume;
VMatrix m_mMat;
};
struct s_JointStruct
{
s_JointStruct()
: m_pDmeSkinnerJoint( NULL )
, m_nJointIndex( -1 )
{
}
s_JointStruct( const s_JointStruct &rhs )
{
m_pDmeSkinnerJoint = rhs.m_pDmeSkinnerJoint;
m_nJointIndex = rhs.m_nJointIndex;
m_volumes.CopyArray( rhs.m_volumes.Base(), m_volumes.Count() );
}
CDmeSkinnerJoint *m_pDmeSkinnerJoint;
int m_nJointIndex;
CUtlVector< s_VolumeStruct > m_volumes;
};
CUtlVector< s_JointStruct > volumeJointList;
{
matrix3x4_t gwm;
pDmeDagMeshParent->GetAbsTransform( gwm );
VMatrix gwvm;
gwvm.CopyFrom3x4( gwm );
VMatrix vm; // Volume matrix
VMatrix vmi; // Volume matrix inverse
matrix3x4_t m0;
matrix3x4_t m1;
CUtlStack< CDmeDag * > depthFirstStack;
depthFirstStack.Push( this );
CDmeDag *pDmeDag;
CDmeSkinnerJoint *pDmeSkinnerJoint;
CDmeSkinnerVolume *pDmeSkinnerVolume;
while ( depthFirstStack.Count() > 0 )
{
depthFirstStack.Pop( pDmeDag );
if ( !pDmeDag )
continue;
pDmeSkinnerJoint = CastElement< CDmeSkinnerJoint >( pDmeDag );
if ( pDmeSkinnerJoint )
{
if ( pDmeSkinnerJoint->m_eVolumeList.Count() > 0 )
{
int nJointIndex = pDmeModel->GetJointIndex( pDmeSkinnerJoint->GetName() );
if ( nJointIndex < 0 ) // This joint isn't in the joint list as well as it's children, so ignore
{
Warning( "DmeSkinner: Skinner Joint %s isn't in DmeModel %s.jointList, ignoring it and all children\n", pDmeSkinnerJoint->GetName(), pDmeModel->GetName() );
continue;
}
s_JointStruct &js = volumeJointList[ volumeJointList.AddToTail() ];
js.m_pDmeSkinnerJoint = pDmeSkinnerJoint;
js.m_nJointIndex = nJointIndex;
for ( int i = 0; i < pDmeSkinnerJoint->m_eVolumeList.Count(); ++i )
{
pDmeSkinnerVolume = CastElement< CDmeSkinnerVolume >( pDmeSkinnerJoint->m_eVolumeList[ i ] );
if ( !pDmeSkinnerVolume )
continue;
s_VolumeStruct &vs = js.m_volumes[ js.m_volumes.AddToTail() ];
vs.m_pDmeSkinnerVolume = pDmeSkinnerVolume;
pDmeSkinnerVolume->m_mMatrix.Get().MatrixMul( pDmeSkinnerJoint->m_mBindWorldMatrix.Get(), vm );
vm.InverseGeneral( vmi );
vmi.MatrixMul( gwvm, vs.m_mMat );
vs.m_mMat = vs.m_mMat.Transpose();
}
}
}
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
{
depthFirstStack.Push( pDmeDag->GetChild( i ) );
}
}
}
Vector v;
Vector sv;
CVertexWeight vw( nJointPerVertexCount );
for ( int i = 0; i < positions.Count(); ++i )
{
vw.Reset();
const Vector &p = positions[ i ];
for ( int j = 0; j < volumeJointList.Count(); ++j )
{
const s_JointStruct &js = volumeJointList[ j ];
float flMaxWeight = 0.0f;
for ( int k = 0; k < js.m_volumes.Count(); ++k )
{
const s_VolumeStruct &vs = js.m_volumes[ k ];
const CDmeSkinnerVolume *pDmeSkinnerVolume = vs.m_pDmeSkinnerVolume;
vs.m_mMat.V3Mul( p, v );
float f = pDmeSkinnerVolume->m_flFalloff;
float l = 0.0;
float w = 0.0;
if ( pDmeSkinnerVolume->IsEllipse() )
{
l = v.Length();
w = RemapValClamped( l, 1.0f, f + 1.0f, 1.0f, 0.0f );
}
else
{
double dIFS = 1.0 / ( 1.0 + static_cast< double >( f ) );
sv.x = v.x * dIFS;
sv.y = v.y * dIFS;
sv.z = v.z * dIFS;
if ( CSuperEllipsoid( pDmeSkinnerVolume->m_flPowerXZ, pDmeSkinnerVolume->m_flPowerY ).IntersectFromOriginThroughPoint( sv, l ) )
{
if ( l == 0.0 )
{
w = 1.0;
}
else
{
w = RemapValClamped( sv.Length(), l * dIFS, l, 1.0, 0.0 );
}
}
}
float s = pDmeSkinnerVolume->m_flStrength;
switch ( pDmeSkinnerVolume->m_nFalloffType )
{
case CDmeSkinnerVolume::FT_SMOOTH:
w = ( cosf( ( 1.0f - w ) * M_PI ) + 1.0f ) / 2.0f;
break;
case CDmeSkinnerVolume::FT_SPIKE:
w = 1.0f - cosf( w * M_PI / 2.0 );
break;
case CDmeSkinnerVolume::FT_DOME:
w = cosf( ( 1.0f - w ) * M_PI / 2.0 );
break;
default:
break;
}
w = fabs( w * s );
if ( w > 0.00001 && w > flMaxWeight )
{
flMaxWeight = w;
}
}
if ( flMaxWeight > 0.0 )
{
vw.AddWeight( js.m_nJointIndex, flMaxWeight );
}
}
vw.Sort();
const float flTotalWeight = vw.ComputeTotalWeight();
if ( vw.Count() <= 0 || flTotalWeight <= 0 )
{
Warning( "Mesh Vertex %s.v[%d] is not influenced by any volume\n", pDmeDagMeshParent->GetName(), i );
}
else
{
for ( int j = 0; j < nJointPerVertexCount; ++j )
{
jointIndices.AddToTail( vw.GetJointIndex( j ) );
jointWeights.AddToTail( vw.GetWeight( j ) / flTotalWeight );
}
}
}
pDmeVertexData->AddVertexData( nJointIndicesField, jointIndices.Count() );
pDmeVertexData->SetVertexData( nJointIndicesField, 0, jointIndices.Count(), AT_INT, jointIndices.Base() );
pDmeVertexData->AddVertexData( nJointWeightsField, jointWeights.Count() );
pDmeVertexData->SetVertexData( nJointWeightsField, 0, jointWeights.Count(), AT_FLOAT, jointWeights.Base() );
return true;
}
//====== Copyright (c) 1996-2009, Valve Corporation, All rights reserved. =====
//
// TODO: This belongs in a lib somewhere, comes from sdktools/maya/vsSkinner
//
//=============================================================================
//-----------------------------------------------------------------------------
// Statics
//-----------------------------------------------------------------------------
const double CSuperEllipsoid::s_dEpsilon = 1.0e-6;
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CSuperEllipsoid::CSuperEllipsoid(
double powerXZ /* = 1.0 */,
double powerY /* = 1.0 */ )
: m_powerXZ( powerXZ )
, m_powerY( powerY )
, m_nMaxIterations( 100 )
{
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
double CSuperEllipsoid::RootEvaluate( const Vector &p ) const
{
return pow( pow( abs( static_cast< double >( p.x ) ), 2.0 / m_powerXZ ) + pow( abs( static_cast< double >( p.z ) ), 2.0 / m_powerXZ ), m_powerXZ / m_powerY ) + pow( abs( static_cast< double >( p.y ) ), 2.0 / m_powerY ) - 1.0;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CSuperEllipsoid::IntersectFromOriginThroughPoint(
Vector point,
double dDistance ) const
{
if ( abs( point.x ) < s_dEpsilon ) point.x = 0.0;
if ( abs( point.y ) < s_dEpsilon ) point.y = 0.0;
if ( abs( point.z ) < s_dEpsilon ) point.z = 0.0;
// Point Is The Origin
if ( point.Length() < s_dEpsilon )
{
dDistance = 0.0;
return true;
}
// Check for early exit cases
const double dVal = RootEvaluate( point );
// Close enough
if ( abs( dVal ) < s_dEpsilon )
{
dDistance = point.Length();
return true;
}
// Outside of the Super Ellipsoid
if ( dVal > 0.0 )
return false;
Vector rayDirection = point;
rayDirection.NormalizeInPlace();
double dMin = 0.0;
double dMax = 0.0;
if ( !IntersectUnitBoxFromOrigin( rayDirection, dMin, dMax ) )
return false;
// This ought to work!
dMin = point.Length();
Vector intPoint0;
EvaluateRayFromOrigin( rayDirection, dMin, intPoint0 );
const double v0 = RootEvaluate( intPoint0 );
if ( abs( v0 ) < s_dEpsilon )
{
dDistance = dMin;
return true;
}
Vector intPoint1;
EvaluateRayFromOrigin( rayDirection, dMax, intPoint1 );
const double v1 = RootEvaluate( intPoint1 );
if ( abs( v1 ) < s_dEpsilon )
{
dDistance = dMax;
return true;
}
Vector intPoint;
SolveHit( v0, intPoint0, v1, intPoint1, intPoint );
dDistance = VectorLength( intPoint );
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CSuperEllipsoid::EvaluateRayFromOrigin(
const Vector &rayDirection,
double dDistance,
Vector &pointOnRay )
{
pointOnRay = rayDirection * dDistance;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CSuperEllipsoid::IntersectUnitBoxFromOrigin(
const Vector &rayDirection,
double &dMin,
double &dMax )
{
static const double dMaxValue = 1.0;
static const double dMinValue = -dMaxValue;
const double dMaxBound = sqrt( 3.0 * dMaxValue * dMaxValue );
const double dMinBound = -dMaxBound;
double tMin = 0.0;
double tMax = 0.0;
/* Left/right. */
if ( abs( rayDirection.x ) > s_dEpsilon )
{
if ( rayDirection.x > s_dEpsilon )
{
dMin = dMinValue / rayDirection.x;
dMax = dMaxValue / rayDirection.x;
if ( dMax < s_dEpsilon )
return false;
}
else
{
dMax = dMinValue / rayDirection.x;
if ( dMax < s_dEpsilon )
return false;
dMin = dMaxValue / rayDirection.x;
}
if ( dMin > dMax )
return false;
}
else
{
dMin = dMinBound;
dMax = dMaxBound;
}
/* Top/bottom. */
if ( abs( rayDirection.y ) > s_dEpsilon )
{
if ( rayDirection.y > s_dEpsilon )
{
tMin = dMinValue / rayDirection.y;
tMax = dMaxValue / rayDirection.y;
}
else
{
tMax = dMinValue / rayDirection.y;
tMin = dMaxValue / rayDirection.y;
}
if ( tMax < dMax )
{
if ( tMax < s_dEpsilon )
return false;
if ( tMin > dMin )
{
if ( tMin > tMax )
return false;
dMin = tMin;
}
else
{
if ( dMin > tMax )
return false;
}
dMax = tMax;
}
else
{
if ( tMin > dMin )
{
if ( tMin > dMax )
return false;
dMin = tMin;
}
}
}
/* Front/back. */
if ( abs( rayDirection.z ) > s_dEpsilon )
{
if ( rayDirection.z > s_dEpsilon )
{
tMin = dMinValue / rayDirection.z;
tMax = dMaxValue / rayDirection.z;
}
else
{
tMax = dMinValue / rayDirection.z;
tMin = dMaxValue / rayDirection.z;
}
if ( tMax < dMax )
{
if ( tMax < s_dEpsilon )
return false;
if ( tMin > dMin )
{
if ( tMin > tMax )
return false;
dMin = tMin;
}
else
{
if ( dMin > tMax )
return false;
}
dMax = tMax;
}
else
{
if ( tMin > dMin )
{
if ( tMin > dMax )
return false;
dMin = tMin;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CSuperEllipsoid::SolveHit(
double v0,
const Vector &intPoint0,
double v1,
const Vector &intPoint1,
Vector &intPoint ) const
{
Vector p0 = intPoint0;
Vector p1 = intPoint1;
double x;
Vector p2;
double v2;
Vector p3;
double v3;
for ( int i = 0; i < m_nMaxIterations; ++i )
{
if ( abs( v0 ) < s_dEpsilon )
{
intPoint = p0;
return;
}
if ( fabs( v1 ) < s_dEpsilon )
{
intPoint = p1;
return;
}
x = abs( v0 ) / abs( v1 - v0 );
VectorSubtract( p1, p0, p2 );
VectorMultiply( p2, x, p2 );
VectorAdd( p0, p2, p2 );
v2 = RootEvaluate( p2 );
VectorSubtract( p1, p0, p3 );
VectorMultiply( p3, 0.5, p3 );
VectorAdd( p0, p3, p3 );
v3 = RootEvaluate( p3 );
if ( v2 * v3 < 0.0 )
{
v0 = v2;
p0 = p2;
v1 = v3;
p1 = p3;
}
else
{
if ( abs( v2 ) < abs( v3 ) )
{
if ( v0 * v2 < 0.0 )
{
v1 = v2;
p1 = p2;
}
else
{
v0 = v2;
p0 = p2;
}
}
else
{
if ( v0 * v3 < 0.0 )
{
v1 = v3;
p1 = p3;
}
else
{
v0 = v3;
p0 = p3;
}
}
}
}
if ( fabs( v0 ) < fabs( v1 ) )
{
intPoint = p0;
}
else
{
intPoint = p1;
}
}