diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5c91367160..582bc4fcb4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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), @@ -1867,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; @@ -2082,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) { @@ -2130,8 +2135,8 @@ void Application::update(float deltaTime) { #endif if (TESTING_PARTICLE_SYSTEM) { - _particleSystem.simulate(deltaTime); - } + updateParticleSystem(deltaTime); + } } void Application::updateAvatar(float deltaTime) { @@ -2580,7 +2585,9 @@ void Application::displaySide(Camera& whichCamera) { } if (TESTING_PARTICLE_SYSTEM) { - _particleSystem.render(); + if (_particleSystemInitialized) { + _particleSystem.render(); + } } // Render the world box @@ -3492,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); + } +} + + + diff --git a/interface/src/Application.h b/interface/src/Application.h index e342880264..9333808a27 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -84,6 +84,8 @@ public: const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel); + void updateParticleSystem(float deltaTime); + Avatar* getAvatar() { return &_myAvatar; } Audio* getAudio() { return &_audio; } Camera* getCamera() { return &_myCamera; } @@ -290,6 +292,8 @@ private: timeval _timerStart, _timerEnd; timeval _lastTimeUpdated; bool _justStarted; + bool _particleSystemInitialized; + int _coolDemoParticleEmitter; Stars _stars; diff --git a/interface/src/Hand.cpp b/interface/src/Hand.cpp index d0ff0abd5a..13365ca8ec 100755 --- a/interface/src/Hand.cpp +++ b/interface/src/Hand.cpp @@ -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& 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); + } +} + + + diff --git a/interface/src/Hand.h b/interface/src/Hand.h index e1b0dc9ff0..fb009ad04c 100755 --- a/interface/src/Hand.h +++ b/interface/src/Hand.h @@ -15,9 +15,11 @@ #include "world.h" #include "InterfaceConfig.h" #include "SerialInterface.h" +#include "ParticleSystem.h" #include #include +const int NUM_FINGERS_PER_HAND = 5; class Avatar; class ProgramObject; @@ -46,8 +48,10 @@ public: const std::vector& fingerRoots); void setLeapHands (const std::vector& handPositions, const std::vector& 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 _leapBalls; + bool _particleSystemInitialized; + int _fingerParticleEmitter[NUM_FINGERS_PER_HAND]; + // private methods void renderRaveGloveStage(); void renderHandSpheres(); diff --git a/interface/src/ParticleSystem.cpp b/interface/src/ParticleSystem.cpp index 3c27f3a8df..fcc24a2663 100644 --- a/interface/src/ParticleSystem.cpp +++ b/interface/src/ParticleSystem.cpp @@ -8,74 +8,157 @@ #include #include "InterfaceConfig.h" #include - #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(); +} + - diff --git a/interface/src/ParticleSystem.h b/interface/src/ParticleSystem.h index 38eb0a1777..c94acd03ba 100644 --- a/interface/src/ParticleSystem.h +++ b/interface/src/ParticleSystem.h @@ -9,51 +9,82 @@ #ifndef hifi_ParticleSystem_h #define hifi_ParticleSystem_h +#include + 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