mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-24 17:06:44 +02:00
measure linear velocity of moving objects
This commit is contained in:
parent
abb40ca080
commit
5180e7e715
5 changed files with 72 additions and 24 deletions
|
@ -62,6 +62,9 @@ void EntityMotionState::stepKinematicSimulation(quint64 now) {
|
||||||
// which is different from physical kinematic motion (inside getWorldTransform())
|
// which is different from physical kinematic motion (inside getWorldTransform())
|
||||||
// which steps in physics simulation time.
|
// which steps in physics simulation time.
|
||||||
_entity->simulate(now);
|
_entity->simulate(now);
|
||||||
|
// TODO: we can't use ObjectMotionState::measureVelocityAndAcceleration() here because the entity
|
||||||
|
// has no RigidBody and the timestep is a little bit out of sync with the physics simulation anyway.
|
||||||
|
// Hence we must manually measure kinematic velocity and acceleration.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::isMoving() const {
|
bool EntityMotionState::isMoving() const {
|
||||||
|
@ -71,7 +74,7 @@ bool EntityMotionState::isMoving() const {
|
||||||
// This callback is invoked by the physics simulation in two cases:
|
// This callback is invoked by the physics simulation in two cases:
|
||||||
// (1) when the RigidBody is first added to the world
|
// (1) when the RigidBody is first added to the world
|
||||||
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
|
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
|
||||||
// (2) at the beginning of each simulation frame for KINEMATIC RigidBody's --
|
// (2) at the beginning of each simulation step for KINEMATIC RigidBody's --
|
||||||
// it is an opportunity for outside code to update the object's simulation position
|
// it is an opportunity for outside code to update the object's simulation position
|
||||||
void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||||
if (_isKinematic) {
|
if (_isKinematic) {
|
||||||
|
@ -89,9 +92,10 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||||
worldTrans.setRotation(glmToBullet(_entity->getRotation()));
|
worldTrans.setRotation(glmToBullet(_entity->getRotation()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This callback is invoked by the physics simulation at the end of each simulation frame...
|
// This callback is invoked by the physics simulation at the end of each simulation step...
|
||||||
// iff the corresponding RigidBody is DYNAMIC and has moved.
|
// iff the corresponding RigidBody is DYNAMIC and has moved.
|
||||||
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
|
measureVelocityAndAcceleration();
|
||||||
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
|
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
|
||||||
_entity->setRotation(bulletToGLM(worldTrans.getRotation()));
|
_entity->setRotation(bulletToGLM(worldTrans.getRotation()));
|
||||||
|
|
||||||
|
@ -116,7 +120,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
|
void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) {
|
||||||
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
|
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
|
||||||
if (flags & EntityItem::DIRTY_POSITION) {
|
if (flags & EntityItem::DIRTY_POSITION) {
|
||||||
_sentPosition = _entity->getPosition() - ObjectMotionState::getWorldOffset();
|
_sentPosition = _entity->getPosition() - ObjectMotionState::getWorldOffset();
|
||||||
|
@ -131,7 +135,7 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
|
||||||
if (flags & EntityItem::DIRTY_VELOCITY) {
|
if (flags & EntityItem::DIRTY_VELOCITY) {
|
||||||
updateObjectVelocities();
|
updateObjectVelocities();
|
||||||
}
|
}
|
||||||
_sentFrame = frame;
|
_sentStep = step;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: entity support for friction and restitution
|
// TODO: entity support for friction and restitution
|
||||||
|
@ -179,7 +183,7 @@ float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const {
|
||||||
return _entity->computeMass();
|
return _entity->computeMass();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) {
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||||
if (!_entity->isKnownID()) {
|
if (!_entity->isKnownID()) {
|
||||||
return; // never update entities that are unknown
|
return; // never update entities that are unknown
|
||||||
}
|
}
|
||||||
|
@ -231,7 +235,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
}
|
}
|
||||||
if (_numNonMovingUpdates <= 1) {
|
if (_numNonMovingUpdates <= 1) {
|
||||||
// we only update lastEdited when we're sending new physics data
|
// we only update lastEdited when we're sending new physics data
|
||||||
// (i.e. NOT when we just simulate the positions forward, nore when we resend non-moving data)
|
// (i.e. NOT when we just simulate the positions forward, nor when we resend non-moving data)
|
||||||
// NOTE: Andrew & Brad to discuss. Let's make sure we're using lastEdited, lastSimulated, and lastUpdated correctly
|
// NOTE: Andrew & Brad to discuss. Let's make sure we're using lastEdited, lastSimulated, and lastUpdated correctly
|
||||||
quint64 lastSimulated = _entity->getLastSimulated();
|
quint64 lastSimulated = _entity->getLastSimulated();
|
||||||
_entity->setLastEdited(lastSimulated);
|
_entity->setLastEdited(lastSimulated);
|
||||||
|
@ -264,7 +268,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
// The outgoing flags only itemized WHAT to send, not WHETHER to send, hence we always set them
|
// The outgoing flags only itemized WHAT to send, not WHETHER to send, hence we always set them
|
||||||
// to the full set. These flags may be momentarily cleared by incoming external changes.
|
// to the full set. These flags may be momentarily cleared by incoming external changes.
|
||||||
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
|
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
|
||||||
_sentFrame = frame;
|
_sentStep = step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,13 +50,13 @@ public:
|
||||||
virtual void setWorldTransform(const btTransform& worldTrans);
|
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||||
|
|
||||||
// these relay incoming values to the RigidBody
|
// these relay incoming values to the RigidBody
|
||||||
virtual void updateObjectEasy(uint32_t flags, uint32_t frame);
|
virtual void updateObjectEasy(uint32_t flags, uint32_t step);
|
||||||
virtual void updateObjectVelocities();
|
virtual void updateObjectVelocities();
|
||||||
|
|
||||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
||||||
virtual float computeMass(const ShapeInfo& shapeInfo) const;
|
virtual float computeMass(const ShapeInfo& shapeInfo) const;
|
||||||
|
|
||||||
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame);
|
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
|
||||||
|
|
||||||
virtual uint32_t getIncomingDirtyFlags() const;
|
virtual uint32_t getIncomingDirtyFlags() const;
|
||||||
virtual void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); }
|
virtual void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); }
|
||||||
|
|
|
@ -22,7 +22,7 @@ const float MAX_FRICTION = 10.0f;
|
||||||
|
|
||||||
const float DEFAULT_RESTITUTION = 0.5f;
|
const float DEFAULT_RESTITUTION = 0.5f;
|
||||||
|
|
||||||
// origin of physics simulation in world frame
|
// origin of physics simulation in world-frame
|
||||||
glm::vec3 _worldOffset(0.0f);
|
glm::vec3 _worldOffset(0.0f);
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -35,6 +35,12 @@ const glm::vec3& ObjectMotionState::getWorldOffset() {
|
||||||
return _worldOffset;
|
return _worldOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
uint32_t _simulationStep = 0;
|
||||||
|
void ObjectMotionState::setSimulationStep(uint32_t step) {
|
||||||
|
assert(step > _simulationStep);
|
||||||
|
_simulationStep = step;
|
||||||
|
}
|
||||||
|
|
||||||
ObjectMotionState::ObjectMotionState() :
|
ObjectMotionState::ObjectMotionState() :
|
||||||
_friction(DEFAULT_FRICTION),
|
_friction(DEFAULT_FRICTION),
|
||||||
|
@ -46,12 +52,15 @@ ObjectMotionState::ObjectMotionState() :
|
||||||
_sentMoving(false),
|
_sentMoving(false),
|
||||||
_numNonMovingUpdates(0),
|
_numNonMovingUpdates(0),
|
||||||
_outgoingPacketFlags(DIRTY_PHYSICS_FLAGS),
|
_outgoingPacketFlags(DIRTY_PHYSICS_FLAGS),
|
||||||
_sentFrame(0),
|
_sentStep(0),
|
||||||
_sentPosition(0.0f),
|
_sentPosition(0.0f),
|
||||||
_sentRotation(),
|
_sentRotation(),
|
||||||
_sentVelocity(0.0f),
|
_sentVelocity(0.0f),
|
||||||
_sentAngularVelocity(0.0f),
|
_sentAngularVelocity(0.0f),
|
||||||
_sentAcceleration(0.0f) {
|
_sentAcceleration(0.0f),
|
||||||
|
_lastSimulationStep(0),
|
||||||
|
_lastVelocity(0.0f),
|
||||||
|
_measuredAcceleration(0.0f)
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectMotionState::~ObjectMotionState() {
|
ObjectMotionState::~ObjectMotionState() {
|
||||||
|
@ -59,6 +68,27 @@ ObjectMotionState::~ObjectMotionState() {
|
||||||
assert(_body == NULL);
|
assert(_body == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::measureVelocityAndAcceleration() {
|
||||||
|
// try to manually measure the true acceleration of the object
|
||||||
|
uint32_t numSubsteps = _simulationStep - _lastSimulationStep;
|
||||||
|
if (numSubsteps > 0) {
|
||||||
|
float dt = ((float)numSubsteps * PHYSICS_ENGINE_FIXED_SUBSTEP);
|
||||||
|
float invDt = 1.0f / dt;
|
||||||
|
_lastSimulationStep = _simulationStep;
|
||||||
|
|
||||||
|
// Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt
|
||||||
|
// hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt
|
||||||
|
glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity());
|
||||||
|
_measuredAcceleration = (velocity / powf(1.0f - _linearDamping, dt) - _lastVelocity) * invDt;
|
||||||
|
_lastVelocity = velocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::resetMeasuredVelocityAndAcceleration() {
|
||||||
|
_lastSimulationStep = _simulationStep;
|
||||||
|
_lastVelocity = bulletToGLM(_body->getLinearVelocity());
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectMotionState::setFriction(float friction) {
|
void ObjectMotionState::setFriction(float friction) {
|
||||||
_friction = btMax(btMin(fabsf(friction), MAX_FRICTION), 0.0f);
|
_friction = btMax(btMin(fabsf(friction), MAX_FRICTION), 0.0f);
|
||||||
}
|
}
|
||||||
|
@ -103,15 +133,16 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const {
|
||||||
return !_body->isActive() && _numNonMovingUpdates > MAX_NUM_NON_MOVING_UPDATES;
|
return !_body->isActive() && _numNonMovingUpdates > MAX_NUM_NON_MOVING_UPDATES;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
assert(_body);
|
assert(_body);
|
||||||
// if we've never checked before, our _sentFrame will be 0, and we need to initialize our state
|
// if we've never checked before, our _sentStep will be 0, and we need to initialize our state
|
||||||
if (_sentFrame == 0) {
|
if (_sentStep == 0) {
|
||||||
_sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin());
|
btTransform xform = _body->getWorldTransform();
|
||||||
|
_sentPosition = bulletToGLM(xform.getOrigin());
|
||||||
|
_sentRotation = bulletToGLM(xform.getRotation());
|
||||||
_sentVelocity = bulletToGLM(_body->getLinearVelocity());
|
_sentVelocity = bulletToGLM(_body->getLinearVelocity());
|
||||||
_sentRotation = bulletToGLM(_body->getWorldTransform().getRotation());
|
|
||||||
_sentAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
_sentAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
||||||
_sentFrame = simulationFrame;
|
_sentStep = simulationStep;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +152,9 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
||||||
glm::vec3 wasAngularVelocity = _sentAngularVelocity;
|
glm::vec3 wasAngularVelocity = _sentAngularVelocity;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int numFrames = simulationFrame - _sentFrame;
|
int numSteps = simulationStep - _sentStep;
|
||||||
float dt = (float)(numFrames) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
_sentFrame = simulationFrame;
|
_sentStep = simulationStep;
|
||||||
bool isActive = _body->isActive();
|
bool isActive = _body->isActive();
|
||||||
|
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
|
@ -179,7 +210,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
||||||
|
|
||||||
// Bullet caps the effective rotation velocity inside its rotation integration step, therefore
|
// Bullet caps the effective rotation velocity inside its rotation integration step, therefore
|
||||||
// we must integrate with the same algorithm and timestep in order achieve similar results.
|
// we must integrate with the same algorithm and timestep in order achieve similar results.
|
||||||
for (int i = 0; i < numFrames; ++i) {
|
for (int i = 0; i < numSteps; ++i) {
|
||||||
_sentRotation = glm::normalize(computeBulletRotationStep(_sentAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP) * _sentRotation);
|
_sentRotation = glm::normalize(computeBulletRotationStep(_sentAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP) * _sentRotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,14 @@ public:
|
||||||
static void setWorldOffset(const glm::vec3& offset);
|
static void setWorldOffset(const glm::vec3& offset);
|
||||||
static const glm::vec3& getWorldOffset();
|
static const glm::vec3& getWorldOffset();
|
||||||
|
|
||||||
|
static void setSimulationStep(uint32_t step);
|
||||||
|
|
||||||
ObjectMotionState();
|
ObjectMotionState();
|
||||||
~ObjectMotionState();
|
~ObjectMotionState();
|
||||||
|
|
||||||
|
void measureVelocityAndAcceleration();
|
||||||
|
void resetMeasuredVelocityAndAcceleration();
|
||||||
|
|
||||||
// An EASY update does not require the object to be removed and then reinserted into the PhysicsEngine
|
// An EASY update does not require the object to be removed and then reinserted into the PhysicsEngine
|
||||||
virtual void updateObjectEasy(uint32_t flags, uint32_t frame) = 0;
|
virtual void updateObjectEasy(uint32_t flags, uint32_t frame) = 0;
|
||||||
virtual void updateObjectVelocities() = 0;
|
virtual void updateObjectVelocities() = 0;
|
||||||
|
@ -87,7 +92,7 @@ public:
|
||||||
void clearOutgoingPacketFlags(uint32_t flags) { _outgoingPacketFlags &= ~flags; }
|
void clearOutgoingPacketFlags(uint32_t flags) { _outgoingPacketFlags &= ~flags; }
|
||||||
|
|
||||||
bool doesNotNeedToSendUpdate() const;
|
bool doesNotNeedToSendUpdate() const;
|
||||||
virtual bool shouldSendUpdate(uint32_t simulationFrame);
|
virtual bool shouldSendUpdate(uint32_t simulationStep);
|
||||||
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) = 0;
|
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) = 0;
|
||||||
|
|
||||||
virtual MotionType computeMotionType() const = 0;
|
virtual MotionType computeMotionType() const = 0;
|
||||||
|
@ -126,12 +131,16 @@ protected:
|
||||||
int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects
|
int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects
|
||||||
|
|
||||||
uint32_t _outgoingPacketFlags;
|
uint32_t _outgoingPacketFlags;
|
||||||
uint32_t _sentFrame;
|
uint32_t _sentStep;
|
||||||
glm::vec3 _sentPosition; // in simulation-frame (not world-frame)
|
glm::vec3 _sentPosition; // in simulation-frame (not world-frame)
|
||||||
glm::quat _sentRotation;;
|
glm::quat _sentRotation;;
|
||||||
glm::vec3 _sentVelocity;
|
glm::vec3 _sentVelocity;
|
||||||
glm::vec3 _sentAngularVelocity; // radians per second
|
glm::vec3 _sentAngularVelocity; // radians per second
|
||||||
glm::vec3 _sentAcceleration;
|
glm::vec3 _sentAcceleration;
|
||||||
|
|
||||||
|
uint32_t _lastSimulationStep;
|
||||||
|
glm::vec3 _lastVelocity;
|
||||||
|
glm::vec3 _measuredAcceleration;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ObjectMotionState_h
|
#endif // hifi_ObjectMotionState_h
|
||||||
|
|
|
@ -193,6 +193,9 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
|
||||||
// hence the MotionState has all the knowledge and authority to perform the update.
|
// hence the MotionState has all the knowledge and authority to perform the update.
|
||||||
motionState->updateObjectEasy(flags, _numSubsteps);
|
motionState->updateObjectEasy(flags, _numSubsteps);
|
||||||
}
|
}
|
||||||
|
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
|
||||||
|
motionState->resetMeasuredVelocityAndAcceleration();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// the only way we should ever get here (motionState exists but no body) is when the object
|
// the only way we should ever get here (motionState exists but no body) is when the object
|
||||||
// is undergoing non-physical kinematic motion.
|
// is undergoing non-physical kinematic motion.
|
||||||
|
@ -508,6 +511,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
|
||||||
body->setFriction(motionState->_friction);
|
body->setFriction(motionState->_friction);
|
||||||
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
|
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
|
||||||
_dynamicsWorld->addRigidBody(body);
|
_dynamicsWorld->addRigidBody(body);
|
||||||
|
motionState->resetMeasuredVelocityAndAcceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) {
|
void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) {
|
||||||
|
|
Loading…
Reference in a new issue