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;
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..9cfb0c6c11 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;
@@ -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, getMass());
+ 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);
@@ -555,6 +555,44 @@ 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;
+}
+
+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
+ // 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, ENTITY_ITEM_MAX_DENSITY);
+ } else {
+ _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
@@ -771,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, getMass);
+ 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);
@@ -799,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);
@@ -999,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;
@@ -1047,9 +1084,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, ENTITY_ITEM_MAX_DENSITY);
+ } else {
+ 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
+ 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..9a19fa2e5d 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -171,8 +171,11 @@ public:
float getLocalRenderAlpha() const { return _localRenderAlpha; }
void setLocalRenderAlpha(float localRenderAlpha) { _localRenderAlpha = localRenderAlpha; }
- float getMass() const { return _mass; }
- void setMass(float value) { _mass = value; }
+ 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
@@ -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,12 @@ protected:
glm::quat _rotation;
float _glowLevel;
float _localRenderAlpha;
- float _mass;
+ 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
+ // 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/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 285788c960..22beb3937e 100644
--- a/libraries/entities/src/EntityItemPropertiesDefaults.h
+++ b/libraries/entities/src/EntityItemPropertiesDefaults.h
@@ -36,8 +36,14 @@ 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_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;
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 7da8165df0..7f3d619e03 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();
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;