Merge pull request #4039 from AndrewMeadows/inertia

Bullet physics part 2
This commit is contained in:
Brad Hefta-Gaub 2015-01-05 13:10:55 -08:00
commit 125dc5010e
13 changed files with 112 additions and 54 deletions

View file

@ -79,7 +79,7 @@ function addButterfly() {
rotation: Quat.fromPitchYawRollDegrees(-80 + Math.random() * 20, Math.random() * 360.0, 0.0),
velocity: { x: 0, y: 0, z: 0 },
gravity: { x: 0, y: GRAVITY, z: 0 },
damping: 0.9999,
damping: 0.00001,
dimensions: dimensions,
color: color,
animationURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/content/butterfly/butterfly.fbx",
@ -138,4 +138,4 @@ Script.scriptEnding.connect(function() {
for (var i = 0; i < numButterflies; i++) {
Entities.deleteEntity(butterflies[i]);
}
});
});

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var damping = 0.9;
var damping = 0.001;
var yaw = 0.0;
var pitch = 0.0;
var roll = 0.0;

View file

@ -208,7 +208,7 @@ function checkControllerSide(hand) {
gravity: { x: 0, y: 0, z: 0},
inHand: true,
dimensions: { x: FRISBEE_RADIUS, y: FRISBEE_RADIUS / 5, z: FRISBEE_RADIUS },
damping: 0.999,
damping: 0.00001,
modelURL: modelUrl,
modelScale: FRISBEE_MODEL_SCALE,
modelRotation: hand.holdRotation(),
@ -235,7 +235,7 @@ function checkControllerSide(hand) {
gravity: { x: 0, y: 0, z: 0},
inHand: true,
dimensions: { x: FRISBEE_RADIUS, y: FRISBEE_RADIUS / 5, z: FRISBEE_RADIUS },
damping: 0.999,
damping: 0.00001,
modelURL: frisbeeURL(),
modelScale: FRISBEE_MODEL_SCALE,
modelRotation: hand.holdRotation(),
@ -444,4 +444,4 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Menu.menuItemEvent.connect(menuItemEvent);
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(checkController);
Script.update.connect(controlFrisbees);
Script.update.connect(controlFrisbees);

View file

@ -145,7 +145,7 @@ function shootTarget() {
velocity: velocity,
gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
lifetime: 1000.0,
damping: 0.99 });
damping: 0.0001 });
// Record start time
shotTime = new Date();

View file

@ -144,7 +144,7 @@ function shootTarget() {
velocity: velocity,
gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
lifetime: TARGET_LIFETIME,
damping: 0.99 });
damping: 0.0001 });
// Record start time
shotTime = new Date();

View file

@ -137,7 +137,7 @@ function checkControllerSide(whichSide) {
gravity: { x: 0, y: 0, z: 0},
inHand: true,
radius: { x: BALL_RADIUS * 2, y: BALL_RADIUS * 2, z: BALL_RADIUS * 2 },
damping: 0.999,
damping: 0.00001,
color: HELD_COLOR,
lifetime: 600 // 10 seconds - same as default, not needed but here as an example

View file

@ -12,7 +12,7 @@ var properties = {
velocity: { x: 0, y: 0, z: 0},
gravity: { x: 0, y: -0.05, z: 0},
radius: radius,
damping: 0.999,
damping: 0.00001,
color: { red: 200, green: 0, blue: 0 },
lifetime: 60
};
@ -22,4 +22,4 @@ position.x -= radius * 1.0;
properties.position = position;
var newEntityTwo = Entities.addEntity(properties);
Script.stop(); // no need to run anymore
Script.stop(); // no need to run anymore

View file

@ -4,6 +4,7 @@ set(TARGET_NAME entities)
setup_hifi_library(Network Script)
include_glm()
include_bullet()
link_hifi_libraries(avatars shared octree gpu model fbx networking animation)

View file

@ -29,7 +29,7 @@ const float EntityItem::DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
const float EntityItem::DEFAULT_MASS = 1.0f;
const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL;
const QString EntityItem::DEFAULT_USER_DATA = QString("");
const float EntityItem::DEFAULT_DAMPING = 2.0f;
const float EntityItem::DEFAULT_DAMPING = 0.39347f; // approx timescale = 2.0 sec (see damping timescale formula in header)
const glm::vec3 EntityItem::NO_VELOCITY = glm::vec3(0, 0, 0);
const float EntityItem::EPSILON_VELOCITY_LENGTH = (1.0f / 1000.0f) / (float)TREE_SCALE; // really small: 1mm/second
const glm::vec3 EntityItem::DEFAULT_VELOCITY = EntityItem::NO_VELOCITY;
@ -647,34 +647,54 @@ void EntityItem::simulate(const quint64& now) {
if (hasAngularVelocity()) {
glm::quat rotation = getRotation();
// angular damping
glm::vec3 angularVelocity = glm::radians(getAngularVelocity());
if (_angularDamping > 0.0f) {
angularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
if (wantDebug) {
qDebug() << " angularDamping :" << _angularDamping;
qDebug() << " newAngularVelocity:" << angularVelocity;
}
}
float angularSpeed = glm::length(angularVelocity);
if (angularSpeed < EPSILON_VELOCITY_LENGTH) {
setAngularVelocity(NO_ANGULAR_VELOCITY);
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // ~0.1 degree/sec
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
angularVelocity = NO_ANGULAR_VELOCITY;
} else {
float angle = timeElapsed * angularSpeed;
glm::quat dQ = glm::angleAxis(angle, glm::normalize(angularVelocity));
rotation = dQ * rotation;
rotation = glm::normalize(dQ * rotation);
setRotation(rotation);
// handle damping for angular velocity
float dampingTimescale = getAngularDamping();
if (dampingTimescale > 0.0f) {
float dampingFactor = glm::clamp(timeElapsed / dampingTimescale, 0.0f, 1.0f);
glm::vec3 newAngularVelocity = (1.0f - dampingFactor) * getAngularVelocity();
setAngularVelocity(newAngularVelocity);
if (wantDebug) {
qDebug() << " dampingTimescale :" << dampingTimescale;
qDebug() << " newAngularVelocity:" << newAngularVelocity;
}
}
}
setAngularVelocity(angularVelocity);
}
#ifdef USE_BULLET_PHYSICS
// When Bullet is available we assume that "zero velocity" means "at rest"
// because of collision conditions this simulation does not know about
// so we don't fall in for the non-zero gravity case here.
if (hasVelocity()) {
#else // !USE_BULLET_PHYSICS
if (hasVelocity() || hasGravity()) {
glm::vec3 position = getPosition();
#endif // USE_BULLET_PHYSICS
// linear damping
glm::vec3 velocity = getVelocity();
if (_damping > 0.0f) {
velocity *= powf(1.0f - _damping, timeElapsed);
if (wantDebug) {
qDebug() << " damping:" << _damping;
qDebug() << " velocity AFTER dampingResistance:" << velocity;
qDebug() << " glm::length(velocity):" << glm::length(velocity);
qDebug() << " EPSILON_VELOCITY_LENGTH:" << EPSILON_VELOCITY_LENGTH;
}
}
// integrate position forward
glm::vec3 position = getPosition();
glm::vec3 newPosition = position + (velocity * timeElapsed);
if (wantDebug) {
@ -695,18 +715,20 @@ void EntityItem::simulate(const quint64& now) {
if (position.y <= getDistanceToBottomOfEntity()) {
velocity = velocity * glm::vec3(1,-1,1);
// if we've slowed considerably, then just stop moving
#ifndef USE_BULLET_PHYSICS
// if we've slowed considerably, then just stop moving, but only if no BULLET
if (glm::length(velocity) <= EPSILON_VELOCITY_LENGTH) {
velocity = NO_VELOCITY;
}
#endif // !USE_BULLET_PHYSICS
position.y = getDistanceToBottomOfEntity();
}
// handle gravity....
// apply gravity
if (hasGravity()) {
// handle resting on surface case, this is definitely a bit of a hack, and it only works on the
// "ground" plane of the domain, but for now it what we've got
// "ground" plane of the domain, but for now it's what we've got
if (isRestingOnSurface()) {
velocity.y = 0.0f;
position.y = getDistanceToBottomOfEntity();
@ -714,28 +736,15 @@ void EntityItem::simulate(const quint64& now) {
velocity += getGravity() * timeElapsed;
}
}
// handle damping for velocity
float dampingTimescale = getDamping();
if (dampingTimescale > 0.0f) {
float dampingFactor = glm::clamp(timeElapsed / dampingTimescale, 0.0f, 1.0f);
velocity *= (1.0f - dampingFactor);
if (wantDebug) {
qDebug() << " dampingTimescale:" << dampingTimescale;
qDebug() << " newVelocity:" << velocity;
}
}
if (wantDebug) {
qDebug() << " velocity AFTER dampingResistance:" << velocity;
qDebug() << " glm::length(velocity):" << glm::length(velocity);
qDebug() << " EPSILON_VELOCITY_LENGTH:" << EPSILON_VELOCITY_LENGTH;
}
// round velocity to zero if it's close enough...
#ifdef USE_BULLET_PHYSICS
// When Bullet is available we assume that it will tell us when velocities go to zero...
#else // !USE_BULLET_PHYSICS
// ... otherwise we help things come to rest by clamping small velocities.
if (glm::length(velocity) <= EPSILON_VELOCITY_LENGTH) {
velocity = NO_VELOCITY;
}
#endif // USE_BULLET_PHYSICS
// NOTE: the simulation should NOT set any DirtyFlags on this entity
setPosition(position); // this will automatically recalculate our collision shape
@ -753,7 +762,13 @@ void EntityItem::simulate(const quint64& now) {
}
bool EntityItem::isMoving() const {
#ifdef USE_BULLET_PHYSICS
// When Bullet is available we assume that "zero velocity" means "at rest"
// because of collision conditions this simulation does not know about.
return hasVelocity() || hasAngularVelocity();
#else // !USE_BULLET_PHYSICS
return hasVelocity() || (hasGravity() && !isRestingOnSurface()) || hasAngularVelocity();
#endif //USE_BULLET_PHYSICS
}
bool EntityItem::lifetimeHasExpired() const {
@ -806,12 +821,12 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, updateMass);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, updateDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, updateAngularVelocity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, updateAngularDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
@ -1007,11 +1022,11 @@ const float MIN_DIMENSION_DELTA = 0.0001f;
const float MIN_ALIGNMENT_DOT = 0.9999f;
const float MIN_MASS_DELTA = 0.001f;
const float MIN_VELOCITY_DELTA = 0.025f;
const float MIN_DAMPING_DELTA = 0.001f;
const float MIN_GRAVITY_DELTA = 0.001f;
const float MIN_SPIN_DELTA = 0.0003f;
void EntityItem::updatePosition(const glm::vec3& value) {
glm::vec3 debugPosition = value * (float) TREE_SCALE;
if (glm::distance(_position, value) * (float)TREE_SCALE > MIN_POSITION_DELTA) {
_position = value;
recalculateCollisionShape();
@ -1083,6 +1098,13 @@ void EntityItem::updateVelocityInMeters(const glm::vec3& value) {
}
}
void EntityItem::updateDamping(float value) {
if (fabsf(_damping - value) > MIN_DAMPING_DELTA) {
_damping = glm::clamp(value, 0.0f, 1.0f);
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
}
}
void EntityItem::updateGravity(const glm::vec3& value) {
if (glm::distance(_gravity, value) * (float)TREE_SCALE > MIN_GRAVITY_DELTA) {
_gravity = value;
@ -1105,6 +1127,13 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
}
}
void EntityItem::updateAngularDamping(float value) {
if (fabsf(_angularDamping - value) > MIN_DAMPING_DELTA) {
_angularDamping = glm::clamp(value, 0.0f, 1.0f);
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
}
}
void EntityItem::updateIgnoreForCollisions(bool value) {
if (_ignoreForCollisions != value) {
_ignoreForCollisions = value;

View file

@ -285,9 +285,11 @@ public:
void updateMass(float value);
void updateVelocity(const glm::vec3& value);
void updateVelocityInMeters(const glm::vec3& value);
void updateDamping(float value);
void updateGravity(const glm::vec3& value);
void updateGravityInMeters(const glm::vec3& value);
void updateAngularVelocity(const glm::vec3& value);
void updateAngularDamping(float value);
void updateIgnoreForCollisions(bool value);
void updateCollisionsWillMove(bool value);
void updateLifetime(float value);
@ -325,17 +327,27 @@ protected:
float _mass;
glm::vec3 _velocity;
glm::vec3 _gravity;
float _damping; // timescale
float _damping;
float _lifetime;
QString _script;
glm::vec3 _registrationPoint;
glm::vec3 _angularVelocity;
float _angularDamping; // timescale
float _angularDamping;
bool _visible;
bool _ignoreForCollisions;
bool _collisionsWillMove;
bool _locked;
QString _userData;
// NOTE: Damping is applied like this: v *= pow(1 - damping, dt)
//
// Hence the damping coefficient must range from 0 (no damping) to 1 (immediate stop).
// Each damping value relates to a corresponding exponential decay timescale as follows:
//
// timescale = -1 / ln(1 - damping)
//
// damping = 1 - exp(-1 / timescale)
//
// NOTE: Radius support is obsolete, but these private helper functions are available for this class to
// parse old data streams

View file

@ -48,6 +48,8 @@ ObjectMotionState::ObjectMotionState() :
_volume(DEFAULT_VOLUME),
_friction(DEFAULT_FRICTION),
_restitution(DEFAULT_RESTITUTION),
_linearDamping(0.0f),
_angularDamping(0.0f),
_wasInWorld(false),
_motionType(MOTION_TYPE_STATIC),
_body(NULL),
@ -79,6 +81,14 @@ void ObjectMotionState::setRestitution(float restitution) {
_restitution = btMax(btMin(fabsf(restitution), 1.0f), 0.0f);
}
void ObjectMotionState::setLinearDamping(float damping) {
_linearDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f);
}
void ObjectMotionState::setAngularDamping(float damping) {
_angularDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f);
}
void ObjectMotionState::setVolume(float volume) {
_volume = btMax(btMin(fabsf(volume), MAX_VOLUME), MIN_VOLUME);
}

View file

@ -66,6 +66,8 @@ public:
void setDensity(float density);
void setFriction(float friction);
void setRestitution(float restitution);
void setLinearDamping(float damping);
void setAngularDamping(float damping);
void setVolume(float volume);
float getMass() const { return _volume * _density; }
@ -93,6 +95,8 @@ protected:
float _volume;
float _friction;
float _restitution;
float _linearDamping;
float _angularDamping;
bool _wasInWorld;
MotionType _motionType;

View file

@ -327,6 +327,7 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
_dynamicsWorld->addRigidBody(body);
return true;
}
@ -438,6 +439,7 @@ void PhysicsEngine::updateObjectEasy(btRigidBody* body, ObjectMotionState* motio
}
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
if (flags & EntityItem::DIRTY_MASS) {
float mass = motionState->getMass();