mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-22 16:13:45 +00:00
code review:
1) got rid of evey class having it's own gravity 2) rigidshape inheritance simplifications 3) gravitymod from physicszones taking buoyancy into account natively (we still track raw bouyancy to cancel it out for player) 4) disableMove used throughout 5) items can now also be influenced by the appliedforce from physicszones
This commit is contained in:
parent
76c5e30869
commit
afb39d398f
13 changed files with 191 additions and 487 deletions
|
|
@ -47,7 +47,8 @@
|
|||
#include "sfx/sfxSystem.h"
|
||||
#include "T3D/fx/particleEmitter.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
#include "T3D/physics/physicsPlugin.h"
|
||||
#include "T3D/physics/physicsCollision.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData);
|
||||
|
||||
|
|
@ -152,45 +153,33 @@ ConsoleDocClass( RigidShape,
|
|||
"@ingroup Physics\n"
|
||||
);
|
||||
|
||||
IMPLEMENT_CALLBACK(RigidShapeData, onEnterLiquid, void, (RigidShape* obj, F32 coverage, const char* type), (obj, coverage, type),
|
||||
"Called when the vehicle enters liquid.\n"
|
||||
"@param obj the Vehicle object\n"
|
||||
"@param coverage percentage of the vehicle's bounding box covered by the liquid\n"
|
||||
"@param type type of liquid the vehicle has entered\n");
|
||||
|
||||
IMPLEMENT_CALLBACK( RigidShape, onEnterLiquid, void, ( const char* objId, F32 waterCoverage, const char* liquidType ),
|
||||
( objId, waterCoverage, liquidType ),
|
||||
"@brief Called whenever this RigidShape object enters liquid.\n\n"
|
||||
"@param objId The ID of the rigidShape object.\n"
|
||||
"@param waterCoverage Amount of water coverage the RigidShape has.\n"
|
||||
"@param liquidType Type of liquid that was entered.\n\n"
|
||||
"@tsexample\n"
|
||||
"// The RigidShape object falls in a body of liquid, causing the callback to occur.\n"
|
||||
"RigidShape::onEnterLiquid(%this,%objId,%waterCoverage,%liquidType)\n"
|
||||
" {\n"
|
||||
" // Code to run whenever this callback occurs.\n"
|
||||
" }\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see ShapeBase\n\n"
|
||||
);
|
||||
|
||||
IMPLEMENT_CALLBACK( RigidShape, onLeaveLiquid, void, ( const char* objId, const char* liquidType ),( objId, liquidType ),
|
||||
"@brief Called whenever the RigidShape object exits liquid.\n\n"
|
||||
"@param objId The ID of the RigidShape object.\n"
|
||||
"@param liquidType Type if liquid that was exited.\n\n"
|
||||
"@tsexample\n"
|
||||
"// The RigidShape object exits in a body of liquid, causing the callback to occur.\n"
|
||||
"RigidShape::onLeaveLiquid(%this,%objId,%liquidType)\n"
|
||||
" {\n"
|
||||
" // Code to run whenever this callback occurs.\n"
|
||||
" }\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see ShapeBase\n\n"
|
||||
);
|
||||
IMPLEMENT_CALLBACK(RigidShapeData, onLeaveLiquid, void, (RigidShape* obj, const char* type), (obj, type),
|
||||
"Called when the vehicle leaves liquid.\n"
|
||||
"@param obj the Vehicle object\n"
|
||||
"@param type type of liquid the vehicle has left\n");
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
static U32 sWorkingQueryBoxStaleThreshold = 10; // The maximum number of ticks that go by before
|
||||
// the mWorkingQueryBox is considered stale and
|
||||
// needs updating. Set to -1 to disable.
|
||||
|
||||
static F32 sWorkingQueryBoxSizeMultiplier = 2.0f; // How much larger should the mWorkingQueryBox be
|
||||
// made when updating the working collision list.
|
||||
// The larger this number the less often the working list
|
||||
// will be updated due to motion, but any non-static shape
|
||||
// that moves into the query box will not be noticed.
|
||||
// Client prediction
|
||||
const S32 sMaxWarpTicks = 3; // Max warp duration in ticks
|
||||
const S32 sMaxPredictionTicks = 30; // Number of ticks to predict
|
||||
const F32 sRigidShapeGravity = -20;
|
||||
|
||||
// Physics and collision constants
|
||||
static F32 sRestTol = 0.5; // % of gravity energy to be at rest
|
||||
|
|
@ -265,6 +254,7 @@ RigidShapeData::RigidShapeData()
|
|||
softSplashSoundVel = 1.0;
|
||||
medSplashSoundVel = 2.0;
|
||||
hardSplashSoundVel = 3.0;
|
||||
enablePhysicsRep = true;
|
||||
|
||||
dMemset(waterSound, 0, sizeof(waterSound));
|
||||
|
||||
|
|
@ -390,6 +380,7 @@ void RigidShapeData::packData(BitStream* stream)
|
|||
stream->write(softSplashSoundVel);
|
||||
stream->write(medSplashSoundVel);
|
||||
stream->write(hardSplashSoundVel);
|
||||
stream->write(enablePhysicsRep);
|
||||
|
||||
// write the water sound profiles
|
||||
for( U32 i = 0; i < MaxSounds; ++ i )
|
||||
|
|
@ -448,6 +439,7 @@ void RigidShapeData::unpackData(BitStream* stream)
|
|||
stream->read(&softSplashSoundVel);
|
||||
stream->read(&medSplashSoundVel);
|
||||
stream->read(&hardSplashSoundVel);
|
||||
stream->read(&enablePhysicsRep);
|
||||
|
||||
// write the water sound profiles
|
||||
for( U32 i = 0; i < MaxSounds; ++ i )
|
||||
|
|
@ -477,6 +469,11 @@ void RigidShapeData::unpackData(BitStream* stream)
|
|||
|
||||
void RigidShapeData::initPersistFields()
|
||||
{
|
||||
addGroup("Physics");
|
||||
addField("enablePhysicsRep", TypeBool, Offset(enablePhysicsRep, RigidShapeData),
|
||||
"@brief Creates a representation of the object in the physics plugin.\n");
|
||||
endGroup("Physics");
|
||||
|
||||
addField("massCenter", TypePoint3F, Offset(massCenter, RigidShapeData), "Center of mass for rigid body.");
|
||||
addField("massBox", TypePoint3F, Offset(massBox, RigidShapeData), "Size of inertial box.");
|
||||
addField("bodyRestitution", TypeF32, Offset(body.restitution, RigidShapeData), "The percentage of kinetic energy kept by this object in a collision.");
|
||||
|
|
@ -592,6 +589,12 @@ RigidShape::RigidShape()
|
|||
restCount = 0;
|
||||
|
||||
inLiquid = false;
|
||||
|
||||
mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
|
||||
mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
|
||||
mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
|
||||
|
||||
mPhysicsRep = NULL;
|
||||
}
|
||||
|
||||
RigidShape::~RigidShape()
|
||||
|
|
@ -619,6 +622,9 @@ bool RigidShape::onAdd()
|
|||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
|
||||
mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
|
||||
|
||||
// When loading from a mission script, the base SceneObject's transform
|
||||
// will have been set and needs to be transfered to the rigid body.
|
||||
mRigid.setTransform(mObjToWorld);
|
||||
|
|
@ -672,6 +678,7 @@ bool RigidShape::onAdd()
|
|||
mConvex.box.minExtents.convolve(mObjScale);
|
||||
mConvex.box.maxExtents.convolve(mObjScale);
|
||||
mConvex.findNodeTransform();
|
||||
_createPhysics();
|
||||
|
||||
addToScene();
|
||||
|
||||
|
|
@ -722,14 +729,36 @@ void RigidShape::onRemove()
|
|||
}
|
||||
}
|
||||
|
||||
mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
|
||||
mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void RigidShape::_createPhysics()
|
||||
{
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
if (!PHYSICSMGR || !mDataBlock->enablePhysicsRep)
|
||||
return;
|
||||
|
||||
TSShape* shape = mShapeInstance->getShape();
|
||||
PhysicsCollision* colShape = NULL;
|
||||
colShape = shape->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());
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void RigidShape::processTick(const Move* move)
|
||||
{
|
||||
PROFILE_SCOPE(RigidShape_ProcessTick);
|
||||
|
||||
Parent::processTick(move);
|
||||
if ( isMounted() )
|
||||
return;
|
||||
|
|
@ -776,6 +805,8 @@ void RigidShape::processTick(const Move* move)
|
|||
|
||||
// Update the physics based on the integration rate
|
||||
S32 count = mDataBlock->integration;
|
||||
--mWorkingQueryBoxCountDown;
|
||||
|
||||
if (!mDisableMove)
|
||||
updateWorkingCollisionSet(getCollisionMask());
|
||||
for (U32 i = 0; i < count; i++)
|
||||
|
|
@ -790,6 +821,11 @@ void RigidShape::processTick(const Move* move)
|
|||
setPosition(mRigid.linPosition, mRigid.angPosition);
|
||||
setMaskBits(PositionMask);
|
||||
updateContainer();
|
||||
|
||||
//TODO: Only update when position has actually changed
|
||||
//no need to check if mDataBlock->enablePhysicsRep is false as mPhysicsRep will be NULL if it is
|
||||
if (mPhysicsRep)
|
||||
mPhysicsRep->moveKinematicTo(getTransform());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1036,6 +1072,8 @@ void RigidShape::enableCollision()
|
|||
|
||||
void RigidShape::updatePos(F32 dt)
|
||||
{
|
||||
PROFILE_SCOPE(Vehicle_UpdatePos);
|
||||
|
||||
Point3F origVelocity = mRigid.linVelocity;
|
||||
|
||||
// Update internal forces acting on the body.
|
||||
|
|
@ -1044,19 +1082,19 @@ void RigidShape::updatePos(F32 dt)
|
|||
|
||||
// Update collision information based on our current pos.
|
||||
bool collided = false;
|
||||
if (!mRigid.atRest && !mDisableMove)
|
||||
if (!mRigid.atRest && !mDisableMove)
|
||||
{
|
||||
collided = updateCollision(dt);
|
||||
|
||||
// Now that all the forces have been processed, lets
|
||||
// Now that all the forces have been processed, lets
|
||||
// see if we're at rest. Basically, if the kinetic energy of
|
||||
// the shape is less than some percentage of the energy added
|
||||
// the rigid body is less than some percentage of the energy added
|
||||
// by gravity for a short period, we're considered at rest.
|
||||
// This should really be part of the rigid class...
|
||||
if (mCollisionList.getCount())
|
||||
if (mCollisionList.getCount())
|
||||
{
|
||||
F32 k = mRigid.getKineticEnergy();
|
||||
F32 G = sRigidShapeGravity * dt;
|
||||
F32 G = mNetGravity * dt;
|
||||
F32 Kg = 0.5 * mRigid.mass * G * G;
|
||||
if (k < sRestTol * Kg && ++restCount > sRestCount)
|
||||
mRigid.setAtRest();
|
||||
|
|
@ -1070,7 +1108,7 @@ void RigidShape::updatePos(F32 dt)
|
|||
mRigid.integrate(dt);
|
||||
|
||||
// Deal with client and server scripting, sounds, etc.
|
||||
if (isServerObject())
|
||||
if (isServerObject())
|
||||
{
|
||||
|
||||
// Check triggers and other objects that we normally don't
|
||||
|
|
@ -1083,7 +1121,7 @@ void RigidShape::updatePos(F32 dt)
|
|||
notifyCollision();
|
||||
|
||||
// Server side impact script callback
|
||||
if (collided)
|
||||
if (collided)
|
||||
{
|
||||
VectorF collVec = mRigid.linVelocity - origVelocity;
|
||||
F32 collSpeed = collVec.len();
|
||||
|
|
@ -1091,15 +1129,15 @@ void RigidShape::updatePos(F32 dt)
|
|||
onImpact(collVec);
|
||||
}
|
||||
|
||||
// Water script callbacks
|
||||
if (!inLiquid && mWaterCoverage != 0.0f)
|
||||
// Water script callbacks
|
||||
if (!inLiquid && mWaterCoverage != 0.0f)
|
||||
{
|
||||
onEnterLiquid_callback(getIdString(), mWaterCoverage, mLiquidType.c_str() );
|
||||
mDataBlock->onEnterLiquid_callback(this, mWaterCoverage, mLiquidType.c_str());
|
||||
inLiquid = true;
|
||||
}
|
||||
else if (inLiquid && mWaterCoverage == 0.0f)
|
||||
else if (inLiquid && mWaterCoverage == 0.0f)
|
||||
{
|
||||
onLeaveLiquid_callback(getIdString(), mLiquidType.c_str() );
|
||||
mDataBlock->onLeaveLiquid_callback(this, mLiquidType.c_str());
|
||||
inLiquid = false;
|
||||
}
|
||||
|
||||
|
|
@ -1123,7 +1161,7 @@ void RigidShape::updatePos(F32 dt)
|
|||
// Water volume sounds
|
||||
F32 vSpeed = getVelocity().len();
|
||||
if (!inLiquid && mWaterCoverage >= 0.8f) {
|
||||
if (vSpeed >= mDataBlock->hardSplashSoundVel)
|
||||
if (vSpeed >= mDataBlock->hardSplashSoundVel)
|
||||
SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactHard], &getTransform());
|
||||
else
|
||||
if (vSpeed >= mDataBlock->medSplashSoundVel)
|
||||
|
|
@ -1132,9 +1170,9 @@ void RigidShape::updatePos(F32 dt)
|
|||
if (vSpeed >= mDataBlock->softSplashSoundVel)
|
||||
SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactSoft], &getTransform());
|
||||
inLiquid = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
if(inLiquid && mWaterCoverage < 0.8f) {
|
||||
if (inLiquid && mWaterCoverage < 0.8f) {
|
||||
if (vSpeed >= mDataBlock->exitSplashSoundVel)
|
||||
SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ExitWater], &getTransform());
|
||||
inLiquid = false;
|
||||
|
|
@ -1142,40 +1180,31 @@ void RigidShape::updatePos(F32 dt)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void RigidShape::updateForces(F32 /*dt*/)
|
||||
void RigidShape::updateForces(F32 dt)
|
||||
{
|
||||
if (mDisableMove) return;
|
||||
Point3F gravForce(0, 0, sRigidShapeGravity * mRigid.mass * mGravityMod);
|
||||
|
||||
MatrixF currTransform;
|
||||
mRigid.getTransform(&currTransform);
|
||||
|
||||
Point3F torque(0, 0, 0);
|
||||
Point3F force(0, 0, 0);
|
||||
|
||||
Point3F vel = mRigid.linVelocity;
|
||||
|
||||
// Gravity
|
||||
force += gravForce;
|
||||
Point3F force(0, 0, mRigid.mass * mNetGravity);
|
||||
|
||||
// Apply drag
|
||||
Point3F vDrag = mRigid.linVelocity;
|
||||
vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor));
|
||||
force -= vDrag * mDataBlock->dragForce;
|
||||
Point3F vertDrag = mRigid.linVelocity*Point3F(1, 1, mDataBlock->vertFactor);
|
||||
force -= vertDrag * mDataBlock->dragForce;
|
||||
|
||||
// Add in physical zone force
|
||||
force += mAppliedForce;
|
||||
|
||||
// Container buoyancy & drag
|
||||
force += Point3F(0, 0,-mBuoyancy * sRigidShapeGravity * mRigid.mass * mGravityMod);
|
||||
force -= mRigid.linVelocity * mDrag;
|
||||
torque -= mRigid.angMomentum * mDrag;
|
||||
|
||||
mRigid.force = force;
|
||||
mRigid.torque = torque;
|
||||
|
||||
// If we're still atRest, make sure we're not accumulating anything
|
||||
if (mRigid.atRest)
|
||||
mRigid.setAtRest();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1369,17 +1398,53 @@ bool RigidShape::resolveDisplacement(Rigid& ns,CollisionState *state, F32 dt)
|
|||
|
||||
void RigidShape::updateWorkingCollisionSet(const U32 mask)
|
||||
{
|
||||
PROFILE_SCOPE( Vehicle_UpdateWorkingCollisionSet );
|
||||
|
||||
// First, we need to adjust our velocity for possible acceleration. It is assumed
|
||||
// that we will never accelerate more than 20 m/s for gravity, plus 30 m/s for
|
||||
// jetting, and an equivalent 10 m/s for vehicle accel. We also assume that our
|
||||
// working list is updated on a Tick basis, which means we only expand our box by
|
||||
// the possible movement in that tick, plus some extra for caching purposes
|
||||
Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
|
||||
F32 len = (mRigid.linVelocity.len() + 50) * TickSec;
|
||||
F32 l = (len * 1.1) + 0.1; // fudge factor
|
||||
convexBox.minExtents -= Point3F(l, l, l);
|
||||
convexBox.maxExtents += Point3F(l, l, l);
|
||||
|
||||
disableCollision();
|
||||
mConvex.updateWorkingList(convexBox, mask);
|
||||
enableCollision();
|
||||
}
|
||||
// Check to see if it is actually necessary to construct the new working list,
|
||||
// or if we can use the cached version from the last query. We use the x
|
||||
// component of the min member of the mWorkingQueryBox, which is lame, but
|
||||
// it works ok.
|
||||
bool updateSet = false;
|
||||
|
||||
// Check containment
|
||||
if ((sWorkingQueryBoxStaleThreshold == -1 || mWorkingQueryBoxCountDown > 0) && mWorkingQueryBox.minExtents.x != -1e9f)
|
||||
{
|
||||
if (mWorkingQueryBox.isContained(convexBox) == false)
|
||||
// Needed region is outside the cached region. Update it.
|
||||
updateSet = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Must update
|
||||
updateSet = true;
|
||||
}
|
||||
|
||||
// Actually perform the query, if necessary
|
||||
if (updateSet == true)
|
||||
{
|
||||
mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
|
||||
|
||||
const Point3F lPoint( sWorkingQueryBoxSizeMultiplier * l );
|
||||
mWorkingQueryBox = convexBox;
|
||||
mWorkingQueryBox.minExtents -= lPoint;
|
||||
mWorkingQueryBox.maxExtents += lPoint;
|
||||
|
||||
disableCollision();
|
||||
mConvex.updateWorkingList(mWorkingQueryBox, mask);
|
||||
enableCollision();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/** Check collisions with trigger and items
|
||||
|
|
@ -1573,6 +1638,25 @@ void RigidShape::unpackUpdate(NetConnection *con, BitStream *stream)
|
|||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void RigidShape::consoleInit()
|
||||
{
|
||||
Con::addVariable("$rigidPhysics::workingQueryBoxStaleThreshold", TypeS32, &sWorkingQueryBoxStaleThreshold,
|
||||
"@brief The maximum number of ticks that go by before the mWorkingQueryBox is considered stale and needs updating.\n\n"
|
||||
"Other factors can cause the collision working query box to become invalidated, such as the rigid body moving far "
|
||||
"enough outside of this cached box. The smaller this number, the more times the working list of triangles that are "
|
||||
"considered for collision is refreshed. This has the greatest impact with colliding with high triangle count meshes.\n\n"
|
||||
"@note Set to -1 to disable any time-based forced check.\n\n"
|
||||
"@ingroup GameObjects\n");
|
||||
|
||||
Con::addVariable("$rigidPhysics::workingQueryBoxSizeMultiplier", TypeF32, &sWorkingQueryBoxSizeMultiplier,
|
||||
"@brief How much larger the mWorkingQueryBox should be made when updating the working collision list.\n\n"
|
||||
"The larger this number the less often the working list will be updated due to motion, but any non-static shape that "
|
||||
"moves into the query box will not be noticed.\n\n"
|
||||
"@ingroup GameObjects\n");
|
||||
}
|
||||
|
||||
void RigidShape::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue