From 5830f1134ea6f6d3c11da3eaa423c71a9930cb37 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 12 Dec 2013 17:52:22 -0800 Subject: [PATCH 1/3] add particle collisions with voxels --- interface/src/Application.cpp | 3 + interface/src/Application.h | 2 + interface/src/Audio.h | 9 +- libraries/audio/src/AbstractAudioInterface.h | 19 +++ libraries/octree/src/Octree.cpp | 25 +-- libraries/octree/src/Octree.h | 4 +- .../particles/src/ParticleCollisionSystem.cpp | 147 ++++++++++++++++++ .../particles/src/ParticleCollisionSystem.h | 56 +++++++ .../particles/src/ParticleEditHandle.cpp | 7 +- libraries/particles/src/ParticleTreeElement.h | 1 + 10 files changed, 256 insertions(+), 17 deletions(-) create mode 100644 libraries/audio/src/AbstractAudioInterface.h create mode 100644 libraries/particles/src/ParticleCollisionSystem.cpp create mode 100644 libraries/particles/src/ParticleCollisionSystem.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c84165456e..68e1bbc716 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1849,6 +1849,8 @@ void Application::init() { _particles.init(); _particles.setViewFrustum(getViewFrustum()); + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio); + _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1); @@ -2517,6 +2519,7 @@ void Application::update(float deltaTime) { updateCursor(deltaTime); // Handle cursor updates _particles.update(); // update the particles... + _particleCollisionSystem.update(); // handle collisions for the particles... } void Application::updateAvatar(float deltaTime) { diff --git a/interface/src/Application.h b/interface/src/Application.h index fd44d44ea1..a37e13147a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -352,6 +353,7 @@ private: ViewFrustum _sharedVoxelSystemViewFrustum; ParticleTreeRenderer _particles; + ParticleCollisionSystem _particleCollisionSystem; QByteArray _voxelsFilename; bool _wantToKillLocalVoxels; diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 60f563b221..ed9472bcf1 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -16,6 +16,7 @@ #include +#include #include #include @@ -34,7 +35,7 @@ class QAudioInput; class QAudioOutput; class QIODevice; -class Audio : public QObject { +class Audio : public QObject, public AbstractAudioInterface { Q_OBJECT public: // setup for audio I/O @@ -52,9 +53,9 @@ public: void lowPassFilter(int16_t* inputBuffer); - void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen); - void startDrumSound(float volume, float frequency, float duration, float decay); - + virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen); + virtual void startDrumSound(float volume, float frequency, float duration, float decay); + float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; } bool getCollisionFlashesScreen() { return _collisionFlashesScreen; } diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h new file mode 100644 index 0000000000..3662572a9a --- /dev/null +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -0,0 +1,19 @@ +// +// AbstractAudioInterface.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__AbstractAudioInterface__ +#define __hifi__AbstractAudioInterface__ + +class AbstractAudioInterface { +public: + virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen) = 0; + virtual void startDrumSound(float volume, float frequency, float duration, float decay) = 0; +}; + +#endif /* defined(__hifi__AbstractAudioInterface__) */ \ No newline at end of file diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d10248d9e3..b101ce1063 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -583,33 +583,40 @@ public: float radius; glm::vec3& penetration; bool found; + OctreeElement* penetratedElement; }; -bool findSpherePenetrationOp(OctreeElement* node, void* extraData) { +bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { SphereArgs* args = static_cast(extraData); // coarse check against bounds - const AABox& box = node->getAABox(); + const AABox& box = element->getAABox(); if (!box.expandedContains(args->center, args->radius)) { return false; } - if (!node->isLeaf()) { + if (!element->isLeaf()) { return true; // recurse on children } - if (node->hasContent()) { - glm::vec3 nodePenetration; - if (box.findSpherePenetration(args->center, args->radius, nodePenetration)) { - args->penetration = addPenetrations(args->penetration, nodePenetration * (float)TREE_SCALE); + if (element->hasContent()) { + glm::vec3 elementPenetration; + if (box.findSpherePenetration(args->center, args->radius, elementPenetration)) { + args->penetration = addPenetrations(args->penetration, elementPenetration * (float)TREE_SCALE); args->found = true; + args->penetratedElement = element; } } return false; } -bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) { - SphereArgs args = { center / (float)TREE_SCALE, radius / TREE_SCALE, penetration }; +bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, + OctreeElement** penetratedElement) { + + SphereArgs args = { center / (float)TREE_SCALE, radius / TREE_SCALE, penetration, false, NULL }; penetration = glm::vec3(0.0f, 0.0f, 0.0f); recurseTreeWithOperation(findSpherePenetrationOp, &args); + if (penetratedElement) { + *penetratedElement = args.penetratedElement; + } return args.found; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 1b4cee866a..0954ef1813 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -218,7 +218,9 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElement*& node, float& distance, BoxFace& face); - bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration); + bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, + OctreeElement** penetratedElement = NULL); + bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); // Note: this assumes the fileFormat is the HIO individual voxels code files diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp new file mode 100644 index 0000000000..1e0301f022 --- /dev/null +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -0,0 +1,147 @@ +// +// ParticleCollisionSystem.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#include +#include + +#include "Particle.h" +#include "ParticleCollisionSystem.h" +#include "ParticleEditHandle.h" +#include "ParticleEditPacketSender.h" +#include "ParticleTree.h" + +ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, + ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) { + init(packetSender, particles, voxels, audio); +} + +void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, + ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) { + _packetSender = packetSender; + _particles = particles; + _voxels = voxels; + _audio = audio; +} + +ParticleCollisionSystem::~ParticleCollisionSystem() { +} + +bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extraData) { + ParticleCollisionSystem* system = static_cast(extraData); + ParticleTreeElement* particleTreeElement = static_cast(element); + + // iterate the particles... + std::vector& particles = particleTreeElement->getParticles(); + uint16_t numberOfParticles = particles.size(); + for (uint16_t i = 0; i < numberOfParticles; i++) { + Particle* particle = &particles[i]; + system->checkParticle(particle); + } + + return true; +} + + +void ParticleCollisionSystem::update() { + // update all particles + _particles->recurseTreeWithOperation(updateOperation, this); +} + + +void ParticleCollisionSystem::checkParticle(Particle* particle) { + updateCollisionWithVoxels(particle); +} + +void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { + glm::vec3 center = particle->getPosition() * (float)TREE_SCALE; + float radius = particle->getRadius() * (float)TREE_SCALE; + const float VOXEL_ELASTICITY = 1.4f; + const float VOXEL_DAMPING = 0.0; + const float VOXEL_COLLISION_FREQUENCY = 0.5f; + glm::vec3 penetration; + OctreeElement* penetratedVoxel; + if (_voxels->findSpherePenetration(center, radius, penetration, &penetratedVoxel)) { + printf("findSpherePenetration()... penetration=%f,%f,%f\n",penetration.x, penetration.y, penetration.z); + printf("penetratedElement=%f,%f,%f,%f\n", penetratedVoxel->getCorner().x, penetratedVoxel->getCorner().y, + penetratedVoxel->getCorner().z, penetratedVoxel->getScale()); + + penetration /= (float)TREE_SCALE; + updateCollisionSound(particle, penetration, VOXEL_COLLISION_FREQUENCY); + applyHardCollision(particle, penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + } +} + +void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, + float elasticity, float damping) { + // + // Update the avatar in response to a hard collision. Position will be reset exactly + // to outside the colliding surface. Velocity will be modified according to elasticity. + // + // if elasticity = 1.0, collision is inelastic. + // if elasticity > 1.0, collision is elastic. + // + glm::vec3 position = particle->getPosition(); + glm::vec3 velocity = particle->getVelocity(); + + position -= penetration; + static float HALTING_VELOCITY = 0.2f / (float) TREE_SCALE; + // cancel out the velocity component in the direction of penetration + float penetrationLength = glm::length(penetration); + const float EPSILON = 0.0f; + + if (penetrationLength > EPSILON) { + glm::vec3 direction = penetration / penetrationLength; + velocity -= glm::dot(velocity, direction) * direction * elasticity; + velocity *= glm::clamp(1.f - damping, 0.0f, 1.0f); + if (glm::length(velocity) < HALTING_VELOCITY) { + // If moving really slowly after a collision, and not applying forces, stop altogether + velocity *= 0.f; + } + } + ParticleEditHandle particleEditHandle(_packetSender, _particles, particle->getID()); + particleEditHandle.updateParticle(position, particle->getRadius(), particle->getColor(), velocity, + particle->getGravity(), particle->getDamping(), particle->getUpdateScript()); +} + + +void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) { + + // consider whether to have the collision make a sound + const float AUDIBLE_COLLISION_THRESHOLD = 0.02f; + const float COLLISION_LOUDNESS = 1.f; + const float DURATION_SCALING = 0.004f; + const float NOISE_SCALING = 0.1f; + glm::vec3 velocity = particle->getVelocity() * (float)TREE_SCALE; + + /* + // how do we want to handle this?? + // + glm::vec3 gravity = particle->getGravity() * (float)TREE_SCALE; + + if (glm::length(gravity) > EPSILON) { + // If gravity is on, remove the effect of gravity on velocity for this + // frame, so that we are not constantly colliding with the surface + velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); + } + */ + float velocityTowardCollision = glm::dot(velocity, glm::normalize(penetration)); + float velocityTangentToCollision = glm::length(velocity) - velocityTowardCollision; + + if (velocityTowardCollision > AUDIBLE_COLLISION_THRESHOLD) { + // Volume is proportional to collision velocity + // Base frequency is modified upward by the angle of the collision + // Noise is a function of the angle of collision + // Duration of the sound is a function of both base frequency and velocity of impact + _audio->startCollisionSound( + fmin(COLLISION_LOUDNESS * velocityTowardCollision, 1.f), + frequency * (1.f + velocityTangentToCollision / velocityTowardCollision), + fmin(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f), + 1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true); + } +} \ No newline at end of file diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h new file mode 100644 index 0000000000..4ec2ce738b --- /dev/null +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -0,0 +1,56 @@ +// +// ParticleCollisionSystem.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__ParticleCollisionSystem__ +#define __hifi__ParticleCollisionSystem__ + +#include +#include + +#include +#include + +#include +#include + +#include "Particle.h" + +class AbstractAudioInterface; +class ParticleEditPacketSender; +class ParticleTree; +class VoxelTree; + +class ParticleCollisionSystem { +public: + ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL, + VoxelTree* voxels = NULL, + AbstractAudioInterface* audio = NULL); + + void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, + AbstractAudioInterface* audio = NULL); + + ~ParticleCollisionSystem(); + + void update(); + void checkParticle(Particle* particle); + void updateCollisionWithVoxels(Particle* particle); + void applyHardCollision(Particle* particle, const glm::vec3& penetration, float elasticity, float damping); + void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency); + +private: + static bool updateOperation(OctreeElement* element, void* extraData); + + + ParticleEditPacketSender* _packetSender; + ParticleTree* _particles; + VoxelTree* _voxels; + AbstractAudioInterface* _audio; +}; + +#endif /* defined(__hifi__ParticleCollisionSystem__) */ \ No newline at end of file diff --git a/libraries/particles/src/ParticleEditHandle.cpp b/libraries/particles/src/ParticleEditHandle.cpp index c14c960d3f..24700fbcba 100644 --- a/libraries/particles/src/ParticleEditHandle.cpp +++ b/libraries/particles/src/ParticleEditHandle.cpp @@ -15,7 +15,6 @@ std::map ParticleEditHandle::_allHandles; uint32_t ParticleEditHandle::_nextCreatorTokenID = 0; - ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree, uint32_t id) { if (id == NEW_PARTICLE) { _creatorTokenID = _nextCreatorTokenID; @@ -36,7 +35,9 @@ ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, P ParticleEditHandle::~ParticleEditHandle() { // remove us from our _allHandles map - _allHandles.erase(_allHandles.find(_creatorTokenID)); + if (_creatorTokenID != UNKNOWN_TOKEN) { + _allHandles.erase(_allHandles.find(_creatorTokenID)); + } } void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, @@ -71,7 +72,7 @@ bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor ParticleDetail newParticleDetail = { _id, usecTimestampNow(), position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, updateScript, _creatorTokenID }; - + // queue the packet _packetSender->queueParticleEditMessages(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &newParticleDetail); diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index b513a4a860..d654bc80b3 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -74,6 +74,7 @@ public: virtual bool deleteApproved() const { return !hasParticles(); } const std::vector& getParticles() const { return _particles; } + std::vector& getParticles() { return _particles; } bool hasParticles() const { return _particles.size() > 0; } void update(ParticleTreeUpdateArgs& args); From 51f07cad883b5decc4daebc13624798fbaf72f94 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 12 Dec 2013 17:56:56 -0800 Subject: [PATCH 2/3] removed some debug --- libraries/particles/src/ParticleCollisionSystem.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 1e0301f022..af4b4ee16b 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -67,10 +67,6 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { glm::vec3 penetration; OctreeElement* penetratedVoxel; if (_voxels->findSpherePenetration(center, radius, penetration, &penetratedVoxel)) { - printf("findSpherePenetration()... penetration=%f,%f,%f\n",penetration.x, penetration.y, penetration.z); - printf("penetratedElement=%f,%f,%f,%f\n", penetratedVoxel->getCorner().x, penetratedVoxel->getCorner().y, - penetratedVoxel->getCorner().z, penetratedVoxel->getScale()); - penetration /= (float)TREE_SCALE; updateCollisionSound(particle, penetration, VOXEL_COLLISION_FREQUENCY); applyHardCollision(particle, penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); From d5c6c60836b83becb2cfc109f54496ced292b15f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 12 Dec 2013 18:04:27 -0800 Subject: [PATCH 3/3] fix crash in collisions --- libraries/particles/src/ParticleCollisionSystem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index af4b4ee16b..61960db168 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -50,7 +50,9 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr void ParticleCollisionSystem::update() { // update all particles + _particles->lockForWrite(); _particles->recurseTreeWithOperation(updateOperation, this); + _particles->unlock(); }