Merge pull request #6476 from AndrewMeadows/arrow

fix for objects suffering from very small updates to physics properties
This commit is contained in:
Philip Rosedale 2015-11-24 11:22:25 -08:00
commit dde01b2071
9 changed files with 180 additions and 166 deletions

View file

@ -799,19 +799,13 @@ void EntityItem::setDensity(float density) {
_density = glm::max(glm::min(density, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); _density = glm::max(glm::min(density, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY);
} }
const float ACTIVATION_RELATIVE_DENSITY_DELTA = 0.01f; // 1 percent
void EntityItem::updateDensity(float density) { void EntityItem::updateDensity(float density) {
float clampedDensity = glm::max(glm::min(density, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); float clampedDensity = glm::max(glm::min(density, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY);
if (_density != clampedDensity) { if (_density != clampedDensity) {
_density = clampedDensity; _density = clampedDensity;
if (fabsf(_density - clampedDensity) / _density > ACTIVATION_RELATIVE_DENSITY_DELTA) {
// the density has changed enough that we should update the physics simulation
_dirtyFlags |= Simulation::DIRTY_MASS; _dirtyFlags |= Simulation::DIRTY_MASS;
} }
} }
}
void EntityItem::setMass(float mass) { void EntityItem::setMass(float mass) {
// Setting the mass actually changes the _density (at fixed volume), however // Setting the mass actually changes the _density (at fixed volume), however
@ -822,11 +816,16 @@ void EntityItem::setMass(float mass) {
// compute new density // compute new density
const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3 const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3
float newDensity = 1.0f;
if (volume < 1.0e-6f) { if (volume < 1.0e-6f) {
// avoid divide by zero // avoid divide by zero
_density = glm::min(mass / MIN_VOLUME, ENTITY_ITEM_MAX_DENSITY); newDensity = glm::min(mass / MIN_VOLUME, ENTITY_ITEM_MAX_DENSITY);
} else { } else {
_density = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); newDensity = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY);
}
if (_density != newDensity) {
_density = newDensity;
_dirtyFlags |= Simulation::DIRTY_MASS;
} }
} }
@ -1312,26 +1311,18 @@ void EntityItem::updatePosition(const glm::vec3& value) {
if (shouldSuppressLocationEdits()) { if (shouldSuppressLocationEdits()) {
return; return;
} }
auto delta = glm::distance(getPosition(), value); if (getPosition() != value) {
if (delta > IGNORE_POSITION_DELTA) {
_dirtyFlags |= Simulation::DIRTY_POSITION;
setPosition(value); setPosition(value);
if (delta > ACTIVATION_POSITION_DELTA) { _dirtyFlags |= Simulation::DIRTY_POSITION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
} }
} }
void EntityItem::updateDimensions(const glm::vec3& value) { void EntityItem::updateDimensions(const glm::vec3& value) {
auto delta = glm::distance(getDimensions(), value); if (getDimensions() != value) {
if (delta > IGNORE_DIMENSIONS_DELTA) {
setDimensions(value); setDimensions(value);
if (delta > ACTIVATION_DIMENSIONS_DELTA) {
// rebuilding the shape will always activate
_dirtyFlags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); _dirtyFlags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
} }
} }
}
void EntityItem::updateRotation(const glm::quat& rotation) { void EntityItem::updateRotation(const glm::quat& rotation) {
if (shouldSuppressLocationEdits()) { if (shouldSuppressLocationEdits()) {
@ -1339,15 +1330,8 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
} }
if (getRotation() != rotation) { if (getRotation() != rotation) {
setRotation(rotation); setRotation(rotation);
auto alignmentDot = glm::abs(glm::dot(getRotation(), rotation));
if (alignmentDot < IGNORE_ALIGNMENT_DOT) {
_dirtyFlags |= Simulation::DIRTY_ROTATION; _dirtyFlags |= Simulation::DIRTY_ROTATION;
} }
if (alignmentDot < ACTIVATION_ALIGNMENT_DOT) {
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
} }
void EntityItem::updateMass(float mass) { void EntityItem::updateMass(float mass) {
@ -1367,11 +1351,8 @@ void EntityItem::updateMass(float mass) {
newDensity = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); newDensity = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY);
} }
float oldDensity = _density; if (_density != newDensity) {
_density = newDensity; _density = newDensity;
if (fabsf(_density - oldDensity) / _density > ACTIVATION_RELATIVE_DENSITY_DELTA) {
// the density has changed enough that we should update the physics simulation
_dirtyFlags |= Simulation::DIRTY_MASS; _dirtyFlags |= Simulation::DIRTY_MASS;
} }
} }
@ -1380,38 +1361,29 @@ void EntityItem::updateVelocity(const glm::vec3& value) {
if (shouldSuppressLocationEdits()) { if (shouldSuppressLocationEdits()) {
return; return;
} }
auto delta = glm::distance(_velocity, value); if (_velocity != value) {
if (delta > IGNORE_LINEAR_VELOCITY_DELTA) {
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
const float MIN_LINEAR_SPEED = 0.001f; const float MIN_LINEAR_SPEED = 0.001f;
if (glm::length(value) < MIN_LINEAR_SPEED) { if (glm::length(value) < MIN_LINEAR_SPEED) {
_velocity = ENTITY_ITEM_ZERO_VEC3; _velocity = ENTITY_ITEM_ZERO_VEC3;
} else { } else {
_velocity = value; _velocity = value;
// only activate when setting non-zero velocity
if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) {
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
} }
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
} }
} }
void EntityItem::updateDamping(float value) { void EntityItem::updateDamping(float value) {
auto clampedDamping = glm::clamp(value, 0.0f, 1.0f); auto clampedDamping = glm::clamp(value, 0.0f, 1.0f);
if (fabsf(_damping - clampedDamping) > IGNORE_DAMPING_DELTA) { if (_damping != clampedDamping) {
_damping = clampedDamping; _damping = clampedDamping;
_dirtyFlags |= Simulation::DIRTY_MATERIAL; _dirtyFlags |= Simulation::DIRTY_MATERIAL;
} }
} }
void EntityItem::updateGravity(const glm::vec3& value) { void EntityItem::updateGravity(const glm::vec3& value) {
auto delta = glm::distance(_gravity, value); if (_gravity != value) {
if (delta > IGNORE_GRAVITY_DELTA) {
_gravity = value; _gravity = value;
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
if (delta > ACTIVATION_GRAVITY_DELTA) {
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
} }
} }
@ -1419,25 +1391,20 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
if (shouldSuppressLocationEdits()) { if (shouldSuppressLocationEdits()) {
return; return;
} }
auto delta = glm::distance(_angularVelocity, value); if (_angularVelocity != value) {
if (delta > IGNORE_ANGULAR_VELOCITY_DELTA) {
_dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY;
const float MIN_ANGULAR_SPEED = 0.0002f; const float MIN_ANGULAR_SPEED = 0.0002f;
if (glm::length(value) < MIN_ANGULAR_SPEED) { if (glm::length(value) < MIN_ANGULAR_SPEED) {
_angularVelocity = ENTITY_ITEM_ZERO_VEC3; _angularVelocity = ENTITY_ITEM_ZERO_VEC3;
} else { } else {
_angularVelocity = value; _angularVelocity = value;
// only activate when setting non-zero velocity
if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) {
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
} }
_dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY;
} }
} }
void EntityItem::updateAngularDamping(float value) { void EntityItem::updateAngularDamping(float value) {
auto clampedDamping = glm::clamp(value, 0.0f, 1.0f); auto clampedDamping = glm::clamp(value, 0.0f, 1.0f);
if (fabsf(_angularDamping - clampedDamping) > IGNORE_DAMPING_DELTA) { if (_angularDamping != clampedDamping) {
_angularDamping = clampedDamping; _angularDamping = clampedDamping;
_dirtyFlags |= Simulation::DIRTY_MATERIAL; _dirtyFlags |= Simulation::DIRTY_MATERIAL;
} }

View file

@ -48,6 +48,7 @@ namespace render {
class PendingChanges; class PendingChanges;
} }
/*
// these thesholds determine what updates will be ignored (client and server) // these thesholds determine what updates will be ignored (client and server)
const float IGNORE_POSITION_DELTA = 0.0001f; const float IGNORE_POSITION_DELTA = 0.0001f;
const float IGNORE_DIMENSIONS_DELTA = 0.0005f; const float IGNORE_DIMENSIONS_DELTA = 0.0005f;
@ -64,6 +65,7 @@ const float ACTIVATION_ALIGNMENT_DOT = 0.99990f;
const float ACTIVATION_LINEAR_VELOCITY_DELTA = 0.01f; const float ACTIVATION_LINEAR_VELOCITY_DELTA = 0.01f;
const float ACTIVATION_GRAVITY_DELTA = 0.1f; const float ACTIVATION_GRAVITY_DELTA = 0.1f;
const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f; const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
*/
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };

View file

@ -95,7 +95,7 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) {
} }
// virtual // virtual
bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { bool EntityMotionState::handleEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
assert(entityTreeIsLocked()); assert(entityTreeIsLocked());
updateServerPhysicsVariables(engine->getSessionID()); updateServerPhysicsVariables(engine->getSessionID());
ObjectMotionState::handleEasyChanges(flags, engine); ObjectMotionState::handleEasyChanges(flags, engine);
@ -133,7 +133,7 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
// virtual // virtual
bool EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
updateServerPhysicsVariables(engine->getSessionID()); updateServerPhysicsVariables(engine->getSessionID());
return ObjectMotionState::handleHardAndEasyChanges(flags, engine); return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
} }

View file

@ -29,8 +29,8 @@ public:
virtual ~EntityMotionState(); virtual ~EntityMotionState();
void updateServerPhysicsVariables(const QUuid& sessionID); void updateServerPhysicsVariables(const QUuid& sessionID);
virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual bool handleEasyChanges(uint32_t& flags, PhysicsEngine* engine);
virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
virtual MotionType computeObjectMotionType() const; virtual MotionType computeObjectMotionType() const;

View file

@ -17,6 +17,14 @@
#include "PhysicsHelpers.h" #include "PhysicsHelpers.h"
#include "PhysicsLogging.h" #include "PhysicsLogging.h"
// these thresholds determine what updates (object-->body) will activate the physical object
const float ACTIVATION_POSITION_DELTA = 0.005f;
const float ACTIVATION_ALIGNMENT_DOT = 0.99990f;
const float ACTIVATION_LINEAR_VELOCITY_DELTA = 0.01f;
const float ACTIVATION_GRAVITY_DELTA = 0.1f;
const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
// origin of physics simulation in world-frame // origin of physics simulation in world-frame
glm::vec3 _worldOffset(0.0f); glm::vec3 _worldOffset(0.0f);
@ -128,28 +136,65 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
} }
} }
bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { bool ObjectMotionState::handleEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
if (flags & Simulation::DIRTY_POSITION) { if (flags & Simulation::DIRTY_POSITION) {
btTransform worldTrans; btTransform worldTrans = _body->getWorldTransform();
if (flags & Simulation::DIRTY_ROTATION) { btVector3 newPosition = glmToBullet(getObjectPosition());
worldTrans.setRotation(glmToBullet(getObjectRotation())); float delta = (newPosition - worldTrans.getOrigin()).length();
} else { if (delta > ACTIVATION_POSITION_DELTA) {
worldTrans = _body->getWorldTransform(); flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
worldTrans.setOrigin(newPosition);
if (flags & Simulation::DIRTY_ROTATION) {
btQuaternion newRotation = glmToBullet(getObjectRotation());
float alignmentDot = fabsf(worldTrans.getRotation().dot(newRotation));
if (alignmentDot < ACTIVATION_ALIGNMENT_DOT) {
flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
worldTrans.setRotation(newRotation);
} }
worldTrans.setOrigin(glmToBullet(getObjectPosition()));
_body->setWorldTransform(worldTrans); _body->setWorldTransform(worldTrans);
} else if (flags & Simulation::DIRTY_ROTATION) { } else if (flags & Simulation::DIRTY_ROTATION) {
btTransform worldTrans = _body->getWorldTransform(); btTransform worldTrans = _body->getWorldTransform();
worldTrans.setRotation(glmToBullet(getObjectRotation())); btQuaternion newRotation = glmToBullet(getObjectRotation());
float alignmentDot = fabsf(worldTrans.getRotation().dot(newRotation));
if (alignmentDot < ACTIVATION_ALIGNMENT_DOT) {
flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
worldTrans.setRotation(newRotation);
_body->setWorldTransform(worldTrans); _body->setWorldTransform(worldTrans);
} }
if (flags & Simulation::DIRTY_LINEAR_VELOCITY) { if (flags & Simulation::DIRTY_LINEAR_VELOCITY) {
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity())); btVector3 newLinearVelocity = glmToBullet(getObjectLinearVelocity());
_body->setGravity(glmToBullet(getObjectGravity())); if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
float delta = (newLinearVelocity - _body->getLinearVelocity()).length();
if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) {
flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
_body->setLinearVelocity(newLinearVelocity);
btVector3 newGravity = glmToBullet(getObjectGravity());
if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
float delta = (newGravity - _body->getGravity()).length();
if (delta > ACTIVATION_GRAVITY_DELTA ||
(delta > 0.0f && _body->getGravity().length2() == 0.0f)) {
flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
_body->setGravity(newGravity);
} }
if (flags & Simulation::DIRTY_ANGULAR_VELOCITY) { if (flags & Simulation::DIRTY_ANGULAR_VELOCITY) {
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity())); btVector3 newAngularVelocity = glmToBullet(getObjectAngularVelocity());
if (!(flags & Simulation::DIRTY_PHYSICS_ACTIVATION)) {
float delta = (newAngularVelocity - _body->getAngularVelocity()).length();
if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) {
flags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
_body->setAngularVelocity(newAngularVelocity);
} }
if (flags & Simulation::DIRTY_MATERIAL) { if (flags & Simulation::DIRTY_MATERIAL) {
@ -163,7 +208,7 @@ bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
return true; return true;
} }
bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
if (flags & Simulation::DIRTY_SHAPE) { if (flags & Simulation::DIRTY_SHAPE) {
// make sure the new shape is valid // make sure the new shape is valid
if (!isReadyToComputeShape()) { if (!isReadyToComputeShape()) {
@ -195,8 +240,8 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
if (flags & EASY_DIRTY_PHYSICS_FLAGS) { if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
handleEasyChanges(flags, engine); handleEasyChanges(flags, engine);
} }
// it is possible that there are no HARD flags at this point (if DIRTY_SHAPE was removed) // it is possible there are no HARD flags at this point (if DIRTY_SHAPE was removed)
// so we check again befoe we reinsert: // so we check again before we reinsert:
if (flags & HARD_DIRTY_PHYSICS_FLAGS) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
engine->reinsertObject(this); engine->reinsertObject(this);
} }

View file

@ -80,8 +80,8 @@ public:
ObjectMotionState(btCollisionShape* shape); ObjectMotionState(btCollisionShape* shape);
~ObjectMotionState(); ~ObjectMotionState();
virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual bool handleEasyChanges(uint32_t& flags, PhysicsEngine* engine);
virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
void updateBodyMaterialProperties(); void updateBodyMaterialProperties();
void updateBodyVelocities(); void updateBodyVelocities();

View file

@ -244,7 +244,7 @@ void PhysicsEngine::stepSimulation() {
float timeStep = btMin(dt, MAX_TIMESTEP); float timeStep = btMin(dt, MAX_TIMESTEP);
if (_myAvatarController) { if (_myAvatarController) {
// ADEBUG TODO: move this stuff outside and in front of stepSimulation, because // TODO: move this stuff outside and in front of stepSimulation, because
// the updateShapeIfNecessary() call needs info from MyAvatar and should // the updateShapeIfNecessary() call needs info from MyAvatar and should
// be done on the main thread during the pre-simulation stuff // be done on the main thread during the pre-simulation stuff
if (_myAvatarController->needsRemoval()) { if (_myAvatarController->needsRemoval()) {