mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 05:58:27 +02:00
optimize kinematic motion math
This commit is contained in:
parent
905c5398c4
commit
3639ffe53e
4 changed files with 72 additions and 98 deletions
|
@ -731,7 +731,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
|
|
||||||
// we want to extrapolate the motion forward to compensate for packet travel time, but
|
// we want to extrapolate the motion forward to compensate for packet travel time, but
|
||||||
// we don't want the side effect of flag setting.
|
// we don't want the side effect of flag setting.
|
||||||
simulateKinematicMotion(skipTimeForward, false);
|
stepKinematicMotion(skipTimeForward);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overwriteLocalData) {
|
if (overwriteLocalData) {
|
||||||
|
@ -872,130 +872,104 @@ void EntityItem::simulate(const quint64& now) {
|
||||||
qCDebug(entities) << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated;
|
qCDebug(entities) << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
simulateKinematicMotion(timeElapsed);
|
if (!hasActions()) {
|
||||||
|
if (!stepKinematicMotion(timeElapsed)) {
|
||||||
|
// this entity is no longer moving
|
||||||
|
// flag it to transition from KINEMATIC to STATIC
|
||||||
|
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
_lastSimulated = now;
|
_lastSimulated = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
bool EntityItem::stepKinematicMotion(float timeElapsed) {
|
||||||
#ifdef WANT_DEBUG
|
if (timeElapsed < 0.0f) {
|
||||||
qCDebug(entities) << "EntityItem::simulateKinematicMotion timeElapsed" << timeElapsed;
|
return true;
|
||||||
#endif
|
|
||||||
|
|
||||||
const float MIN_TIME_SKIP = 0.0f;
|
|
||||||
const float MAX_TIME_SKIP = 1.0f; // in seconds
|
|
||||||
|
|
||||||
timeElapsed = glm::clamp(timeElapsed, MIN_TIME_SKIP, MAX_TIME_SKIP);
|
|
||||||
|
|
||||||
if (hasActions()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasLocalAngularVelocity()) {
|
const float MAX_TIME_ELAPSED = 1.0f; // seconds
|
||||||
glm::vec3 localAngularVelocity = getLocalAngularVelocity();
|
timeElapsed = glm::min(timeElapsed, MAX_TIME_ELAPSED);
|
||||||
|
|
||||||
|
Transform transform;
|
||||||
|
glm::vec3 linearVelocity;
|
||||||
|
glm::vec3 angularVelocity;
|
||||||
|
getLocalTransformAndVelocities(transform, linearVelocity, angularVelocity);
|
||||||
|
|
||||||
|
bool moving = false;
|
||||||
|
if (glm::length2(angularVelocity) > 0.0f) {
|
||||||
// angular damping
|
// angular damping
|
||||||
if (_angularDamping > 0.0f) {
|
if (_angularDamping > 0.0f) {
|
||||||
localAngularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
|
angularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
|
||||||
#ifdef WANT_DEBUG
|
|
||||||
qCDebug(entities) << " angularDamping :" << _angularDamping;
|
|
||||||
qCDebug(entities) << " newAngularVelocity:" << localAngularVelocity;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float angularSpeed = glm::length(localAngularVelocity);
|
const float MIN_KINEMATIC_ANGULAR_SPEED_SQUARED = 0.0017453f * 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec
|
||||||
|
if (glm::length2(angularVelocity) < MIN_KINEMATIC_ANGULAR_SPEED_SQUARED) {
|
||||||
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec
|
angularVelocity = Vectors::ZERO;
|
||||||
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
|
|
||||||
if (setFlags && angularSpeed > 0.0f) {
|
|
||||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
|
||||||
}
|
|
||||||
localAngularVelocity = ENTITY_ITEM_ZERO_VEC3;
|
|
||||||
} else {
|
} else {
|
||||||
// for improved agreement with the way Bullet integrates rotations we use an approximation
|
// for improved agreement with the way Bullet integrates rotations we use an approximation
|
||||||
// and break the integration into bullet-sized substeps
|
// and break the integration into bullet-sized substeps
|
||||||
glm::quat rotation = getRotation();
|
glm::quat rotation = transform.getRotation();
|
||||||
float dt = timeElapsed;
|
float dt = timeElapsed;
|
||||||
while (dt > PHYSICS_ENGINE_FIXED_SUBSTEP) {
|
while (dt > 0.0f) {
|
||||||
glm::quat dQ = computeBulletRotationStep(localAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP);
|
glm::quat dQ = computeBulletRotationStep(angularVelocity, glm::min(dt, PHYSICS_ENGINE_FIXED_SUBSTEP));
|
||||||
rotation = glm::normalize(dQ * rotation);
|
rotation = glm::normalize(dQ * rotation);
|
||||||
dt -= PHYSICS_ENGINE_FIXED_SUBSTEP;
|
dt -= PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
}
|
}
|
||||||
// NOTE: this final partial substep can drift away from a real Bullet simulation however
|
transform.setRotation(rotation);
|
||||||
// it only becomes significant for rapidly rotating objects
|
|
||||||
// (e.g. around PI/4 radians per substep, or 7.5 rotations/sec at 60 substeps/sec).
|
|
||||||
glm::quat dQ = computeBulletRotationStep(localAngularVelocity, dt);
|
|
||||||
rotation = glm::normalize(dQ * rotation);
|
|
||||||
|
|
||||||
bool success;
|
|
||||||
setOrientation(rotation, success, false);
|
|
||||||
}
|
}
|
||||||
|
moving = true;
|
||||||
setLocalAngularVelocity(localAngularVelocity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasLocalVelocity()) {
|
glm::vec3 position = transform.getTranslation();
|
||||||
|
float linearSpeedSquared = glm::length2(linearVelocity);
|
||||||
// acceleration is in the global frame, so transform it into the local frame.
|
if (linearSpeedSquared > 0.0f) {
|
||||||
// TODO: Move this into SpatiallyNestable.
|
glm::vec3 deltaVelocity = Vectors::ZERO;
|
||||||
bool success;
|
|
||||||
Transform transform = getParentTransform(success);
|
|
||||||
glm::vec3 localAcceleration(glm::vec3::_null);
|
|
||||||
if (success) {
|
|
||||||
localAcceleration = glm::inverse(transform.getRotation()) * getAcceleration();
|
|
||||||
} else {
|
|
||||||
localAcceleration = getAcceleration();
|
|
||||||
}
|
|
||||||
|
|
||||||
// linear damping
|
// linear damping
|
||||||
glm::vec3 localVelocity = getLocalVelocity();
|
|
||||||
if (_damping > 0.0f) {
|
if (_damping > 0.0f) {
|
||||||
localVelocity *= powf(1.0f - _damping, timeElapsed);
|
deltaVelocity = (powf(1.0f - _damping, timeElapsed) - 1.0f) * linearVelocity;
|
||||||
#ifdef WANT_DEBUG
|
|
||||||
qCDebug(entities) << " damping:" << _damping;
|
|
||||||
qCDebug(entities) << " velocity AFTER dampingResistance:" << localVelocity;
|
|
||||||
qCDebug(entities) << " glm::length(velocity):" << glm::length(localVelocity);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// integrate position forward
|
const float MIN_KINEMATIC_LINEAR_ACCELERATION_SQUARED = 1.0e-4f; // 0.01 m/sec^2
|
||||||
glm::vec3 localPosition = getLocalPosition();
|
const float MIN_KINEMATIC_LINEAR_SPEED_SQUARED = 1.0e-6f; // 0.001 m/sec^2
|
||||||
glm::vec3 newLocalPosition = localPosition + (localVelocity * timeElapsed) + 0.5f * localAcceleration * timeElapsed * timeElapsed;
|
if (glm::length2(_gravity) > MIN_KINEMATIC_LINEAR_ACCELERATION_SQUARED) {
|
||||||
|
// yes gravity
|
||||||
|
// acceleration is in world-frame but we need it in local-frame
|
||||||
|
glm::vec3 linearAcceleration = _gravity;
|
||||||
|
bool success;
|
||||||
|
Transform parentTransform = getParentTransform(success);
|
||||||
|
if (success) {
|
||||||
|
linearAcceleration = glm::inverse(parentTransform.getRotation()) * linearAcceleration;
|
||||||
|
}
|
||||||
|
deltaVelocity += linearAcceleration * timeElapsed;
|
||||||
|
|
||||||
#ifdef WANT_DEBUG
|
if (glm::length2(deltaVelocity) < MIN_KINEMATIC_LINEAR_SPEED_SQUARED
|
||||||
qCDebug(entities) << " EntityItem::simulate()....";
|
&& glm::length2(linearVelocity) < MIN_KINEMATIC_LINEAR_SPEED_SQUARED) {
|
||||||
qCDebug(entities) << " timeElapsed:" << timeElapsed;
|
linearVelocity = Vectors::ZERO;
|
||||||
qCDebug(entities) << " old AACube:" << getMaximumAACube();
|
} else {
|
||||||
qCDebug(entities) << " old position:" << localPosition;
|
// position's acceleration term uses deltaVelocity rather than raw gravity
|
||||||
qCDebug(entities) << " old velocity:" << localVelocity;
|
// for more accuracy (includes damping effects)
|
||||||
qCDebug(entities) << " old getAABox:" << getAABox();
|
position += timeElapsed * (linearVelocity + 0.5f * deltaVelocity);
|
||||||
qCDebug(entities) << " newPosition:" << newPosition;
|
linearVelocity += deltaVelocity;
|
||||||
qCDebug(entities) << " glm::distance(newPosition, position):" << glm::distance(newLocalPosition, localPosition);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
localPosition = newLocalPosition;
|
|
||||||
|
|
||||||
// apply effective acceleration, which will be the same as gravity if the Entity isn't at rest.
|
|
||||||
localVelocity += localAcceleration * timeElapsed;
|
|
||||||
|
|
||||||
float speed = glm::length(localVelocity);
|
|
||||||
const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec
|
|
||||||
if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) {
|
|
||||||
setVelocity(ENTITY_ITEM_ZERO_VEC3);
|
|
||||||
if (setFlags && speed > 0.0f) {
|
|
||||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setLocalPosition(localPosition);
|
// no gravity
|
||||||
setLocalVelocity(localVelocity);
|
if (linearSpeedSquared < MIN_KINEMATIC_LINEAR_SPEED_SQUARED) {
|
||||||
|
linearVelocity = Vectors::ZERO;
|
||||||
|
} else {
|
||||||
|
// NOTE: don't use acceleration term for linear displacement
|
||||||
|
position += timeElapsed * linearVelocity;
|
||||||
|
linearVelocity += deltaVelocity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
moving = true;
|
||||||
#ifdef WANT_DEBUG
|
|
||||||
qCDebug(entities) << " new position:" << position;
|
|
||||||
qCDebug(entities) << " new velocity:" << velocity;
|
|
||||||
qCDebug(entities) << " new AACube:" << getMaximumAACube();
|
|
||||||
qCDebug(entities) << " old getAABox:" << getAABox();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (moving) {
|
||||||
|
transform.setTranslation(position);
|
||||||
|
setLocalTransformAndVelocities(transform, linearVelocity, angularVelocity);
|
||||||
|
}
|
||||||
|
return moving;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::isMoving() const {
|
bool EntityItem::isMoving() const {
|
||||||
|
|
|
@ -152,7 +152,7 @@ public:
|
||||||
|
|
||||||
// perform linear extrapolation for SimpleEntitySimulation
|
// perform linear extrapolation for SimpleEntitySimulation
|
||||||
void simulate(const quint64& now);
|
void simulate(const quint64& now);
|
||||||
void simulateKinematicMotion(float timeElapsed, bool setFlags=true);
|
bool stepKinematicMotion(float timeElapsed); // return 'true' if moving
|
||||||
|
|
||||||
virtual bool needsToCallUpdate() const { return false; }
|
virtual bool needsToCallUpdate() const { return false; }
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||||
// of the physics simulation.
|
// of the physics simulation.
|
||||||
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
||||||
float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
_entity->simulateKinematicMotion(dt);
|
_entity->stepKinematicMotion(dt);
|
||||||
|
|
||||||
// bypass const-ness so we can remember the step
|
// bypass const-ness so we can remember the step
|
||||||
const_cast<EntityMotionState*>(this)->_lastKinematicStep = thisStep;
|
const_cast<EntityMotionState*>(this)->_lastKinematicStep = thisStep;
|
||||||
|
|
|
@ -419,7 +419,7 @@ void SpatiallyNestable::setVelocity(const glm::vec3& velocity, bool& success) {
|
||||||
// _velocity is a vs parent value and any request for a world-frame velocity must
|
// _velocity is a vs parent value and any request for a world-frame velocity must
|
||||||
// be computed), do this to avoid equipped (parenting-grabbed) things from drifting.
|
// be computed), do this to avoid equipped (parenting-grabbed) things from drifting.
|
||||||
// turning a zero velocity into a non-zero _velocity (because the avatar is moving)
|
// turning a zero velocity into a non-zero _velocity (because the avatar is moving)
|
||||||
// causes EntityItem::simulateKinematicMotion to have an effect on the equipped entity,
|
// causes EntityItem::stepKinematicMotion to have an effect on the equipped entity,
|
||||||
// which causes it to drift from the hand.
|
// which causes it to drift from the hand.
|
||||||
if (hasAncestorOfType(NestableType::Avatar)) {
|
if (hasAncestorOfType(NestableType::Avatar)) {
|
||||||
_velocity = velocity;
|
_velocity = velocity;
|
||||||
|
|
Loading…
Reference in a new issue