EntityItem stores density rather than mass.

This commit is contained in:
Andrew Meadows 2015-01-14 15:00:57 -08:00
parent 8236837dd0
commit 03b8badd81
10 changed files with 113 additions and 51 deletions

View file

@ -134,8 +134,8 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
glm::vec3 axis = glm::normalize(penetration); glm::vec3 axis = glm::normalize(penetration);
glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis;
float massA = entityA->getMass(); float massA = entityA->computeMass();
float massB = entityB->getMass(); float massB = entityB->computeMass();
float totalMass = massA + massB; float totalMass = massA + massB;
float massRatioA = (2.0f * massB / totalMass); float massRatioA = (2.0f * massB / totalMass);
float massRatioB = (2.0f * massA / totalMass); float massRatioB = (2.0f * massA / totalMass);

View file

@ -37,10 +37,10 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_position = ENTITY_ITEM_ZERO_VEC3; _position = ENTITY_ITEM_ZERO_VEC3;
_dimensions = ENTITY_ITEM_DEFAULT_DIMENSIONS; _dimensions = ENTITY_ITEM_DEFAULT_DIMENSIONS;
_density = ENTITY_ITEM_DEFAULT_DENSITY;
_rotation = ENTITY_ITEM_DEFAULT_ROTATION; _rotation = ENTITY_ITEM_DEFAULT_ROTATION;
_glowLevel = ENTITY_ITEM_DEFAULT_GLOW_LEVEL; _glowLevel = ENTITY_ITEM_DEFAULT_GLOW_LEVEL;
_localRenderAlpha = ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA; _localRenderAlpha = ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA;
_mass = ENTITY_ITEM_DEFAULT_MASS;
_velocity = ENTITY_ITEM_DEFAULT_VELOCITY; _velocity = ENTITY_ITEM_DEFAULT_VELOCITY;
_gravity = ENTITY_ITEM_DEFAULT_GRAVITY; _gravity = ENTITY_ITEM_DEFAULT_GRAVITY;
_damping = ENTITY_ITEM_DEFAULT_DAMPING; _damping = ENTITY_ITEM_DEFAULT_DAMPING;
@ -219,7 +219,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
} }
APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation()); APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation());
APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, getMass()); APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, computeMass());
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity());
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, getGravity()); APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, getGravity());
APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping());
@ -555,7 +555,34 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
} }
} }
float EntityItem::computeMass() const {
// NOTE: we group the operations here in and attempt to reduce floating point error.
return ((_density * (_volumeMultiplier * _dimensions.x)) * _dimensions.y) * _dimensions.z;
}
const float ENTITY_ITEM_EPSILON_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE; const float ENTITY_ITEM_EPSILON_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE;
const float MAX_DENSITY = 10000.0f; // kg/m^3 density of silver
const float MIN_DENSITY = 100.0f; // kg/m^3 density of styrofoam
void EntityItem::setMass(float mass) {
// Setting the mass actually changes the _density (at fixed volume), however
// we must protect the density range to help maintain stability of physics simulation
// therefore this method might not accept the mass that is supplied.
// NOTE: when computing the volume we group the _volumeMultiplier (typically a very large number, due
// to the TREE_SCALE transformation) with the first dimension component (typically a very small number)
// in an attempt to reduce floating point error of the final result.
float volume = (_volumeMultiplier * _dimensions.x) * _dimensions.y * _dimensions.z;
// compute new density
const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3
if (volume < 1.0e-6f) {
// avoid divide by zero
_density = glm::min(mass / MIN_VOLUME, MAX_DENSITY);
} else {
_density = glm::max(glm::min(mass / volume, MAX_DENSITY), MIN_DENSITY);
}
}
// TODO: we probably want to change this to make "down" be the direction of the entity's gravity vector // TODO: we probably want to change this to make "down" be the direction of the entity's gravity vector
// for now, this is always true DOWN even if entity has non-down gravity. // for now, this is always true DOWN even if entity has non-down gravity.
@ -771,7 +798,7 @@ EntityItemProperties EntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPositionInMeters); COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPositionInMeters);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation); COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(mass, getMass); COPY_ENTITY_PROPERTY_TO_PROPERTIES(mass, computeMass);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocityInMeters); COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocityInMeters);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravityInMeters); COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravityInMeters);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping); COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping);
@ -1047,9 +1074,29 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
} }
} }
void EntityItem::updateMass(float value) { void EntityItem::updateMass(float mass) {
if (fabsf(_mass - value) > MIN_MASS_DELTA) { // Setting the mass actually changes the _density (at fixed volume), however
_mass = value; // we must protect the density range to help maintain stability of physics simulation
// therefore this method might not accept the mass that is supplied.
// NOTE: when computing the volume we group the _volumeMultiplier (typically a very large number, due
// to the TREE_SCALE transformation) with the first dimension component (typically a very small number)
// in an attempt to reduce floating point error of the final result.
float volume = (_volumeMultiplier * _dimensions.x) * _dimensions.y * _dimensions.z;
// compute new density
float newDensity = _density;
const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3
if (volume < 1.0e-6f) {
// avoid divide by zero
newDensity = glm::min(mass / MIN_VOLUME, MAX_DENSITY);
} else {
newDensity = glm::max(glm::min(mass / volume, MAX_DENSITY), MIN_DENSITY);
}
const float MIN_DENSITY_CHANGE_FACTOR = 0.001f; // 0.1 percent
if (fabsf(_density - newDensity) / _density > MIN_DENSITY_CHANGE_FACTOR) {
_density = newDensity;
_dirtyFlags |= EntityItem::DIRTY_MASS; _dirtyFlags |= EntityItem::DIRTY_MASS;
} }
} }

View file

@ -171,8 +171,8 @@ public:
float getLocalRenderAlpha() const { return _localRenderAlpha; } float getLocalRenderAlpha() const { return _localRenderAlpha; }
void setLocalRenderAlpha(float localRenderAlpha) { _localRenderAlpha = localRenderAlpha; } void setLocalRenderAlpha(float localRenderAlpha) { _localRenderAlpha = localRenderAlpha; }
float getMass() const { return _mass; } float computeMass() const;
void setMass(float value) { _mass = value; } void setMass(float mass);
const glm::vec3& getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second const glm::vec3& getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second
glm::vec3 getVelocityInMeters() const { return _velocity * (float) TREE_SCALE; } /// get velocity in meters glm::vec3 getVelocityInMeters() const { return _velocity * (float) TREE_SCALE; } /// get velocity in meters
@ -303,7 +303,12 @@ protected:
glm::quat _rotation; glm::quat _rotation;
float _glowLevel; float _glowLevel;
float _localRenderAlpha; float _localRenderAlpha;
float _mass; float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg
// NOTE: _volumeMultiplier is used to compute volume:
// volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z = m^3
// DANGER: due to the size of TREE_SCALE the _volumeMultiplier is always a large number, and therefore
// will tend to introduce floating point error. We must keep this in mind when using it.
float _volumeMultiplier = (float)TREE_SCALE * (float)TREE_SCALE * (float)TREE_SCALE;
glm::vec3 _velocity; glm::vec3 _velocity;
glm::vec3 _gravity; glm::vec3 _gravity;
float _damping; float _damping;

View file

@ -36,8 +36,11 @@ const float ENTITY_ITEM_IMMORTAL_LIFETIME = -1.0f; /// special lifetime which me
const float ENTITY_ITEM_DEFAULT_LIFETIME = ENTITY_ITEM_IMMORTAL_LIFETIME; const float ENTITY_ITEM_DEFAULT_LIFETIME = ENTITY_ITEM_IMMORTAL_LIFETIME;
const glm::quat ENTITY_ITEM_DEFAULT_ROTATION; const glm::quat ENTITY_ITEM_DEFAULT_ROTATION;
const glm::vec3 ENTITY_ITEM_DEFAULT_DIMENSIONS = glm::vec3(0.1f) / (float)TREE_SCALE; const float ENTITY_ITEM_DEFAULT_WIDTH = 0.1f;
const float ENTITY_ITEM_DEFAULT_MASS = 1.0f; const glm::vec3 ENTITY_ITEM_DEFAULT_DIMENSIONS = glm::vec3(ENTITY_ITEM_DEFAULT_WIDTH) / (float)TREE_SCALE;
const float ENTITY_ITEM_DEFAULT_DENSITY = 1000.0f; // density of water
const float ENTITY_ITEM_DEFAULT_VOLUME = ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH;
const float ENTITY_ITEM_DEFAULT_MASS = ENTITY_ITEM_DEFAULT_DENSITY * ENTITY_ITEM_DEFAULT_VOLUME;
const glm::vec3 ENTITY_ITEM_DEFAULT_VELOCITY = ENTITY_ITEM_ZERO_VEC3; const glm::vec3 ENTITY_ITEM_DEFAULT_VELOCITY = ENTITY_ITEM_ZERO_VEC3;
const glm::vec3 ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY = ENTITY_ITEM_ZERO_VEC3; const glm::vec3 ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY = ENTITY_ITEM_ZERO_VEC3;

View file

@ -32,6 +32,10 @@ SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID, const Entit
{ {
_type = EntityTypes::Sphere; _type = EntityTypes::Sphere;
setProperties(properties); setProperties(properties);
// NOTE: _volumeMultiplier is used to compute volume:
// volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z
// The formula below looks funny because _dimension.xyz = diameter rather than radius.
_volumeMultiplier *= PI / 6.0f;
} }
EntityItemProperties SphereEntityItem::getProperties() const { EntityItemProperties SphereEntityItem::getProperties() const {

View file

@ -109,7 +109,7 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
_body->setDamping(_linearDamping, _angularDamping); _body->setDamping(_linearDamping, _angularDamping);
if (flags & EntityItem::DIRTY_MASS) { if (flags & EntityItem::DIRTY_MASS) {
float mass = getMass(); float mass = _entity->computeMass();
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
_body->getCollisionShape()->calculateLocalInertia(mass, inertia); _body->getCollisionShape()->calculateLocalInertia(mass, inertia);
_body->setMassProps(mass, inertia); _body->setMassProps(mass, inertia);
@ -137,8 +137,12 @@ void EntityMotionState::updateObjectVelocities() {
#endif // USE_BULLET_PHYSICS #endif // USE_BULLET_PHYSICS
} }
void EntityMotionState::computeShapeInfo(ShapeInfo& info) { void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) {
_entity->computeShapeInfo(info); _entity->computeShapeInfo(shapeInfo);
}
float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const {
return _entity->computeMass();
} }
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) { void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) {

View file

@ -57,7 +57,8 @@ public:
void updateObjectEasy(uint32_t flags, uint32_t frame); void updateObjectEasy(uint32_t flags, uint32_t frame);
void updateObjectVelocities(); void updateObjectVelocities();
void computeShapeInfo(ShapeInfo& info); void computeShapeInfo(ShapeInfo& shapeInfo);
float computeMass(const ShapeInfo& shapeInfo) const;
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame); void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame);

View file

@ -44,13 +44,10 @@ const glm::vec3& ObjectMotionState::getWorldOffset() {
ObjectMotionState::ObjectMotionState() : ObjectMotionState::ObjectMotionState() :
_density(DEFAULT_DENSITY),
_volume(DEFAULT_VOLUME),
_friction(DEFAULT_FRICTION), _friction(DEFAULT_FRICTION),
_restitution(DEFAULT_RESTITUTION), _restitution(DEFAULT_RESTITUTION),
_linearDamping(0.0f), _linearDamping(0.0f),
_angularDamping(0.0f), _angularDamping(0.0f),
_wasInWorld(false),
_motionType(MOTION_TYPE_STATIC), _motionType(MOTION_TYPE_STATIC),
_body(NULL), _body(NULL),
_sentMoving(false), _sentMoving(false),
@ -69,10 +66,6 @@ ObjectMotionState::~ObjectMotionState() {
assert(_body == NULL); assert(_body == NULL);
} }
void ObjectMotionState::setDensity(float density) {
_density = btMax(btMin(fabsf(density), MAX_DENSITY), MIN_DENSITY);
}
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);
} }
@ -89,10 +82,6 @@ void ObjectMotionState::setAngularDamping(float damping) {
_angularDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f); _angularDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f);
} }
void ObjectMotionState::setVolume(float volume) {
_volume = btMax(btMin(fabsf(volume), MAX_VOLUME), MIN_VOLUME);
}
void ObjectMotionState::setVelocity(const glm::vec3& velocity) const { void ObjectMotionState::setVelocity(const glm::vec3& velocity) const {
_body->setLinearVelocity(glmToBullet(velocity)); _body->setLinearVelocity(glmToBullet(velocity));
} }

View file

@ -60,18 +60,15 @@ public:
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;
virtual void computeShapeInfo(ShapeInfo& info) = 0;
virtual MotionType getMotionType() const { return _motionType; } virtual MotionType getMotionType() const { return _motionType; }
void setDensity(float density); virtual void computeShapeInfo(ShapeInfo& info) = 0;
virtual float computeMass(const ShapeInfo& shapeInfo) const = 0;
void setFriction(float friction); void setFriction(float friction);
void setRestitution(float restitution); void setRestitution(float restitution);
void setLinearDamping(float damping); void setLinearDamping(float damping);
void setAngularDamping(float damping); void setAngularDamping(float damping);
void setVolume(float volume);
float getMass() const { return _volume * _density; }
void setVelocity(const glm::vec3& velocity) const; void setVelocity(const glm::vec3& velocity) const;
void setAngularVelocity(const glm::vec3& velocity) const; void setAngularVelocity(const glm::vec3& velocity) const;
@ -92,13 +89,12 @@ public:
friend class PhysicsEngine; friend class PhysicsEngine;
protected: protected:
float _density; // TODO: move these materials properties to EntityItem
float _volume;
float _friction; float _friction;
float _restitution; float _restitution;
float _linearDamping; float _linearDamping;
float _angularDamping; float _angularDamping;
bool _wasInWorld;
MotionType _motionType; MotionType _motionType;
// _body has NO setters -- it is only changed by PhysicsEngine // _body has NO setters -- it is only changed by PhysicsEngine

View file

@ -246,9 +246,9 @@ void PhysicsEngine::stepSimulation() {
bool PhysicsEngine::addObject(ObjectMotionState* motionState) { bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
assert(motionState); assert(motionState);
ShapeInfo info; ShapeInfo shapeInfo;
motionState->computeShapeInfo(info); motionState->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(info); btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) { if (shape) {
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f; float mass = 0.0f;
@ -263,7 +263,7 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
break; break;
} }
case MOTION_TYPE_DYNAMIC: { case MOTION_TYPE_DYNAMIC: {
mass = motionState->getMass(); mass = motionState->computeMass(shapeInfo);
shape->calculateLocalInertia(mass, inertia); shape->calculateLocalInertia(mass, inertia);
body = new btRigidBody(mass, motionState, shape, inertia); body = new btRigidBody(mass, motionState, shape, inertia);
body->updateInertiaTensor(); body->updateInertiaTensor();
@ -301,10 +301,10 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
btRigidBody* body = motionState->_body; btRigidBody* body = motionState->_body;
if (body) { if (body) {
const btCollisionShape* shape = body->getCollisionShape(); const btCollisionShape* shape = body->getCollisionShape();
ShapeInfo info; ShapeInfo shapeInfo;
ShapeInfoUtil::collectInfoFromShape(shape, info); ShapeInfoUtil::collectInfoFromShape(shape, shapeInfo);
_dynamicsWorld->removeRigidBody(body); _dynamicsWorld->removeRigidBody(body);
_shapeManager.releaseShape(info); _shapeManager.releaseShape(shapeInfo);
delete body; delete body;
motionState->_body = NULL; motionState->_body = NULL;
return true; return true;
@ -320,20 +320,31 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
_dynamicsWorld->removeRigidBody(body); _dynamicsWorld->removeRigidBody(body);
if (flags & EntityItem::DIRTY_SHAPE) { if (flags & EntityItem::DIRTY_SHAPE) {
// MASS bit should be set whenever SHAPE is set
assert(flags & EntityItem::DIRTY_MASS);
// get new shape
btCollisionShape* oldShape = body->getCollisionShape(); btCollisionShape* oldShape = body->getCollisionShape();
ShapeInfo info; ShapeInfo shapeInfo;
motionState->computeShapeInfo(info); motionState->computeShapeInfo(shapeInfo);
btCollisionShape* newShape = _shapeManager.getShape(info); btCollisionShape* newShape = _shapeManager.getShape(shapeInfo);
if (newShape != oldShape) { if (newShape != oldShape) {
// BUG: if shape doesn't change but density does then we won't compute new mass properties
// TODO: fix this BUG by replacing DIRTY_MASS with DIRTY_DENSITY and then fix logic accordingly.
body->setCollisionShape(newShape); body->setCollisionShape(newShape);
_shapeManager.releaseShape(oldShape); _shapeManager.releaseShape(oldShape);
// compute mass properties
float mass = motionState->computeMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f);
body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia);
body->updateInertiaTensor();
} else { } else {
// whoops, shape hasn't changed after all so we must release the reference // whoops, shape hasn't changed after all so we must release the reference
// that was created when looking it up // that was created when looking it up
_shapeManager.releaseShape(newShape); _shapeManager.releaseShape(newShape);
} }
// MASS bit should be set whenever SHAPE is set
assert(flags & EntityItem::DIRTY_MASS);
} }
bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS; bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS;
if (easyUpdate) { if (easyUpdate) {
@ -356,9 +367,11 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
int collisionFlags = body->getCollisionFlags() & ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT); int collisionFlags = body->getCollisionFlags() & ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
body->setCollisionFlags(collisionFlags); body->setCollisionFlags(collisionFlags);
if (! (flags & EntityItem::DIRTY_MASS)) { if (! (flags & EntityItem::DIRTY_MASS)) {
// always update mass properties when going dynamic (unless it's already been done) // always update mass properties when going dynamic (unless it's already been done above)
ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo);
float mass = motionState->computeMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = motionState->getMass();
body->getCollisionShape()->calculateLocalInertia(mass, inertia); body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia); body->setMassProps(mass, inertia);
body->updateInertiaTensor(); body->updateInertiaTensor();