Merge branch 'master' of https://github.com/worklist/hifi into voxel_animation

This commit is contained in:
ZappoMan 2013-07-17 14:46:20 -07:00
commit b1435fba44
13 changed files with 615 additions and 164 deletions

View file

@ -178,6 +178,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_frameCount(0),
_fps(120.0f),
_justStarted(true),
_particleSystemInitialized(false),
_coolDemoParticleEmitter(-1),
_wantToKillLocalVoxels(false),
_frustumDrawingMode(FRUSTUM_DRAW_MODE_ALL),
_viewFrustumOffsetYaw(-135.0),
@ -193,6 +195,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_isTouchPressed(false),
_yawFromTouch(0.0f),
_pitchFromTouch(0.0f),
_groundPlaneImpact(0.0f),
_mousePressed(false),
_mouseVoxelScale(1.0f / 1024.0f),
_justEditedVoxel(false),
@ -1866,9 +1869,10 @@ void Application::init() {
_palette.addTool(&_swatch);
_palette.addAction(_colorVoxelMode, 0, 2);
_palette.addAction(_eyedropperMode, 0, 3);
_palette.addAction(_selectVoxelMode, 0, 4);
_palette.addAction(_selectVoxelMode, 0, 4);
}
const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
const float HEAD_SPHERE_RADIUS = 0.07;
@ -2081,6 +2085,8 @@ void Application::update(float deltaTime) {
_myAvatar.simulate(deltaTime, NULL);
}
_myAvatar.getHand().simulate(deltaTime, true);
if (!OculusManager::isConnected()) {
if (_lookingInMirror->isChecked()) {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
@ -2129,8 +2135,8 @@ void Application::update(float deltaTime) {
#endif
if (TESTING_PARTICLE_SYSTEM) {
_particleSystem.simulate(deltaTime);
}
updateParticleSystem(deltaTime);
}
}
void Application::updateAvatar(float deltaTime) {
@ -2523,7 +2529,7 @@ void Application::displaySide(Camera& whichCamera) {
//draw a grid ground plane....
if (_renderGroundPlaneOn->isChecked()) {
drawGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE);
renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude());
}
// Draw voxels
if (_renderVoxels->isChecked()) {
@ -2579,7 +2585,9 @@ void Application::displaySide(Camera& whichCamera) {
}
if (TESTING_PARTICLE_SYSTEM) {
_particleSystem.render();
if (_particleSystemInitialized) {
_particleSystem.render();
}
}
// Render the world box
@ -2599,6 +2607,9 @@ void Application::displayOverlay() {
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// Display a single screen-size quad to
renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude());
#ifndef _WIN32
_audio.render(_glWidget->width(), _glWidget->height());
if (_oscilloscopeOn->isChecked()) {
@ -3488,3 +3499,76 @@ void Application::exportSettings() {
}
void Application::updateParticleSystem(float deltaTime) {
if (!_particleSystemInitialized) {
// create a stable test emitter and spit out a bunch of particles
_coolDemoParticleEmitter = _particleSystem.addEmitter();
if (_coolDemoParticleEmitter != -1) {
_particleSystem.setShowingEmitter(_coolDemoParticleEmitter, true);
glm::vec3 particleEmitterPosition = glm::vec3(5.0f, 1.0f, 5.0f);
_particleSystem.setEmitterPosition(_coolDemoParticleEmitter, particleEmitterPosition);
float radius = 0.01f;
glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
glm::vec3 velocity(0.0f, 0.1f, 0.0f);
float lifespan = 100000.0f;
// determine a collision sphere
glm::vec3 collisionSpherePosition = glm::vec3( 5.0f, 0.5f, 5.0f );
float collisionSphereRadius = 0.5f;
_particleSystem.setCollisionSphere(_coolDemoParticleEmitter, collisionSpherePosition, collisionSphereRadius);
_particleSystem.emitParticlesNow(_coolDemoParticleEmitter, 1500, radius, color, velocity, lifespan);
}
// signal that the particle system has been initialized
_particleSystemInitialized = true;
// apply a preset color palette
_particleSystem.setOrangeBlueColorPalette();
} else {
// update the particle system
static float t = 0.0f;
t += deltaTime;
if (_coolDemoParticleEmitter != -1) {
glm::vec3 tilt = glm::vec3
(
30.0f * sinf( t * 0.55f ),
0.0f,
30.0f * cosf( t * 0.75f )
);
_particleSystem.setEmitterRotation(_coolDemoParticleEmitter, glm::quat(glm::radians(tilt)));
ParticleSystem::ParticleAttributes attributes;
attributes.gravity = 0.0f + 0.05f * sinf( t * 0.52f );
attributes.airFriction = 2.5 + 2.0f * sinf( t * 0.32f );
attributes.jitter = 0.05f + 0.05f * sinf( t * 0.42f );
attributes.emitterAttraction = 0.015f + 0.015f * cosf( t * 0.6f );
attributes.tornadoForce = 0.0f + 0.03f * sinf( t * 0.7f );
attributes.neighborAttraction = 0.1f + 0.1f * cosf( t * 0.8f );
attributes.neighborRepulsion = 0.2f + 0.2f * sinf( t * 0.4f );
attributes.bounce = 1.0f;
attributes.usingCollisionSphere = true;
attributes.collisionSpherePosition = glm::vec3( 5.0f, 0.5f, 5.0f );
attributes.collisionSphereRadius = 0.5f;
if (attributes.gravity < 0.0f) {
attributes.gravity = 0.0f;
}
_particleSystem.setParticleAttributesForEmitter(_coolDemoParticleEmitter, attributes);
}
_particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
_particleSystem.simulate(deltaTime);
}
}

View file

@ -84,7 +84,10 @@ public:
const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel);
void updateParticleSystem(float deltaTime);
Avatar* getAvatar() { return &_myAvatar; }
Audio* getAudio() { return &_audio; }
Camera* getCamera() { return &_myCamera; }
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
VoxelSystem* getVoxels() { return &_voxels; }
@ -101,6 +104,9 @@ public:
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
GeometryCache* getGeometryCache() { return &_geometryCache; }
void setGroundPlaneImpact(float groundPlaneImpact) { _groundPlaneImpact = groundPlaneImpact; }
private slots:
void timer();
@ -206,7 +212,7 @@ private:
void deleteVoxelUnderCursor();
void eyedropperVoxelUnderCursor();
void resetSensors();
void setMenuShortcutsEnabled(bool enabled);
void updateCursor();
@ -286,6 +292,8 @@ private:
timeval _timerStart, _timerEnd;
timeval _lastTimeUpdated;
bool _justStarted;
bool _particleSystemInitialized;
int _coolDemoParticleEmitter;
Stars _stars;
@ -344,6 +352,8 @@ private:
float _yawFromTouch;
float _pitchFromTouch;
float _groundPlaneImpact;
VoxelDetail _mouseVoxelDragging;
glm::vec3 _voxelThrust;
bool _mousePressed; // true if mouse has been pressed (clear when finished)

View file

@ -76,9 +76,12 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
NodeList* nodeList = NodeList::getInstance();
Application* interface = Application::getInstance();
Avatar* interfaceAvatar = interface->getAvatar();
memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
// Add Procedural effects to input samples
addProceduralSounds(inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
addProceduralSounds(inputLeft, outputLeft, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
if (nodeList && inputLeft) {
@ -135,12 +138,8 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
+ leadingBytes);
}
}
memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
AudioRingBuffer* ringBuffer = &_ringBuffer;
// if there is anything in the ring buffer, decide what to do:
@ -251,11 +250,11 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
}
}
#ifndef TEST_AUDIO_LOOPBACK
outputLeft[s] = leftSample;
outputRight[s] = rightSample;
outputLeft[s] += leftSample;
outputRight[s] += rightSample;
#else
outputLeft[s] = inputLeft[s];
outputRight[s] = inputLeft[s];
outputLeft[s] += inputLeft[s];
outputRight[s] += inputLeft[s];
#endif
}
ringBuffer->setNextOutput(ringBuffer->getNextOutput() + PACKET_LENGTH_SAMPLES);
@ -333,7 +332,13 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
_lastYawMeasuredMaximum(0),
_flangeIntensity(0.0f),
_flangeRate(0.0f),
_flangeWeight(0.0f)
_flangeWeight(0.0f),
_collisionSoundMagnitude(0.0f),
_collisionSoundFrequency(0.0f),
_collisionSoundNoise(0.0f),
_collisionSoundDuration(0.0f),
_proceduralEffectSample(0),
_heartbeatMagnitude(0.0f)
{
outputPortAudioError(Pa_Initialize());
@ -589,7 +594,10 @@ void Audio::lowPassFilter(int16_t* inputBuffer) {
}
// Take a pointer to the acquired microphone input samples and add procedural sounds
void Audio::addProceduralSounds(int16_t* inputBuffer, int numSamples) {
void Audio::addProceduralSounds(int16_t* inputBuffer,
int16_t* outputLeft,
int16_t* outputRight,
int numSamples) {
const float MAX_AUDIBLE_VELOCITY = 6.0;
const float MIN_AUDIBLE_VELOCITY = 0.1;
const int VOLUME_BASELINE = 400;
@ -598,14 +606,48 @@ void Audio::addProceduralSounds(int16_t* inputBuffer, int numSamples) {
float speed = glm::length(_lastVelocity);
float volume = VOLUME_BASELINE * (1.f - speed / MAX_AUDIBLE_VELOCITY);
int sample;
//
// Travelling noise
//
// Add a noise-modulated sinewave with volume that tapers off with speed increasing
if ((speed > MIN_AUDIBLE_VELOCITY) && (speed < MAX_AUDIBLE_VELOCITY)) {
for (int i = 0; i < numSamples; i++) {
inputBuffer[i] += (int16_t)((sinf((float) i / SOUND_PITCH * speed) * randFloat()) * volume * speed);
inputBuffer[i] += (int16_t)(sinf((float) (_proceduralEffectSample + i) / SOUND_PITCH ) * volume * (1.f + randFloat() * 0.25f) * speed);
}
}
const float COLLISION_SOUND_CUTOFF_LEVEL = 0.01f;
const float COLLISION_SOUND_MAX_VOLUME = 1000.f;
const float UP_MAJOR_FIFTH = powf(1.5f, 4.0f);
const float DOWN_TWO_OCTAVES = 4.f;
const float DOWN_FOUR_OCTAVES = 16.f;
float t;
if (_collisionSoundMagnitude > COLLISION_SOUND_CUTOFF_LEVEL) {
for (int i = 0; i < numSamples; i++) {
t = (float) _proceduralEffectSample + (float) i;
sample = sinf(t * _collisionSoundFrequency) +
sinf(t * _collisionSoundFrequency / DOWN_TWO_OCTAVES) +
sinf(t * _collisionSoundFrequency / DOWN_FOUR_OCTAVES * UP_MAJOR_FIFTH);
sample *= _collisionSoundMagnitude * COLLISION_SOUND_MAX_VOLUME;
inputBuffer[i] += sample;
outputLeft[i] += sample;
outputRight[i] += sample;
_collisionSoundMagnitude *= _collisionSoundDuration;
}
}
_proceduralEffectSample += numSamples;
}
//
// Starts a collision sound. magnitude is 0-1, with 1 the loudest possible sound.
//
void Audio::startCollisionSound(float magnitude, float frequency, float noise, float duration) {
_collisionSoundMagnitude = magnitude;
_collisionSoundFrequency = frequency;
_collisionSoundNoise = noise;
_collisionSoundDuration = duration;
}
// -----------------------------------------------------------
// Accoustic ping (audio system round trip time determination)
// -----------------------------------------------------------

View file

@ -42,7 +42,11 @@ public:
int getJitterBufferSamples() { return _jitterBufferSamples; };
void lowPassFilter(int16_t* inputBuffer);
void startCollisionSound(float magnitude, float frequency, float noise, float duration);
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; };
void ping();
// Call periodically to eventually perform round trip time analysis,
@ -80,7 +84,13 @@ private:
float _flangeIntensity;
float _flangeRate;
float _flangeWeight;
float _collisionSoundMagnitude;
float _collisionSoundFrequency;
float _collisionSoundNoise;
float _collisionSoundDuration;
int _proceduralEffectSample;
float _heartbeatMagnitude;
// Audio callback in class context.
inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);
@ -92,7 +102,7 @@ private:
inline void analyzePing();
// Add sounds that we want the user to not hear themselves, by adding on top of mic input signal
void addProceduralSounds(int16_t* inputBuffer, int numSamples);
void addProceduralSounds(int16_t* inputBuffer, int16_t* outputLeft, int16_t* outputRight, int numSamples);
// Audio callback called by portaudio. Calls 'performIO'.

View file

@ -546,8 +546,8 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_position += _scale * _gravity * (GRAVITY_EARTH * deltaTime) * deltaTime;
}
updateCollisionWithEnvironment();
updateCollisionWithVoxels();
updateCollisionWithEnvironment(deltaTime);
updateCollisionWithVoxels(deltaTime);
updateAvatarCollisions(deltaTime);
}
@ -874,29 +874,41 @@ void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float d
}
}
void Avatar::updateCollisionWithEnvironment() {
void Avatar::updateCollisionWithEnvironment(float deltaTime) {
glm::vec3 up = getBodyUpDirection();
float radius = _height * 0.125f;
const float ENVIRONMENT_SURFACE_ELASTICITY = 1.0f;
const float ENVIRONMENT_SURFACE_DAMPING = 0.01;
const float ENVIRONMENT_COLLISION_FREQUENCY = 0.05f;
const float VISIBLE_GROUND_COLLISION_VELOCITY = 0.2f;
glm::vec3 penetration;
if (Application::getInstance()->getEnvironment()->findCapsulePenetration(
_position - up * (_pelvisFloatingHeight - radius),
_position + up * (_height - _pelvisFloatingHeight - radius), radius, penetration)) {
float velocityTowardCollision = glm::dot(_velocity, glm::normalize(penetration));
if (velocityTowardCollision > VISIBLE_GROUND_COLLISION_VELOCITY) {
Application::getInstance()->setGroundPlaneImpact(1.0f);
}
updateCollisionSound(penetration, deltaTime, ENVIRONMENT_COLLISION_FREQUENCY);
applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING);
}
}
void Avatar::updateCollisionWithVoxels() {
void Avatar::updateCollisionWithVoxels(float deltaTime) {
float radius = _height * 0.125f;
const float VOXEL_ELASTICITY = 1.4f;
const float VOXEL_DAMPING = 0.0;
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
glm::vec3 penetration;
if (Application::getInstance()->getVoxels()->findCapsulePenetration(
_position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f),
_position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) {
updateCollisionSound(penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
}
@ -925,6 +937,36 @@ void Avatar::applyHardCollision(const glm::vec3& penetration, float elasticity,
}
}
void Avatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, 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 = _velocity;
glm::vec3 gravity = getGravity();
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
Application::getInstance()->getAudio()->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);
}
}
void Avatar::updateAvatarCollisions(float deltaTime) {
// Reset detector for nearest avatar

View file

@ -162,6 +162,8 @@ public:
glm::quat getOrientation () const;
glm::quat getWorldAlignedOrientation() const;
glm::vec3 getGravity () const { return _gravity; }
glm::vec3 getUprightHeadPosition() const;
AvatarVoxelSystem* getVoxels() { return &_voxels; }
@ -262,9 +264,10 @@ private:
void updateAvatarCollisions(float deltaTime);
void updateArmIKAndConstraints( float deltaTime );
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
void updateCollisionWithEnvironment();
void updateCollisionWithVoxels();
void updateCollisionWithEnvironment(float deltaTime);
void updateCollisionWithVoxels(float deltaTime);
void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
void checkForMouseRayTouching();
};

View file

@ -23,8 +23,13 @@ Hand::Hand(Avatar* owningAvatar) :
_lookingInMirror(false),
_ballColor(0.0, 0.0, 0.4),
_position(0.0, 0.4, 0.0),
_orientation(0.0, 0.0, 0.0, 1.0)
_orientation(0.0, 0.0, 0.0, 1.0),
_particleSystemInitialized(false)
{
// initialize all finger particle emitters with an invalid id as default
for (int f = 0; f< NUM_FINGERS_PER_HAND; f ++ ) {
_fingerParticleEmitter[f] = -1;
}
}
void Hand::init() {
@ -40,6 +45,7 @@ void Hand::reset() {
}
void Hand::simulate(float deltaTime, bool isMine) {
updateFingerParticles(deltaTime);
}
glm::vec3 Hand::leapPositionToWorldPosition(const glm::vec3& leapPosition) {
@ -69,6 +75,10 @@ void Hand::calculateGeometry() {
void Hand::render(bool lookingInMirror) {
if (_particleSystemInitialized) {
_particleSystem.render();
}
_renderAlpha = 1.0;
_lookingInMirror = lookingInMirror;
@ -163,3 +173,52 @@ void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
}
void Hand::updateFingerParticles(float deltaTime) {
if (!_particleSystemInitialized) {
for ( int f = 0; f< NUM_FINGERS_PER_HAND; f ++ ) {
_fingerParticleEmitter[f] = _particleSystem.addEmitter();
_particleSystem.setShowingEmitter(_fingerParticleEmitter[f], true);
}
_particleSystemInitialized = true;
} else {
// update the particles
static float t = 0.0f;
t += deltaTime;
for ( int f = 0; f< _fingerTips.size(); f ++ ) {
if (_fingerParticleEmitter[f] != -1) {
glm::vec3 particleEmitterPosition = leapPositionToWorldPosition(_fingerTips[f]);
// this aspect is still being designed....
glm::vec3 tilt = glm::vec3
(
30.0f * sinf( t * 0.55f ),
0.0f,
30.0f * cosf( t * 0.75f )
);
glm::quat particleEmitterRotation = glm::quat(glm::radians(tilt));
_particleSystem.setEmitterPosition(_fingerParticleEmitter[0], particleEmitterPosition);
_particleSystem.setEmitterRotation(_fingerParticleEmitter[0], particleEmitterRotation);
float radius = 0.005f;
glm::vec4 color(1.0f, 0.6f, 0.0f, 0.5f);
glm::vec3 velocity(0.0f, 0.005f, 0.0f);
float lifespan = 0.3f;
_particleSystem.emitParticlesNow(_fingerParticleEmitter[0], 1, radius, color, velocity, lifespan);
}
}
_particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
_particleSystem.simulate(deltaTime);
}
}

View file

@ -15,9 +15,11 @@
#include "world.h"
#include "InterfaceConfig.h"
#include "SerialInterface.h"
#include "ParticleSystem.h"
#include <SharedUtil.h>
#include <vector>
const int NUM_FINGERS_PER_HAND = 5;
class Avatar;
class ProgramObject;
@ -46,8 +48,10 @@ public:
const std::vector<glm::vec3>& fingerRoots);
void setLeapHands (const std::vector<glm::vec3>& handPositions,
const std::vector<glm::vec3>& handNormals);
void updateFingerParticles(float deltaTime);
void setRaveGloveActive(bool active) { _isRaveGloveActive = active; }
// getters
const glm::vec3& getLeapBallPosition (int ball) const { return _leapBalls[ball].position;}
bool isRaveGloveActive () const { return _isRaveGloveActive; }
@ -59,6 +63,8 @@ private:
// disallow copies of the Hand, copy of owning Avatar is disallowed too
Hand(const Hand&);
Hand& operator= (const Hand&);
ParticleSystem _particleSystem;
Avatar* _owningAvatar;
float _renderAlpha;
@ -69,6 +75,9 @@ private:
glm::quat _orientation;
std::vector<HandBall> _leapBalls;
bool _particleSystemInitialized;
int _fingerParticleEmitter[NUM_FINGERS_PER_HAND];
// private methods
void renderRaveGloveStage();
void renderHandSpheres();

View file

@ -227,7 +227,8 @@ void Head::simulate(float deltaTime, bool isMine) {
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.5f;
const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f;
const float CAMERA_STOP_TOLERANCE_DEGREES = 0.1f;
const float CAMERA_START_TOLERANCE_DEGREES = 2.0f;
const float CAMERA_PITCH_START_TOLERANCE_DEGREES = 10.0f;
const float CAMERA_YAW_START_TOLERANCE_DEGREES = 3.0f;
float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw));
if (_isCameraMoving) {
_cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE,
@ -240,7 +241,8 @@ void Head::simulate(float deltaTime, bool isMine) {
_isCameraMoving = false;
}
} else {
if (cameraHeadAngleDifference > CAMERA_START_TOLERANCE_DEGREES) {
if ((fabs(_pitch - _cameraPitch) > CAMERA_PITCH_START_TOLERANCE_DEGREES) ||
(fabs(_yaw - _cameraYaw) > CAMERA_YAW_START_TOLERANCE_DEGREES)) {
_isCameraMoving = true;
_cameraFollowHeadRate = CAMERA_FOLLOW_HEAD_RATE_START;
}

View file

@ -8,74 +8,157 @@
#include <glm/glm.hpp>
#include "InterfaceConfig.h"
#include <SharedUtil.h>
#include "ParticleSystem.h"
#include "Application.h"
const float DEFAULT_PARTICLE_BOUNCE = 1.0f;
const float DEFAULT_PARTICLE_AIR_FRICTION = 2.0f;
ParticleSystem::ParticleSystem() {
_numberOfParticles = 1500;
assert(_numberOfParticles <= MAX_PARTICLES);
_timer = 0.0f;
_numEmitters = 0;
_numParticles = 0;
_upDirection = glm::vec3(0.0f, 1.0f, 0.0f); // default
for (unsigned int e = 0; e < MAX_EMITTERS; e++) {
_emitter[e].position = glm::vec3(0.0f, 0.0f, 0.0f);
_emitter[e].rotation = glm::quat();
_emitter[e].right = IDENTITY_RIGHT;
_emitter[e].up = IDENTITY_UP;
_emitter[e].front = IDENTITY_FRONT;
_emitter[e].showingEmitter = false;
_emitter[e].particleAttributes.bounce = DEFAULT_PARTICLE_BOUNCE;
_emitter[e].particleAttributes.airFriction = DEFAULT_PARTICLE_AIR_FRICTION;
_emitter[e].particleAttributes.gravity = 0.0f;
_emitter[e].particleAttributes.jitter = 0.0f;
_emitter[e].particleAttributes.emitterAttraction = 0.0f;
_emitter[e].particleAttributes.tornadoForce = 0.0f;
_emitter[e].particleAttributes.neighborAttraction = 0.0f;
_emitter[e].particleAttributes.neighborRepulsion = 0.0f;
_emitter[e].particleAttributes.collisionSphereRadius = 0.0f;
_emitter[e].particleAttributes.collisionSpherePosition = glm::vec3(0.0f, 0.0f, 0.0f);
_emitter[e].particleAttributes.usingCollisionSphere = false;
};
_bounce = 0.9f;
_timer = 0.0f;
_airFriction = 6.0f;
_jitter = 0.1f;
_homeAttraction = 0.0f;
_tornadoForce = 0.0f;
_neighborAttraction = 0.02f;
_neighborRepulsion = 0.9f;
_tornadoAxis = glm::normalize(glm::vec3(0.1f, 1.0f, 0.1f));
_home = glm::vec3(5.0f, 1.0f, 5.0f);
_TEST_bigSphereRadius = 0.5f;
_TEST_bigSpherePosition = glm::vec3( 5.0f, _TEST_bigSphereRadius, 5.0f);
for (unsigned int p = 0; p < _numberOfParticles; p++) {
_particle[p].position = _home;
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
float radian = ((float)p / (float)_numberOfParticles) * PI_TIMES_TWO;
float wave = sinf(radian);
float red = 0.5f + 0.5f * wave;
float green = 0.3f + 0.3f * wave;
float blue = 0.2f - 0.2f * wave;
_particle[p].color = glm::vec3(red, green, blue);
_particle[p].age = 0.0f;
_particle[p].radius = 0.01f;
}
for (unsigned int p = 0; p < MAX_PARTICLES; p++) {
_particle[p].alive = false;
_particle[p].age = 0.0f;
_particle[p].lifespan = 0.0f;
_particle[p].radius = 0.0f;
_particle[p].emitterIndex = 0;
_particle[p].position = glm::vec3(0.0f, 0.0f, 0.0f);
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
}
}
int ParticleSystem::addEmitter() {
_numEmitters ++;
if (_numEmitters > MAX_EMITTERS) {
return -1;
}
return _numEmitters - 1;
}
void ParticleSystem::simulate(float deltaTime) {
runSpecialEffectsTest(deltaTime);
for (unsigned int p = 0; p < _numberOfParticles; p++) {
updateParticle(p, deltaTime);
// update emitters
for (unsigned int e = 0; e < _numEmitters; e++) {
updateEmitter(e, deltaTime);
}
// update particles
for (unsigned int p = 0; p < _numParticles; p++) {
if (_particle[p].alive) {
updateParticle(p, deltaTime);
}
}
}
void ParticleSystem::updateEmitter(int e, float deltaTime) {
_emitter[e].front = _emitter[e].rotation * IDENTITY_FRONT;
_emitter[e].right = _emitter[e].rotation * IDENTITY_RIGHT;
_emitter[e].up = _emitter[e].rotation * IDENTITY_UP;
}
void ParticleSystem::emitParticlesNow(int e, int num, float radius, glm::vec4 color, glm::vec3 velocity, float lifespan) {
for (unsigned int p = 0; p < num; p++) {
createParticle(e, _emitter[e].position, velocity, radius, color, lifespan);
}
}
void ParticleSystem::createParticle(int e, glm::vec3 position, glm::vec3 velocity, float radius, glm::vec4 color, float lifespan) {
for (unsigned int p = 0; p < MAX_PARTICLES; p++) {
if (!_particle[p].alive) {
_particle[p].emitterIndex = e;
_particle[p].lifespan = lifespan;
_particle[p].alive = true;
_particle[p].age = 0.0f;
_particle[p].position = position;
_particle[p].velocity = velocity;
_particle[p].radius = radius;
_particle[p].color = color;
_numParticles ++;
assert(_numParticles <= MAX_PARTICLES);
return;
}
}
}
void ParticleSystem::killParticle(int p) {
assert( p >= 0);
assert( p < MAX_PARTICLES);
assert( _numParticles > 0);
_particle[p].alive = false;
_numParticles --;
}
void ParticleSystem::setOrangeBlueColorPalette() {
for (unsigned int p = 0; p < _numParticles; p++) {
float radian = ((float)p / (float)_numParticles) * PI_TIMES_TWO;
float wave = sinf(radian);
float red = 0.5f + 0.5f * wave;
float green = 0.3f + 0.3f * wave;
float blue = 0.2f - 0.2f * wave;
float alpha = 1.0f;
_particle[p].color = glm::vec4(red, green, blue, alpha);
}
}
void ParticleSystem::setParticleAttributesForEmitter(int emitterIndex, ParticleAttributes attributes) {
void ParticleSystem::runSpecialEffectsTest(float deltaTime) {
_timer += deltaTime;
_gravity = 0.01f + 0.01f * sinf( _timer * 0.52f );
_airFriction = 3.0f + 2.0f * sinf( _timer * 0.32f );
_jitter = 0.05f + 0.05f * sinf( _timer * 0.42f );
_homeAttraction = 0.01f + 0.01f * cosf( _timer * 0.6f );
_tornadoForce = 0.0f + 0.03f * sinf( _timer * 0.7f );
_neighborAttraction = 0.1f + 0.1f * cosf( _timer * 0.8f );
_neighborRepulsion = 0.4f + 0.3f * sinf( _timer * 0.4f );
_tornadoAxis = glm::vec3
(
0.0f + 0.5f * sinf( _timer * 0.55f ),
1.0f,
0.0f + 0.5f * cosf( _timer * 0.75f )
);
_emitter[emitterIndex].particleAttributes.bounce = attributes.bounce;
_emitter[emitterIndex].particleAttributes.gravity = attributes.gravity;
_emitter[emitterIndex].particleAttributes.airFriction = attributes.airFriction;
_emitter[emitterIndex].particleAttributes.jitter = attributes.jitter;
_emitter[emitterIndex].particleAttributes.emitterAttraction = attributes.emitterAttraction;
_emitter[emitterIndex].particleAttributes.tornadoForce = attributes.tornadoForce;
_emitter[emitterIndex].particleAttributes.neighborAttraction = attributes.neighborAttraction;
_emitter[emitterIndex].particleAttributes.neighborRepulsion = attributes.neighborRepulsion;
_emitter[emitterIndex].particleAttributes.usingCollisionSphere = attributes.usingCollisionSphere;
_emitter[emitterIndex].particleAttributes.collisionSpherePosition = attributes.collisionSpherePosition;
_emitter[emitterIndex].particleAttributes.collisionSphereRadius = attributes.collisionSphereRadius;
}
@ -83,41 +166,49 @@ void ParticleSystem::runSpecialEffectsTest(float deltaTime) {
void ParticleSystem::updateParticle(int p, float deltaTime) {
_particle[p].age += deltaTime;
if (_particle[p].age > _particle[p].lifespan) {
killParticle(p);
}
Emitter myEmitter = _emitter[_particle[p].emitterIndex];
// apply random jitter
_particle[p].velocity +=
glm::vec3
(
-_jitter * ONE_HALF + _jitter * randFloat(),
-_jitter * ONE_HALF + _jitter * randFloat(),
-_jitter * ONE_HALF + _jitter * randFloat()
-myEmitter.particleAttributes.jitter * ONE_HALF + myEmitter.particleAttributes.jitter * randFloat(),
-myEmitter.particleAttributes.jitter * ONE_HALF + myEmitter.particleAttributes.jitter * randFloat(),
-myEmitter.particleAttributes.jitter * ONE_HALF + myEmitter.particleAttributes.jitter * randFloat()
) * deltaTime;
// apply attraction to home position
glm::vec3 vectorToHome = _home - _particle[p].position;
_particle[p].velocity += vectorToHome * _homeAttraction * deltaTime;
glm::vec3 vectorToHome = myEmitter.position - _particle[p].position;
_particle[p].velocity += vectorToHome * myEmitter.particleAttributes.emitterAttraction * deltaTime;
// apply neighbor attraction
int neighbor = p + 1;
if (neighbor == _numberOfParticles ) {
if (neighbor == _numParticles ) {
neighbor = 0;
}
glm::vec3 vectorToNeighbor = _particle[p].position - _particle[neighbor].position;
_particle[p].velocity -= vectorToNeighbor * _neighborAttraction * deltaTime;
if ( _particle[neighbor].emitterIndex == _particle[p].emitterIndex) {
glm::vec3 vectorToNeighbor = _particle[p].position - _particle[neighbor].position;
_particle[p].velocity -= vectorToNeighbor * myEmitter.particleAttributes.neighborAttraction * deltaTime;
float distanceToNeighbor = glm::length(vectorToNeighbor);
if (distanceToNeighbor > 0.0f) {
_particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * _neighborRepulsion * deltaTime;
float distanceToNeighbor = glm::length(vectorToNeighbor);
if (distanceToNeighbor > 0.0f) {
_particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * myEmitter.particleAttributes.neighborRepulsion * deltaTime;
}
}
// apply tornado force
glm::vec3 tornadoDirection = glm::cross(vectorToHome, _tornadoAxis);
_particle[p].velocity += tornadoDirection * _tornadoForce * deltaTime;
glm::vec3 tornadoDirection = glm::cross(vectorToHome, myEmitter.up);
_particle[p].velocity += tornadoDirection * myEmitter.particleAttributes.tornadoForce * deltaTime;
// apply air friction
float drag = 1.0 - _airFriction * deltaTime;
float drag = 1.0 - myEmitter.particleAttributes.airFriction * deltaTime;
if (drag < 0.0f) {
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
} else {
@ -125,7 +216,7 @@ void ParticleSystem::updateParticle(int p, float deltaTime) {
}
// apply gravity
_particle[p].velocity.y -= _gravity * deltaTime;
_particle[p].velocity -= _upDirection * myEmitter.particleAttributes.gravity * deltaTime;
// update position by velocity
_particle[p].position += _particle[p].velocity;
@ -135,49 +226,98 @@ void ParticleSystem::updateParticle(int p, float deltaTime) {
_particle[p].position.y = _particle[p].radius;
if (_particle[p].velocity.y < 0.0f) {
_particle[p].velocity.y *= -_bounce;
_particle[p].velocity.y *= -myEmitter.particleAttributes.bounce;
}
}
// collision with sphere
glm::vec3 vectorToSphereCenter = _TEST_bigSpherePosition - _particle[p].position;
float distanceToSphereCenter = glm::length(vectorToSphereCenter);
float combinedRadius = _TEST_bigSphereRadius + _particle[p].radius;
if (distanceToSphereCenter < combinedRadius) {
if (distanceToSphereCenter > 0.0f){
glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter;
_particle[p].position = _TEST_bigSpherePosition - directionToSphereCenter * combinedRadius;
if (myEmitter.particleAttributes.usingCollisionSphere) {
glm::vec3 vectorToSphereCenter = myEmitter.particleAttributes.collisionSpherePosition - _particle[p].position;
float distanceToSphereCenter = glm::length(vectorToSphereCenter);
float combinedRadius = myEmitter.particleAttributes.collisionSphereRadius + _particle[p].radius;
if (distanceToSphereCenter < combinedRadius) {
if (distanceToSphereCenter > 0.0f){
glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter;
_particle[p].position = myEmitter.particleAttributes.collisionSpherePosition - directionToSphereCenter * combinedRadius;
}
}
}
}
void ParticleSystem::setCollisionSphere(int e, glm::vec3 position, float radius) {
_emitter[e].particleAttributes.usingCollisionSphere = true;
_emitter[e].particleAttributes.collisionSpherePosition = position;
_emitter[e].particleAttributes.collisionSphereRadius = radius;
}
void ParticleSystem::render() {
for (unsigned int p = 0; p < _numberOfParticles; p++) {
glColor3f(_particle[p].color.x, _particle[p].color.y, _particle[p].color.z);
glPushMatrix();
glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glutSolidSphere(_particle[p].radius, 6, 6);
glPopMatrix();
// render velocity lines
glColor4f( _particle[p].color.x, _particle[p].color.y, _particle[p].color.z, 0.5f);
glm::vec3 end = _particle[p].position - _particle[p].velocity * 2.0f;
glBegin(GL_LINES);
glVertex3f(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glVertex3f(end.x, end.y, end.z);
glEnd();
// render the emitters
for (unsigned int e = 0; e < _numEmitters; e++) {
if (_emitter[e].showingEmitter) {
renderEmitter(e, 0.2f);
}
};
// render the particles
for (unsigned int p = 0; p < _numParticles; p++) {
if (_particle[p].alive) {
renderParticle(p);
}
}
}
void ParticleSystem::renderParticle(int p) {
glColor4f(_particle[p].color.r, _particle[p].color.g, _particle[p].color.b, _particle[p].color.a );
glPushMatrix();
glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glutSolidSphere(_particle[p].radius, 6, 6);
glPopMatrix();
// render velocity lines
glColor4f( _particle[p].color.x, _particle[p].color.y, _particle[p].color.z, 0.5f);
glm::vec3 end = _particle[p].position - _particle[p].velocity * 2.0f;
glBegin(GL_LINES);
glVertex3f(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glVertex3f(end.x, end.y, end.z);
glEnd();
}
void ParticleSystem::renderEmitter(int e, float size) {
glm::vec3 r = _emitter[e].right * size;
glm::vec3 u = _emitter[e].up * size;
glm::vec3 f = _emitter[e].front * size;
glLineWidth(2.0f);
glColor3f(0.8f, 0.4, 0.4);
glBegin(GL_LINES);
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
glVertex3f(_emitter[e].position.x + r.x, _emitter[e].position.y + r.y, _emitter[e].position.z + r.z);
glEnd();
glColor3f(0.4f, 0.8, 0.4);
glBegin(GL_LINES);
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
glVertex3f(_emitter[e].position.x + u.x, _emitter[e].position.y + u.y, _emitter[e].position.z + u.z);
glEnd();
glColor3f(0.4f, 0.4, 0.8);
glBegin(GL_LINES);
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
glVertex3f(_emitter[e].position.x + f.x, _emitter[e].position.y + f.y, _emitter[e].position.z + f.z);
glEnd();
}

View file

@ -9,51 +9,82 @@
#ifndef hifi_ParticleSystem_h
#define hifi_ParticleSystem_h
#include <glm/gtc/quaternion.hpp>
const int MAX_PARTICLES = 5000;
const int MAX_EMITTERS = 10;
const int MAX_EMITTERS = 20;
class ParticleSystem {
public:
struct ParticleAttributes {
float bounce;
float gravity;
float airFriction;
float jitter;
float emitterAttraction;
float tornadoForce;
float neighborAttraction;
float neighborRepulsion;
bool usingCollisionSphere;
glm::vec3 collisionSpherePosition;
float collisionSphereRadius;
};
ParticleSystem();
int addEmitter(); // add (create) an emitter and get its unique id
void emitParticlesNow(int e, int numParticles, float radius, glm::vec4 color, glm::vec3 velocity, float lifespan);
void simulate(float deltaTime);
void render();
void setParticleAttributesForEmitter(int emitterIndex, ParticleAttributes attributes);
void setOrangeBlueColorPalette(); // apply a nice preset color palette to the particles
void setUpDirection(glm::vec3 upDirection) {_upDirection = upDirection;} // tell particle system which direction is up
void setCollisionSphere(int emitterIndex, glm::vec3 position, float radius); // specify a sphere for the particles to collide with
void setEmitterPosition(int emitterIndex, glm::vec3 position) { _emitter[emitterIndex].position = position; } // set position of emitter
void setEmitterRotation(int emitterIndex, glm::quat rotation) { _emitter[emitterIndex].rotation = rotation; } // set rotation of emitter
void setShowingEmitter (int emitterIndex, bool showing ) { _emitter[emitterIndex].showingEmitter = showing; } // set its visibiity
private:
struct Emitter {
glm::vec3 position;
glm::quat rotation;
glm::vec3 right;
glm::vec3 up;
glm::vec3 front;
bool showingEmitter;
ParticleAttributes particleAttributes;
};
struct Particle {
glm::vec3 position;
glm::vec3 velocity;
glm::vec3 color;
float age;
float radius;
bool alive; // is the particle active?
glm::vec3 position; // position
glm::vec3 velocity; // velocity
glm::vec4 color; // color (rgba)
float age; // age in seconds
float radius; // radius
float lifespan; // how long this particle stays alive (in seconds)
int emitterIndex; // which emitter created this particle?
};
struct Emitter {
glm::vec3 position;
glm::vec3 direction;
};
float _bounce;
float _gravity;
glm::vec3 _upDirection;
float _timer;
Emitter _emitter[MAX_EMITTERS];
Particle _particle[MAX_PARTICLES];
int _numberOfParticles;
glm::vec3 _home;
glm::vec3 _tornadoAxis;
float _airFriction;
float _jitter;
float _homeAttraction;
float _tornadoForce;
float _neighborAttraction;
float _neighborRepulsion;
float _TEST_bigSphereRadius;
glm::vec3 _TEST_bigSpherePosition;
int _numParticles;
int _numEmitters;
// private methods
void updateEmitter(int e, float deltaTime);
void updateParticle(int index, float deltaTime);
void runSpecialEffectsTest(float deltaTime);
void createParticle(int e, glm::vec3 position, glm::vec3 velocity, float radius, glm::vec4 color, float lifespan);
//void runSpecialEffectsTest(int e, float deltaTime); // for debugging and artistic exploration
void killParticle(int p);
void renderEmitter(int emitterIndex, float size);
void renderParticle(int p);
};
#endif

View file

@ -325,22 +325,38 @@ void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, gl
glPopMatrix();
}
void renderCollisionOverlay(int width, int height, float magnitude) {
const float MIN_VISIBLE_COLLISION = 0.01f;
if (magnitude > MIN_VISIBLE_COLLISION) {
glColor4f(0, 0, 0, magnitude);
glBegin(GL_QUADS);
glVertex2f(0, 0);
glVertex2d(width, 0);
glVertex2d(width, height);
glVertex2d(0, height);
glEnd();
}
}
void drawGroundPlaneGrid(float size) {
glColor3f(0.4f, 0.5f, 0.3f);
void renderGroundPlaneGrid(float size, float impact) {
glLineWidth(2.0);
glm::vec4 impactColor(1, 0, 0, 1);
glm::vec3 lineColor(0.4, 0.5, 0.3);
glm::vec4 surfaceColor(0.5, 0.5, 0.5, 0.4);
glColor3fv(&lineColor.x);
for (float x = 0; x <= size; x++) {
glBegin(GL_LINES);
glVertex3f(x, 0.0f, 0);
glVertex3f(x, 0.0f, size);
glVertex3f(0, 0.0f, x);
glVertex3f(size, 0.0f, x);
glVertex3f(x, 0, 0);
glVertex3f(x, 0, size);
glVertex3f(0, 0, x);
glVertex3f(size, 0, x);
glEnd();
}
// Draw a translucent quad just underneath the grid.
glColor4f(0.5, 0.5, 0.5, 0.4);
// Draw the floor, colored for recent impact
glm::vec4 floorColor = impact * impactColor + (1.f - impact) * surfaceColor;
glColor4fv(&floorColor.x);
glBegin(GL_QUADS);
glVertex3f(0, 0, 0);
glVertex3f(size, 0, 0);

View file

@ -57,7 +57,10 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
double diffclock(timeval *clock1,timeval *clock2);
void drawGroundPlaneGrid(float size);
void renderGroundPlaneGrid(float size, float impact);
void renderCollisionOverlay(int width, int height, float magnitude);
void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness);