#include "Mix/Private/Dynamics/BallJoint.h"

#include "Mix/Private/Dynamics/Utility.h"
#include "Mix/Private/Dynamics/RigidBody.h"

#include "Mix/Graphics/Utility/IPerspectiveRenderer.h"

namespace Mix{ namespace Dynamics{

const Float32 BallJoint::DEF_DAMPING = 0.5f;
const wchar_t* BallJoint::FAILED_CREATE = L"ボールジョイントの作成に失敗";

const Int32 BallJoint::DEBUG_DRAW_SWING_SEGMENTS = 8 * 4;
const Float32 BallJoint::DEBUG_DRAW_TWIST_STEP_DEG = 10.0f;

BallJoint* BallJoint::CreateInstance(	Mix::Dynamics::IRigidBody* pRigidBodyA,
										const Mix::Vector3& posA,
										const Mix::Vector3& twistAxis,
										const Mix::Vector3& swingAxis )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, BallJoint, pRigidBodyA, posA, twistAxis, swingAxis );
}

BallJoint* BallJoint::CreateInstance(	Mix::Dynamics::IRigidBody* pRigidBodyA,
										Mix::Dynamics::IRigidBody* pRigidBodyB,
										const Mix::Vector3& posA,
										const Mix::Vector3& posB,
										const Mix::Vector3& twistAxis,
										const Mix::Vector3& swingAxis )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, BallJoint, pRigidBodyA, pRigidBodyB, posA, posB, twistAxis, swingAxis );
}

BallJoint::BallJoint(	Mix::Dynamics::IRigidBody* pRigidBodyA,
						const Mix::Vector3& posA,
						const Mix::Vector3& twistAxis,
						const Mix::Vector3& swingAxis ) :
m_pRigidBodyA( pRigidBodyA ),
m_pRigidBodyB( NULL ),
m_PivotA( posA ),
m_PivotB( posA ),
m_TwistAxis( twistAxis ),
m_SwingAxis( swingAxis ),
m_pObject( NULL ),
m_Damping( BallJoint::DEF_DAMPING ),
m_ParamFlags( 0 )
{
	MIX_ADD_REF( m_pRigidBodyA );
}

BallJoint::BallJoint(	Mix::Dynamics::IRigidBody* pRigidBodyA,
						Mix::Dynamics::IRigidBody* pRigidBodyB,
						const Mix::Vector3& posA,
						const Mix::Vector3& posB,
						const Mix::Vector3& twistAxis,
						const Mix::Vector3& swingAxis ) :
m_pRigidBodyA( pRigidBodyA ),
m_pRigidBodyB( pRigidBodyB ),
m_PivotA( posA ),
m_PivotB( posB ),
m_TwistAxis( twistAxis ),
m_SwingAxis( swingAxis ),
m_pObject( NULL ),
m_Damping( BallJoint::DEF_DAMPING ),
m_ParamFlags( 0 )
{
	MIX_ADD_REF( m_pRigidBodyA );
	MIX_ADD_REF( m_pRigidBodyB );
}

BallJoint::~BallJoint( void )
{
	MIX_LIB_DELETE( m_pObject );
	MIX_RELEASE( m_pRigidBodyA );
	MIX_RELEASE( m_pRigidBodyB );
}

Boolean BallJoint::Initialize( const wchar_t* pDebugName )
{
	btTransform frameA;
	btTransform frameB;

	if( m_pRigidBodyB != NULL )
	{
		btRigidBody* rbA = static_cast<Mix::Dynamics::RigidBody*>( m_pRigidBodyA )->Bullet_GetRigidBodyPtr();
		btRigidBody* rbB = static_cast<Mix::Dynamics::RigidBody*>( m_pRigidBodyB )->Bullet_GetRigidBodyPtr();

		Bullet_ComputeFrames(	rbA,
								rbB,
								btVector3( m_PivotA.x, m_PivotA.y, m_PivotA.z ),
								btVector3( m_PivotB.x, m_PivotB.y, m_PivotB.z ),
								btVector3( m_TwistAxis.x, m_TwistAxis.y, m_TwistAxis.z ),
								btVector3( m_SwingAxis.x, m_SwingAxis.y, m_SwingAxis.z ),
								frameA,
								frameB );

		m_pObject = MIX_LIB_NEW btConeTwistConstraint( *rbA, *rbB, frameA, frameB );
		if( m_pObject != NULL )
		{
			const btVector3& posA = frameA.getOrigin();
			const btVector3& posB = frameB.getOrigin();

			m_PivotA.x = posA.x();
			m_PivotA.y = posA.y();
			m_PivotA.z = posA.z();

			m_PivotB.x = posB.x();
			m_PivotB.y = posB.y();
			m_PivotB.z = posB.z();
		}
	}
	else
	{
		btRigidBody* rbA = static_cast<Mix::Dynamics::RigidBody*>( m_pRigidBodyA )->Bullet_GetRigidBodyPtr();

		Bullet_ComputeFrames(	rbA,
								btVector3( m_PivotA.x, m_PivotA.y, m_PivotA.z ),
								btVector3( m_TwistAxis.x, m_TwistAxis.y, m_TwistAxis.z ),
								btVector3( m_SwingAxis.x, m_SwingAxis.y, m_SwingAxis.z ),
								frameA,
								frameB );

		m_pObject = MIX_LIB_NEW btConeTwistConstraint( *rbA, btTypedConstraint::getFixedBody(), frameA, frameB );
		if( m_pObject != NULL )
		{
			const btVector3& posB = frameB.getOrigin();

			m_PivotB.x = posB.x();
			m_PivotB.y = posB.y();
			m_PivotB.z = posB.z();
		}
	}

	if( m_pObject == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", BallJoint::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	m_pObject->buildJacobian();

	m_pObject->setLimit( 0.7f/*40*/, 0.7f/*40*/, 0.52f/*30*/ );
	m_pObject->setDamping( m_Damping );

	return True;
}

void BallJoint::UpdatePivots( void )
{
	btTransform frameA = m_pObject->getFrameOffsetA();
	btTransform frameB = m_pObject->getFrameOffsetB();

	frameA.getOrigin().setValue( m_PivotA.x, m_PivotA.y, m_PivotA.z );
	frameB.getOrigin().setValue( m_PivotB.x, m_PivotB.y, m_PivotB.z );

	m_pObject->setFrames( frameA, frameB );
}

void BallJoint::UpdateFrames( void )
{
	btTransform frameA;
	btTransform frameB;

	if( m_pRigidBodyB != NULL )
	{
		btRigidBody* rbA = static_cast<Mix::Dynamics::RigidBody*>( m_pRigidBodyA )->Bullet_GetRigidBodyPtr();
		btRigidBody* rbB = static_cast<Mix::Dynamics::RigidBody*>( m_pRigidBodyB )->Bullet_GetRigidBodyPtr();

		Bullet_ComputeFrames(	rbA,
								rbB,
								btVector3( m_PivotA.x, m_PivotA.y, m_PivotA.z ),
								btVector3( m_PivotB.x, m_PivotB.y, m_PivotB.z ),
								btVector3( m_TwistAxis.x, m_TwistAxis.y, m_TwistAxis.z ),
								btVector3( m_SwingAxis.x, m_SwingAxis.y, m_SwingAxis.z ),
								frameA,
								frameB );
	}
	else
	{
		btRigidBody* rbA = static_cast<Mix::Dynamics::RigidBody*>( m_pRigidBodyA )->Bullet_GetRigidBodyPtr();

		Bullet_ComputeFrames(	rbA,
								btVector3( m_PivotA.x, m_PivotA.y, m_PivotA.z ),
								btVector3( m_TwistAxis.x, m_TwistAxis.y, m_TwistAxis.z ),
								btVector3( m_SwingAxis.x, m_SwingAxis.y, m_SwingAxis.z ),
								frameA,
								frameB );

		const btVector3& pivotB = frameB.getOrigin();

		m_PivotB.x = pivotB.x();
		m_PivotB.y = pivotB.y();
		m_PivotB.z = pivotB.z();
	}

	m_pObject->setFrames( frameA, frameB );
}

void BallJoint::Bullet_ComputeFrames(	const btRigidBody* rbA,
										const btVector3& posA,
										const btVector3& twistAxis,
										const btVector3& swingAxis,
										btTransform& frameA,
										btTransform& frameB )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Aフレーム( Twist )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	const btVector3& rbAxisA0 = twistAxis;
	btVector3 rbAxisA1;
	btVector3 rbAxisA2;

	btPlaneSpace1( rbAxisA0, rbAxisA1, rbAxisA2 );

	frameA.getOrigin() = posA;
	frameA.getBasis().setValue(	rbAxisA0.getX(), rbAxisA1.getX(), rbAxisA2.getX(),
								rbAxisA0.getY(), rbAxisA1.getY(), rbAxisA2.getY(),
								rbAxisA0.getZ(), rbAxisA1.getZ(), rbAxisA2.getZ() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Bフレーム( Swing )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	const btVector3& rbAxisB0 = swingAxis;
	btVector3 rbAxisB1;
	btVector3 rbAxisB2;

	btPlaneSpace1( rbAxisB0, rbAxisB1, rbAxisB2 );

	frameB.getOrigin() = posA;
	frameB.getBasis().setValue(	rbAxisB0.getX(), rbAxisB1.getX(), rbAxisB2.getX(),
								rbAxisB0.getY(), rbAxisB1.getY(), rbAxisB2.getY(),
								rbAxisB0.getZ(), rbAxisB1.getZ(), rbAxisB2.getZ() );

	if( rbA->isKinematicObject() == true )
	{
		btTransform tr;

		rbA->getMotionState()->getWorldTransform( tr );

		frameB = tr * frameB;
	}
	else
	{
		frameB = rbA->getCenterOfMassTransform() * frameB;
	}
}

void BallJoint::Bullet_ComputeFrames(	const btRigidBody* rbA,
										const btRigidBody* rbB,
										const btVector3& posA,
										const btVector3& posB,
										const btVector3& twistAxis,
										const btVector3& swingAxis,
										btTransform& frameA,
										btTransform& frameB )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Aフレーム( Twist )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	const btVector3& rbAxisA0 = twistAxis;
	btVector3 rbAxisA1;
	btVector3 rbAxisA2;

	btPlaneSpace1( rbAxisA0, rbAxisA1, rbAxisA2 );

	frameA.getOrigin() = posA;
	frameA.getBasis().setValue(	rbAxisA0.getX(), rbAxisA1.getX(), rbAxisA2.getX(),
								rbAxisA0.getY(), rbAxisA1.getY(), rbAxisA2.getY(),
								rbAxisA0.getZ(), rbAxisA1.getZ(), rbAxisA2.getZ() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Bフレーム( Swing )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	const btVector3& rbAxisB0 = swingAxis;
	btVector3 rbAxisB1;
	btVector3 rbAxisB2;

	btMatrix3x3 basisA;
	btMatrix3x3 basisB;

	btPlaneSpace1( rbAxisB0, rbAxisB1, rbAxisB2 );

	frameB.getOrigin() = posB;
	frameB.getBasis().setValue(	rbAxisB0.getX(), rbAxisB1.getX(), rbAxisB2.getX(),
								rbAxisB0.getY(), rbAxisB1.getY(), rbAxisB2.getY(),
								rbAxisB0.getZ(), rbAxisB1.getZ(), rbAxisB2.getZ() );

	if( rbA->isKinematicObject() == true )
	{
		btTransform tr;

		rbA->getMotionState()->getWorldTransform( tr );
		basisA = tr.getBasis();
	}
	else
	{
		basisA = rbA->getCenterOfMassTransform().getBasis();
	}

	if( rbB->isKinematicObject() == true )
	{
		btTransform tr;

		rbB->getMotionState()->getWorldTransform( tr );
		basisB = tr.getBasis();
	}
	else
	{
		basisB = rbB->getCenterOfMassTransform().getBasis();
	}

	//Aの空間に変換
	frameB.getBasis() = basisA * frameB.getBasis();

	//Aの空間で作成されたフレームをBの空間へ変換
	frameB.getBasis() = basisB.inverse() * frameB.getBasis();
}

Mix::Dynamics::IJoint::TYPE BallJoint::GetType( void ) const
{
	return Mix::Dynamics::IJoint::BALL;
}

Boolean BallJoint::IsInWorld( void ) const
{
	return Joint::IsInWorld();
}

Boolean BallJoint::IsEnabled( void ) const
{
	return m_pObject->isEnabled();
}

void BallJoint::SetEnabled( Boolean state )
{
	m_pObject->setEnabled( ( state == True ) );
}

Float32 BallJoint::GetBreakingImpulseThreshold( void ) const
{
	return m_pObject->getBreakingImpulseThreshold();
}

void BallJoint::SetBreakingImpulseThreshold( Float32 threshold )
{
	m_pObject->setBreakingImpulseThreshold( threshold );
}

Boolean BallJoint::IsSingle( void ) const
{
	return ( m_pRigidBodyB == NULL );
}

void BallJoint::GetRigidBodyA( Mix::Dynamics::IRigidBody** ppRigidBody )
{
	MIX_ADD_REF( m_pRigidBodyA );
	( *ppRigidBody ) = m_pRigidBodyA;
}

void BallJoint::GetRigidBodyB( Mix::Dynamics::IRigidBody** ppRigidBody )
{
	MIX_ADD_REF( m_pRigidBodyB );
	( *ppRigidBody ) = m_pRigidBodyB;
}

const Mix::Vector3& BallJoint::GetPivotA( void ) const
{
	return m_PivotA;
}

void BallJoint::SetPivotA( const Mix::Vector3& pivot )
{
	m_PivotA = pivot;

	UpdatePivots();
}

const Mix::Vector3& BallJoint::GetPivotB( void ) const
{
	return m_PivotB;
}

void BallJoint::SetPivotB( const Mix::Vector3& pivot )
{
	m_PivotB = pivot;

	UpdatePivots();
}

UInt32 BallJoint::Debug_GetDrawFlags( void ) const
{
	return m_DebugDrawFlags;
}

void BallJoint::Debug_SetDrawFlags( UInt32 flags )
{
	m_DebugDrawFlags = flags;
}

Float32 BallJoint::Debug_GetDrawFrameMinSize( void ) const
{
	return m_DebugDrawFrameMinSize;
}

void BallJoint::Debug_SetDrawFrameMinSize( Float32 minSize )
{
	m_DebugDrawFrameMinSize = minSize;
}

Float32 BallJoint::Debug_GetDrawLimitScaling( void ) const
{
	return m_DebugDrawLimitScaling;
}

void BallJoint::Debug_SetDrawLimitScaling( Float32 scaling )
{
	m_DebugDrawLimitScaling = scaling;
}

void BallJoint::Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, Float32 opacity )
{
	Mix::Matrix4x4 oldMat = pPerspectiveRenderer->GetMatrix();
	Mix::Vector4 oldColor = pPerspectiveRenderer->GetColor();

	const btTransform& frameA = m_pObject->getAFrame();
	const btTransform& frameB = m_pObject->getBFrame();
	const btVector3& pivotA = frameA.getOrigin();
	const btVector3& pivotB = frameB.getOrigin();
	const btRigidBody* pRigidBodyA = &( m_pObject->getRigidBodyA() );
	const btRigidBody* pRigidBodyB = &( m_pObject->getRigidBodyB() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 開始
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pPerspectiveRenderer->SetMatrix( Mix::Matrix4x4::Identity() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フレーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( m_DebugDrawFlags, Mix::Dynamics::DD_JOINT_FRAME ) == Mix::Dynamics::DD_JOINT_FRAME )
	{
		pPerspectiveRenderer->SetColor( Mix::Dynamics::Debug::GetColor( Mix::Dynamics::DDC_JOINT_FRAME, opacity ) );

		Debug::DrawPivot(	pPerspectiveRenderer,
							m_pObject,
							pivotA,
							pivotB,
							m_DebugDrawFrameMinSize );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リミット
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( m_DebugDrawFlags, Mix::Dynamics::DD_JOINT_LIMIT ) == Mix::Dynamics::DD_JOINT_LIMIT )
	{
		btTransform	tr;

		pPerspectiveRenderer->SetColor( Mix::Dynamics::Debug::GetColor( Mix::Dynamics::DDC_JOINT_LIMIT, opacity ) );

		/*
			スィング
		*/

		Float32 angleInRadians = btScalar( 2.0f * 3.1415926f ) * ( btScalar )( BallJoint::DEBUG_DRAW_SWING_SEGMENTS - 1 ) / btScalar( BallJoint::DEBUG_DRAW_SWING_SEGMENTS );
		btVector3 prev = m_pObject->GetPointForAngle( angleInRadians, m_DebugDrawLimitScaling );
		btVector3 cur;

		if( pRigidBodyB != &btTypedConstraint::getFixedBody() )
		{
			if( pRigidBodyB->isKinematicObject() == true )
			{
				btTransform mstr;
				pRigidBodyB->getMotionState()->getWorldTransform( mstr );
				tr = mstr * m_pObject->getBFrame();
			}
			else
			{
				tr = pRigidBodyB->getCenterOfMassTransform() * m_pObject->getBFrame();
			}
		}
		else
		{
			tr = pRigidBodyA->getCenterOfMassTransform() * m_pObject->getAFrame();
		}

		prev = tr * prev;

		for( Int32 i = 0; i < BallJoint::DEBUG_DRAW_SWING_SEGMENTS; i++ )
		{
			angleInRadians = btScalar( 2.0f * 3.1415926f ) * ( btScalar )( i / btScalar( BallJoint::DEBUG_DRAW_SWING_SEGMENTS ) );
			cur = m_pObject->GetPointForAngle( angleInRadians, m_DebugDrawLimitScaling );

			cur = tr * cur;
			pPerspectiveRenderer->AddLine( Mix::Vector3( prev.x(), prev.y(), prev.z() ), Mix::Vector3( cur.x(), cur.y(), cur.z() ) );

			if( i % ( BallJoint::DEBUG_DRAW_SWING_SEGMENTS / 8 ) == 0 )
			{
				const btVector3& origin = tr.getOrigin();
				pPerspectiveRenderer->AddLine( Mix::Vector3( origin.x(), origin.y(), origin.z() ), Mix::Vector3( cur.x(), cur.y(), cur.z() ) );
			}

			prev = cur;
		}

		/*
			ツイスト
		*/

		btScalar tws = m_pObject->getTwistSpan();
		btScalar twa = m_pObject->getTwistAngle();
		Float32 minAngle = -twa - tws;
		Float32 maxAngle = -twa + tws;

		if( pRigidBodyA->isKinematicObject() == true )
		{
			btTransform mstr;
			pRigidBodyA->getMotionState()->getWorldTransform( mstr );
			tr = mstr * m_pObject->getAFrame();
		}
		else
		{
			tr = pRigidBodyA->getCenterOfMassTransform() * m_pObject->getAFrame();
		}

		Debug::DrawArc( pPerspectiveRenderer,
						tr.getOrigin(),
						tr.getBasis().getColumn( 0 ),
						tr.getBasis().getColumn( 1 ),
						m_DebugDrawLimitScaling,
						m_DebugDrawLimitScaling,
						minAngle,
						maxAngle );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 終了
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pPerspectiveRenderer->SetColor( oldColor );
	pPerspectiveRenderer->SetMatrix( oldMat );
}

const Mix::Vector3& BallJoint::GetTwistAxis( void ) const
{
	return m_TwistAxis;
}

void BallJoint::SetTwistAxis( const Mix::Vector3& axis )
{
	m_TwistAxis = axis;

	UpdateFrames();
}

Float32 BallJoint::GetTwistLimit( void ) const
{
	return m_pObject->getTwistSpan();
}

void BallJoint::SetTwistLimit( Float32 limit )
{
	m_pObject->setLimit(	m_pObject->getSwingSpan1(),
							m_pObject->getSwingSpan2(),
							limit );
}

const Mix::Vector3& BallJoint::GetSwingAxis( void ) const
{
	return m_SwingAxis;
}

void BallJoint::SetSwingAxis( const Mix::Vector3& axis )
{
	m_SwingAxis = axis;

	UpdateFrames();
}

Float32 BallJoint::GetSwingLimit1( void ) const
{
	return m_pObject->getSwingSpan1();
}

void BallJoint::SetSwingLimit1( Float32 limit )
{
	m_pObject->setLimit(	limit,
							m_pObject->getSwingSpan2(),
							m_pObject->getTwistSpan() );
}

Float32 BallJoint::GetSwingLimit2( void ) const
{
	return m_pObject->getSwingSpan2();
}

void BallJoint::SetSwingLimit2( Float32 limit )
{
	m_pObject->setLimit(	m_pObject->getSwingSpan1(),
							limit,
							m_pObject->getTwistSpan() );
}

Float32 BallJoint::GetDamping( void ) const
{
	return m_Damping;
}

void BallJoint::SetDamping( Float32 damping )
{
	m_Damping = damping;
	m_pObject->setDamping( damping );
}

Float32 BallJoint::GetPivotSpring( void ) const
{
	if( MIX_TESTBIT( m_ParamFlags, BallJoint::PF_PIVOT_SPRING ) != BallJoint::PF_PIVOT_SPRING )
	{
		return 0.0f;
	}

	return m_pObject->getParam( BT_CONSTRAINT_CFM, 0 );
}

void BallJoint::SetPivotSpring( Float32 spring )
{
	m_pObject->setParam( BT_CONSTRAINT_CFM, spring, 0 );

	MIX_SETBIT( m_ParamFlags, BallJoint::PF_PIVOT_SPRING );
}

Float32 BallJoint::GetPivotDamper( void ) const
{
	if( MIX_TESTBIT( m_ParamFlags, BallJoint::PF_PIVOT_DAMPER ) != BallJoint::PF_PIVOT_DAMPER )
	{
		return 0.0f;
	}

	return m_pObject->getParam( BT_CONSTRAINT_ERP, 0 );
}

void BallJoint::SetPivotDamper( Float32 damper )
{
	m_pObject->setParam( BT_CONSTRAINT_ERP, damper, 0 );

	MIX_SETBIT( m_ParamFlags, BallJoint::PF_PIVOT_DAMPER );
}

Float32 BallJoint::GetLimitSpring( void ) const
{
	if( MIX_TESTBIT( m_ParamFlags, BallJoint::PF_LIMIT_SPRING ) != BallJoint::PF_LIMIT_SPRING )
	{
		return 0.0f;
	}

	return m_pObject->getParam( BT_CONSTRAINT_CFM, 3 );
}

void BallJoint::SetLimitSpring( Float32 spring )
{
	m_pObject->setParam( BT_CONSTRAINT_CFM, spring, 3 );

	MIX_SETBIT( m_ParamFlags, BallJoint::PF_LIMIT_SPRING );
}

Float32 BallJoint::GetLimitDamper( void ) const
{
	if( MIX_TESTBIT( m_ParamFlags, BallJoint::PF_LIMIT_DAMPER ) != BallJoint::PF_LIMIT_DAMPER )
	{
		return 0.0f;
	}

	return m_pObject->getParam( BT_CONSTRAINT_ERP, 3 );
}

void BallJoint::SetLimitDamper( Float32 damper )
{
	m_pObject->setParam( BT_CONSTRAINT_ERP, damper, 3 );

	MIX_SETBIT( m_ParamFlags, BallJoint::PF_LIMIT_DAMPER );
}

btTypedConstraint* BallJoint::Bullet_GetTypedConstraintPtr( void ) const
{
	return m_pObject;
}

}}
