Torque3D/Engine/source/T3D/physics/bullet/btBody.cpp
2012-09-19 11:15:01 -04:00

375 lines
12 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "T3D/physics/bullet/btBody.h"
#include "T3D/physics/bullet/bt.h"
#include "T3D/physics/bullet/btCasts.h"
#include "T3D/physics/bullet/btWorld.h"
#include "T3D/physics/bullet/btCollision.h"
#include "math/mBox.h"
#include "console/console.h"
BtBody::BtBody() :
mActor( NULL ),
mWorld( NULL ),
mMass( 0.0f ),
mCompound( NULL ),
mCenterOfMass( NULL ),
mInvCenterOfMass( NULL ),
mIsDynamic( false ),
mIsEnabled( false )
{
}
BtBody::~BtBody()
{
_releaseActor();
}
void BtBody::_releaseActor()
{
if ( mActor )
{
mWorld->getDynamicsWorld()->removeRigidBody( mActor );
mActor->setUserPointer( NULL );
SAFE_DELETE( mActor );
}
SAFE_DELETE( mCompound );
SAFE_DELETE( mCenterOfMass );
SAFE_DELETE( mInvCenterOfMass );
mColShape = NULL;
}
bool BtBody::init( PhysicsCollision *shape,
F32 mass,
U32 bodyFlags,
SceneObject *obj,
PhysicsWorld *world )
{
AssertFatal( obj, "BtBody::init - Got a null scene object!" );
AssertFatal( world, "BtBody::init - Got a null world!" );
AssertFatal( dynamic_cast<BtWorld*>( world ), "BtBody::init - The world is the wrong type!" );
AssertFatal( shape, "BtBody::init - Got a null collision shape!" );
AssertFatal( dynamic_cast<BtCollision*>( shape ), "BtBody::init - The collision shape is the wrong type!" );
AssertFatal( ((BtCollision*)shape)->getShape(), "BtBody::init - Got empty collision shape!" );
// Cleanup any previous actor.
_releaseActor();
mWorld = (BtWorld*)world;
mColShape = (BtCollision*)shape;
btCollisionShape *btColShape = mColShape->getShape();
MatrixF localXfm = mColShape->getLocalTransform();
btVector3 localInertia( 0, 0, 0 );
// If we have a mass then we're dynamic.
mIsDynamic = mass > 0.0f;
if ( mIsDynamic )
{
if ( btColShape->isCompound() )
{
btCompoundShape *btCompound = (btCompoundShape*)btColShape;
btScalar *masses = new btScalar[ btCompound->getNumChildShapes() ];
for ( U32 j=0; j < btCompound->getNumChildShapes(); j++ )
masses[j] = mass / btCompound->getNumChildShapes();
btVector3 principalInertia;
btTransform principal;
btCompound->calculatePrincipalAxisTransform( masses, principal, principalInertia );
delete [] masses;
// Create a new compound with the shifted children.
btColShape = mCompound = new btCompoundShape();
for ( U32 i=0; i < btCompound->getNumChildShapes(); i++ )
{
btTransform newChildTransform = principal.inverse() * btCompound->getChildTransform(i);
mCompound->addChildShape( newChildTransform, btCompound->getChildShape(i) );
}
localXfm = btCast<MatrixF>( principal );
}
// Note... this looks like we're changing the shape, but
// we're not. All this does is ask the shape to calculate the
// local inertia vector from the mass... the shape doesn't change.
btColShape->calculateLocalInertia( mass, localInertia );
}
// If we have a local transform then we need to
// store it and the inverse to offset the center
// of mass from the graphics origin.
if ( !localXfm.isIdentity() )
{
mCenterOfMass = new MatrixF( localXfm );
mInvCenterOfMass = new MatrixF( *mCenterOfMass );
mInvCenterOfMass->inverse();
}
mMass = mass;
mActor = new btRigidBody( mass, NULL, btColShape, localInertia );
int btFlags = mActor->getCollisionFlags();
if ( bodyFlags & BF_TRIGGER )
btFlags |= btCollisionObject::CF_NO_CONTACT_RESPONSE;
if ( bodyFlags & BF_KINEMATIC )
{
btFlags &= ~btCollisionObject::CF_STATIC_OBJECT;
btFlags |= btCollisionObject::CF_KINEMATIC_OBJECT;
}
mActor->setCollisionFlags( btFlags );
mWorld->getDynamicsWorld()->addRigidBody( mActor );
mIsEnabled = true;
mUserData.setObject( obj );
mUserData.setBody( this );
mActor->setUserPointer( &mUserData );
return true;
}
void BtBody::setMaterial( F32 restitution,
F32 friction,
F32 staticFriction )
{
AssertFatal( mActor, "BtBody::setMaterial - The actor is null!" );
mActor->setRestitution( restitution );
// TODO: Weird.. Bullet doesn't have seperate dynamic
// and static friction.
//
// Either add it and submit it as an official patch
// or hack it via contact reporting or something
// like that.
mActor->setFriction( friction );
// Wake it up... it may need to move.
mActor->activate();
}
void BtBody::setSleepThreshold( F32 linear, F32 angular )
{
AssertFatal( mActor, "BtBody::setSleepThreshold - The actor is null!" );
mActor->setSleepingThresholds( linear, angular );
}
void BtBody::setDamping( F32 linear, F32 angular )
{
AssertFatal( mActor, "BtBody::setDamping - The actor is null!" );
mActor->setDamping( linear, angular );
}
void BtBody::getState( PhysicsState *outState )
{
AssertFatal( isDynamic(), "BtBody::getState - This call is only for dynamics!" );
// TODO: Fix this to do what we intended... to return
// false so that the caller can early out of the state
// hasn't changed since the last tick.
MatrixF trans;
if ( mInvCenterOfMass )
trans.mul( btCast<MatrixF>( mActor->getCenterOfMassTransform() ), *mInvCenterOfMass );
else
trans = btCast<MatrixF>( mActor->getCenterOfMassTransform() );
outState->position = trans.getPosition();
outState->orientation.set( trans );
outState->linVelocity = btCast<Point3F>( mActor->getLinearVelocity() );
outState->angVelocity = btCast<Point3F>( mActor->getAngularVelocity() );
outState->sleeping = !mActor->isActive();
// Bullet doesn't keep the momentum... recalc it.
outState->momentum = ( 1.0f / mActor->getInvMass() ) * outState->linVelocity;
}
Point3F BtBody::getCMassPosition() const
{
AssertFatal( mActor, "BtBody::getCMassPosition - The actor is null!" );
return btCast<Point3F>( mActor->getCenterOfMassTransform().getOrigin() );
}
void BtBody::setLinVelocity( const Point3F &vel )
{
AssertFatal( mActor, "BtBody::setLinVelocity - The actor is null!" );
AssertFatal( isDynamic(), "BtBody::setLinVelocity - This call is only for dynamics!" );
mActor->setLinearVelocity( btCast<btVector3>( vel ) );
}
void BtBody::setAngVelocity( const Point3F &vel )
{
AssertFatal( mActor, "BtBody::setAngVelocity - The actor is null!" );
AssertFatal( isDynamic(), "BtBody::setAngVelocity - This call is only for dynamics!" );
mActor->setAngularVelocity( btCast<btVector3>( vel ) );
}
Point3F BtBody::getLinVelocity() const
{
AssertFatal( mActor, "BtBody::getLinVelocity - The actor is null!" );
AssertFatal( isDynamic(), "BtBody::getLinVelocity - This call is only for dynamics!" );
return btCast<Point3F>( mActor->getLinearVelocity() );
}
Point3F BtBody::getAngVelocity() const
{
AssertFatal( mActor, "BtBody::getAngVelocity - The actor is null!" );
AssertFatal( isDynamic(), "BtBody::getAngVelocity - This call is only for dynamics!" );
return btCast<Point3F>( mActor->getAngularVelocity() );
}
void BtBody::setSleeping( bool sleeping )
{
AssertFatal( mActor, "BtBody::setSleeping - The actor is null!" );
AssertFatal( isDynamic(), "BtBody::setSleeping - This call is only for dynamics!" );
if ( sleeping )
{
//mActor->setCollisionFlags( mActor->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT );
mActor->setActivationState( WANTS_DEACTIVATION );
mActor->setDeactivationTime( 0.0f );
}
else
{
//mActor->setCollisionFlags( mActor->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT );
mActor->activate();
}
}
PhysicsWorld* BtBody::getWorld()
{
return mWorld;
}
PhysicsCollision* BtBody::getColShape()
{
return mColShape;
}
MatrixF& BtBody::getTransform( MatrixF *outMatrix )
{
AssertFatal( mActor, "BtBody::getTransform - The actor is null!" );
if ( mInvCenterOfMass )
outMatrix->mul( *mInvCenterOfMass, btCast<MatrixF>( mActor->getCenterOfMassTransform() ) );
else
*outMatrix = btCast<MatrixF>( mActor->getCenterOfMassTransform() );
return *outMatrix;
}
void BtBody::setTransform( const MatrixF &transform )
{
AssertFatal( mActor, "BtBody::setTransform - The actor is null!" );
if ( mCenterOfMass )
{
MatrixF xfm;
xfm.mul( transform, *mCenterOfMass );
mActor->setCenterOfMassTransform( btCast<btTransform>( xfm ) );
}
else
mActor->setCenterOfMassTransform( btCast<btTransform>( transform ) );
// If its dynamic we have more to do.
if ( isDynamic() )
{
// Clear any velocity and forces... this is a warp.
mActor->clearForces();
mActor->setLinearVelocity( btVector3( 0, 0, 0 ) );
mActor->setAngularVelocity( btVector3( 0, 0, 0 ) );
mActor->activate();
}
}
void BtBody::applyCorrection( const MatrixF &transform )
{
AssertFatal( mActor, "BtBody::applyCorrection - The actor is null!" );
AssertFatal( isDynamic(), "BtBody::applyCorrection - This call is only for dynamics!" );
if ( mCenterOfMass )
{
MatrixF xfm;
xfm.mul( transform, *mCenterOfMass );
mActor->setCenterOfMassTransform( btCast<btTransform>( xfm ) );
}
else
mActor->setCenterOfMassTransform( btCast<btTransform>( transform ) );
}
void BtBody::applyImpulse( const Point3F &origin, const Point3F &force )
{
AssertFatal( mActor, "BtBody::applyImpulse - The actor is null!" );
AssertFatal( isDynamic(), "BtBody::applyImpulse - This call is only for dynamics!" );
if ( mCenterOfMass )
{
Point3F relOrigin( origin );
mCenterOfMass->mulP( relOrigin );
Point3F relForce( force );
mCenterOfMass->mulV( relForce );
mActor->applyImpulse( btCast<btVector3>( relForce ), btCast<btVector3>( relOrigin ) );
}
else
mActor->applyImpulse( btCast<btVector3>( force ), btCast<btVector3>( origin ) );
if ( !mActor->isActive() )
mActor->activate();
}
Box3F BtBody::getWorldBounds()
{
btVector3 min, max;
mActor->getAabb( min, max );
Box3F bounds( btCast<Point3F>( min ), btCast<Point3F>( max ) );
return bounds;
}
void BtBody::setSimulationEnabled( bool enabled )
{
if ( mIsEnabled == enabled )
return;
if ( !enabled )
mWorld->getDynamicsWorld()->removeRigidBody( mActor );
else
mWorld->getDynamicsWorld()->addRigidBody( mActor );
mIsEnabled = enabled;
}