mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 18:13:47 +02:00
add particle collisions with voxels
This commit is contained in:
parent
d9573f6d6c
commit
5830f1134e
10 changed files with 256 additions and 17 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
19
libraries/audio/src/AbstractAudioInterface.h
Normal file
19
libraries/audio/src/AbstractAudioInterface.h
Normal 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__) */
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
147
libraries/particles/src/ParticleCollisionSystem.cpp
Normal file
147
libraries/particles/src/ParticleCollisionSystem.cpp
Normal 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);
|
||||
}
|
||||
}
|
56
libraries/particles/src/ParticleCollisionSystem.h
Normal file
56
libraries/particles/src/ParticleCollisionSystem.h
Normal 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__) */
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue