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

View file

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

View file

@ -16,6 +16,7 @@
#include <QtCore/QObject>
#include <AbstractAudioInterface.h>
#include <AudioRingBuffer.h>
#include <StdDev.h>
@ -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; }

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;
glm::vec3& penetration;
bool found;
OctreeElement* penetratedElement;
};
bool findSpherePenetrationOp(OctreeElement* node, void* extraData) {
bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
SphereArgs* args = static_cast<SphereArgs*>(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;
}

View file

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

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

View file

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