From 8236837dd035aeaab7e4819bace0c41611801986 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 14 Jan 2015 15:00:31 -0800 Subject: [PATCH 1/4] add ShapeInfo::computeVolume() --- libraries/shared/src/ShapeInfo.cpp | 35 ++++++++++++++++++++++++++++++ libraries/shared/src/ShapeInfo.h | 1 + 2 files changed, 36 insertions(+) diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index d402a048a0..a1e72cdca0 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -24,18 +24,21 @@ void ShapeInfo::clear() { void ShapeInfo::setBox(const glm::vec3& halfExtents) { _type = BOX_SHAPE; _data.clear(); + // _data[0] = < halfX, halfY, halfZ > _data.push_back(halfExtents); } void ShapeInfo::setSphere(float radius) { _type = SPHERE_SHAPE; _data.clear(); + // _data[0] = < radius, radius, radius > _data.push_back(glm::vec3(radius)); } void ShapeInfo::setCylinder(float radius, float halfHeight) { _type = CYLINDER_SHAPE; _data.clear(); + // _data[0] = < radius, halfHeight, radius > // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X _data.push_back(glm::vec3(radius, halfHeight, radius)); } @@ -43,6 +46,7 @@ void ShapeInfo::setCylinder(float radius, float halfHeight) { void ShapeInfo::setCapsule(float radius, float halfHeight) { _type = CAPSULE_SHAPE; _data.clear(); + // _data[0] = < radius, halfHeight, radius > _data.push_back(glm::vec3(radius, halfHeight, radius)); } @@ -58,3 +62,34 @@ glm::vec3 ShapeInfo::getBoundingBoxDiagonal() const { } return glm::vec3(0.0f); } + +float ShapeInfo::computeVolume() const { + const float DEFAULT_VOLUME = 1.0f; + float volume = DEFAULT_VOLUME; + switch(_type) { + case BOX_SHAPE: { + // factor of 8.0 because the components of _data[0] are all halfExtents + volume = 8.0f * _data[0].x * _data[0].y * _data[0].z; + break; + } + case SPHERE_SHAPE: { + float radius = _data[0].x; + volume = 4.0f * PI * radius * radius * radius / 3.0f; + break; + } + case CYLINDER_SHAPE: { + float radius = _data[0].x; + volume = PI * radius * radius * 2.0f * _data[0].y; + break; + } + case CAPSULE_SHAPE: { + float radius = _data[0].x; + volume = PI * radius * radius * (2.0f * _data[0].y + 4.0f * radius / 3.0f); + break; + } + default: + break; + } + assert(volume > 0.0f); + return volume; +} diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 7ba290122c..9b4c587c3f 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -32,6 +32,7 @@ public: const QVector& getData() const { return _data; } glm::vec3 getBoundingBoxDiagonal() const; + float computeVolume() const; protected: int _type; From 03b8badd819c5c3e46a15ef1061b8f6943601709 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 14 Jan 2015 15:00:57 -0800 Subject: [PATCH 2/4] EntityItem stores density rather than mass. --- .../entities/src/EntityCollisionSystem.cpp | 4 +- libraries/entities/src/EntityItem.cpp | 59 +++++++++++++++++-- libraries/entities/src/EntityItem.h | 11 +++- .../src/EntityItemPropertiesDefaults.h | 7 ++- libraries/entities/src/SphereEntityItem.cpp | 4 ++ libraries/physics/src/EntityMotionState.cpp | 10 +++- libraries/physics/src/EntityMotionState.h | 3 +- libraries/physics/src/ObjectMotionState.cpp | 11 ---- libraries/physics/src/ObjectMotionState.h | 14 ++--- libraries/physics/src/PhysicsEngine.cpp | 41 ++++++++----- 10 files changed, 113 insertions(+), 51 deletions(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 61d8447a30..172850bc64 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -134,8 +134,8 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { glm::vec3 axis = glm::normalize(penetration); glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; - float massA = entityA->getMass(); - float massB = entityB->getMass(); + float massA = entityA->computeMass(); + float massB = entityB->computeMass(); float totalMass = massA + massB; float massRatioA = (2.0f * massB / totalMass); float massRatioB = (2.0f * massA / totalMass); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 885d589ed1..3f4d319398 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -37,10 +37,10 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _position = ENTITY_ITEM_ZERO_VEC3; _dimensions = ENTITY_ITEM_DEFAULT_DIMENSIONS; + _density = ENTITY_ITEM_DEFAULT_DENSITY; _rotation = ENTITY_ITEM_DEFAULT_ROTATION; _glowLevel = ENTITY_ITEM_DEFAULT_GLOW_LEVEL; _localRenderAlpha = ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA; - _mass = ENTITY_ITEM_DEFAULT_MASS; _velocity = ENTITY_ITEM_DEFAULT_VELOCITY; _gravity = ENTITY_ITEM_DEFAULT_GRAVITY; _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_MASS, appendValue, getMass()); + APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, computeMass()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity()); APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, getGravity()); 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 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 // 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(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete 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(gravity, getGravityInMeters); COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping); @@ -1047,9 +1074,29 @@ void EntityItem::updateRotation(const glm::quat& rotation) { } } -void EntityItem::updateMass(float value) { - if (fabsf(_mass - value) > MIN_MASS_DELTA) { - _mass = value; +void EntityItem::updateMass(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 + 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; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b84739e07e..3cd4ad9490 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -171,8 +171,8 @@ public: float getLocalRenderAlpha() const { return _localRenderAlpha; } void setLocalRenderAlpha(float localRenderAlpha) { _localRenderAlpha = localRenderAlpha; } - float getMass() const { return _mass; } - void setMass(float value) { _mass = value; } + float computeMass() const; + void setMass(float mass); 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 @@ -303,7 +303,12 @@ protected: glm::quat _rotation; float _glowLevel; 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 _gravity; float _damping; diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 285788c960..acb37eed3f 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -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 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_MASS = 1.0f; +const float ENTITY_ITEM_DEFAULT_WIDTH = 0.1f; +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_ANGULAR_VELOCITY = ENTITY_ITEM_ZERO_VEC3; diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 9a9c07ef9a..1c16ba4a7b 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -32,6 +32,10 @@ SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID, const Entit { _type = EntityTypes::Sphere; 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 { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 019f00fb48..a15f8a9f51 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -109,7 +109,7 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) { _body->setDamping(_linearDamping, _angularDamping); if (flags & EntityItem::DIRTY_MASS) { - float mass = getMass(); + float mass = _entity->computeMass(); btVector3 inertia(0.0f, 0.0f, 0.0f); _body->getCollisionShape()->calculateLocalInertia(mass, inertia); _body->setMassProps(mass, inertia); @@ -137,8 +137,12 @@ void EntityMotionState::updateObjectVelocities() { #endif // USE_BULLET_PHYSICS } -void EntityMotionState::computeShapeInfo(ShapeInfo& info) { - _entity->computeShapeInfo(info); +void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) { + _entity->computeShapeInfo(shapeInfo); +} + +float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const { + return _entity->computeMass(); } void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) { diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 863edebe7d..57f3b52672 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -57,7 +57,8 @@ public: void updateObjectEasy(uint32_t flags, uint32_t frame); void updateObjectVelocities(); - void computeShapeInfo(ShapeInfo& info); + void computeShapeInfo(ShapeInfo& shapeInfo); + float computeMass(const ShapeInfo& shapeInfo) const; void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame); diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 6e0b2a784c..9b3e69eb8d 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -44,13 +44,10 @@ const glm::vec3& ObjectMotionState::getWorldOffset() { ObjectMotionState::ObjectMotionState() : - _density(DEFAULT_DENSITY), - _volume(DEFAULT_VOLUME), _friction(DEFAULT_FRICTION), _restitution(DEFAULT_RESTITUTION), _linearDamping(0.0f), _angularDamping(0.0f), - _wasInWorld(false), _motionType(MOTION_TYPE_STATIC), _body(NULL), _sentMoving(false), @@ -69,10 +66,6 @@ ObjectMotionState::~ObjectMotionState() { assert(_body == NULL); } -void ObjectMotionState::setDensity(float density) { - _density = btMax(btMin(fabsf(density), MAX_DENSITY), MIN_DENSITY); -} - void ObjectMotionState::setFriction(float friction) { _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); } -void ObjectMotionState::setVolume(float volume) { - _volume = btMax(btMin(fabsf(volume), MAX_VOLUME), MIN_VOLUME); -} - void ObjectMotionState::setVelocity(const glm::vec3& velocity) const { _body->setLinearVelocity(glmToBullet(velocity)); } diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index b9d077f4bb..ea3d5de73b 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -60,18 +60,15 @@ public: virtual void updateObjectEasy(uint32_t flags, uint32_t frame) = 0; virtual void updateObjectVelocities() = 0; - virtual void computeShapeInfo(ShapeInfo& info) = 0; - 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 setRestitution(float restitution); void setLinearDamping(float damping); void setAngularDamping(float damping); - void setVolume(float volume); - - float getMass() const { return _volume * _density; } void setVelocity(const glm::vec3& velocity) const; void setAngularVelocity(const glm::vec3& velocity) const; @@ -92,13 +89,12 @@ public: friend class PhysicsEngine; protected: - float _density; - float _volume; + // TODO: move these materials properties to EntityItem float _friction; float _restitution; float _linearDamping; float _angularDamping; - bool _wasInWorld; + MotionType _motionType; // _body has NO setters -- it is only changed by PhysicsEngine diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 666fcd2e89..e08f271255 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -246,9 +246,9 @@ void PhysicsEngine::stepSimulation() { bool PhysicsEngine::addObject(ObjectMotionState* motionState) { assert(motionState); - ShapeInfo info; - motionState->computeShapeInfo(info); - btCollisionShape* shape = _shapeManager.getShape(info); + ShapeInfo shapeInfo; + motionState->computeShapeInfo(shapeInfo); + btCollisionShape* shape = _shapeManager.getShape(shapeInfo); if (shape) { btVector3 inertia(0.0f, 0.0f, 0.0f); float mass = 0.0f; @@ -263,7 +263,7 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) { break; } case MOTION_TYPE_DYNAMIC: { - mass = motionState->getMass(); + mass = motionState->computeMass(shapeInfo); shape->calculateLocalInertia(mass, inertia); body = new btRigidBody(mass, motionState, shape, inertia); body->updateInertiaTensor(); @@ -301,10 +301,10 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { btRigidBody* body = motionState->_body; if (body) { const btCollisionShape* shape = body->getCollisionShape(); - ShapeInfo info; - ShapeInfoUtil::collectInfoFromShape(shape, info); + ShapeInfo shapeInfo; + ShapeInfoUtil::collectInfoFromShape(shape, shapeInfo); _dynamicsWorld->removeRigidBody(body); - _shapeManager.releaseShape(info); + _shapeManager.releaseShape(shapeInfo); delete body; motionState->_body = NULL; return true; @@ -320,20 +320,31 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio _dynamicsWorld->removeRigidBody(body); 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(); - ShapeInfo info; - motionState->computeShapeInfo(info); - btCollisionShape* newShape = _shapeManager.getShape(info); + ShapeInfo shapeInfo; + motionState->computeShapeInfo(shapeInfo); + btCollisionShape* newShape = _shapeManager.getShape(shapeInfo); 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); _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 { // whoops, shape hasn't changed after all so we must release the reference // that was created when looking it up _shapeManager.releaseShape(newShape); } - // MASS bit should be set whenever SHAPE is set - assert(flags & EntityItem::DIRTY_MASS); } bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS; 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); body->setCollisionFlags(collisionFlags); 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); - float mass = motionState->getMass(); body->getCollisionShape()->calculateLocalInertia(mass, inertia); body->setMassProps(mass, inertia); body->updateInertiaTensor(); From ccc50dd94f688b1aeb1a73d4c7177391773fc995 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 14 Jan 2015 15:47:44 -0800 Subject: [PATCH 3/4] use density instead of mass --- libraries/entities/src/EntityItem.cpp | 36 ++++++++++++------- libraries/entities/src/EntityItem.h | 6 +++- .../entities/src/EntityItemProperties.cpp | 14 ++++---- libraries/entities/src/EntityItemProperties.h | 4 +-- .../src/EntityItemPropertiesDefaults.h | 5 ++- 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3f4d319398..9cfb0c6c11 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -103,7 +103,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_POSITION; requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete requestedProperties += PROP_ROTATION; - requestedProperties += PROP_MASS; + requestedProperties += PROP_DENSITY; requestedProperties += PROP_VELOCITY; requestedProperties += PROP_GRAVITY; requestedProperties += PROP_DAMPING; @@ -219,7 +219,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet } APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation()); - APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, computeMass()); + APPEND_ENTITY_PROPERTY(PROP_DENSITY, appendValue, getDensity()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity()); APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, getGravity()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping()); @@ -495,7 +495,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } READ_ENTITY_PROPERTY_QUAT_SETTER(PROP_ROTATION, updateRotation); - READ_ENTITY_PROPERTY_SETTER(PROP_MASS, float, updateMass); + READ_ENTITY_PROPERTY_SETTER(PROP_DENSITY, float, updateDensity); READ_ENTITY_PROPERTY_SETTER(PROP_VELOCITY, glm::vec3, updateVelocity); READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity); READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping); @@ -560,9 +560,18 @@ float EntityItem::computeMass() const { return ((_density * (_volumeMultiplier * _dimensions.x)) * _dimensions.y) * _dimensions.z; } -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::setDensity(float density) { + _density = glm::max(glm::min(density, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); +} + +void EntityItem::updateDensity(float density) { + const float MIN_DENSITY_CHANGE_FACTOR = 0.001f; // 0.1 percent + float newDensity = glm::max(glm::min(density, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); + if (fabsf(_density - newDensity) / _density > MIN_DENSITY_CHANGE_FACTOR) { + _density = newDensity; + _dirtyFlags |= EntityItem::DIRTY_MASS; + } +} void EntityItem::setMass(float mass) { // Setting the mass actually changes the _density (at fixed volume), however @@ -578,12 +587,14 @@ void EntityItem::setMass(float mass) { 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); + _density = glm::min(mass / MIN_VOLUME, ENTITY_ITEM_MAX_DENSITY); } else { - _density = glm::max(glm::min(mass / volume, MAX_DENSITY), MIN_DENSITY); + _density = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); } } +const float ENTITY_ITEM_EPSILON_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE; + // 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. // TODO: the old code had "&& _velocity.y >= -EPSILON && _velocity.y <= EPSILON" --- what was I thinking? @@ -798,7 +809,7 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPositionInMeters); COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(mass, computeMass); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(density, getDensity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocityInMeters); COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravityInMeters); COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping); @@ -826,7 +837,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePositionInMeters); // this will call recalculate collision shape if needed SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, updateDimensionsInMeters); // NOTE: radius is obsolete SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, updateMass); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(density, updateDensity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocityInMeters); SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters); SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, updateDamping); @@ -1026,7 +1037,6 @@ void EntityItem::recalculateCollisionShape() { const float MIN_POSITION_DELTA = 0.0001f; const float MIN_ALIGNMENT_DOT = 0.9999f; -const float MIN_MASS_DELTA = 0.001f; const float MIN_VELOCITY_DELTA = 0.01f; const float MIN_DAMPING_DELTA = 0.001f; const float MIN_GRAVITY_DELTA = 0.001f; @@ -1089,9 +1099,9 @@ void EntityItem::updateMass(float mass) { 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); + newDensity = glm::min(mass / MIN_VOLUME, ENTITY_ITEM_MAX_DENSITY); } else { - newDensity = glm::max(glm::min(mass / volume, MAX_DENSITY), MIN_DENSITY); + newDensity = glm::max(glm::min(mass / volume, ENTITY_ITEM_MAX_DENSITY), ENTITY_ITEM_MIN_DENSITY); } const float MIN_DENSITY_CHANGE_FACTOR = 0.001f; // 0.1 percent diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3cd4ad9490..9a19fa2e5d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -171,9 +171,12 @@ public: float getLocalRenderAlpha() const { return _localRenderAlpha; } void setLocalRenderAlpha(float localRenderAlpha) { _localRenderAlpha = localRenderAlpha; } + void setDensity(float density); float computeMass() const; void setMass(float mass); + float getDensity() const { return _density; } + 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 void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in domain scale units (0.0-1.0) per second @@ -260,6 +263,7 @@ public: void updateDimensions(const glm::vec3& value); void updateDimensionsInMeters(const glm::vec3& value); void updateRotation(const glm::quat& rotation); + void updateDensity(float value); void updateMass(float value); void updateVelocity(const glm::vec3& value); void updateVelocityInMeters(const glm::vec3& value); @@ -303,7 +307,7 @@ protected: glm::quat _rotation; float _glowLevel; float _localRenderAlpha; - float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg + float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3 // 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 diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 77d70e8abd..4da903f6ba 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -30,7 +30,7 @@ EntityItemProperties::EntityItemProperties() : CONSTRUCT_PROPERTY(position, 0), CONSTRUCT_PROPERTY(dimensions, ENTITY_ITEM_DEFAULT_DIMENSIONS), CONSTRUCT_PROPERTY(rotation, ENTITY_ITEM_DEFAULT_ROTATION), - CONSTRUCT_PROPERTY(mass, ENTITY_ITEM_DEFAULT_MASS), + CONSTRUCT_PROPERTY(density, ENTITY_ITEM_DEFAULT_DENSITY), CONSTRUCT_PROPERTY(velocity, ENTITY_ITEM_DEFAULT_VELOCITY), CONSTRUCT_PROPERTY(gravity, ENTITY_ITEM_DEFAULT_GRAVITY), CONSTRUCT_PROPERTY(damping, ENTITY_ITEM_DEFAULT_DAMPING), @@ -175,7 +175,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions); CHECK_PROPERTY_CHANGE(PROP_POSITION, position); CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation); - CHECK_PROPERTY_CHANGE(PROP_MASS, mass); + CHECK_PROPERTY_CHANGE(PROP_DENSITY, density); CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity); CHECK_PROPERTY_CHANGE(PROP_GRAVITY, gravity); CHECK_PROPERTY_CHANGE(PROP_DAMPING, damping); @@ -232,7 +232,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity); COPY_PROPERTY_TO_QSCRIPTVALUE(damping); - COPY_PROPERTY_TO_QSCRIPTVALUE(mass); + COPY_PROPERTY_TO_QSCRIPTVALUE(density); COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable @@ -310,7 +310,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(position, setPosition); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(dimensions, setDimensions); COPY_PROPERTY_FROM_QSCRIPTVALUE_QUAT(rotation, setRotation); - COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(mass, setMass); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(density, setDensity); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(velocity, setVelocity); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(gravity, setGravity); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(damping, setDamping); @@ -479,7 +479,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, properties.getPosition()); APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, appendValue, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, properties.getRotation()); - APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, properties.getMass()); + APPEND_ENTITY_PROPERTY(PROP_DENSITY, appendValue, properties.getDensity()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, properties.getVelocity()); APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, properties.getGravity()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, properties.getDamping()); @@ -700,7 +700,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(PROP_ROTATION, setRotation); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MASS, float, setMass); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); @@ -781,7 +781,7 @@ void EntityItemProperties::markAllChanged() { _positionChanged = true; _dimensionsChanged = true; _rotationChanged = true; - _massChanged = true; + _densityChanged = true; _velocityChanged = true; _gravityChanged = true; _dampingChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index f3424aee43..8d4bf98862 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -42,7 +42,7 @@ enum EntityPropertyList { PROP_RADIUS, // NOTE: PROP_RADIUS is obsolete and only included in old format streams PROP_DIMENSIONS = PROP_RADIUS, PROP_ROTATION, - PROP_MASS, + PROP_DENSITY, PROP_VELOCITY, PROP_GRAVITY, PROP_DAMPING, @@ -145,7 +145,7 @@ public: DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3); DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3); DEFINE_PROPERTY_REF(PROP_ROTATION, Rotation, rotation, glm::quat); - DEFINE_PROPERTY(PROP_MASS, Mass, mass, float); + DEFINE_PROPERTY(PROP_DENSITY, Density, density, float); DEFINE_PROPERTY_REF(PROP_VELOCITY, Velocity, velocity, glm::vec3); DEFINE_PROPERTY_REF(PROP_GRAVITY, Gravity, gravity, glm::vec3); DEFINE_PROPERTY(PROP_DAMPING, Damping, damping, float); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index acb37eed3f..22beb3937e 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -38,8 +38,11 @@ const float ENTITY_ITEM_DEFAULT_LIFETIME = ENTITY_ITEM_IMMORTAL_LIFETIME; const glm::quat ENTITY_ITEM_DEFAULT_ROTATION; const float ENTITY_ITEM_DEFAULT_WIDTH = 0.1f; 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_MAX_DENSITY = 10000.0f; // kg/m^3 density of silver +const float ENTITY_ITEM_MIN_DENSITY = 100.0f; // kg/m^3 density of balsa wood +const float ENTITY_ITEM_DEFAULT_DENSITY = 1000.0f; // density of water 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; From f90b128ac3b7e44091ba2450bf38e14206557c9e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 14 Jan 2015 15:48:05 -0800 Subject: [PATCH 4/4] use density instead of mass in scripts/UI --- examples/html/entityProperties.html | 10 +++++----- examples/libraries/ToolTip.js | 2 +- examples/libraries/entityPropertyDialogBox.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index c23a3cd5ab..6b576e557d 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -114,7 +114,7 @@ var elGravityY = document.getElementById("property-grav-y"); var elGravityZ = document.getElementById("property-grav-z"); - var elMass = document.getElementById("property-mass"); + var elDensity = document.getElementById("property-density"); var elIgnoreForCollisions = document.getElementById("property-ignore-for-collisions"); var elCollisionsWillMove = document.getElementById("property-collisions-will-move"); var elLifetime = document.getElementById("property-lifetime"); @@ -219,7 +219,7 @@ elGravityY.value = properties.gravity.y.toFixed(2); elGravityZ.value = properties.gravity.z.toFixed(2); - elMass.value = properties.mass.toFixed(2); + elDensity.value = properties.density.toFixed(2); elIgnoreForCollisions.checked = properties.ignoreForCollisions; elCollisionsWillMove.checked = properties.collisionsWillMove; elLifetime.value = properties.lifetime; @@ -356,7 +356,7 @@ elGravityY.addEventListener('change', gravityChangeFunction); elGravityZ.addEventListener('change', gravityChangeFunction); - elMass.addEventListener('change', createEmitNumberPropertyUpdateFunction('mass')); + elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density')); elIgnoreForCollisions.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ignoreForCollisions')); elCollisionsWillMove.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionsWillMove')); elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); @@ -616,9 +616,9 @@ - Mass + Density - + diff --git a/examples/libraries/ToolTip.js b/examples/libraries/ToolTip.js index 590eba36d0..f12525af57 100644 --- a/examples/libraries/ToolTip.js +++ b/examples/libraries/ToolTip.js @@ -68,7 +68,7 @@ function Tooltip() { text += "Lifetime: " + properties.lifetime + "\n" } text += "Age: " + properties.ageAsText + "\n" - text += "Mass: " + properties.mass + "\n" + text += "Density: " + properties.density + "\n" text += "Script: " + properties.script + "\n" diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index a686ebb0f2..0cb76276d8 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -164,7 +164,7 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Collisions:", type: "header" }); index++; - array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) }); + array.push({ label: "Density:", value: properties.density.toFixed(decimals) }); index++; array.push({ label: "Ignore for Collisions:", type: "checkbox", value: properties.ignoreForCollisions }); index++; @@ -353,7 +353,7 @@ EntityPropertyDialogBox = (function () { properties.gravity.z = array[index++].value; index++; // skip header - properties.mass = array[index++].value; + properties.density = array[index++].value; properties.ignoreForCollisions = array[index++].value; properties.collisionsWillMove = array[index++].value;