From c036b5fd4ba759a574e24d0463d3f75b16518497 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 3 Jun 2015 10:19:28 -0700 Subject: [PATCH 1/5] Actually commit the changes this time! --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 9 +++++++-- libraries/physics/src/EntityMotionState.cpp | 7 +++++++ libraries/physics/src/EntityMotionState.h | 2 ++ libraries/physics/src/ObjectMotionState.cpp | 3 +++ libraries/physics/src/ObjectMotionState.h | 1 + libraries/physics/src/PhysicsEngine.cpp | 6 ++++-- libraries/shared/src/RegisteredMetaTypes.h | 8 +++++--- 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 23e504bfb8..5e88eae17a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1131,10 +1131,15 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT } const float mass = entity->computeMass(); const float COLLISION_PENTRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() - const float linearVelocity = glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; + // The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact, + // but that first contact depends on exactly where we hit in the physics step. + // We can get a more consistent initial-contact energy reading by using the changed velocity. + const float linearVelocity = (collision.type == CONTACT_EVENT_TYPE_START) ? + glm::length(collision.velocityChange) : + glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; - const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f; + const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 10.0f : 0.5f; const float COLLISION_MINIMUM_VOLUME = 0.005f; const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9a24aabb34..1f5a1b9f4e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -494,6 +494,7 @@ void EntityMotionState::measureBodyAcceleration() { float dt = ((float)numSubsteps * PHYSICS_ENGINE_FIXED_SUBSTEP); float invDt = 1.0f / dt; _lastMeasureStep = thisStep; + _measuredDeltaTime = dt; // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt @@ -502,6 +503,12 @@ void EntityMotionState::measureBodyAcceleration() { _lastVelocity = velocity; } } +glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const { + // This is the dampened change in linear velocity, as calculated in measureBodyAcceleration: dv = a * dt + // It is generally only meaningful during the lifespan of collision. In particular, it is not meaningful + // when the entity first starts moving via direct user action. + return _measuredAcceleration * _measuredDeltaTime; +} // virtual void EntityMotionState::setMotionType(MotionType motionType) { diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 65279dc01a..b38254d42c 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -64,6 +64,7 @@ public: virtual glm::vec3 getObjectLinearVelocity() const { return _entity->getVelocity(); } virtual glm::vec3 getObjectAngularVelocity() const { return _entity->getAngularVelocity(); } virtual glm::vec3 getObjectGravity() const { return _entity->getGravity(); } + virtual glm::vec3 getObjectLinearVelocityChange() const; virtual const QUuid& getObjectID() const { return _entity->getID(); } @@ -101,6 +102,7 @@ protected: uint32_t _lastMeasureStep; glm::vec3 _lastVelocity; glm::vec3 _measuredAcceleration; + float _measuredDeltaTime; quint8 _accelerationNearlyGravityCount; bool _candidateForOwnership; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 57b75f0f3b..67dc8f294c 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -82,6 +82,9 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { return bulletToGLM(_body->getLinearVelocity()); } +glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { + return glm::vec3(); // Subclasses override where meaningful. +} glm::vec3 ObjectMotionState::getBodyAngularVelocity() const { return bulletToGLM(_body->getAngularVelocity()); diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 246ed16627..141fbe252b 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -90,6 +90,7 @@ public: glm::vec3 getBodyLinearVelocity() const; glm::vec3 getBodyAngularVelocity() const; + virtual glm::vec3 getObjectLinearVelocityChange() const; virtual uint32_t getAndClearIncomingDirtyFlags() = 0; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index b622a37136..7b9f499925 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -317,6 +317,8 @@ CollisionEvents& PhysicsEngine::getCollisionEvents() { if(type != CONTACT_EVENT_TYPE_CONTINUE || _numSubsteps % CONTINUE_EVENT_FILTER_FREQUENCY == 0) { ObjectMotionState* A = static_cast(contactItr->first._a); ObjectMotionState* B = static_cast(contactItr->first._b); + glm::vec3 velocityChange = (A ? A->getObjectLinearVelocityChange() : glm::vec3(0.0f)) + + (B ? B->getObjectLinearVelocityChange() : glm::vec3(0.0f)); if (A && A->getType() == MOTIONSTATE_TYPE_ENTITY) { QUuid idA = A->getObjectID(); @@ -326,14 +328,14 @@ CollisionEvents& PhysicsEngine::getCollisionEvents() { } glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset; glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB); - _collisionEvents.push_back(Collision(type, idA, idB, position, penetration)); + _collisionEvents.push_back(Collision(type, idA, idB, position, penetration, velocityChange)); } else if (B && B->getType() == MOTIONSTATE_TYPE_ENTITY) { QUuid idB = B->getObjectID(); glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset; // NOTE: we're flipping the order of A and B (so that the first objectID is never NULL) // hence we must negate the penetration. glm::vec3 penetration = - bulletToGLM(contact.distance * contact.normalWorldOnB); - _collisionEvents.push_back(Collision(type, idB, QUuid(), position, penetration)); + _collisionEvents.push_back(Collision(type, idB, QUuid(), position, penetration, velocityChange)); } } diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 48eecba227..1dcc85107a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -78,15 +78,17 @@ enum ContactEventType { class Collision { public: - Collision() : type(CONTACT_EVENT_TYPE_START), idA(), idB(), contactPoint(0.0f), penetration(0.0f) { } - Collision(ContactEventType cType, const QUuid& cIdA, const QUuid& cIdB, const glm::vec3& cPoint, const glm::vec3& cPenetration) - : type(cType), idA(cIdA), idB(cIdB), contactPoint(cPoint), penetration(cPenetration) { } + Collision() : type(CONTACT_EVENT_TYPE_START), idA(), idB(), contactPoint(0.0f), penetration(0.0f), velocityChange(0.0f) { } + Collision(ContactEventType cType, const QUuid& cIdA, const QUuid& cIdB, const glm::vec3& cPoint, + const glm::vec3& cPenetration, const glm::vec3& velocityChange) + : type(cType), idA(cIdA), idB(cIdB), contactPoint(cPoint), penetration(cPenetration), velocityChange(velocityChange) { } ContactEventType type; QUuid idA; QUuid idB; glm::vec3 contactPoint; glm::vec3 penetration; + glm::vec3 velocityChange; }; Q_DECLARE_METATYPE(Collision) QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision); From 407746b842b7c3d2aaf01d7cc5ee23962425d58c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 3 Jun 2015 10:38:20 -0700 Subject: [PATCH 2/5] Fix (consistent) typo in non-magic-number constant's name. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 5e88eae17a..93e302c81b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1130,13 +1130,13 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT return; } const float mass = entity->computeMass(); - const float COLLISION_PENTRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() + const float COLLISION_PENETRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() // The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact, // but that first contact depends on exactly where we hit in the physics step. // We can get a more consistent initial-contact energy reading by using the changed velocity. const float linearVelocity = (collision.type == CONTACT_EVENT_TYPE_START) ? glm::length(collision.velocityChange) : - glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; + glm::length(collision.penetration) * COLLISION_PENETRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 10.0f : 0.5f; From 5727400b9af023e8fbcea3a19e0740205c991de2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 4 Jun 2015 14:20:19 -0700 Subject: [PATCH 3/5] Sound toys script (in examples/soundToys.js) --- examples/example/soundToys.js | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/example/soundToys.js diff --git a/examples/example/soundToys.js b/examples/example/soundToys.js new file mode 100644 index 0000000000..9d8c824b90 --- /dev/null +++ b/examples/example/soundToys.js @@ -0,0 +1,66 @@ +"use strict"; +// Creates some objects that each play a sound when they are hit (or when they hit something else). +// +// Created by Howard Stearns on June 3, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +var Camera, Vec3, Quat, Entities, Script; // Globals defined by HiFi, var'ed here to keep jslint happy. +var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var SOUND_BUCKET = "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/"; +var MAX_ANGULAR_SPEED = Math.PI; +var N_EACH_OBJECTS = 3; + +var ourToys = []; +function deleteAll() { + ourToys.forEach(Entities.deleteEntity); +} +function makeAll() { + var currentPosition = Vec3.sum(Camera.getPosition(), Vec3.multiply(4, Quat.getFront(Camera.getOrientation()))), + right = Vec3.multiply(0.6, Quat.getRight(Camera.getOrientation())), + currentDimensions, + data = [ + ["models/props/Dice/goldDie.fbx", HIFI_PUBLIC_BUCKET + "sounds/dice/diceCollide.wav"], + ["models/props/Pool/ball_8.fbx", HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"], + ["eric/models/woodFloor.fbx", SOUND_BUCKET + "67LCollision05.wav"] + ]; + currentPosition = Vec3.sum(currentPosition, Vec3.multiply(-1 * data.length * N_EACH_OBJECTS / 2, right)); + function makeOne(model, sound) { + var thisEntity; + function dropOnce() { // Once gravity is added, it will work if picked up and again dropped. + Entities.editEntity(thisEntity, {gravity: {x: 0, y: -9.8, z: 0}}); + Script.removeEventHandler(thisEntity, 'clickDownOnEntity', dropOnce); + } + thisEntity = Entities.addEntity({ + type: "Model", + modelURL: HIFI_PUBLIC_BUCKET + model, + collisionSoundURL: sound, + collisionsWillMove: true, + shapeType: "box", + restitution: 0.8, + dimensions: currentDimensions, + position: currentPosition, + angularVelocity: { + x: Math.random() * MAX_ANGULAR_SPEED, + y: Math.random() * MAX_ANGULAR_SPEED, + z: Math.random() * MAX_ANGULAR_SPEED + } + }); + ourToys.push(thisEntity); + Script.addEventHandler(thisEntity, 'clickDownOnEntity', dropOnce); + currentDimensions = Vec3.multiply(currentDimensions, 2); + currentPosition = Vec3.sum(currentPosition, right); + } + function makeThree(modelSound) { + var i, model = modelSound[0], sound = modelSound[1]; + currentDimensions = {x: 0.1, y: 0.1, z: 0.1}; + for (i = 0; i < N_EACH_OBJECTS; i++) { + makeOne(model, sound); + } + } + data.forEach(makeThree); +} +makeAll(); +Script.scriptEnding.connect(deleteAll); From 5cbb5dadca469653cf3e7b33ecb80fa779ff678f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 4 Jun 2015 14:21:12 -0700 Subject: [PATCH 4/5] More perspicacious zero. --- libraries/physics/src/ObjectMotionState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 67dc8f294c..f7fc044c92 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -83,7 +83,7 @@ glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { return bulletToGLM(_body->getLinearVelocity()); } glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { - return glm::vec3(); // Subclasses override where meaningful. + return glm::vec3(0.0f); // Subclasses override where meaningful. } glm::vec3 ObjectMotionState::getBodyAngularVelocity() const { From 407d3b6a6eff1461d54dd6ff499b3d5e40bd94a5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 4 Jun 2015 14:23:51 -0700 Subject: [PATCH 5/5] Pitch is dependent on (largest dimension of) minimum bounding, not max. Adjust sound range of full volume. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 93e302c81b..7fda8214bb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1134,12 +1134,14 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT // The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact, // but that first contact depends on exactly where we hit in the physics step. // We can get a more consistent initial-contact energy reading by using the changed velocity. + // Note that velocityChange is not a good indicator for continuing collisions, because it does not distinguish + // between bounce and sliding along a surface. const float linearVelocity = (collision.type == CONTACT_EVENT_TYPE_START) ? glm::length(collision.velocityChange) : glm::length(collision.penetration) * COLLISION_PENETRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; - const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 10.0f : 0.5f; + const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 150.0f : 5.0f; const float COLLISION_MINIMUM_VOLUME = 0.005f; const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { @@ -1172,7 +1174,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I); soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0); const int channelCount = sound->isStereo() ? 2 : 1; - const float factor = log(1.0f + (entity->getMaximumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); + const float factor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); const int standardRate = AudioConstants::SAMPLE_RATE; const int resampledRate = standardRate * factor; const int nInputSamples = samples.size() / sizeof(int16_t);