mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 09:25:12 +02:00
Merge pull request #4039 from AndrewMeadows/inertia
Bullet physics part 2
This commit is contained in:
commit
125dc5010e
13 changed files with 112 additions and 54 deletions
|
@ -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]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue