CVertexWeight( int nWeightsPerVertex )
	: m_nWeightPerVertex( MAX( 0, nWeightsPerVertex ) )

// 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;

	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:

{
	}

	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;
}

// 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;
	}
}


// 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;

	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;
}

// 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;
	}
}


// 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;

	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;

	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;
}

// 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;
	}
}


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;
}

// 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;

	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

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;
}

// 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;
	}
}


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;
				}

// 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;

	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;

				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;
}

// 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;
	}
}


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 ) );
		}
	}

// 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;

	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;

	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;
}

// 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;
	}
}


/****************************************************************************
**
**
** This file is part of the QML Shaders plugin of the Qt Toolkit.
**
**
****************************************************************************/

#include "shadereffectbuffer.h"

ShaderEffectBuffer::ShaderEffectBuffer(const QSize & size, const QGLFramebufferObjectFormat & format)
    : QGLFramebufferObject(size, format)
{
}


/****************************************************************************
**
**
** This file is part of the QML Shaders plugin of the Qt Toolkit.
**
**
****************************************************************************/

#include "shadereffectbuffer.h"

ShaderEffectBuffer::ShaderEffectBuffer(const QSize & size, const QGLFramebufferObjectFormat & format)
    : QGLFramebufferObject(size, format)