874 lines
20 KiB
C++
874 lines
20 KiB
C++
//====== 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;
|
|
}
|
|
} |