Merge pull request #5033 from howard-stearns/smooth-initial-crash-volume

Smooth initial crash volume
This commit is contained in:
Andrew Meadows 2015-06-05 08:40:09 -07:00
commit 08b64666fe
8 changed files with 99 additions and 9 deletions

View file

@ -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);

View file

@ -1130,11 +1130,18 @@ 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 linearVelocity = glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY;
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.
// 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 = 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) {
@ -1167,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);

View file

@ -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) {

View file

@ -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;

View file

@ -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(0.0f); // Subclasses override where meaningful.
}
glm::vec3 ObjectMotionState::getBodyAngularVelocity() const {
return bulletToGLM(_body->getAngularVelocity());

View file

@ -90,6 +90,7 @@ public:
glm::vec3 getBodyLinearVelocity() const;
glm::vec3 getBodyAngularVelocity() const;
virtual glm::vec3 getObjectLinearVelocityChange() const;
virtual uint32_t getAndClearIncomingDirtyFlags() = 0;

View file

@ -317,6 +317,8 @@ CollisionEvents& PhysicsEngine::getCollisionEvents() {
if(type != CONTACT_EVENT_TYPE_CONTINUE || _numSubsteps % CONTINUE_EVENT_FILTER_FREQUENCY == 0) {
ObjectMotionState* A = static_cast<ObjectMotionState*>(contactItr->first._a);
ObjectMotionState* B = static_cast<ObjectMotionState*>(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));
}
}

View file

@ -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);