mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-21 12:25:30 +00:00
Adds Component, the various main component classes and their interfaces.
This commit is contained in:
parent
2e339bafba
commit
fa78a2f354
37 changed files with 9736 additions and 0 deletions
368
Engine/source/T3D/components/Physics/physicsBehavior.cpp
Normal file
368
Engine/source/T3D/components/Physics/physicsBehavior.cpp
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "T3D/Components/Physics/physicsBehavior.h"
|
||||
#include "platform/platform.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "lighting/lightQuery.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/containerQuery.h"
|
||||
#include "math/mathIO.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
PhysicsComponent::PhysicsComponent() : Component()
|
||||
{
|
||||
addComponentField("isStatic", "If enabled, object will not simulate physics", "bool", "0", "");
|
||||
addComponentField("gravity", "The direction of gravity affecting this object, as a vector", "vector", "0 0 -9", "");
|
||||
addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
|
||||
addComponentField("mass", "The mass of the object", "float", "1", "");
|
||||
|
||||
mStatic = false;
|
||||
mAtRest = false;
|
||||
mAtRestCounter = 0;
|
||||
|
||||
mGravity = VectorF(0, 0, 0);
|
||||
mVelocity = VectorF(0, 0, 0);
|
||||
mDrag = 0.7f;
|
||||
mMass = 1.f;
|
||||
|
||||
mGravityMod = 1.f;
|
||||
|
||||
csmAtRestTimer = 64;
|
||||
sAtRestVelocity = 0.15f;
|
||||
|
||||
mDelta.pos = Point3F(0, 0, 0);
|
||||
mDelta.posVec = Point3F(0, 0, 0);
|
||||
mDelta.warpTicks = mDelta.warpCount = 0;
|
||||
mDelta.dt = 1;
|
||||
mDelta.move = NullMove;
|
||||
mPredictionCount = 0;
|
||||
}
|
||||
|
||||
PhysicsComponent::~PhysicsComponent()
|
||||
{
|
||||
for(S32 i = 0;i < mFields.size();++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(PhysicsComponent);
|
||||
|
||||
void PhysicsComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
// Initialize interpolation vars.
|
||||
mDelta.rot[1] = mDelta.rot[0] = QuatF(mOwner->getTransform());
|
||||
mDelta.pos = mOwner->getPosition();
|
||||
mDelta.posVec = Point3F(0,0,0);
|
||||
}
|
||||
|
||||
void PhysicsComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("gravity", TypePoint3F, Offset(mGravity, PhysicsComponent));
|
||||
addField("velocity", TypePoint3F, Offset(mVelocity, PhysicsComponent));
|
||||
addField("isStatic", TypeBool, Offset(mStatic, PhysicsComponent));
|
||||
}
|
||||
|
||||
U32 PhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if(stream->writeFlag(mask & VelocityMask))
|
||||
mathWrite( *stream, mVelocity );
|
||||
|
||||
if(stream->writeFlag(mask & UpdateMask))
|
||||
{
|
||||
stream->writeFlag(mStatic);
|
||||
stream->writeFlag(mAtRest);
|
||||
stream->writeInt(mAtRestCounter,8);
|
||||
|
||||
mathWrite( *stream, mGravity );
|
||||
|
||||
stream->writeFloat(mDrag, 12);
|
||||
//stream->writeFloat(mMass, 12);
|
||||
|
||||
stream->writeFloat(mGravityMod, 12);
|
||||
}
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void PhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
if(stream->readFlag())
|
||||
mathRead( *stream, &mVelocity );
|
||||
|
||||
if(stream->readFlag())
|
||||
{
|
||||
mStatic = stream->readFlag();
|
||||
mAtRest = stream->readFlag();
|
||||
mAtRestCounter = stream->readInt(8);
|
||||
|
||||
mathRead( *stream, &mGravity );
|
||||
|
||||
mDrag = stream->readFloat(12);
|
||||
//mMass = stream->readFloat(12);
|
||||
|
||||
mGravityMod = stream->readFloat(12);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
void PhysicsComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
Point3F pos = mDelta.pos + mDelta.posVec * dt;
|
||||
//Point3F rot = mDelta.rot + mDelta.rotVec * dt;
|
||||
|
||||
setRenderPosition(pos,dt);
|
||||
}
|
||||
|
||||
//
|
||||
void PhysicsComponent::updateContainer()
|
||||
{
|
||||
PROFILE_SCOPE( PhysicsBehaviorInstance_updateContainer );
|
||||
|
||||
// Update container drag and buoyancy properties
|
||||
|
||||
// Set default values.
|
||||
//mDrag = mDataBlock->drag;
|
||||
//mBuoyancy = 0.0f;
|
||||
//mGravityMod = 1.0;
|
||||
//mAppliedForce.set(0,0,0);
|
||||
|
||||
ContainerQueryInfo info;
|
||||
info.box = mOwner->getWorldBox();
|
||||
info.mass = mMass;
|
||||
|
||||
mOwner->getContainer()->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info);
|
||||
|
||||
//mWaterCoverage = info.waterCoverage;
|
||||
//mLiquidType = info.liquidType;
|
||||
//mLiquidHeight = info.waterHeight;
|
||||
//setCurrentWaterObject( info.waterObject );
|
||||
|
||||
// This value might be useful as a datablock value,
|
||||
// This is what allows the player to stand in shallow water (below this coverage)
|
||||
// without jiggling from buoyancy
|
||||
if (info.waterCoverage >= 0.25f)
|
||||
{
|
||||
// water viscosity is used as drag for in water.
|
||||
// ShapeBaseData drag is used for drag outside of water.
|
||||
// Combine these two components to calculate this ShapeBase object's
|
||||
// current drag.
|
||||
mDrag = ( info.waterCoverage * info.waterViscosity ) +
|
||||
( 1.0f - info.waterCoverage ) * mDrag;
|
||||
//mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
|
||||
}
|
||||
|
||||
//mAppliedForce = info.appliedForce;
|
||||
mGravityMod = info.gravityScale;
|
||||
}
|
||||
//
|
||||
void PhysicsComponent::_updatePhysics()
|
||||
{
|
||||
/*SAFE_DELETE( mOwner->mPhysicsRep );
|
||||
|
||||
if ( !PHYSICSMGR )
|
||||
return;
|
||||
|
||||
if (mDataBlock->simpleServerCollision)
|
||||
{
|
||||
// We only need the trigger on the server.
|
||||
if ( isServerObject() )
|
||||
{
|
||||
PhysicsCollision *colShape = PHYSICSMGR->createCollision();
|
||||
colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity );
|
||||
|
||||
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
|
||||
mPhysicsRep->setTransform( getTransform() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !mShapeInstance )
|
||||
return;
|
||||
|
||||
PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() );
|
||||
|
||||
if ( colShape )
|
||||
{
|
||||
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world );
|
||||
mPhysicsRep->setTransform( getTransform() );
|
||||
}
|
||||
}*/
|
||||
return;
|
||||
}
|
||||
|
||||
PhysicsBody *PhysicsComponent::getPhysicsRep()
|
||||
{
|
||||
/*if(mOwner)
|
||||
{
|
||||
Entity* ac = dynamic_cast<Entity*>(mOwner);
|
||||
if(ac)
|
||||
return ac->mPhysicsRep;
|
||||
}*/
|
||||
return NULL;
|
||||
}
|
||||
//
|
||||
void PhysicsComponent::setTransform(const MatrixF& mat)
|
||||
{
|
||||
mOwner->setTransform(mat);
|
||||
|
||||
if (!mStatic)
|
||||
{
|
||||
mAtRest = false;
|
||||
mAtRestCounter = 0;
|
||||
}
|
||||
|
||||
if ( getPhysicsRep() )
|
||||
getPhysicsRep()->setTransform( mOwner->getTransform() );
|
||||
|
||||
setMaskBits(UpdateMask);
|
||||
}
|
||||
|
||||
void PhysicsComponent::setPosition(const Point3F& pos)
|
||||
{
|
||||
MatrixF mat = mOwner->getTransform();
|
||||
if (mOwner->isMounted()) {
|
||||
// Use transform from mounted object
|
||||
//mOwner->getObjectMount()->getMountTransform( mOwner->getMountNode(), mMount.xfm, &mat );
|
||||
return;
|
||||
}
|
||||
else {
|
||||
mat.setColumn(3,pos);
|
||||
}
|
||||
|
||||
mOwner->setTransform(mat);
|
||||
|
||||
if ( getPhysicsRep() )
|
||||
getPhysicsRep()->setTransform( mat );
|
||||
}
|
||||
|
||||
|
||||
void PhysicsComponent::setRenderPosition(const Point3F& pos, F32 dt)
|
||||
{
|
||||
MatrixF mat = mOwner->getRenderTransform();
|
||||
if (mOwner->isMounted()) {
|
||||
// Use transform from mounted object
|
||||
//mOwner->getObjectMount()->getMountRenderTransform( dt, mOwner->getMountNode(), mMount.xfm, &mat );
|
||||
return;
|
||||
}
|
||||
else {
|
||||
mat.setColumn(3,pos);
|
||||
}
|
||||
|
||||
mOwner->setRenderTransform(mat);
|
||||
}
|
||||
|
||||
void PhysicsComponent::updateVelocity(const F32 dt)
|
||||
{
|
||||
}
|
||||
|
||||
void PhysicsComponent::setVelocity(const VectorF& vel)
|
||||
{
|
||||
mVelocity = vel;
|
||||
|
||||
mAtRest = false;
|
||||
mAtRestCounter = 0;
|
||||
setMaskBits(VelocityMask);
|
||||
}
|
||||
|
||||
void PhysicsComponent::getVelocity(const Point3F& r, Point3F* v)
|
||||
{
|
||||
*v = mVelocity;
|
||||
}
|
||||
|
||||
void PhysicsComponent::getOriginVector(const Point3F &p,Point3F* r)
|
||||
{
|
||||
*r = p - mOwner->getObjBox().getCenter();
|
||||
}
|
||||
|
||||
F32 PhysicsComponent::getZeroImpulse(const Point3F& r,const Point3F& normal)
|
||||
{
|
||||
Point3F a,b,c;
|
||||
|
||||
//set up our inverse matrix
|
||||
MatrixF iv,qmat;
|
||||
MatrixF inverse = MatrixF::Identity;
|
||||
qmat = mOwner->getTransform();
|
||||
iv.mul(qmat,inverse);
|
||||
qmat.transpose();
|
||||
inverse.mul(iv,qmat);
|
||||
|
||||
mCross(r, normal, &a);
|
||||
inverse.mulV(a, &b);
|
||||
mCross(b, r, &c);
|
||||
|
||||
return 1 / ((1/mMass) + mDot(c, normal));
|
||||
}
|
||||
|
||||
void PhysicsComponent::accumulateForce(F32 dt, Point3F force)
|
||||
{
|
||||
mVelocity += force * dt;
|
||||
}
|
||||
|
||||
void PhysicsComponent::applyImpulse(const Point3F&,const VectorF& vec)
|
||||
{
|
||||
// Items ignore angular velocity
|
||||
VectorF vel;
|
||||
vel.x = vec.x / mMass;
|
||||
vel.y = vec.y / mMass;
|
||||
vel.z = vec.z / mMass;
|
||||
setVelocity(mVelocity + vel);
|
||||
}
|
||||
|
||||
DefineEngineMethod( PhysicsComponent, applyImpulse, bool, ( Point3F pos, VectorF vel ),,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
object->applyImpulse(pos,vel);
|
||||
return true;
|
||||
}
|
||||
135
Engine/source/T3D/components/Physics/physicsBehavior.h
Normal file
135
Engine/source/T3D/components/Physics/physicsBehavior.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PHYSICSBEHAVIOR_H_
|
||||
#define _PHYSICSBEHAVIOR_H_
|
||||
#include "T3D/Components/Component.h"
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _SCENERENDERSTATE_H_
|
||||
#include "scene/sceneRenderState.h"
|
||||
#endif
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#endif
|
||||
#ifndef _ENTITY_H_
|
||||
#include "T3D/Entity.h"
|
||||
#endif
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
#ifndef _BOXCONVEX_H_
|
||||
#include "collision/boxConvex.h"
|
||||
#endif
|
||||
#ifndef _RIGID_H_
|
||||
#include "T3D/rigid.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICS_PHYSICSBODY_H_
|
||||
#include "T3D/physics/physicsBody.h"
|
||||
#endif
|
||||
|
||||
#ifndef _RENDER_COMPONENT_INTERFACE_H_
|
||||
#include "T3D/Components/render/renderComponentInterface.h"
|
||||
#endif
|
||||
|
||||
class TSShapeInstance;
|
||||
class SceneRenderState;
|
||||
class PhysicsBody;
|
||||
class PhysicsBehaviorInstance;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class PhysicsComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
protected:
|
||||
bool mStatic;
|
||||
bool mAtRest;
|
||||
S32 mAtRestCounter;
|
||||
|
||||
VectorF mGravity;
|
||||
VectorF mVelocity;
|
||||
F32 mDrag;
|
||||
F32 mMass;
|
||||
|
||||
F32 mGravityMod;
|
||||
|
||||
S32 csmAtRestTimer;
|
||||
F32 sAtRestVelocity; // Min speed after collisio
|
||||
|
||||
public:
|
||||
enum MaskBits {
|
||||
PositionMask = Parent::NextFreeMask << 0,
|
||||
FreezeMask = Parent::NextFreeMask << 1,
|
||||
ForceMoveMask = Parent::NextFreeMask << 2,
|
||||
VelocityMask = Parent::NextFreeMask << 3,
|
||||
NextFreeMask = Parent::NextFreeMask << 4
|
||||
};
|
||||
|
||||
struct StateDelta
|
||||
{
|
||||
Move move; ///< Last move from server
|
||||
F32 dt; ///< Last interpolation time
|
||||
// Interpolation data
|
||||
Point3F pos;
|
||||
Point3F posVec;
|
||||
QuatF rot[2];
|
||||
// Warp data
|
||||
S32 warpTicks; ///< Number of ticks to warp
|
||||
S32 warpCount; ///< Current pos in warp
|
||||
Point3F warpOffset;
|
||||
QuatF warpRot[2];
|
||||
};
|
||||
|
||||
StateDelta mDelta;
|
||||
S32 mPredictionCount; ///< Number of ticks to predict
|
||||
|
||||
public:
|
||||
PhysicsComponent();
|
||||
virtual ~PhysicsComponent();
|
||||
DECLARE_CONOBJECT(PhysicsComponent);
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void updatePos(const U32 /*mask*/, const F32 dt){}
|
||||
virtual void _updatePhysics();
|
||||
virtual PhysicsBody *getPhysicsRep();
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
virtual void onComponentAdd();
|
||||
|
||||
void updateContainer();
|
||||
|
||||
virtual void updateVelocity(const F32 dt);
|
||||
virtual Point3F getVelocity() { return mVelocity; }
|
||||
virtual void getOriginVector(const Point3F &p, Point3F* r);
|
||||
virtual void getVelocity(const Point3F& r, Point3F* v);
|
||||
virtual void setVelocity(const VectorF& vel);
|
||||
virtual void setTransform(const MatrixF& mat);
|
||||
virtual void setPosition(const Point3F& pos);
|
||||
void setRenderPosition(const Point3F& pos, F32 dt);
|
||||
|
||||
virtual void applyImpulse(const Point3F&, const VectorF& vec);
|
||||
virtual F32 getZeroImpulse(const Point3F& r, const Point3F& normal);
|
||||
virtual void accumulateForce(F32 dt, Point3F force);
|
||||
|
||||
//Rigid Body Collision Conveinence Hooks
|
||||
virtual bool updateCollision(F32 dt, Rigid& ns, CollisionList &cList) { return false; }
|
||||
virtual bool resolveContacts(Rigid& ns, CollisionList& cList, F32 dt) { return false; }
|
||||
//virtual bool resolveCollision(Rigid& ns, CollisionList& cList) { return false; }
|
||||
virtual bool resolveCollision(const Point3F& p, const Point3F &normal) { return false; }
|
||||
};
|
||||
|
||||
#endif // _COMPONENT_H_
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||
#define PHYSICS_COMPONENT_INTERFACE_H
|
||||
|
||||
#ifndef CORE_INTERFACES_H
|
||||
#include "T3D/Components/coreInterfaces.h"
|
||||
#endif
|
||||
|
||||
class PhysicsComponentInterface : public Interface<PhysicsComponentInterface>
|
||||
{
|
||||
protected:
|
||||
VectorF mVelocity;
|
||||
F32 mMass;
|
||||
|
||||
F32 mGravityMod;
|
||||
|
||||
public:
|
||||
void updateForces();
|
||||
|
||||
VectorF getVelocity() { return mVelocity; }
|
||||
void setVelocity(VectorF vel) { mVelocity = vel; }
|
||||
|
||||
F32 getMass() { return mMass; }
|
||||
|
||||
Signal< void(VectorF normal, Vector<SceneObject*> overlappedObjects) > PhysicsComponentInterface::onPhysicsCollision;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,863 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "T3D/Components/Physics/playerControllerComponent.h"
|
||||
#include "platform/platform.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "lighting/lightQuery.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "collision/collision.h"
|
||||
#include "T3D/physics/physicsPlayer.h"
|
||||
#include "T3D/physics/physicsPlugin.h"
|
||||
#include "T3D/Components/Collision/collisionInterfaces.h"
|
||||
#include "T3D/trigger.h"
|
||||
#include "T3D/components/collision/collisionTrigger.h"
|
||||
|
||||
// Movement constants
|
||||
static F32 sVerticalStepDot = 0.173f; // 80
|
||||
static F32 sMinFaceDistance = 0.01f;
|
||||
static F32 sTractionDistance = 0.04f;
|
||||
static F32 sNormalElasticity = 0.01f;
|
||||
static U32 sMoveRetryCount = 5;
|
||||
static F32 sMaxImpulseVelocity = 200.0f;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Callbacks
|
||||
IMPLEMENT_CALLBACK(PlayerControllerComponent, updateMove, void, (PlayerControllerComponent* obj), (obj),
|
||||
"Called when the player updates it's movement, only called if object is set to callback in script(doUpdateMove).\n"
|
||||
"@param obj the Player object\n");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
PlayerControllerComponent::PlayerControllerComponent() : Component()
|
||||
{
|
||||
addComponentField("isStatic", "If enabled, object will not simulate physics", "bool", "0", "");
|
||||
addComponentField("gravity", "The direction of gravity affecting this object, as a vector", "vector", "0 0 -9", "");
|
||||
addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
|
||||
addComponentField("mass", "The mass of the object", "float", "1", "");
|
||||
|
||||
mBuoyancy = 0.f;
|
||||
mFriction = 0.3f;
|
||||
mElasticity = 0.4f;
|
||||
mMaxVelocity = 3000.f;
|
||||
mSticky = false;
|
||||
|
||||
mFalling = false;
|
||||
mSwimming = false;
|
||||
mInWater = false;
|
||||
|
||||
mDelta.pos = mDelta.posVec = Point3F::Zero;
|
||||
mDelta.warpTicks = mDelta.warpCount = 0;
|
||||
mDelta.rot[0].identity();
|
||||
mDelta.rot[1].identity();
|
||||
mDelta.dt = 1;
|
||||
|
||||
mUseDirectMoveInput = false;
|
||||
|
||||
mFriendlyName = "Player Controller";
|
||||
mComponentType = "Physics";
|
||||
|
||||
mDescription = getDescriptionText("A general-purpose physics player controller.");
|
||||
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
mMass = 9.0f; // from ShapeBase
|
||||
mDrag = 1.0f; // from ShapeBase
|
||||
|
||||
maxStepHeight = 1.0f;
|
||||
moveSurfaceAngle = 60.0f;
|
||||
contactSurfaceAngle = 85.0f;
|
||||
|
||||
fallingSpeedThreshold = -10.0f;
|
||||
|
||||
horizMaxSpeed = 80.0f;
|
||||
horizMaxAccel = 100.0f;
|
||||
horizResistSpeed = 38.0f;
|
||||
horizResistFactor = 1.0f;
|
||||
|
||||
upMaxSpeed = 80.0f;
|
||||
upMaxAccel = 100.0f;
|
||||
upResistSpeed = 38.0f;
|
||||
upResistFactor = 1.0f;
|
||||
|
||||
// Air control
|
||||
airControl = 0.0f;
|
||||
|
||||
//Grav mod
|
||||
mGravityMod = 1;
|
||||
|
||||
mInputVelocity = Point3F(0, 0, 0);
|
||||
|
||||
mPhysicsRep = NULL;
|
||||
mPhysicsWorld = NULL;
|
||||
}
|
||||
|
||||
PlayerControllerComponent::~PlayerControllerComponent()
|
||||
{
|
||||
for (S32 i = 0; i < mFields.size(); ++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(PlayerControllerComponent);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool PlayerControllerComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
updatePhysics();
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId())
|
||||
return;
|
||||
|
||||
//test if this is a shape component!
|
||||
CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
|
||||
if (collisionInterface)
|
||||
{
|
||||
collisionInterface->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
|
||||
mOwnerCollisionInterface = collisionInterface;
|
||||
updatePhysics();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId()) //?????????
|
||||
return;
|
||||
|
||||
//test if this is a shape component!
|
||||
CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
|
||||
if (collisionInterface)
|
||||
{
|
||||
collisionInterface->onCollisionChanged.remove(this, &PlayerControllerComponent::updatePhysics);
|
||||
mOwnerCollisionInterface = NULL;
|
||||
updatePhysics();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::updatePhysics(PhysicsCollision *collision)
|
||||
{
|
||||
if (!PHYSICSMGR)
|
||||
return;
|
||||
|
||||
mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||
|
||||
//first, clear the old physRep
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
mPhysicsRep = PHYSICSMGR->createPlayer();
|
||||
|
||||
F32 runSurfaceCos = mCos(mDegToRad(moveSurfaceAngle));
|
||||
|
||||
Point3F ownerBounds = mOwner->getObjBox().getExtents() * mOwner->getScale();
|
||||
|
||||
mPhysicsRep->init("", ownerBounds, runSurfaceCos, maxStepHeight, mOwner, mPhysicsWorld);
|
||||
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("inputVelocity", TypePoint3F, Offset(mInputVelocity, PlayerControllerComponent), "");
|
||||
addField("useDirectMoveInput", TypePoint3F, Offset(mUseDirectMoveInput, PlayerControllerComponent), "");
|
||||
}
|
||||
|
||||
U32 PlayerControllerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
}
|
||||
|
||||
//
|
||||
void PlayerControllerComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
|
||||
if (!isServerObject() || !isActive())
|
||||
return;
|
||||
|
||||
// Warp to catch up to server
|
||||
if (mDelta.warpCount < mDelta.warpTicks)
|
||||
{
|
||||
mDelta.warpCount++;
|
||||
|
||||
// Set new pos.
|
||||
mDelta.pos = mOwner->getPosition();
|
||||
mDelta.pos += mDelta.warpOffset;
|
||||
mDelta.rot[0] = mDelta.rot[1];
|
||||
mDelta.rot[1].interpolate(mDelta.warpRot[0], mDelta.warpRot[1], F32(mDelta.warpCount) / mDelta.warpTicks);
|
||||
|
||||
MatrixF trans;
|
||||
mDelta.rot[1].setMatrix(&trans);
|
||||
trans.setPosition(mDelta.pos);
|
||||
|
||||
mOwner->setTransform(trans);
|
||||
|
||||
// Pos backstepping
|
||||
mDelta.posVec.x = -mDelta.warpOffset.x;
|
||||
mDelta.posVec.y = -mDelta.warpOffset.y;
|
||||
mDelta.posVec.z = -mDelta.warpOffset.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save current rigid state interpolation
|
||||
mDelta.posVec = mOwner->getPosition();
|
||||
mDelta.rot[0] = mOwner->getTransform();
|
||||
|
||||
updateMove();
|
||||
updatePos(TickSec);
|
||||
|
||||
// Wrap up interpolation info
|
||||
mDelta.pos = mOwner->getPosition();
|
||||
mDelta.posVec -= mOwner->getPosition();
|
||||
mDelta.rot[1] = mOwner->getTransform();
|
||||
|
||||
// Update container database
|
||||
setTransform(mOwner->getTransform());
|
||||
|
||||
setMaskBits(VelocityMask);
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::ownerTransformSet(MatrixF *mat)
|
||||
{
|
||||
if (mPhysicsRep)
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::setTransform(const MatrixF& mat)
|
||||
{
|
||||
mOwner->setTransform(mat);
|
||||
|
||||
setMaskBits(UpdateMask);
|
||||
}
|
||||
|
||||
//
|
||||
void PlayerControllerComponent::updateMove()
|
||||
{
|
||||
if (!PHYSICSMGR)
|
||||
return;
|
||||
|
||||
Move *move = &mOwner->lastMove;
|
||||
|
||||
//If we're not set to use mUseDirectMoveInput, then we allow for an override in the form of mInputVelocity
|
||||
if (!mUseDirectMoveInput)
|
||||
{
|
||||
move->x = mInputVelocity.x;
|
||||
move->y = mInputVelocity.y;
|
||||
move->z = mInputVelocity.z;
|
||||
}
|
||||
|
||||
// Is waterCoverage high enough to be 'swimming'?
|
||||
{
|
||||
bool swimming = mOwner->getContainerInfo().waterCoverage > 0.65f/* && canSwim()*/;
|
||||
|
||||
if (swimming != mSwimming)
|
||||
{
|
||||
mSwimming = swimming;
|
||||
}
|
||||
}
|
||||
|
||||
// Update current orientation
|
||||
bool doStandardMove = true;
|
||||
GameConnection* con = mOwner->getControllingClient();
|
||||
|
||||
#ifdef TORQUE_EXTENDED_MOVE
|
||||
// Work with an absolute rotation from the ExtendedMove class?
|
||||
if (con && con->getControlSchemeAbsoluteRotation())
|
||||
{
|
||||
doStandardMove = false;
|
||||
const ExtendedMove* emove = dynamic_cast<const ExtendedMove*>(move);
|
||||
U32 emoveIndex = smExtendedMoveHeadPosRotIndex;
|
||||
if (emoveIndex >= ExtendedMove::MaxPositionsRotations)
|
||||
emoveIndex = 0;
|
||||
|
||||
if (emove->EulerBasedRotation[emoveIndex])
|
||||
{
|
||||
// Head pitch
|
||||
mHead.x += (emove->rotX[emoveIndex] - mLastAbsolutePitch);
|
||||
|
||||
// Do we also include the relative yaw value?
|
||||
if (con->getControlSchemeAddPitchToAbsRot())
|
||||
{
|
||||
F32 x = move->pitch;
|
||||
if (x > M_PI_F)
|
||||
x -= M_2PI_F;
|
||||
|
||||
mHead.x += x;
|
||||
}
|
||||
|
||||
// Constrain the range of mHead.x
|
||||
while (mHead.x < -M_PI_F)
|
||||
mHead.x += M_2PI_F;
|
||||
while (mHead.x > M_PI_F)
|
||||
mHead.x -= M_2PI_F;
|
||||
|
||||
// Rotate (heading) head or body?
|
||||
if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
|
||||
{
|
||||
// Rotate head
|
||||
mHead.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
|
||||
|
||||
// Do we also include the relative yaw value?
|
||||
if (con->getControlSchemeAddYawToAbsRot())
|
||||
{
|
||||
F32 z = move->yaw;
|
||||
if (z > M_PI_F)
|
||||
z -= M_2PI_F;
|
||||
|
||||
mHead.z += z;
|
||||
}
|
||||
|
||||
// Constrain the range of mHead.z
|
||||
while (mHead.z < 0.0f)
|
||||
mHead.z += M_2PI_F;
|
||||
while (mHead.z > M_2PI_F)
|
||||
mHead.z -= M_2PI_F;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rotate body
|
||||
mRot.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
|
||||
|
||||
// Do we also include the relative yaw value?
|
||||
if (con->getControlSchemeAddYawToAbsRot())
|
||||
{
|
||||
F32 z = move->yaw;
|
||||
if (z > M_PI_F)
|
||||
z -= M_2PI_F;
|
||||
|
||||
mRot.z += z;
|
||||
}
|
||||
|
||||
// Constrain the range of mRot.z
|
||||
while (mRot.z < 0.0f)
|
||||
mRot.z += M_2PI_F;
|
||||
while (mRot.z > M_2PI_F)
|
||||
mRot.z -= M_2PI_F;
|
||||
}
|
||||
mLastAbsoluteYaw = emove->rotZ[emoveIndex];
|
||||
mLastAbsolutePitch = emove->rotX[emoveIndex];
|
||||
|
||||
// Head bank
|
||||
mHead.y = emove->rotY[emoveIndex];
|
||||
|
||||
// Constrain the range of mHead.y
|
||||
while (mHead.y > M_PI_F)
|
||||
mHead.y -= M_2PI_F;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MatrixF zRot;
|
||||
zRot.set(EulerF(0.0f, 0.0f, mOwner->getRotation().asEulerF().z));
|
||||
|
||||
// Desired move direction & speed
|
||||
VectorF moveVec;
|
||||
F32 moveSpeed = mInputVelocity.len();
|
||||
|
||||
zRot.getColumn(0, &moveVec);
|
||||
moveVec *= move->x;
|
||||
VectorF tv;
|
||||
zRot.getColumn(1, &tv);
|
||||
moveVec += tv * move->y;
|
||||
|
||||
// Acceleration due to gravity
|
||||
VectorF acc(mPhysicsWorld->getGravity() * mGravityMod * TickSec);
|
||||
|
||||
// Determine ground contact normal. Only look for contacts if
|
||||
// we can move and aren't mounted.
|
||||
mContactInfo.contactNormal = VectorF::Zero;
|
||||
mContactInfo.jump = false;
|
||||
mContactInfo.run = false;
|
||||
|
||||
bool jumpSurface = false, runSurface = false;
|
||||
if (!mOwner->isMounted())
|
||||
findContact(&mContactInfo.run, &mContactInfo.jump, &mContactInfo.contactNormal);
|
||||
if (mContactInfo.jump)
|
||||
mJumpSurfaceNormal = mContactInfo.contactNormal;
|
||||
|
||||
// If we don't have a runSurface but we do have a contactNormal,
|
||||
// then we are standing on something that is too steep.
|
||||
// Deflect the force of gravity by the normal so we slide.
|
||||
// We could also try aligning it to the runSurface instead,
|
||||
// but this seems to work well.
|
||||
if (!mContactInfo.run && !mContactInfo.contactNormal.isZero())
|
||||
acc = (acc - 2 * mContactInfo.contactNormal * mDot(acc, mContactInfo.contactNormal));
|
||||
|
||||
// Acceleration on run surface
|
||||
if (mContactInfo.run && !mSwimming)
|
||||
{
|
||||
mContactTimer = 0;
|
||||
|
||||
VectorF pv = moveVec;
|
||||
|
||||
// Adjust the player's requested dir. to be parallel
|
||||
// to the contact surface.
|
||||
F32 pvl = pv.len();
|
||||
|
||||
// Convert to acceleration
|
||||
if (pvl)
|
||||
pv *= moveSpeed / pvl;
|
||||
VectorF runAcc = pv - (mVelocity + acc);
|
||||
F32 runSpeed = runAcc.len();
|
||||
|
||||
// Clamp acceleration, player also accelerates faster when
|
||||
// in his hard landing recover state.
|
||||
F32 maxAcc;
|
||||
|
||||
maxAcc = (horizMaxAccel / mMass) * TickSec;
|
||||
|
||||
if (runSpeed > maxAcc)
|
||||
runAcc *= maxAcc / runSpeed;
|
||||
|
||||
acc += runAcc;
|
||||
}
|
||||
else if (!mSwimming && airControl > 0.0f)
|
||||
{
|
||||
VectorF pv;
|
||||
pv = moveVec;
|
||||
F32 pvl = pv.len();
|
||||
|
||||
if (pvl)
|
||||
pv *= moveSpeed / pvl;
|
||||
|
||||
VectorF runAcc = pv - (mVelocity + acc);
|
||||
runAcc.z = 0;
|
||||
runAcc.x = runAcc.x * airControl;
|
||||
runAcc.y = runAcc.y * airControl;
|
||||
F32 runSpeed = runAcc.len();
|
||||
|
||||
// We don't test for sprinting when performing air control
|
||||
F32 maxAcc = (horizMaxAccel / mMass) * TickSec * 0.3f;
|
||||
|
||||
if (runSpeed > maxAcc)
|
||||
runAcc *= maxAcc / runSpeed;
|
||||
|
||||
acc += runAcc;
|
||||
|
||||
// There are no special air control animations
|
||||
// so... increment this unless you really want to
|
||||
// play the run anims in the air.
|
||||
mContactTimer++;
|
||||
}
|
||||
else if (mSwimming)
|
||||
{
|
||||
// Remove acc into contact surface (should only be gravity)
|
||||
// Clear out floating point acc errors, this will allow
|
||||
// the player to "rest" on the ground.
|
||||
F32 vd = -mDot(acc, mContactInfo.contactNormal);
|
||||
if (vd > 0.0f)
|
||||
{
|
||||
VectorF dv = mContactInfo.contactNormal * (vd + 0.002f);
|
||||
acc += dv;
|
||||
if (acc.len() < 0.0001f)
|
||||
acc.set(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// get the head pitch and add it to the moveVec
|
||||
// This more accurate swim vector calc comes from Matt Fairfax
|
||||
MatrixF xRot, zRot;
|
||||
xRot.set(EulerF(mOwner->getRotation().asEulerF().x, 0, 0));
|
||||
zRot.set(EulerF(0, 0, mOwner->getRotation().asEulerF().z));
|
||||
MatrixF rot;
|
||||
rot.mul(zRot, xRot);
|
||||
rot.getColumn(0, &moveVec);
|
||||
|
||||
moveVec *= move->x;
|
||||
VectorF tv;
|
||||
rot.getColumn(1, &tv);
|
||||
moveVec += tv * move->y;
|
||||
rot.getColumn(2, &tv);
|
||||
moveVec += tv * move->z;
|
||||
|
||||
// Force a 0 move if there is no energy, and only drain
|
||||
// move energy if we're moving.
|
||||
VectorF swimVec = moveVec;
|
||||
|
||||
// If we are swimming but close enough to the shore/ground
|
||||
// we can still have a surface-normal. In this case align the
|
||||
// velocity to the normal to make getting out of water easier.
|
||||
|
||||
moveVec.normalize();
|
||||
F32 isSwimUp = mDot(moveVec, mContactInfo.contactNormal);
|
||||
|
||||
if (!mContactInfo.contactNormal.isZero() && isSwimUp < 0.1f)
|
||||
{
|
||||
F32 pvl = swimVec.len();
|
||||
|
||||
if (pvl)
|
||||
{
|
||||
VectorF nn;
|
||||
mCross(swimVec, VectorF(0.0f, 0.0f, 1.0f), &nn);
|
||||
nn *= 1.0f / pvl;
|
||||
VectorF cv = mContactInfo.contactNormal;
|
||||
cv -= nn * mDot(nn, cv);
|
||||
swimVec -= cv * mDot(swimVec, cv);
|
||||
}
|
||||
}
|
||||
|
||||
F32 swimVecLen = swimVec.len();
|
||||
|
||||
// Convert to acceleration.
|
||||
if (swimVecLen)
|
||||
swimVec *= moveSpeed / swimVecLen;
|
||||
VectorF swimAcc = swimVec - (mVelocity + acc);
|
||||
F32 swimSpeed = swimAcc.len();
|
||||
|
||||
// Clamp acceleration.
|
||||
F32 maxAcc = (horizMaxAccel / mMass) * TickSec;
|
||||
if (swimSpeed > maxAcc)
|
||||
swimAcc *= maxAcc / swimSpeed;
|
||||
|
||||
acc += swimAcc;
|
||||
|
||||
mContactTimer++;
|
||||
}
|
||||
else
|
||||
mContactTimer++;
|
||||
|
||||
// Add in force from physical zones...
|
||||
acc += (mOwner->getContainerInfo().appliedForce / mMass) * TickSec;
|
||||
|
||||
// Adjust velocity with all the move & gravity acceleration
|
||||
// TG: I forgot why doesn't the TickSec multiply happen here...
|
||||
mVelocity += acc;
|
||||
|
||||
// apply horizontal air resistance
|
||||
|
||||
F32 hvel = mSqrt(mVelocity.x * mVelocity.x + mVelocity.y * mVelocity.y);
|
||||
|
||||
if (hvel > horizResistSpeed)
|
||||
{
|
||||
F32 speedCap = hvel;
|
||||
if (speedCap > horizMaxSpeed)
|
||||
speedCap = horizMaxSpeed;
|
||||
speedCap -= horizResistFactor * TickSec * (speedCap - horizResistSpeed);
|
||||
F32 scale = speedCap / hvel;
|
||||
mVelocity.x *= scale;
|
||||
mVelocity.y *= scale;
|
||||
}
|
||||
if (mVelocity.z > upResistSpeed)
|
||||
{
|
||||
if (mVelocity.z > upMaxSpeed)
|
||||
mVelocity.z = upMaxSpeed;
|
||||
mVelocity.z -= upResistFactor * TickSec * (mVelocity.z - upResistSpeed);
|
||||
}
|
||||
|
||||
// Apply drag
|
||||
mVelocity -= mVelocity * mDrag * TickSec;
|
||||
|
||||
// Clamp very small velocity to zero
|
||||
if (mVelocity.isZero())
|
||||
mVelocity = Point3F::Zero;
|
||||
|
||||
// If we are not touching anything and have sufficient -z vel,
|
||||
// we are falling.
|
||||
if (mContactInfo.run)
|
||||
{
|
||||
mFalling = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorF vel;
|
||||
mOwner->getWorldToObj().mulV(mVelocity, &vel);
|
||||
mFalling = vel.z < fallingSpeedThreshold;
|
||||
}
|
||||
|
||||
// Enter/Leave Liquid
|
||||
if (!mInWater && mOwner->getContainerInfo().waterCoverage > 0.0f)
|
||||
{
|
||||
mInWater = true;
|
||||
}
|
||||
else if (mInWater && mOwner->getContainerInfo().waterCoverage <= 0.0f)
|
||||
{
|
||||
mInWater = false;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::updatePos(const F32 travelTime)
|
||||
{
|
||||
if (!PHYSICSMGR)
|
||||
return;
|
||||
|
||||
PROFILE_SCOPE(PlayerControllerComponent_UpdatePos);
|
||||
|
||||
Point3F newPos;
|
||||
|
||||
Collision col;
|
||||
dMemset(&col, 0, sizeof(col));
|
||||
|
||||
static CollisionList collisionList;
|
||||
collisionList.clear();
|
||||
|
||||
newPos = mPhysicsRep->move(mVelocity * travelTime, collisionList);
|
||||
|
||||
bool haveCollisions = false;
|
||||
bool wasFalling = mFalling;
|
||||
if (collisionList.getCount() > 0)
|
||||
{
|
||||
mFalling = false;
|
||||
haveCollisions = true;
|
||||
|
||||
//TODO: clean this up so the phys component doesn't have to tell the col interface to do this
|
||||
CollisionInterface* colInterface = mOwner->getComponent<CollisionInterface>();
|
||||
if (colInterface)
|
||||
{
|
||||
colInterface->handleCollisionList(collisionList, mVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
if (haveCollisions)
|
||||
{
|
||||
// Pick the collision that most closely matches our direction
|
||||
VectorF velNormal = mVelocity;
|
||||
velNormal.normalizeSafe();
|
||||
const Collision *collision = &collisionList[0];
|
||||
F32 collisionDot = mDot(velNormal, collision->normal);
|
||||
const Collision *cp = collision + 1;
|
||||
const Collision *ep = collision + collisionList.getCount();
|
||||
for (; cp != ep; cp++)
|
||||
{
|
||||
F32 dp = mDot(velNormal, cp->normal);
|
||||
if (dp < collisionDot)
|
||||
{
|
||||
collisionDot = dp;
|
||||
collision = cp;
|
||||
}
|
||||
}
|
||||
|
||||
// Modify our velocity based on collisions
|
||||
for (U32 i = 0; i<collisionList.getCount(); ++i)
|
||||
{
|
||||
F32 bd = -mDot(mVelocity, collisionList[i].normal);
|
||||
VectorF dv = collisionList[i].normal * (bd + sNormalElasticity);
|
||||
mVelocity += dv;
|
||||
}
|
||||
|
||||
// Store the last collision for use later on. The handle collision
|
||||
// code only expects a single collision object.
|
||||
if (collisionList.getCount() > 0)
|
||||
col = collisionList[collisionList.getCount() - 1];
|
||||
|
||||
// We'll handle any player-to-player collision, and the last collision
|
||||
// with other obejct types.
|
||||
for (U32 i = 0; i<collisionList.getCount(); ++i)
|
||||
{
|
||||
Collision& colCheck = collisionList[i];
|
||||
if (colCheck.object)
|
||||
{
|
||||
col = colCheck;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MatrixF newMat;
|
||||
newMat.setPosition(newPos);
|
||||
mPhysicsRep->setTransform(newMat);
|
||||
|
||||
mOwner->setPosition(newPos);
|
||||
}
|
||||
|
||||
//
|
||||
void PlayerControllerComponent::setVelocity(const VectorF& vel)
|
||||
{
|
||||
mVelocity = vel;
|
||||
|
||||
// Clamp against the maximum velocity.
|
||||
if (mMaxVelocity > 0)
|
||||
{
|
||||
F32 len = mVelocity.magnitudeSafe();
|
||||
if (len > mMaxVelocity)
|
||||
{
|
||||
Point3F excess = mVelocity * (1.0f - (mMaxVelocity / len));
|
||||
mVelocity -= excess;
|
||||
}
|
||||
}
|
||||
|
||||
setMaskBits(VelocityMask);
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::findContact(bool *run, bool *jump, VectorF *contactNormal)
|
||||
{
|
||||
SceneObject *contactObject = NULL;
|
||||
|
||||
Vector<SceneObject*> overlapObjects;
|
||||
|
||||
mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
|
||||
|
||||
F32 vd = (*contactNormal).z;
|
||||
*run = vd > mCos(mDegToRad(moveSurfaceAngle));
|
||||
*jump = vd > mCos(mDegToRad(contactSurfaceAngle));
|
||||
|
||||
// Check for triggers
|
||||
for (U32 i = 0; i < overlapObjects.size(); i++)
|
||||
{
|
||||
SceneObject *obj = overlapObjects[i];
|
||||
U32 objectMask = obj->getTypeMask();
|
||||
|
||||
// Check: triggers, corpses and items...
|
||||
//
|
||||
if (objectMask & TriggerObjectType)
|
||||
{
|
||||
if (Trigger* pTrigger = dynamic_cast<Trigger*>(obj))
|
||||
{
|
||||
pTrigger->potentialEnterObject(mOwner);
|
||||
}
|
||||
else if (CollisionTrigger* pTriggerEx = dynamic_cast<CollisionTrigger*>(obj))
|
||||
{
|
||||
if (pTriggerEx)
|
||||
pTriggerEx->potentialEnterObject(mOwner);
|
||||
}
|
||||
//Add any other custom classes and the sort here that should be filtered against
|
||||
/*else if (TriggerExample* pTriggerEx = dynamic_cast<TriggerExample*>(obj))
|
||||
{
|
||||
if (pTriggerEx)
|
||||
pTriggerEx->potentialEnterObject(mOwner);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
mContactInfo.contacted = contactObject != NULL;
|
||||
mContactInfo.contactObject = contactObject;
|
||||
|
||||
if (mContactInfo.contacted)
|
||||
mContactInfo.contactNormal = *contactNormal;
|
||||
}
|
||||
|
||||
void PlayerControllerComponent::applyImpulse(const Point3F &pos, const VectorF &vec)
|
||||
{
|
||||
|
||||
AssertFatal(!mIsNaN(vec), "Player::applyImpulse() - The vector is NaN!");
|
||||
|
||||
// Players ignore angular velocity
|
||||
VectorF vel;
|
||||
vel.x = vec.x / getMass();
|
||||
vel.y = vec.y / getMass();
|
||||
vel.z = vec.z / getMass();
|
||||
|
||||
// Make sure the impulse isn't too bigg
|
||||
F32 len = vel.magnitudeSafe();
|
||||
if (len > sMaxImpulseVelocity)
|
||||
{
|
||||
Point3F excess = vel * (1.0f - (sMaxImpulseVelocity / len));
|
||||
vel -= excess;
|
||||
}
|
||||
|
||||
setVelocity(mVelocity + vel);
|
||||
}
|
||||
|
||||
DefineEngineMethod(PlayerControllerComponent, applyImpulse, bool, (Point3F pos, VectorF vel), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
object->applyImpulse(pos, vel);
|
||||
return true;
|
||||
}
|
||||
|
||||
DefineEngineMethod(PlayerControllerComponent, getContactNormal, Point3F, (), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->getContactNormal();
|
||||
}
|
||||
|
||||
DefineEngineMethod(PlayerControllerComponent, getContactObject, SceneObject*, (), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->getContactObject();
|
||||
}
|
||||
|
||||
DefineEngineMethod(PlayerControllerComponent, isContacted, bool, (), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->isContacted();
|
||||
}
|
||||
212
Engine/source/T3D/components/Physics/playerControllerComponent.h
Normal file
212
Engine/source/T3D/components/Physics/playerControllerComponent.h
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef PLAYER_CONTORLLER_COMPONENT_H
|
||||
#define PLAYER_CONTORLLER_COMPONENT_H
|
||||
|
||||
#ifndef PHYSICSBEHAVIOR_H
|
||||
#include "T3D/Components/Physics/physicsBehavior.h"
|
||||
#endif
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _SCENERENDERSTATE_H_
|
||||
#include "scene/sceneRenderState.h"
|
||||
#endif
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#endif
|
||||
#ifndef ENTITY_H
|
||||
#include "T3D/Entity.h"
|
||||
#endif
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
#ifndef _BOXCONVEX_H_
|
||||
#include "collision/boxConvex.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||
#include "T3D/physics/physicsCommon.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
|
||||
#include "T3D/physics/physicsWorld.h"
|
||||
#endif
|
||||
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||
#include "T3D/Components/physics/physicsComponentInterface.h"
|
||||
#endif
|
||||
#ifndef COLLISION_INTERFACES_H
|
||||
#include "T3D/Components/collision/collisionInterfaces.h"
|
||||
#endif
|
||||
|
||||
class SceneRenderState;
|
||||
class PhysicsWorld;
|
||||
class PhysicsPlayer;
|
||||
class SimplePhysicsBehaviorInstance;
|
||||
class CollisionInterface;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class PlayerControllerComponent : public Component,
|
||||
public PhysicsComponentInterface
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
enum MaskBits {
|
||||
VelocityMask = Parent::NextFreeMask << 0,
|
||||
PositionMask = Parent::NextFreeMask << 1,
|
||||
NextFreeMask = Parent::NextFreeMask << 2
|
||||
};
|
||||
|
||||
struct StateDelta
|
||||
{
|
||||
Move move; ///< Last move from server
|
||||
F32 dt; ///< Last interpolation time
|
||||
// Interpolation data
|
||||
Point3F pos;
|
||||
Point3F posVec;
|
||||
QuatF rot[2];
|
||||
// Warp data
|
||||
S32 warpTicks; ///< Number of ticks to warp
|
||||
S32 warpCount; ///< Current pos in warp
|
||||
Point3F warpOffset;
|
||||
QuatF warpRot[2];
|
||||
};
|
||||
|
||||
StateDelta mDelta;
|
||||
|
||||
PhysicsPlayer *mPhysicsRep;
|
||||
PhysicsWorld *mPhysicsWorld;
|
||||
|
||||
CollisionInterface* mOwnerCollisionInterface;
|
||||
|
||||
struct ContactInfo
|
||||
{
|
||||
bool contacted, jump, run;
|
||||
SceneObject *contactObject;
|
||||
VectorF contactNormal;
|
||||
F32 contactTime;
|
||||
|
||||
void clear()
|
||||
{
|
||||
contacted = jump = run = false;
|
||||
contactObject = NULL;
|
||||
contactNormal.set(1, 1, 1);
|
||||
}
|
||||
|
||||
ContactInfo() { clear(); }
|
||||
|
||||
} mContactInfo;
|
||||
|
||||
protected:
|
||||
F32 mDrag;
|
||||
F32 mBuoyancy;
|
||||
F32 mFriction;
|
||||
F32 mElasticity;
|
||||
F32 mMaxVelocity;
|
||||
bool mSticky;
|
||||
|
||||
bool mFalling;
|
||||
bool mSwimming;
|
||||
bool mInWater;
|
||||
|
||||
S32 mContactTimer; ///< Ticks since last contact
|
||||
|
||||
U32 mIntegrationCount;
|
||||
|
||||
Point3F mJumpSurfaceNormal; ///< Normal of the surface the player last jumped on
|
||||
|
||||
F32 maxStepHeight; ///< Maximum height the player can step up
|
||||
F32 moveSurfaceAngle; ///< Maximum angle from vertical in degrees the player can run up
|
||||
F32 contactSurfaceAngle; ///< Maximum angle from vertical in degrees we consider having real 'contact'
|
||||
|
||||
F32 horizMaxSpeed; ///< Max speed attainable in the horizontal
|
||||
F32 horizMaxAccel;
|
||||
F32 horizResistSpeed; ///< Speed at which resistance will take place
|
||||
F32 horizResistFactor; ///< Factor of resistance once horizResistSpeed has been reached
|
||||
|
||||
F32 upMaxSpeed; ///< Max vertical speed attainable
|
||||
F32 upMaxAccel;
|
||||
F32 upResistSpeed; ///< Speed at which resistance will take place
|
||||
F32 upResistFactor; ///< Factor of resistance once upResistSpeed has been reached
|
||||
|
||||
F32 fallingSpeedThreshold; ///< Downward speed at which we consider the player falling
|
||||
|
||||
// Air control
|
||||
F32 airControl;
|
||||
|
||||
Point3F mInputVelocity;
|
||||
|
||||
bool mUseDirectMoveInput;
|
||||
|
||||
public:
|
||||
PlayerControllerComponent();
|
||||
virtual ~PlayerControllerComponent();
|
||||
DECLARE_CONOBJECT(PlayerControllerComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
|
||||
virtual void ownerTransformSet(MatrixF *mat);
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
void updatePhysics(PhysicsCollision *collision = NULL);
|
||||
|
||||
virtual void processTick();
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void updatePos(const F32 dt);
|
||||
void updateMove();
|
||||
|
||||
virtual VectorF getVelocity() { return mVelocity; }
|
||||
virtual void setVelocity(const VectorF& vel);
|
||||
virtual void setTransform(const MatrixF& mat);
|
||||
|
||||
void findContact(bool *run, bool *jump, VectorF *contactNormal);
|
||||
Point3F getContactNormal() { return mContactInfo.contactNormal; }
|
||||
SceneObject* getContactObject() { return mContactInfo.contactObject; }
|
||||
bool isContacted() { return mContactInfo.contacted; }
|
||||
|
||||
//
|
||||
void applyImpulse(const Point3F &pos, const VectorF &vec);
|
||||
|
||||
//This is a weird artifact of the PhysicsReps. We want the collision component to be privvy to any events that happen
|
||||
//so when the physics components do a findContact test during their update, they'll have a signal collision components
|
||||
//can be listening to to update themselves with that info
|
||||
Signal< void(SceneObject*) > PlayerControllerComponent::onContactSignal;
|
||||
|
||||
//
|
||||
DECLARE_CALLBACK(void, updateMove, (PlayerControllerComponent* obj));
|
||||
};
|
||||
|
||||
#endif // _COMPONENT_H_
|
||||
467
Engine/source/T3D/components/Physics/rigidBodyComponent.cpp
Normal file
467
Engine/source/T3D/components/Physics/rigidBodyComponent.cpp
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "T3D/Components/physics/RigidBodyComponent.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "T3D/physics/physicsBody.h"
|
||||
#include "T3D/physics/physicsPlugin.h"
|
||||
#include "T3D/physics/physicsWorld.h"
|
||||
#include "T3D/physics/physicsCollision.h"
|
||||
#include "T3D/Components/Collision/collisionComponent.h"
|
||||
|
||||
bool RigidBodyComponent::smNoCorrections = false;
|
||||
bool RigidBodyComponent::smNoSmoothing = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
RigidBodyComponent::RigidBodyComponent() : Component()
|
||||
{
|
||||
mMass = 20;
|
||||
mDynamicFriction = 1;
|
||||
mStaticFriction = 0.1f;
|
||||
mRestitution = 10;
|
||||
mLinearDamping = 0;
|
||||
mAngularDamping = 0;
|
||||
mLinearSleepThreshold = 1;
|
||||
mAngularSleepThreshold = 1;
|
||||
mWaterDampingScale = 0.1f;
|
||||
mBuoyancyDensity = 1;
|
||||
|
||||
mSimType = SimType_ServerOnly;
|
||||
|
||||
mPhysicsRep = NULL;
|
||||
mResetPos = MatrixF::Identity;
|
||||
|
||||
mOwnerColComponent = NULL;
|
||||
|
||||
mFriendlyName = "RigidBody(Component)";
|
||||
}
|
||||
|
||||
RigidBodyComponent::~RigidBodyComponent()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(RigidBodyComponent);
|
||||
|
||||
bool RigidBodyComponent::onAdd()
|
||||
{
|
||||
if(! Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RigidBodyComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
void RigidBodyComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
|
||||
void RigidBodyComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
storeRestorePos();
|
||||
PhysicsPlugin::getPhysicsResetSignal().notify(this, &RigidBodyComponent::_onPhysicsReset);
|
||||
}
|
||||
|
||||
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||
if (colComp)
|
||||
{
|
||||
colComp->onCollisionChanged.notify(this, &RigidBodyComponent::updatePhysics);
|
||||
updatePhysics(colComp->getCollisionData());
|
||||
}
|
||||
else
|
||||
updatePhysics();
|
||||
}
|
||||
|
||||
void RigidBodyComponent::onComponentRemove()
|
||||
{
|
||||
Parent::onComponentRemove();
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
PhysicsPlugin::getPhysicsResetSignal().remove(this, &RigidBodyComponent::_onPhysicsReset);
|
||||
}
|
||||
|
||||
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
|
||||
if (colComp)
|
||||
{
|
||||
colComp->onCollisionChanged.remove(this, &RigidBodyComponent::updatePhysics);
|
||||
}
|
||||
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
}
|
||||
|
||||
void RigidBodyComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
CollisionComponent *colComp = dynamic_cast<CollisionComponent*>(comp);
|
||||
if (colComp)
|
||||
{
|
||||
colComp->onCollisionChanged.notify(this, &RigidBodyComponent::updatePhysics);
|
||||
updatePhysics(colComp->getCollisionData());
|
||||
}
|
||||
}
|
||||
|
||||
void RigidBodyComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
//test if this is a shape component!
|
||||
CollisionComponent *colComp = dynamic_cast<CollisionComponent*>(comp);
|
||||
if (colComp)
|
||||
{
|
||||
colComp->onCollisionChanged.remove(this, &RigidBodyComponent::updatePhysics);
|
||||
updatePhysics();
|
||||
}
|
||||
}
|
||||
|
||||
void RigidBodyComponent::ownerTransformSet(MatrixF *mat)
|
||||
{
|
||||
if (mPhysicsRep)
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||
}
|
||||
|
||||
void RigidBodyComponent::updatePhysics(PhysicsCollision* collision)
|
||||
{
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
if (!PHYSICSMGR)
|
||||
return;
|
||||
|
||||
mWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||
|
||||
if (!collision)
|
||||
return;
|
||||
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
|
||||
mPhysicsRep->init(collision, mMass, 0, mOwner, mWorld);
|
||||
|
||||
mPhysicsRep->setMaterial(mRestitution, mDynamicFriction, mStaticFriction);
|
||||
|
||||
mPhysicsRep->setDamping(mLinearDamping, mAngularDamping);
|
||||
mPhysicsRep->setSleepThreshold(mLinearSleepThreshold, mAngularSleepThreshold);
|
||||
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||
|
||||
// The reset position is the transform on the server
|
||||
// at creation time... its not used on the client.
|
||||
if (isServerObject())
|
||||
{
|
||||
storeRestorePos();
|
||||
PhysicsPlugin::getPhysicsResetSignal().notify(this, &RigidBodyComponent::_onPhysicsReset);
|
||||
}
|
||||
}
|
||||
|
||||
U32 RigidBodyComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if (stream->writeFlag(mask & StateMask))
|
||||
{
|
||||
// This will encode the position relative to the control
|
||||
// object position.
|
||||
//
|
||||
// This will compress the position to as little as 6.25
|
||||
// bytes if the position is within about 30 meters of the
|
||||
// control object.
|
||||
//
|
||||
// Worst case its a full 12 bytes + 2 bits if the position
|
||||
// is more than 500 meters from the control object.
|
||||
//
|
||||
stream->writeCompressedPoint(mState.position);
|
||||
|
||||
// Use only 3.5 bytes to send the orientation.
|
||||
stream->writeQuat(mState.orientation, 9);
|
||||
|
||||
// If the server object has been set to sleep then
|
||||
// we don't need to send any velocity.
|
||||
if (!stream->writeFlag(mState.sleeping))
|
||||
{
|
||||
// This gives me ~0.015f resolution in velocity magnitude
|
||||
// while only costing me 1 bit of the velocity is zero length,
|
||||
// <5 bytes in normal cases, and <8 bytes if the velocity is
|
||||
// greater than 1000.
|
||||
AssertWarn(mState.linVelocity.len() < 1000.0f,
|
||||
"PhysicsShape::packUpdate - The linVelocity is out of range!");
|
||||
stream->writeVector(mState.linVelocity, 1000.0f, 16, 9);
|
||||
|
||||
// For angular velocity we get < 0.01f resolution in magnitude
|
||||
// with the most common case being under 4 bytes.
|
||||
AssertWarn(mState.angVelocity.len() < 10.0f,
|
||||
"PhysicsShape::packUpdate - The angVelocity is out of range!");
|
||||
stream->writeVector(mState.angVelocity, 10.0f, 10, 9);
|
||||
}
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void RigidBodyComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
if (stream->readFlag()) // StateMask
|
||||
{
|
||||
PhysicsState state;
|
||||
|
||||
// Read the encoded and compressed position... commonly only 6.25 bytes.
|
||||
stream->readCompressedPoint(&state.position);
|
||||
|
||||
// Read the compressed quaternion... 3.5 bytes.
|
||||
stream->readQuat(&state.orientation, 9);
|
||||
|
||||
state.sleeping = stream->readFlag();
|
||||
if (!state.sleeping)
|
||||
{
|
||||
stream->readVector(&state.linVelocity, 1000.0f, 16, 9);
|
||||
stream->readVector(&state.angVelocity, 10.0f, 10, 9);
|
||||
}
|
||||
|
||||
if (!smNoCorrections && mPhysicsRep && mPhysicsRep->isDynamic())
|
||||
{
|
||||
// Set the new state on the physics object immediately.
|
||||
mPhysicsRep->applyCorrection(state.getTransform());
|
||||
|
||||
mPhysicsRep->setSleeping(state.sleeping);
|
||||
if (!state.sleeping)
|
||||
{
|
||||
mPhysicsRep->setLinVelocity(state.linVelocity);
|
||||
mPhysicsRep->setAngVelocity(state.angVelocity);
|
||||
}
|
||||
|
||||
mPhysicsRep->getState(&mState);
|
||||
}
|
||||
|
||||
// If there is no physics object then just set the
|
||||
// new state... the tick will take care of the
|
||||
// interpolation and extrapolation.
|
||||
if (!mPhysicsRep || !mPhysicsRep->isDynamic())
|
||||
mState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void RigidBodyComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
|
||||
if (!mPhysicsRep || !PHYSICSMGR)
|
||||
return;
|
||||
|
||||
// Note that unlike TSStatic, the serverside PhysicsShape does not
|
||||
// need to play the ambient animation because even if the animation were
|
||||
// to move collision shapes it would not affect the physx representation.
|
||||
|
||||
PROFILE_START(RigidBodyComponent_ProcessTick);
|
||||
|
||||
if (!mPhysicsRep->isDynamic())
|
||||
return;
|
||||
|
||||
// SINGLE PLAYER HACK!!!!
|
||||
if (PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject())
|
||||
{
|
||||
RigidBodyComponent *servObj = (RigidBodyComponent*)getServerObject();
|
||||
mOwner->setTransform(servObj->mState.getTransform());
|
||||
mRenderState[0] = servObj->mRenderState[0];
|
||||
mRenderState[1] = servObj->mRenderState[1];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the last render state.
|
||||
mRenderState[0] = mRenderState[1];
|
||||
|
||||
// If the last render state doesn't match the last simulation
|
||||
// state then we got a correction and need to
|
||||
Point3F errorDelta = mRenderState[1].position - mState.position;
|
||||
const bool doSmoothing = !errorDelta.isZero() && !smNoSmoothing;
|
||||
|
||||
const bool wasSleeping = mState.sleeping;
|
||||
|
||||
// Get the new physics state.
|
||||
mPhysicsRep->getState(&mState);
|
||||
updateContainerForces();
|
||||
|
||||
// Smooth the correction back into the render state.
|
||||
mRenderState[1] = mState;
|
||||
if (doSmoothing)
|
||||
{
|
||||
F32 correction = mClampF(errorDelta.len() / 20.0f, 0.1f, 0.9f);
|
||||
mRenderState[1].position.interpolate(mState.position, mRenderState[0].position, correction);
|
||||
mRenderState[1].orientation.interpolate(mState.orientation, mRenderState[0].orientation, correction);
|
||||
}
|
||||
|
||||
//Check if any collisions occured
|
||||
findContact();
|
||||
|
||||
// If we haven't been sleeping then update our transform
|
||||
// and set ourselves as dirty for the next client update.
|
||||
if (!wasSleeping || !mState.sleeping)
|
||||
{
|
||||
// Set the transform on the parent so that
|
||||
// the physics object isn't moved.
|
||||
mOwner->setTransform(mState.getTransform());
|
||||
|
||||
// If we're doing server simulation then we need
|
||||
// to send the client a state update.
|
||||
if (isServerObject() && mPhysicsRep && !smNoCorrections &&
|
||||
!PHYSICSMGR->isSinglePlayer() // SINGLE PLAYER HACK!!!!
|
||||
)
|
||||
setMaskBits(StateMask);
|
||||
}
|
||||
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
void RigidBodyComponent::findContact()
|
||||
{
|
||||
SceneObject *contactObject = NULL;
|
||||
|
||||
VectorF *contactNormal = new VectorF(0, 0, 0);
|
||||
|
||||
Vector<SceneObject*> overlapObjects;
|
||||
|
||||
mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
|
||||
|
||||
if (!overlapObjects.empty())
|
||||
{
|
||||
//fire our signal that the physics sim said collisions happened
|
||||
onPhysicsCollision.trigger(*contactNormal, overlapObjects);
|
||||
}
|
||||
}
|
||||
|
||||
void RigidBodyComponent::_onPhysicsReset(PhysicsResetEvent reset)
|
||||
{
|
||||
if (reset == PhysicsResetEvent_Store)
|
||||
mResetPos = mOwner->getTransform();
|
||||
|
||||
else if (reset == PhysicsResetEvent_Restore)
|
||||
{
|
||||
mOwner->setTransform(mResetPos);
|
||||
}
|
||||
}
|
||||
|
||||
void RigidBodyComponent::storeRestorePos()
|
||||
{
|
||||
mResetPos = mOwner->getTransform();
|
||||
}
|
||||
|
||||
void RigidBodyComponent::applyImpulse(const Point3F &pos, const VectorF &vec)
|
||||
{
|
||||
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||
mPhysicsRep->applyImpulse(pos, vec);
|
||||
}
|
||||
|
||||
void RigidBodyComponent::applyRadialImpulse(const Point3F &origin, F32 radius, F32 magnitude)
|
||||
{
|
||||
if (!mPhysicsRep || !mPhysicsRep->isDynamic())
|
||||
return;
|
||||
|
||||
// TODO: Find a better approximation of the
|
||||
// force vector using the object box.
|
||||
|
||||
VectorF force = mOwner->getWorldBox().getCenter() - origin;
|
||||
F32 dist = force.magnitudeSafe();
|
||||
force.normalize();
|
||||
|
||||
if (dist == 0.0f)
|
||||
force *= magnitude;
|
||||
else
|
||||
force *= mClampF(radius / dist, 0.0f, 1.0f) * magnitude;
|
||||
|
||||
mPhysicsRep->applyImpulse(origin, force);
|
||||
|
||||
// TODO: There is no simple way to really sync this sort of an
|
||||
// event with the client.
|
||||
//
|
||||
// The best is to send the current physics snapshot, calculate the
|
||||
// time difference from when this event occured and the time when the
|
||||
// client recieves it, and then extrapolate where it should be.
|
||||
//
|
||||
// Even then its impossible to be absolutely sure its synced.
|
||||
//
|
||||
// Bottom line... you shouldn't use physics over the network like this.
|
||||
//
|
||||
}
|
||||
|
||||
void RigidBodyComponent::updateContainerForces()
|
||||
{
|
||||
PROFILE_SCOPE(RigidBodyComponent_updateContainerForces);
|
||||
|
||||
// If we're not simulating don't update forces.
|
||||
PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||
if (!world || !world->isEnabled())
|
||||
return;
|
||||
|
||||
ContainerQueryInfo info;
|
||||
info.box = mOwner->getWorldBox();
|
||||
info.mass = mMass;
|
||||
|
||||
// Find and retreive physics info from intersecting WaterObject(s)
|
||||
mOwner->getContainer()->findObjects(mOwner->getWorldBox(), WaterObjectType | PhysicalZoneObjectType, findRouter, &info);
|
||||
|
||||
// Calculate buoyancy and drag
|
||||
F32 angDrag = mAngularDamping;
|
||||
F32 linDrag = mLinearDamping;
|
||||
F32 buoyancy = 0.0f;
|
||||
Point3F cmass = mPhysicsRep->getCMassPosition();
|
||||
|
||||
F32 density = mBuoyancyDensity;
|
||||
if (density > 0.0f)
|
||||
{
|
||||
if (info.waterCoverage > 0.0f)
|
||||
{
|
||||
F32 waterDragScale = info.waterViscosity * mWaterDampingScale;
|
||||
F32 powCoverage = mPow(info.waterCoverage, 0.25f);
|
||||
|
||||
angDrag = mLerp(angDrag, angDrag * waterDragScale, powCoverage);
|
||||
linDrag = mLerp(linDrag, linDrag * waterDragScale, powCoverage);
|
||||
}
|
||||
|
||||
buoyancy = (info.waterDensity / density) * mPow(info.waterCoverage, 2.0f);
|
||||
|
||||
// A little hackery to prevent oscillation
|
||||
// Based on this blog post:
|
||||
// (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html)
|
||||
// JCF: disabled!
|
||||
Point3F buoyancyForce = buoyancy * -world->getGravity() * TickSec * mMass;
|
||||
mPhysicsRep->applyImpulse(cmass, buoyancyForce);
|
||||
}
|
||||
|
||||
// Update the dampening as the container might have changed.
|
||||
mPhysicsRep->setDamping(linDrag, angDrag);
|
||||
|
||||
// Apply physical zone forces.
|
||||
if (!info.appliedForce.isZero())
|
||||
mPhysicsRep->applyImpulse(cmass, info.appliedForce);
|
||||
}
|
||||
183
Engine/source/T3D/components/Physics/rigidBodyComponent.h
Normal file
183
Engine/source/T3D/components/Physics/rigidBodyComponent.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef RIGID_BODY_COMPONENT_H
|
||||
#define RIGID_BODY_COMPONENT_H
|
||||
|
||||
#ifndef COMPONENT_H
|
||||
#include "T3D/Components/Component.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||
#include "T3D/physics/physicsCommon.h"
|
||||
#endif
|
||||
#ifndef COLLISION_COMPONENT_H
|
||||
#include "T3D/Components/collision/collisionComponent.h"
|
||||
#endif
|
||||
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||
#include "T3D/Components/physics/physicsComponentInterface.h"
|
||||
#endif
|
||||
|
||||
class PhysicsBody;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class RigidBodyComponent : public Component, public PhysicsComponentInterface
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
enum SimType
|
||||
{
|
||||
/// This physics representation only exists on the client
|
||||
/// world and the server only does ghosting.
|
||||
SimType_ClientOnly,
|
||||
|
||||
/// The physics representation only exists on the server world
|
||||
/// and the client gets delta updates for rendering.
|
||||
SimType_ServerOnly,
|
||||
|
||||
/// The physics representation exists on the client and the server
|
||||
/// worlds with corrections occuring when the client gets out of sync.
|
||||
SimType_ClientServer,
|
||||
|
||||
/// The bits used to pack the SimType field.
|
||||
SimType_Bits = 3,
|
||||
|
||||
} mSimType;
|
||||
|
||||
//
|
||||
//
|
||||
/// The current physics state.
|
||||
PhysicsState mState;
|
||||
|
||||
/// The previous and current render states.
|
||||
PhysicsState mRenderState[2];
|
||||
|
||||
/// The abstracted physics actor.
|
||||
PhysicsBody *mPhysicsRep;
|
||||
|
||||
PhysicsWorld *mWorld;
|
||||
|
||||
/// The starting position to place the shape when
|
||||
/// the level begins or is reset.
|
||||
MatrixF mResetPos;
|
||||
//
|
||||
//
|
||||
|
||||
/// If true then no corrections are sent from the server
|
||||
/// and/or applied from the client.
|
||||
///
|
||||
/// This is only ment for debugging.
|
||||
///
|
||||
static bool smNoCorrections;
|
||||
|
||||
/// If true then no smoothing is done on the client when
|
||||
/// applying server corrections.
|
||||
///
|
||||
/// This is only ment for debugging.
|
||||
///
|
||||
static bool smNoSmoothing;
|
||||
|
||||
///
|
||||
F32 mMass;
|
||||
|
||||
///
|
||||
F32 mDynamicFriction;
|
||||
|
||||
///
|
||||
F32 mStaticFriction;
|
||||
|
||||
///
|
||||
F32 mRestitution;
|
||||
|
||||
///
|
||||
F32 mLinearDamping;
|
||||
|
||||
///
|
||||
F32 mAngularDamping;
|
||||
|
||||
///
|
||||
F32 mLinearSleepThreshold;
|
||||
|
||||
///
|
||||
F32 mAngularSleepThreshold;
|
||||
|
||||
// A scale applied to the normal linear and angular damping
|
||||
// when the object enters a water volume.
|
||||
F32 mWaterDampingScale;
|
||||
|
||||
// The density of this object used for water buoyancy effects.
|
||||
F32 mBuoyancyDensity;
|
||||
|
||||
CollisionComponent* mOwnerColComponent;
|
||||
|
||||
enum MaskBits {
|
||||
PositionMask = Parent::NextFreeMask << 0,
|
||||
FreezeMask = Parent::NextFreeMask << 1,
|
||||
StateMask = Parent::NextFreeMask << 2,
|
||||
VelocityMask = Parent::NextFreeMask << 3,
|
||||
NextFreeMask = Parent::NextFreeMask << 4
|
||||
};
|
||||
|
||||
public:
|
||||
RigidBodyComponent();
|
||||
virtual ~RigidBodyComponent();
|
||||
DECLARE_CONOBJECT(RigidBodyComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
|
||||
virtual void ownerTransformSet(MatrixF *mat);
|
||||
|
||||
inline F32 getMass() { return mMass; }
|
||||
Point3F getVelocity() const { return mState.linVelocity; }
|
||||
void applyImpulse(const Point3F &pos, const VectorF &vec);
|
||||
void applyRadialImpulse(const Point3F &origin, F32 radius, F32 magnitude);
|
||||
|
||||
void updateContainerForces();
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
virtual void processTick();
|
||||
|
||||
void findContact();
|
||||
|
||||
/// Save the current transform as where we return to when a physics reset
|
||||
/// event occurs. This is automatically set in onAdd but some manipulators
|
||||
/// such as Prefab need to make use of this.
|
||||
void storeRestorePos();
|
||||
|
||||
void updatePhysics(PhysicsCollision *collision = NULL);
|
||||
|
||||
void _onPhysicsReset(PhysicsResetEvent reset);
|
||||
};
|
||||
|
||||
#endif // _RIGID_BODY_COMPONENT_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue