mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
221 lines
6.4 KiB
C++
221 lines
6.4 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/physicsForce.h"
|
|
|
|
#include "T3D/physics/physicsPlugin.h"
|
|
#include "T3D/physics/physicsWorld.h"
|
|
#include "T3D/physics/physicsBody.h"
|
|
#include "console/engineAPI.h"
|
|
|
|
|
|
IMPLEMENT_CONOBJECT(PhysicsForce);
|
|
|
|
|
|
ConsoleDocClass( PhysicsForce,
|
|
"@brief Helper object for gameplay physical forces.\n\n"
|
|
"%PhysicsForces can be created and \"attached\" to other @link PhysicsBody PhysicsBodies@endlink "
|
|
"to attract them to the position of the PhysicsForce."
|
|
"@ingroup Physics\n"
|
|
);
|
|
|
|
|
|
PhysicsForce::PhysicsForce()
|
|
: mWorld( NULL ),
|
|
mBody( NULL ),
|
|
mPhysicsTick( false )
|
|
{
|
|
}
|
|
|
|
PhysicsForce::~PhysicsForce()
|
|
{
|
|
}
|
|
|
|
void PhysicsForce::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
}
|
|
|
|
|
|
bool PhysicsForce::onAdd()
|
|
{
|
|
if ( !Parent::onAdd() )
|
|
return false;
|
|
|
|
// Find the physics world we're in.
|
|
if ( PHYSICSMGR )
|
|
mWorld = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
|
|
|
|
setProcessTick( true );
|
|
getProcessList()->preTickSignal().notify( this, &PhysicsForce::_preTick );
|
|
|
|
return true;
|
|
}
|
|
|
|
void PhysicsForce::onRemove()
|
|
{
|
|
mWorld = NULL;
|
|
mBody = NULL;
|
|
getProcessList()->preTickSignal().remove( this, &PhysicsForce::_preTick );
|
|
setProcessTick( false );
|
|
|
|
Parent::onRemove();
|
|
}
|
|
|
|
void PhysicsForce::attach( const Point3F &start, const Point3F &direction, F32 maxDist )
|
|
{
|
|
detach();
|
|
|
|
// If there is no physics world then we cannot apply any forces.
|
|
if ( !mWorld )
|
|
return;
|
|
|
|
PhysicsBody *body = mWorld->castRay( start, start + ( direction * maxDist ), PhysicsWorld::BT_Dynamic );
|
|
if ( !body )
|
|
return;
|
|
|
|
mBody = body;
|
|
}
|
|
|
|
void PhysicsForce::detach( const Point3F &force )
|
|
{
|
|
if ( mBody && !force.isZero() )
|
|
{
|
|
Point3F cMass = mBody->getCMassPosition();
|
|
F32 mass = mBody->getMass();
|
|
|
|
Point3F impulse = ( mass * force ) / TickSec;
|
|
mBody->applyImpulse( cMass, impulse );
|
|
}
|
|
|
|
mBody = NULL;
|
|
}
|
|
|
|
void PhysicsForce::onMount( SceneObject *obj, S32 node )
|
|
{
|
|
Parent::onMount( obj, node );
|
|
|
|
processAfter( obj );
|
|
|
|
MatrixF mat( true );
|
|
mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
|
|
setTransform( mat );
|
|
}
|
|
|
|
void PhysicsForce::onUnmount( SceneObject *obj, S32 node )
|
|
{
|
|
clearProcessAfter();
|
|
Parent::onUnmount( obj, node );
|
|
}
|
|
|
|
void PhysicsForce::_preTick()
|
|
{
|
|
// How Torque works we sometimes get double or more
|
|
// ticks within a single simulation step. This occurs
|
|
// for various reasons including odd timesteps and low
|
|
// framerate.
|
|
//
|
|
// In order to keep performance from plumeting we do
|
|
// not tick physics multiple times... instead it just
|
|
// looses time.
|
|
//
|
|
// We set this variable below to let processTick know
|
|
// that physics hasn't stepped yet and to skip processing.
|
|
//
|
|
// This doesn't completely solve the issue, but it does
|
|
// make things much better.
|
|
//
|
|
mPhysicsTick = true;
|
|
}
|
|
|
|
void PhysicsForce::processTick( const Move * )
|
|
{
|
|
if ( isMounted() )
|
|
{
|
|
MatrixF test( true );
|
|
test.setPosition( Point3F( 0, 4, 0 ) );
|
|
AssertFatal( test != mMount.xfm, "Error!" );
|
|
|
|
MatrixF mat( true );
|
|
mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
|
|
setTransform( mat );
|
|
}
|
|
|
|
// Nothing to do without a body or if physics hasn't ticked.
|
|
if ( !mBody || !mPhysicsTick )
|
|
return;
|
|
|
|
mPhysicsTick = false;
|
|
|
|
// If we lost the body then release it.
|
|
if ( !mBody->isDynamic() || !mBody->isSimulationEnabled() )
|
|
{
|
|
detach();
|
|
return;
|
|
}
|
|
|
|
// Get our distance to the body.
|
|
Point3F cMass = mBody->getCMassPosition();
|
|
Point3F vector = getPosition() - cMass;
|
|
|
|
// Apply the force!
|
|
F32 mass = mBody->getMass();
|
|
Point3F impulse = ( mass * vector ) / TickSec;
|
|
|
|
// Counter balance the linear impulse.
|
|
Point3F linVel = mBody->getLinVelocity();
|
|
Point3F currentForce = linVel * mass;
|
|
|
|
// Apply it.
|
|
mBody->applyImpulse( cMass, impulse - currentForce );
|
|
}
|
|
|
|
DefineEngineMethod( PhysicsForce, attach, void, ( Point3F start, Point3F direction, F32 maxDist ),,
|
|
"@brief Attempts to associate the PhysicsForce with a PhysicsBody.\n\n"
|
|
"Performs a physics ray cast of the provided length and direction. The %PhysicsForce "
|
|
"will attach itself to the first dynamic PhysicsBody the ray collides with. "
|
|
"On every tick, the attached body will be attracted towards the position of the %PhysicsForce.\n\n"
|
|
"A %PhysicsForce can only be attached to one body at a time.\n\n"
|
|
"@note To determine if an %attach was successful, check isAttached() immediately after "
|
|
"calling this function.n\n")
|
|
{
|
|
object->attach( start, direction, maxDist );
|
|
}
|
|
|
|
DefineEngineMethod( PhysicsForce, detach, void, ( Point3F force ), ( Point3F::Zero ),
|
|
"@brief Disassociates the PhysicsForce from any attached PhysicsBody.\n\n"
|
|
"@param force Optional force to apply to the attached PhysicsBody "
|
|
"before detaching.\n\n"
|
|
"@note Has no effect if the %PhysicsForce is not attached to anything.\n\n")
|
|
{
|
|
object->detach( force );
|
|
}
|
|
|
|
DefineEngineMethod( PhysicsForce, isAttached, bool, (),,
|
|
"@brief Returns true if the %PhysicsForce is currently attached to an object.\n\n"
|
|
"@see PhysicsForce::attach()")
|
|
{
|
|
return object->isAttached();
|
|
}
|
|
|