mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-15 00:24:40 +00:00
update rigid to work with physics api
This commit is contained in:
parent
c03cedec6f
commit
839debff57
4 changed files with 454 additions and 151 deletions
|
|
@ -47,8 +47,8 @@ Rigid::Rigid()
|
||||||
friction = 0.5f;
|
friction = 0.5f;
|
||||||
atRest = false;
|
atRest = false;
|
||||||
|
|
||||||
sleepLinearThreshold = POINT_EPSILON;
|
sleepLinearThreshold = 0.01f;
|
||||||
sleepAngThreshold = POINT_EPSILON;
|
sleepAngThreshold = 0.01f;
|
||||||
sleepTimeThreshold = 0.75f;
|
sleepTimeThreshold = 0.75f;
|
||||||
sleepTimer = 0.0f;
|
sleepTimer = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
@ -76,8 +76,8 @@ void Rigid::integrate(F32 delta)
|
||||||
{
|
{
|
||||||
QuatF dq;
|
QuatF dq;
|
||||||
F32 sinHalfAngle;
|
F32 sinHalfAngle;
|
||||||
mSinCos(angle * delta * -0.5f, sinHalfAngle, dq.w);
|
mSinCos(angle * -0.5f, sinHalfAngle, dq.w);
|
||||||
sinHalfAngle *= 1.0f / angle;
|
sinHalfAngle *= delta / angle;
|
||||||
dq.x = angVelocity.x * sinHalfAngle;
|
dq.x = angVelocity.x * sinHalfAngle;
|
||||||
dq.y = angVelocity.y * sinHalfAngle;
|
dq.y = angVelocity.y * sinHalfAngle;
|
||||||
dq.z = angVelocity.z * sinHalfAngle;
|
dq.z = angVelocity.z * sinHalfAngle;
|
||||||
|
|
@ -101,7 +101,7 @@ void Rigid::integrate(F32 delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. refresh ang velocity
|
// 5. refresh ang velocity
|
||||||
updateAngularVelocity(delta);
|
updateAngularVelocity();
|
||||||
|
|
||||||
|
|
||||||
// 6. CoM update
|
// 6. CoM update
|
||||||
|
|
@ -138,7 +138,7 @@ void Rigid::updateCenterOfMass()
|
||||||
|
|
||||||
void Rigid::applyImpulse(const Point3F &r, const Point3F &impulse)
|
void Rigid::applyImpulse(const Point3F &r, const Point3F &impulse)
|
||||||
{
|
{
|
||||||
if ((impulse.lenSquared() - mass) < POINT_EPSILON) return;
|
if (impulse.lenSquared() < POINT_EPSILON) return;
|
||||||
wake();
|
wake();
|
||||||
|
|
||||||
// Linear momentum and velocity
|
// Linear momentum and velocity
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ public:
|
||||||
//
|
//
|
||||||
void setSleepThresholds(F32 linVel2, F32 angVel2, F32 timeToSleep);
|
void setSleepThresholds(F32 linVel2, F32 angVel2, F32 timeToSleep);
|
||||||
void wake();
|
void wake();
|
||||||
TORQUE_FORCEINLINE void updateAngularVelocity(F32 delta) { Point3F deltaVel = angVelocity * delta; invWorldInertia.mulV(angMomentum, &deltaVel); }
|
TORQUE_FORCEINLINE void updateAngularVelocity() { invWorldInertia.mulV(angMomentum, &angVelocity); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
#include "console/engineAPI.h"
|
#include "console/engineAPI.h"
|
||||||
#include "T3D/physics/physicsPlugin.h"
|
#include "T3D/physics/physicsPlugin.h"
|
||||||
#include "T3D/physics/physicsCollision.h"
|
#include "T3D/physics/physicsCollision.h"
|
||||||
|
#include "T3D/containerQuery.h"
|
||||||
|
|
||||||
IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData);
|
IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData);
|
||||||
|
|
||||||
|
|
@ -175,6 +176,7 @@ namespace {
|
||||||
// The larger this number the less often the working list
|
// The larger this number the less often the working list
|
||||||
// will be updated due to motion, but any non-static shape
|
// will be updated due to motion, but any non-static shape
|
||||||
// that moves into the query box will not be noticed.
|
// that moves into the query box will not be noticed.
|
||||||
|
//
|
||||||
// Client prediction
|
// Client prediction
|
||||||
const S32 sMaxWarpTicks = 3; // Max warp duration in ticks
|
const S32 sMaxWarpTicks = 3; // Max warp duration in ticks
|
||||||
const S32 sMaxPredictionTicks = 30; // Number of ticks to predict
|
const S32 sMaxPredictionTicks = 30; // Number of ticks to predict
|
||||||
|
|
@ -189,6 +191,8 @@ namespace {
|
||||||
|
|
||||||
const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
|
const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
|
||||||
const U32 sClientCollisionMask = sCollisionMoveMask;
|
const U32 sClientCollisionMask = sCollisionMoveMask;
|
||||||
|
bool smNoCorrections = false;
|
||||||
|
bool smNoSmoothing = false;
|
||||||
|
|
||||||
void nonFilter(SceneObject* object,void *key)
|
void nonFilter(SceneObject* object,void *key)
|
||||||
{
|
{
|
||||||
|
|
@ -272,6 +276,7 @@ RigidShapeData::RigidShapeData()
|
||||||
medSplashSoundVel = 2.0;
|
medSplashSoundVel = 2.0;
|
||||||
hardSplashSoundVel = 3.0;
|
hardSplashSoundVel = 3.0;
|
||||||
enablePhysicsRep = true;
|
enablePhysicsRep = true;
|
||||||
|
isDynamic = false;
|
||||||
|
|
||||||
for (S32 i = 0; i < Sounds::MaxSounds; i++)
|
for (S32 i = 0; i < Sounds::MaxSounds; i++)
|
||||||
INIT_SOUNDASSET_ARRAY(WaterSounds, i);
|
INIT_SOUNDASSET_ARRAY(WaterSounds, i);
|
||||||
|
|
@ -453,6 +458,7 @@ void RigidShapeData::packData(BitStream* stream)
|
||||||
stream->write(medSplashSoundVel);
|
stream->write(medSplashSoundVel);
|
||||||
stream->write(hardSplashSoundVel);
|
stream->write(hardSplashSoundVel);
|
||||||
stream->write(enablePhysicsRep);
|
stream->write(enablePhysicsRep);
|
||||||
|
stream->write(isDynamic);
|
||||||
|
|
||||||
// write the water sound profiles
|
// write the water sound profiles
|
||||||
for (U32 i = 0; i < Sounds::MaxSounds; ++i)
|
for (U32 i = 0; i < Sounds::MaxSounds; ++i)
|
||||||
|
|
@ -517,6 +523,7 @@ void RigidShapeData::unpackData(BitStream* stream)
|
||||||
stream->read(&medSplashSoundVel);
|
stream->read(&medSplashSoundVel);
|
||||||
stream->read(&hardSplashSoundVel);
|
stream->read(&hardSplashSoundVel);
|
||||||
stream->read(&enablePhysicsRep);
|
stream->read(&enablePhysicsRep);
|
||||||
|
stream->read(&isDynamic);
|
||||||
|
|
||||||
// write the water sound profiles
|
// write the water sound profiles
|
||||||
for (U32 i = 0; i < Sounds::MaxSounds; ++i)
|
for (U32 i = 0; i < Sounds::MaxSounds; ++i)
|
||||||
|
|
@ -568,6 +575,12 @@ void RigidShapeData::initPersistFields()
|
||||||
addGroup("Physics");
|
addGroup("Physics");
|
||||||
addField("enablePhysicsRep", TypeBool, Offset(enablePhysicsRep, RigidShapeData),
|
addField("enablePhysicsRep", TypeBool, Offset(enablePhysicsRep, RigidShapeData),
|
||||||
"@brief Creates a representation of the object in the physics plugin.\n");
|
"@brief Creates a representation of the object in the physics plugin.\n");
|
||||||
|
addField("isDynamic", TypeBool, Offset(isDynamic, RigidShapeData),
|
||||||
|
"@brief When true and a physics plugin is active, the body is fully dynamic:\n"
|
||||||
|
"the plugin owns collision detection, constraint solving and sleep.\n"
|
||||||
|
"When false (default) the body is kinematic — the built-in Rigid integrator\n"
|
||||||
|
"drives the simulation, which is correct for player-controlled shapes that\n"
|
||||||
|
"receive Move input each tick.\n");
|
||||||
addField("massCenter", TypePoint3F, Offset(massCenter, RigidShapeData), "Center of mass for rigid body.");
|
addField("massCenter", TypePoint3F, Offset(massCenter, RigidShapeData), "Center of mass for rigid body.");
|
||||||
addField("massBox", TypePoint3F, Offset(massBox, RigidShapeData), "Size of inertial box.");
|
addField("massBox", TypePoint3F, Offset(massBox, RigidShapeData), "Size of inertial box.");
|
||||||
addFieldV("bodyRestitution", TypeRangedF32, Offset(body.restitution, RigidShapeData), &CommonValidators::PositiveFloat, "The percentage of kinetic energy kept by this object in a collision.");
|
addFieldV("bodyRestitution", TypeRangedF32, Offset(body.restitution, RigidShapeData), &CommonValidators::PositiveFloat, "The percentage of kinetic energy kept by this object in a collision.");
|
||||||
|
|
@ -652,6 +665,13 @@ RigidShape::RigidShape()
|
||||||
mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
|
mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
|
||||||
|
|
||||||
mPhysicsRep = NULL;
|
mPhysicsRep = NULL;
|
||||||
|
// NEW — initialise PhysicsState members used by the dynamic path
|
||||||
|
mState.position.set(0, 0, 0);
|
||||||
|
mState.orientation.identity();
|
||||||
|
mState.linVelocity.set(0, 0, 0);
|
||||||
|
mState.angVelocity.set(0, 0, 0);
|
||||||
|
mState.sleeping = false;
|
||||||
|
mRenderState[0] = mRenderState[1] = mState;
|
||||||
}
|
}
|
||||||
|
|
||||||
RigidShape::~RigidShape()
|
RigidShape::~RigidShape()
|
||||||
|
|
@ -799,16 +819,26 @@ void RigidShape::_createPhysics()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TSShape* shape = mShapeInstance->getShape();
|
TSShape* shape = mShapeInstance->getShape();
|
||||||
PhysicsCollision* colShape = NULL;
|
PhysicsCollision* colShape = shape->buildColShape(false, getScale());
|
||||||
colShape = shape->buildColShape(false, getScale());
|
if (!colShape)
|
||||||
|
return;
|
||||||
|
|
||||||
if (colShape)
|
const bool dynamic = mDataBlock->isDynamic;
|
||||||
{
|
|
||||||
PhysicsWorld* world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
PhysicsWorld* world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||||
mPhysicsRep = PHYSICSMGR->createBody();
|
mPhysicsRep = PHYSICSMGR->createBody();
|
||||||
mPhysicsRep->init(colShape, 0, PhysicsBody::BF_KINEMATIC, this, world);
|
|
||||||
mPhysicsRep->setTransform(getTransform());
|
mPhysicsRep->init(colShape, dynamic ? mDataBlock->mass : 0,
|
||||||
}
|
dynamic ? 0 : PhysicsBody::BF_KINEMATIC,
|
||||||
|
this, world);
|
||||||
|
|
||||||
|
mPhysicsRep->setMaterial(mDataBlock->body.restitution,
|
||||||
|
mDataBlock->body.friction,
|
||||||
|
mDataBlock->body.friction);
|
||||||
|
|
||||||
|
mPhysicsRep->setDamping(mDataBlock->minDrag, mDataBlock->minDrag);
|
||||||
|
|
||||||
|
mPhysicsRep->setTransform(getTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
@ -836,70 +866,156 @@ void RigidShape::processTick(const Move* move)
|
||||||
mDelta.posVec.x = -mDelta.warpOffset.x;
|
mDelta.posVec.x = -mDelta.warpOffset.x;
|
||||||
mDelta.posVec.y = -mDelta.warpOffset.y;
|
mDelta.posVec.y = -mDelta.warpOffset.y;
|
||||||
mDelta.posVec.z = -mDelta.warpOffset.z;
|
mDelta.posVec.z = -mDelta.warpOffset.z;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!move)
|
||||||
{
|
{
|
||||||
if (!move)
|
if (isGhost())
|
||||||
{
|
{
|
||||||
if (isGhost())
|
// If we haven't run out of prediction time,
|
||||||
{
|
// predict using the last known move.
|
||||||
// If we haven't run out of prediction time,
|
if (mPredictionCount-- <= 0)
|
||||||
// predict using the last known move.
|
return;
|
||||||
if (mPredictionCount-- <= 0)
|
move = &mDelta.move;
|
||||||
return;
|
}
|
||||||
move = &mDelta.move;
|
else
|
||||||
}
|
move = &NullMove;
|
||||||
else
|
}
|
||||||
move = &NullMove;
|
|
||||||
|
// Process input move
|
||||||
|
updateMove(move);
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// DYNAMIC BODY PATH — physics plugin owns all simulation
|
||||||
|
// =========================================================================
|
||||||
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
{
|
||||||
|
// Single-player shortcut: mirror server state directly to avoid a full
|
||||||
|
// network round-trip (mirrors PhysicsShape::processTick pattern).
|
||||||
|
if (PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject())
|
||||||
|
{
|
||||||
|
RigidShape* srv = static_cast<RigidShape*>(getServerObject());
|
||||||
|
Parent::setTransform(srv->mState.getTransform());
|
||||||
|
mRenderState[0] = srv->mRenderState[0];
|
||||||
|
mRenderState[1] = srv->mRenderState[1];
|
||||||
|
mState = srv->mState;
|
||||||
|
// Keep mRigid in sync for anything that reads it (camera, sounds, etc.)
|
||||||
|
mRigid.linVelocity = mState.linVelocity;
|
||||||
|
mRigid.angVelocity = mState.angVelocity;
|
||||||
|
mRigid.linPosition = mState.position;
|
||||||
|
mRigid.angPosition = mState.orientation;
|
||||||
|
mRigid.atRest = mState.sleeping;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process input move
|
// Store previous render state for correction smoothing
|
||||||
updateMove(move);
|
mRenderState[0] = mRenderState[1];
|
||||||
|
Point3F errorDelta = mRenderState[1].position - mState.position;
|
||||||
|
const bool doSmoothing = !errorDelta.isZero() && !smNoSmoothing;
|
||||||
|
const bool wasSleeping = mState.sleeping;
|
||||||
|
|
||||||
// Save current rigid state interpolation
|
// Freeze support: put the body to sleep when movement is disabled
|
||||||
mDelta.posVec = mRigid.linPosition;
|
if (mDisableMove)
|
||||||
mDelta.rot[0] = mRigid.angPosition;
|
{
|
||||||
|
mPhysicsRep->setSleeping(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Pull the newly-integrated state from the physics plugin
|
||||||
|
mPhysicsRep->getState(&mState);
|
||||||
|
_updateContainerForces(); // water, zones
|
||||||
|
}
|
||||||
|
|
||||||
// Update the physics based on the integration rate
|
// Smooth any server correction back into the render state
|
||||||
S32 count = mDataBlock->integration;
|
mRenderState[1] = mState;
|
||||||
--mWorkingQueryBoxCountDown;
|
if (doSmoothing)
|
||||||
|
{
|
||||||
|
F32 blend = mClampF(errorDelta.len() / 20.0f, 0.1f, 0.9f);
|
||||||
|
mRenderState[1].position.interpolate(mState.position,
|
||||||
|
mRenderState[0].position, blend);
|
||||||
|
mRenderState[1].orientation.interpolate(mState.orientation,
|
||||||
|
mRenderState[0].orientation, blend);
|
||||||
|
}
|
||||||
|
|
||||||
if (!mDisableMove)
|
// Keep mRigid in sync for subsystems that read it directly
|
||||||
updateWorkingCollisionSet(getCollisionMask());
|
mRigid.linPosition = mState.position;
|
||||||
for (U32 i = 0; i < count; i++)
|
mRigid.angPosition = mState.orientation;
|
||||||
updatePos(TickSec / count);
|
mRigid.linVelocity = mState.linVelocity;
|
||||||
|
mRigid.angVelocity = mState.angVelocity;
|
||||||
|
mRigid.atRest = mState.sleeping;
|
||||||
|
|
||||||
// Wrap up interpolation info
|
if (!wasSleeping || !mState.sleeping)
|
||||||
mDelta.pos = mRigid.linPosition;
|
{
|
||||||
mDelta.posVec -= mRigid.linPosition;
|
// Update engine transform from physics state
|
||||||
mDelta.rot[1] = mRigid.angPosition;
|
Parent::setTransform(mState.getTransform());
|
||||||
|
|
||||||
// Update container database
|
if (isServerObject() && !smNoCorrections && !PHYSICSMGR->isSinglePlayer())
|
||||||
setPosition(mRigid.linPosition, mRigid.angPosition);
|
setMaskBits(PositionMask);
|
||||||
setMaskBits(PositionMask);
|
|
||||||
updateContainer();
|
|
||||||
|
|
||||||
//TODO: Only update when position has actually changed
|
updateContainer();
|
||||||
//no need to check if mDataBlock->enablePhysicsRep is false as mPhysicsRep will be NULL if it is
|
}
|
||||||
if (mPhysicsRep)
|
|
||||||
mPhysicsRep->moveKinematicTo(getTransform());
|
if (isServerObject())
|
||||||
|
{
|
||||||
|
checkTriggers();
|
||||||
|
notifyCollision();
|
||||||
|
}
|
||||||
|
return; // done — do NOT fall through to Rigid path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// KINEMATIC / RIGID SIMULATION PATH (original behaviour, bug-fixed)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
mDelta.posVec = mRigid.linPosition;
|
||||||
|
mDelta.rot[0] = mRigid.angPosition;
|
||||||
|
|
||||||
|
S32 count = mDataBlock->integration;
|
||||||
|
--mWorkingQueryBoxCountDown;
|
||||||
|
|
||||||
|
if (!mDisableMove)
|
||||||
|
updateWorkingCollisionSet(getCollisionMask());
|
||||||
|
|
||||||
|
for (U32 i = 0; i < count; i++)
|
||||||
|
updatePos(TickSec / count);
|
||||||
|
|
||||||
|
mDelta.pos = mRigid.linPosition;
|
||||||
|
mDelta.posVec -= mRigid.linPosition;
|
||||||
|
mDelta.rot[1] = mRigid.angPosition;
|
||||||
|
|
||||||
|
setPosition(mRigid.linPosition, mRigid.angPosition);
|
||||||
|
setMaskBits(PositionMask);
|
||||||
|
updateContainer();
|
||||||
|
|
||||||
|
// Keep the kinematic physics body in sync so other dynamic actors see us
|
||||||
|
if (mPhysicsRep)
|
||||||
|
mPhysicsRep->moveKinematicTo(getTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidShape::interpolateTick(F32 dt)
|
void RigidShape::interpolateTick(F32 dt)
|
||||||
{
|
{
|
||||||
Parent::interpolateTick(dt);
|
Parent::interpolateTick(dt);
|
||||||
if ( isMounted() )
|
if (isMounted()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if(dt == 0.0f)
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
{
|
||||||
|
PhysicsState state;
|
||||||
|
state.interpolate(mRenderState[1], mRenderState[0], dt);
|
||||||
|
setRenderTransform(state.getTransform());
|
||||||
|
mDelta.dt = dt;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original Rigid path
|
||||||
|
if (dt == 0.0f)
|
||||||
setRenderPosition(mDelta.pos, mDelta.rot[1]);
|
setRenderPosition(mDelta.pos, mDelta.rot[1]);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QuatF rot;
|
QuatF rot;
|
||||||
rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt);
|
rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt);
|
||||||
Point3F pos = mDelta.pos + mDelta.posVec * dt;
|
Point3F pos = mDelta.pos + mDelta.posVec * dt;
|
||||||
setRenderPosition(pos,rot);
|
setRenderPosition(pos, rot);
|
||||||
}
|
}
|
||||||
mDelta.dt = dt;
|
mDelta.dt = dt;
|
||||||
}
|
}
|
||||||
|
|
@ -1062,13 +1178,21 @@ void RigidShape::getCameraTransform(F32* pos,MatrixF* mat)
|
||||||
|
|
||||||
void RigidShape::getVelocity(const Point3F& r, Point3F* v)
|
void RigidShape::getVelocity(const Point3F& r, Point3F* v)
|
||||||
{
|
{
|
||||||
mRigid.getVelocity(r, v);
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
*v = mState.linVelocity;
|
||||||
|
else
|
||||||
|
mRigid.getVelocity(r, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidShape::applyImpulse(const Point3F &pos, const Point3F &impulse)
|
void RigidShape::applyImpulse(const Point3F &pos, const Point3F &impulse)
|
||||||
{
|
{
|
||||||
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
{
|
||||||
|
mPhysicsRep->applyImpulse(pos, impulse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Point3F r;
|
Point3F r;
|
||||||
mRigid.getOriginVector(pos,&r);
|
mRigid.getOriginVector(pos, &r);
|
||||||
mRigid.applyImpulse(r, impulse);
|
mRigid.applyImpulse(r, impulse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1104,6 +1228,17 @@ void RigidShape::setTransform(const MatrixF& newMat)
|
||||||
Parent::setTransform(newMat);
|
Parent::setTransform(newMat);
|
||||||
mRigid.atRest = false;
|
mRigid.atRest = false;
|
||||||
mContacts.clear();
|
mContacts.clear();
|
||||||
|
|
||||||
|
// For dynamic bodies, also keep mState consistent
|
||||||
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
{
|
||||||
|
mState.position = newMat.getPosition();
|
||||||
|
mState.orientation.set(newMat);
|
||||||
|
mRenderState[0] = mRenderState[1] = mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPhysicsRep)
|
||||||
|
mPhysicsRep->setTransform(newMat); // covers both kinematic and dynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidShape::forceClientTransform()
|
void RigidShape::forceClientTransform()
|
||||||
|
|
@ -1153,8 +1288,8 @@ void RigidShape::updatePos(F32 dt)
|
||||||
if (mCollisionList.getCount())
|
if (mCollisionList.getCount())
|
||||||
{
|
{
|
||||||
F32 k = mRigid.getKineticEnergy();
|
F32 k = mRigid.getKineticEnergy();
|
||||||
F32 G = mNetGravity* dt * TickMs / mDataBlock->integration;
|
F32 G = mNetGravity * dt;
|
||||||
F32 Kg = mRigid.mass * G * G * TickSec;
|
F32 Kg = mRigid.mass * G * G;
|
||||||
if (k < sRestTol * Kg && ++restCount > sRestCount)
|
if (k < sRestTol * Kg && ++restCount > sRestCount)
|
||||||
mRigid.setAtRest();
|
mRigid.setAtRest();
|
||||||
}
|
}
|
||||||
|
|
@ -1241,6 +1376,53 @@ void RigidShape::updatePos(F32 dt)
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void RigidShape::_updateContainerForces()
|
||||||
|
{
|
||||||
|
if (!mPhysicsRep || !mPhysicsRep->isDynamic())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ContainerQueryInfo info;
|
||||||
|
info.box = getWorldBox();
|
||||||
|
info.mass = mDataBlock->mass;
|
||||||
|
|
||||||
|
getContainer()->findObjects(getWorldBox(), WaterObjectType | PhysicalZoneObjectType,
|
||||||
|
findRouter, &info);
|
||||||
|
|
||||||
|
// Base drag from the datablock's dragForce field
|
||||||
|
F32 linDrag = mDataBlock->dragForce;
|
||||||
|
F32 angDrag = mDataBlock->dragForce;
|
||||||
|
|
||||||
|
Point3F cmass = mPhysicsRep->getCMassPosition();
|
||||||
|
|
||||||
|
if (info.waterCoverage > 0.0f)
|
||||||
|
{
|
||||||
|
// Scale drag by water viscosity
|
||||||
|
F32 waterScale = info.waterViscosity * 2.0f;
|
||||||
|
F32 pow4 = mPow(info.waterCoverage, 0.25f);
|
||||||
|
linDrag = mLerp(linDrag, linDrag * waterScale, pow4);
|
||||||
|
angDrag = mLerp(angDrag, angDrag * waterScale, pow4);
|
||||||
|
|
||||||
|
// Buoyancy — uses ShapeBaseData::density (inherited by RigidShapeData)
|
||||||
|
F32 density = mDataBlock->density;
|
||||||
|
if (density > 0.0f)
|
||||||
|
{
|
||||||
|
F32 buoyancy = (info.waterDensity / density) * mPow(info.waterCoverage, 2.0f);
|
||||||
|
// mNetGravity is signed (negative = downward in Torque Z-up).
|
||||||
|
// Buoyancy opposes gravity, so the force is in the +Z direction.
|
||||||
|
Point3F buoyancyForce(0.0f, 0.0f, buoyancy * -mNetGravity * TickSec * mDataBlock->mass);
|
||||||
|
mPhysicsRep->applyImpulse(cmass, buoyancyForce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPhysicsRep->setDamping(linDrag, angDrag);
|
||||||
|
|
||||||
|
// Physical zone forces (wind, push, etc.)
|
||||||
|
if (!info.appliedForce.isZero())
|
||||||
|
mPhysicsRep->applyImpulse(cmass, info.appliedForce);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
void RigidShape::updateForces(F32 dt)
|
void RigidShape::updateForces(F32 dt)
|
||||||
{
|
{
|
||||||
if (mDisableMove)
|
if (mDisableMove)
|
||||||
|
|
@ -1542,41 +1724,80 @@ void RigidShape::writePacketData(GameConnection *connection, BitStream *stream)
|
||||||
{
|
{
|
||||||
Parent::writePacketData(connection, stream);
|
Parent::writePacketData(connection, stream);
|
||||||
|
|
||||||
mathWrite(*stream, mRigid.linPosition);
|
if (mDataBlock->isDynamic)
|
||||||
if (!stream->writeFlag(mRigid.atRest))
|
|
||||||
{
|
{
|
||||||
mathWrite(*stream, mRigid.angPosition);
|
mathWrite(*stream, mState.position);
|
||||||
mathWrite(*stream, mRigid.linMomentum);
|
if (!stream->writeFlag(mState.sleeping))
|
||||||
mathWrite(*stream, mRigid.angMomentum);
|
{
|
||||||
|
stream->writeQuat(mState.orientation, 9);
|
||||||
|
stream->writeVector(mState.linVelocity, 1000.0f, 16, 9);
|
||||||
|
stream->writeVector(mState.angVelocity, 10.0f, 10, 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mathWrite(*stream, mRigid.linPosition);
|
||||||
|
if (!stream->writeFlag(mRigid.atRest))
|
||||||
|
{
|
||||||
|
mathWrite(*stream, mRigid.angPosition);
|
||||||
|
mathWrite(*stream, mRigid.linMomentum);
|
||||||
|
mathWrite(*stream, mRigid.angMomentum);
|
||||||
|
}
|
||||||
|
stream->writeFlag(mContacts.getCount() == 0);
|
||||||
}
|
}
|
||||||
stream->writeFlag(mContacts.getCount() == 0);
|
|
||||||
|
|
||||||
stream->writeFlag(mDisableMove);
|
stream->writeFlag(mDisableMove);
|
||||||
stream->setCompressionPoint(mRigid.linPosition);
|
stream->setCompressionPoint(mDataBlock->isDynamic ? mState.position : mRigid.linPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidShape::readPacketData(GameConnection *connection, BitStream *stream)
|
void RigidShape::readPacketData(GameConnection *connection, BitStream *stream)
|
||||||
{
|
{
|
||||||
Parent::readPacketData(connection, stream);
|
Parent::readPacketData(connection, stream);
|
||||||
|
|
||||||
mathRead(*stream, &mRigid.linPosition);
|
if (mDataBlock->isDynamic)
|
||||||
if (stream->readFlag())
|
|
||||||
{
|
{
|
||||||
mRigid.setAtRest();
|
mathRead(*stream, &mState.position);
|
||||||
|
if (stream->readFlag()) // sleeping
|
||||||
|
{
|
||||||
|
mState.sleeping = true;
|
||||||
|
if (mPhysicsRep) mPhysicsRep->setSleeping(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mState.sleeping = false;
|
||||||
|
stream->readQuat(&mState.orientation, 9);
|
||||||
|
stream->readVector(&mState.linVelocity, 1000.0f, 16, 9);
|
||||||
|
stream->readVector(&mState.angVelocity, 10.0f, 10, 9);
|
||||||
|
|
||||||
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
{
|
||||||
|
mPhysicsRep->setTransform(mState.getTransform());
|
||||||
|
mPhysicsRep->setLinVelocity(mState.linVelocity);
|
||||||
|
mPhysicsRep->setAngVelocity(mState.angVelocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mathRead(*stream, &mRigid.angPosition);
|
mathRead(*stream, &mRigid.linPosition);
|
||||||
mathRead(*stream, &mRigid.linMomentum);
|
if (stream->readFlag())
|
||||||
mathRead(*stream, &mRigid.angMomentum);
|
{
|
||||||
mRigid.updateInertialTensor();
|
mRigid.setAtRest();
|
||||||
mRigid.updateVelocity();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mathRead(*stream, &mRigid.angPosition);
|
||||||
|
mathRead(*stream, &mRigid.linMomentum);
|
||||||
|
mathRead(*stream, &mRigid.angMomentum);
|
||||||
|
mRigid.updateInertialTensor();
|
||||||
|
mRigid.updateVelocity();
|
||||||
|
}
|
||||||
|
if (stream->readFlag())
|
||||||
|
mContacts.clear();
|
||||||
}
|
}
|
||||||
if (stream->readFlag())
|
|
||||||
mContacts.clear();
|
|
||||||
|
|
||||||
mDisableMove = stream->readFlag();
|
mDisableMove = stream->readFlag();
|
||||||
stream->setCompressionPoint(mRigid.linPosition);
|
stream->setCompressionPoint(mDataBlock->isDynamic ? mState.position : mRigid.linPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1597,16 +1818,32 @@ U32 RigidShape::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
{
|
{
|
||||||
stream->writeFlag(mask & ForceMoveMask);
|
stream->writeFlag(mask & ForceMoveMask);
|
||||||
|
|
||||||
stream->writeCompressedPoint(mRigid.linPosition);
|
if (mDataBlock->isDynamic)
|
||||||
if (!stream->writeFlag(mRigid.atRest))
|
|
||||||
{
|
{
|
||||||
mathWrite(*stream, mRigid.angPosition);
|
// PhysicsState packet: position, orientation, sleeping, velocities.
|
||||||
mathWrite(*stream, mRigid.linMomentum);
|
// mState was updated in processTick from mPhysicsRep->getState().
|
||||||
mathWrite(*stream, mRigid.angMomentum);
|
stream->writeCompressedPoint(mState.position);
|
||||||
|
stream->writeQuat(mState.orientation, 9);
|
||||||
|
if (!stream->writeFlag(mState.sleeping))
|
||||||
|
{
|
||||||
|
stream->writeVector(mState.linVelocity, 1000.0f, 16, 9);
|
||||||
|
stream->writeVector(mState.angVelocity, 10.0f, 10, 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Original Rigid momentum packet
|
||||||
|
stream->writeCompressedPoint(mRigid.linPosition);
|
||||||
|
if (!stream->writeFlag(mRigid.atRest))
|
||||||
|
{
|
||||||
|
mathWrite(*stream, mRigid.angPosition);
|
||||||
|
mathWrite(*stream, mRigid.linMomentum);
|
||||||
|
mathWrite(*stream, mRigid.angMomentum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stream->writeFlag(mask & FreezeMask))
|
if (stream->writeFlag(mask & FreezeMask))
|
||||||
stream->writeFlag(mDisableMove);
|
stream->writeFlag(mDisableMove);
|
||||||
|
|
||||||
return retMask;
|
return retMask;
|
||||||
|
|
@ -1614,93 +1851,137 @@ U32 RigidShape::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||||
|
|
||||||
void RigidShape::unpackUpdate(NetConnection *con, BitStream *stream)
|
void RigidShape::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||||
{
|
{
|
||||||
Parent::unpackUpdate(con,stream);
|
Parent::unpackUpdate(con, stream);
|
||||||
|
|
||||||
if (stream->readFlag())
|
if (stream->readFlag())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mDelta.move.unpack(stream);
|
mDelta.move.unpack(stream);
|
||||||
|
|
||||||
if (stream->readFlag())
|
if (stream->readFlag()) // PositionMask
|
||||||
{
|
{
|
||||||
// Check if we need to jump to the given transform
|
|
||||||
// rather than interpolate to it.
|
|
||||||
bool forceUpdate = stream->readFlag();
|
bool forceUpdate = stream->readFlag();
|
||||||
|
|
||||||
mPredictionCount = sMaxPredictionTicks;
|
mPredictionCount = sMaxPredictionTicks;
|
||||||
F32 speed = mRigid.linVelocity.len();
|
|
||||||
mDelta.warpRot[0] = mRigid.angPosition;
|
|
||||||
|
|
||||||
// Read in new position and momentum values
|
if (mDataBlock->isDynamic)
|
||||||
stream->readCompressedPoint(&mRigid.linPosition);
|
|
||||||
|
|
||||||
if (stream->readFlag())
|
|
||||||
{
|
{
|
||||||
mRigid.setAtRest();
|
// --- Dynamic path ---
|
||||||
}
|
PhysicsState newState;
|
||||||
else
|
stream->readCompressedPoint(&newState.position);
|
||||||
{
|
stream->readQuat(&newState.orientation, 9);
|
||||||
mathRead(*stream, &mRigid.angPosition);
|
newState.sleeping = stream->readFlag();
|
||||||
mathRead(*stream, &mRigid.linMomentum);
|
if (!newState.sleeping)
|
||||||
mathRead(*stream, &mRigid.angMomentum);
|
{
|
||||||
mRigid.updateVelocity();
|
stream->readVector(&newState.linVelocity, 1000.0f, 16, 9);
|
||||||
}
|
stream->readVector(&newState.angVelocity, 10.0f, 10, 9);
|
||||||
|
}
|
||||||
|
|
||||||
if (!forceUpdate && isProperlyAdded())
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
{
|
{
|
||||||
// Determine number of ticks to warp based on the average
|
if (forceUpdate)
|
||||||
// of the client and server velocities.
|
{
|
||||||
Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
|
// Hard snap — no smoothing, straight to the authoritative position
|
||||||
mDelta.warpOffset = mRigid.linPosition - cp;
|
mPhysicsRep->setTransform(newState.getTransform());
|
||||||
|
}
|
||||||
|
else if (!smNoCorrections)
|
||||||
|
{
|
||||||
|
// Soft correction — the physics body blends toward the new state
|
||||||
|
mPhysicsRep->applyCorrection(newState.getTransform());
|
||||||
|
}
|
||||||
|
|
||||||
// Calc the distance covered in one tick as the average of
|
mPhysicsRep->setSleeping(newState.sleeping);
|
||||||
// the old speed and the new speed from the server.
|
if (!newState.sleeping)
|
||||||
F32 dt,as = (speed + mRigid.linVelocity.len()) * 0.5 * TickSec;
|
{
|
||||||
|
mPhysicsRep->setLinVelocity(newState.linVelocity);
|
||||||
// Cal how many ticks it will take to cover the warp offset.
|
mPhysicsRep->setAngVelocity(newState.angVelocity);
|
||||||
// If it's less than what's left in the current tick, we'll just
|
}
|
||||||
// warp in the remaining time.
|
// Re-read so mState reflects what the body is actually doing after
|
||||||
if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks)
|
// the correction (applyCorrection may blend rather than snap).
|
||||||
dt = mDelta.dt + sMaxWarpTicks;
|
mPhysicsRep->getState(&mState);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
dt = (dt <= mDelta.dt)? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt;
|
|
||||||
|
|
||||||
// Adjust current frame interpolation
|
|
||||||
if (mDelta.dt)
|
|
||||||
{
|
{
|
||||||
mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt));
|
// No live physics rep — store state for extrapolation / render
|
||||||
mDelta.posVec = (cp - mDelta.pos) / mDelta.dt;
|
mState = newState;
|
||||||
QuatF cr;
|
|
||||||
cr.interpolate(mDelta.rot[1],mDelta.rot[0],mDelta.dt);
|
|
||||||
mDelta.rot[1].interpolate(cr,mRigid.angPosition,mDelta.dt / dt);
|
|
||||||
mDelta.rot[0].extrapolate(mDelta.rot[1],cr,mDelta.dt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculated multi-tick warp
|
if (forceUpdate || !isProperlyAdded())
|
||||||
mDelta.warpCount = 0;
|
|
||||||
mDelta.warpTicks = (S32)(mFloor(dt));
|
|
||||||
if (mDelta.warpTicks)
|
|
||||||
{
|
{
|
||||||
mDelta.warpOffset = mRigid.linPosition - mDelta.pos;
|
Parent::setTransform(mState.getTransform());
|
||||||
mDelta.warpOffset /= mDelta.warpTicks;
|
mRenderState[0] = mRenderState[1] = mState;
|
||||||
mDelta.warpRot[0] = mDelta.rot[1];
|
|
||||||
mDelta.warpRot[1] = mRigid.angPosition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync mRigid so anything reading it sees sane values
|
||||||
|
mRigid.linPosition = mState.position;
|
||||||
|
mRigid.angPosition = mState.orientation;
|
||||||
|
mRigid.linVelocity = mState.linVelocity;
|
||||||
|
mRigid.angVelocity = mState.angVelocity;
|
||||||
|
mRigid.atRest = mState.sleeping;
|
||||||
|
mRigid.updateCenterOfMass();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Set the shape to the server position
|
// --- Original Rigid (kinematic) path ---
|
||||||
mDelta.dt = 0;
|
F32 speed = mRigid.linVelocity.len();
|
||||||
mDelta.pos = mRigid.linPosition;
|
mDelta.warpRot[0] = mRigid.angPosition;
|
||||||
mDelta.posVec.set(0,0,0);
|
|
||||||
mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
|
stream->readCompressedPoint(&mRigid.linPosition);
|
||||||
mDelta.warpCount = mDelta.warpTicks = 0;
|
if (stream->readFlag())
|
||||||
setPosition(mRigid.linPosition, mRigid.angPosition);
|
{
|
||||||
|
mRigid.setAtRest();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mathRead(*stream, &mRigid.angPosition);
|
||||||
|
mathRead(*stream, &mRigid.linMomentum);
|
||||||
|
mathRead(*stream, &mRigid.angMomentum);
|
||||||
|
mRigid.updateVelocity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forceUpdate && isProperlyAdded())
|
||||||
|
{
|
||||||
|
Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
|
||||||
|
mDelta.warpOffset = mRigid.linPosition - cp;
|
||||||
|
F32 dt, as = (speed + mRigid.linVelocity.len()) * 0.5f * TickSec;
|
||||||
|
if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks)
|
||||||
|
dt = mDelta.dt + sMaxWarpTicks;
|
||||||
|
else
|
||||||
|
dt = (dt <= mDelta.dt) ? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt;
|
||||||
|
|
||||||
|
if (mDelta.dt)
|
||||||
|
{
|
||||||
|
mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt));
|
||||||
|
mDelta.posVec = (cp - mDelta.pos) / mDelta.dt;
|
||||||
|
QuatF cr;
|
||||||
|
cr.interpolate(mDelta.rot[1], mDelta.rot[0], mDelta.dt);
|
||||||
|
mDelta.rot[1].interpolate(cr, mRigid.angPosition, mDelta.dt / dt);
|
||||||
|
mDelta.rot[0].extrapolate(mDelta.rot[1], cr, mDelta.dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDelta.warpCount = 0;
|
||||||
|
mDelta.warpTicks = (S32)(mFloor(dt));
|
||||||
|
if (mDelta.warpTicks)
|
||||||
|
{
|
||||||
|
mDelta.warpOffset = mRigid.linPosition - mDelta.pos;
|
||||||
|
mDelta.warpOffset /= mDelta.warpTicks;
|
||||||
|
mDelta.warpRot[0] = mDelta.rot[1];
|
||||||
|
mDelta.warpRot[1] = mRigid.angPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mDelta.dt = 0;
|
||||||
|
mDelta.pos = mRigid.linPosition;
|
||||||
|
mDelta.posVec.set(0, 0, 0);
|
||||||
|
mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
|
||||||
|
mDelta.warpCount = mDelta.warpTicks = 0;
|
||||||
|
setPosition(mRigid.linPosition, mRigid.angPosition);
|
||||||
|
}
|
||||||
|
mRigid.updateCenterOfMass();
|
||||||
}
|
}
|
||||||
mRigid.updateCenterOfMass();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stream->readFlag())
|
if (stream->readFlag()) // FreezeMask
|
||||||
mDisableMove = stream->readFlag();
|
mDisableMove = stream->readFlag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1724,6 +2005,14 @@ void RigidShape::consoleInit()
|
||||||
"The larger this number the less often the working list will be updated due to motion, but any non-static shape that "
|
"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"
|
"moves into the query box will not be noticed.\n\n"
|
||||||
"@ingroup GameObjects\n");
|
"@ingroup GameObjects\n");
|
||||||
|
|
||||||
|
// NEW — mirrors PhysicsShape debug knobs for the dynamic path
|
||||||
|
Con::addVariable("$RigidShape::noCorrections", TypeBool, &smNoCorrections,
|
||||||
|
"@brief When true, the server will not send state corrections to the client "
|
||||||
|
"for dynamic RigidShapes. Debug only.\n\n");
|
||||||
|
Con::addVariable("$RigidShape::noSmoothing", TypeBool, &smNoSmoothing,
|
||||||
|
"@brief When true, dynamic RigidShape clients snap to corrected positions "
|
||||||
|
"instead of smoothly interpolating. Debug only.\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidShape::initPersistFields()
|
void RigidShape::initPersistFields()
|
||||||
|
|
@ -1857,12 +2146,20 @@ void RigidShape::reset()
|
||||||
{
|
{
|
||||||
mRigid.clearForces();
|
mRigid.clearForces();
|
||||||
mRigid.setAtRest();
|
mRigid.setAtRest();
|
||||||
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
{
|
||||||
|
mPhysicsRep->setLinVelocity(Point3F::Zero);
|
||||||
|
mPhysicsRep->setAngVelocity(Point3F::Zero);
|
||||||
|
mPhysicsRep->setSleeping(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidShape::freezeSim(bool frozen)
|
void RigidShape::freezeSim(bool frozen)
|
||||||
{
|
{
|
||||||
mDisableMove = frozen;
|
mDisableMove = frozen;
|
||||||
setMaskBits(FreezeMask);
|
setMaskBits(FreezeMask);
|
||||||
|
if (mPhysicsRep && mPhysicsRep->isDynamic())
|
||||||
|
mPhysicsRep->setSleeping(frozen);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod( RigidShape, reset, void, (),,
|
DefineEngineMethod( RigidShape, reset, void, (),,
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class RigidShapeData : public ShapeBaseData
|
||||||
F32 splashVelEpsilon;
|
F32 splashVelEpsilon;
|
||||||
|
|
||||||
bool enablePhysicsRep;
|
bool enablePhysicsRep;
|
||||||
|
bool isDynamic;
|
||||||
|
|
||||||
F32 dragForce;
|
F32 dragForce;
|
||||||
F32 vertFactor;
|
F32 vertFactor;
|
||||||
|
|
@ -189,6 +189,10 @@ class RigidShape: public ShapeBase
|
||||||
};
|
};
|
||||||
|
|
||||||
PhysicsBody* mPhysicsRep;
|
PhysicsBody* mPhysicsRep;
|
||||||
|
/// Current physics state (used by the dynamic-body path).
|
||||||
|
PhysicsState mState;
|
||||||
|
/// Previous and current render states for the dynamic-body path.
|
||||||
|
PhysicsState mRenderState[2];
|
||||||
|
|
||||||
StateDelta mDelta;
|
StateDelta mDelta;
|
||||||
S32 mPredictionCount; ///< Number of ticks to predict
|
S32 mPredictionCount; ///< Number of ticks to predict
|
||||||
|
|
@ -243,6 +247,8 @@ class RigidShape: public ShapeBase
|
||||||
|
|
||||||
void updateForces(F32 dt);
|
void updateForces(F32 dt);
|
||||||
|
|
||||||
|
void _updateContainerForces();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Test code...
|
// Test code...
|
||||||
static ClippedPolyList* sPolyList;
|
static ClippedPolyList* sPolyList;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue