|
|
//====== Copyright � 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; } }
|