add particle collisions with voxels

This commit is contained in:
ZappoMan 2013-12-12 17:52:22 -08:00
parent d9573f6d6c
commit 5830f1134e
10 changed files with 256 additions and 17 deletions

View file

@ -1849,6 +1849,8 @@ void Application::init() {
_particles.init(); _particles.init();
_particles.setViewFrustum(getViewFrustum()); _particles.setViewFrustum(getViewFrustum());
_particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio);
_palette.init(_glWidget->width(), _glWidget->height()); _palette.init(_glWidget->width(), _glWidget->height());
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1);
@ -2517,6 +2519,7 @@ void Application::update(float deltaTime) {
updateCursor(deltaTime); // Handle cursor updates updateCursor(deltaTime); // Handle cursor updates
_particles.update(); // update the particles... _particles.update(); // update the particles...
_particleCollisionSystem.update(); // handle collisions for the particles...
} }
void Application::updateAvatar(float deltaTime) { void Application::updateAvatar(float deltaTime) {

View file

@ -22,6 +22,7 @@
#include <NetworkPacket.h> #include <NetworkPacket.h>
#include <NodeList.h> #include <NodeList.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <ParticleCollisionSystem.h>
#include <ParticleEditPacketSender.h> #include <ParticleEditPacketSender.h>
#include <VoxelQuery.h> #include <VoxelQuery.h>
@ -352,6 +353,7 @@ private:
ViewFrustum _sharedVoxelSystemViewFrustum; ViewFrustum _sharedVoxelSystemViewFrustum;
ParticleTreeRenderer _particles; ParticleTreeRenderer _particles;
ParticleCollisionSystem _particleCollisionSystem;
QByteArray _voxelsFilename; QByteArray _voxelsFilename;
bool _wantToKillLocalVoxels; bool _wantToKillLocalVoxels;

View file

@ -16,6 +16,7 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include <AbstractAudioInterface.h>
#include <AudioRingBuffer.h> #include <AudioRingBuffer.h>
#include <StdDev.h> #include <StdDev.h>
@ -34,7 +35,7 @@ class QAudioInput;
class QAudioOutput; class QAudioOutput;
class QIODevice; class QIODevice;
class Audio : public QObject { class Audio : public QObject, public AbstractAudioInterface {
Q_OBJECT Q_OBJECT
public: public:
// setup for audio I/O // setup for audio I/O
@ -52,8 +53,8 @@ public:
void lowPassFilter(int16_t* inputBuffer); void lowPassFilter(int16_t* inputBuffer);
void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen); virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen);
void startDrumSound(float volume, float frequency, float duration, float decay); virtual void startDrumSound(float volume, float frequency, float duration, float decay);
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; } float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; }

View file

@ -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__) */

View file

@ -583,33 +583,40 @@ public:
float radius; float radius;
glm::vec3& penetration; glm::vec3& penetration;
bool found; bool found;
OctreeElement* penetratedElement;
}; };
bool findSpherePenetrationOp(OctreeElement* node, void* extraData) { bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
SphereArgs* args = static_cast<SphereArgs*>(extraData); SphereArgs* args = static_cast<SphereArgs*>(extraData);
// coarse check against bounds // coarse check against bounds
const AABox& box = node->getAABox(); const AABox& box = element->getAABox();
if (!box.expandedContains(args->center, args->radius)) { if (!box.expandedContains(args->center, args->radius)) {
return false; return false;
} }
if (!node->isLeaf()) { if (!element->isLeaf()) {
return true; // recurse on children return true; // recurse on children
} }
if (node->hasContent()) { if (element->hasContent()) {
glm::vec3 nodePenetration; glm::vec3 elementPenetration;
if (box.findSpherePenetration(args->center, args->radius, nodePenetration)) { if (box.findSpherePenetration(args->center, args->radius, elementPenetration)) {
args->penetration = addPenetrations(args->penetration, nodePenetration * (float)TREE_SCALE); args->penetration = addPenetrations(args->penetration, elementPenetration * (float)TREE_SCALE);
args->found = true; args->found = true;
args->penetratedElement = element;
} }
} }
return false; return false;
} }
bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) { bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration,
SphereArgs args = { center / (float)TREE_SCALE, radius / TREE_SCALE, penetration }; OctreeElement** penetratedElement) {
SphereArgs args = { center / (float)TREE_SCALE, radius / TREE_SCALE, penetration, false, NULL };
penetration = glm::vec3(0.0f, 0.0f, 0.0f); penetration = glm::vec3(0.0f, 0.0f, 0.0f);
recurseTreeWithOperation(findSpherePenetrationOp, &args); recurseTreeWithOperation(findSpherePenetrationOp, &args);
if (penetratedElement) {
*penetratedElement = args.penetratedElement;
}
return args.found; return args.found;
} }

View file

@ -218,7 +218,9 @@ public:
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElement*& node, float& distance, BoxFace& face); 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); 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 // Note: this assumes the fileFormat is the HIO individual voxels code files

View file

@ -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 <AbstractAudioInterface.h>
#include <VoxelTree.h>
#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<ParticleCollisionSystem*>(extraData);
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
// iterate the particles...
std::vector<Particle>& 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);
}
}

View file

@ -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 <glm/glm.hpp>
#include <stdint.h>
#include <QtScript/QScriptEngine>
#include <QtCore/QObject>
#include <SharedUtil.h>
#include <OctreePacketData.h>
#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__) */

View file

@ -15,7 +15,6 @@
std::map<uint32_t,ParticleEditHandle*> ParticleEditHandle::_allHandles; std::map<uint32_t,ParticleEditHandle*> ParticleEditHandle::_allHandles;
uint32_t ParticleEditHandle::_nextCreatorTokenID = 0; uint32_t ParticleEditHandle::_nextCreatorTokenID = 0;
ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree, uint32_t id) { ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree, uint32_t id) {
if (id == NEW_PARTICLE) { if (id == NEW_PARTICLE) {
_creatorTokenID = _nextCreatorTokenID; _creatorTokenID = _nextCreatorTokenID;
@ -36,7 +35,9 @@ ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, P
ParticleEditHandle::~ParticleEditHandle() { ParticleEditHandle::~ParticleEditHandle() {
// remove us from our _allHandles map // remove us from our _allHandles map
if (_creatorTokenID != UNKNOWN_TOKEN) {
_allHandles.erase(_allHandles.find(_creatorTokenID)); _allHandles.erase(_allHandles.find(_creatorTokenID));
}
} }
void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,

View file

@ -74,6 +74,7 @@ public:
virtual bool deleteApproved() const { return !hasParticles(); } virtual bool deleteApproved() const { return !hasParticles(); }
const std::vector<Particle>& getParticles() const { return _particles; } const std::vector<Particle>& getParticles() const { return _particles; }
std::vector<Particle>& getParticles() { return _particles; }
bool hasParticles() const { return _particles.size() > 0; } bool hasParticles() const { return _particles.size() > 0; }
void update(ParticleTreeUpdateArgs& args); void update(ParticleTreeUpdateArgs& args);