diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a87564cf2e..2001ba5d71 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3575,6 +3575,7 @@ void Application::updateParticleSystem(float deltaTime) { ParticleSystem::ParticleAttributes attributes; + attributes.radius = 0.01f; 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 ); @@ -3591,7 +3592,7 @@ void Application::updateParticleSystem(float deltaTime) { attributes.gravity = 0.0f; } - _particleSystem.setParticleAttributesForEmitter(_coolDemoParticleEmitter, attributes); + _particleSystem.setParticleAttributes(_coolDemoParticleEmitter, attributes); } _particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f)); diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index c723bb3b8d..9d1e4da0fb 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -822,10 +822,13 @@ void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovem } // If there's a leap-interaction hand visible, use that as the endpoint - if (getHand().getHandPositions().size() > 0) { - _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = - getHand().leapPositionToWorldPosition(getHand().getHandPositions()[0]); + for (size_t i = 0; i < getHand().getPalms().size(); ++i) { + PalmData& palm = getHand().getPalms()[i]; + if (palm.isActive()) { + _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = palm.getPosition(); + } } + }//if (_isMine) //constrain right arm length and re-adjust elbow position as it bends diff --git a/interface/src/Hand.cpp b/interface/src/Hand.cpp index 13365ca8ec..bbfd58c5b1 100755 --- a/interface/src/Hand.cpp +++ b/interface/src/Hand.cpp @@ -22,8 +22,6 @@ Hand::Hand(Avatar* owningAvatar) : _renderAlpha(1.0), _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), _particleSystemInitialized(false) { // initialize all finger particle emitters with an invalid id as default @@ -45,48 +43,55 @@ void Hand::reset() { } void Hand::simulate(float deltaTime, bool isMine) { - updateFingerParticles(deltaTime); -} - -glm::vec3 Hand::leapPositionToWorldPosition(const glm::vec3& leapPosition) { - float unitScale = 0.001; // convert mm to meters - return _position + _orientation * (leapPosition * unitScale); + if (_isRaveGloveActive) { + updateFingerParticles(deltaTime); + } } void Hand::calculateGeometry() { glm::vec3 offset(0.2, -0.2, -0.3); // place the hand in front of the face where we can see it Head& head = _owningAvatar->getHead(); - _position = head.getPosition() + head.getOrientation() * offset; - _orientation = head.getOrientation(); + _basePosition = head.getPosition() + head.getOrientation() * offset; + _baseOrientation = head.getOrientation(); - int numLeapBalls = _fingerTips.size(); - _leapBalls.resize(numLeapBalls); - - for (int i = 0; i < _fingerTips.size(); ++i) { - _leapBalls[i].rotation = _orientation; - _leapBalls[i].position = leapPositionToWorldPosition(_fingerTips[i]); - _leapBalls[i].radius = 0.01; - _leapBalls[i].touchForce = 0.0; - _leapBalls[i].isCollidable = true; + _leapBalls.clear(); + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + if (palm.isActive()) { + for (size_t f = 0; f < palm.getNumFingers(); ++f) { + FingerData& finger = palm.getFingers()[f]; + if (finger.isActive()) { + const float standardBallRadius = 0.01f; + _leapBalls.resize(_leapBalls.size() + 1); + HandBall& ball = _leapBalls.back(); + ball.rotation = _baseOrientation; + ball.position = finger.getTipPosition(); + ball.radius = standardBallRadius; + ball.touchForce = 0.0; + ball.isCollidable = true; + } + } + } } } void Hand::render(bool lookingInMirror) { - - if (_particleSystemInitialized) { - _particleSystem.render(); - } - + _renderAlpha = 1.0; _lookingInMirror = lookingInMirror; calculateGeometry(); - if (_isRaveGloveActive) + if (_isRaveGloveActive) { renderRaveGloveStage(); + if (_particleSystemInitialized) { + _particleSystem.render(); + } + } + glEnable(GL_DEPTH_TEST); glEnable(GL_RESCALE_NORMAL); @@ -138,21 +143,29 @@ void Hand::renderHandSpheres() { } // Draw the finger root cones - if (_fingerTips.size() == _fingerRoots.size()) { - for (size_t i = 0; i < _fingerTips.size(); ++i) { - glColor4f(_ballColor.r, _ballColor.g, _ballColor.b, 0.5); - glm::vec3 tip = leapPositionToWorldPosition(_fingerTips[i]); - glm::vec3 root = leapPositionToWorldPosition(_fingerRoots[i]); - Avatar::renderJointConnectingCone(root, tip, 0.001, 0.003); + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + if (palm.isActive()) { + for (size_t f = 0; f < palm.getNumFingers(); ++f) { + FingerData& finger = palm.getFingers()[f]; + if (finger.isActive()) { + glColor4f(_ballColor.r, _ballColor.g, _ballColor.b, 0.5); + glm::vec3 tip = finger.getTipPosition(); + glm::vec3 root = finger.getRootPosition(); + Avatar::renderJointConnectingCone(root, tip, 0.001, 0.003); + } + } } } // Draw the palms - if (_handPositions.size() == _handNormals.size()) { - for (size_t i = 0; i < _handPositions.size(); ++i) { + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + if (palm.isActive()) { + const float palmThickness = 0.002f; glColor4f(_ballColor.r, _ballColor.g, _ballColor.b, 0.25); - glm::vec3 tip = leapPositionToWorldPosition(_handPositions[i]); - glm::vec3 root = leapPositionToWorldPosition(_handPositions[i] + (_handNormals[i] * 2.0f)); + glm::vec3 tip = palm.getPosition(); + glm::vec3 root = palm.getPosition() + palm.getNormal() * palmThickness; Avatar::renderJointConnectingCone(root, tip, 0.05, 0.03); } } @@ -162,57 +175,123 @@ void Hand::renderHandSpheres() { void Hand::setLeapFingers(const std::vector& fingerTips, const std::vector& fingerRoots) { - _fingerTips = fingerTips; - _fingerRoots = fingerRoots; + // TODO: add id-checking here to increase finger stability + + size_t fingerIndex = 0; + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + for (size_t f = 0; f < palm.getNumFingers(); ++f) { + FingerData& finger = palm.getFingers()[f]; + if (fingerIndex < fingerTips.size()) { + finger.setActive(true); + finger.setRawTipPosition(fingerTips[fingerIndex]); + finger.setRawRootPosition(fingerRoots[fingerIndex]); + fingerIndex++; + } + else { + finger.setActive(false); + } + } + } } void Hand::setLeapHands(const std::vector& handPositions, const std::vector& handNormals) { - _handPositions = handPositions; - _handNormals = handNormals; + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + if (i < handPositions.size()) { + palm.setActive(true); + palm.setRawPosition(handPositions[i]); + palm.setRawNormal(handNormals[i]); + } + else { + palm.setActive(false); + } + } } 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); + + ParticleSystem::ParticleAttributes attributes; + + // set attributes for each life stage of the particle: + attributes.radius = 0.0f; + attributes.color = glm::vec4( 1.0f, 1.0f, 0.5f, 0.5f); + attributes.gravity = 0.0f; + attributes.airFriction = 0.0f; + attributes.jitter = 0.002f; + attributes.emitterAttraction = 0.0f; + attributes.tornadoForce = 0.0f; + attributes.neighborAttraction = 0.0f; + attributes.neighborRepulsion = 0.0f; + attributes.bounce = 1.0f; + attributes.usingCollisionSphere = false; + _particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 0, attributes); + + attributes.radius = 0.01f; + attributes.jitter = 0.0f; + attributes.gravity = -0.005f; + attributes.color = glm::vec4( 1.0f, 0.2f, 0.0f, 0.4f); + _particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 1, attributes); + + attributes.radius = 0.01f; + attributes.gravity = 0.0f; + attributes.color = glm::vec4( 0.0f, 0.0f, 0.0f, 0.2f); + _particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 2, attributes); + + attributes.radius = 0.02f; + attributes.color = glm::vec4( 0.0f, 0.0f, 0.0f, 0.0f); + _particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 3, attributes); } + _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); - } + int fingerIndex = 0; + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + if (palm.isActive()) { + for (size_t f = 0; f < palm.getNumFingers(); ++f) { + FingerData& finger = palm.getFingers()[f]; + if (finger.isActive()) { + if (_fingerParticleEmitter[fingerIndex] != -1) { + + glm::vec3 particleEmitterPosition = finger.getTipPosition(); + + glm::vec3 fingerDirection = particleEmitterPosition - leapPositionToWorldPosition(finger.getRootPosition()); + float fingerLength = glm::length(fingerDirection); + + if (fingerLength > 0.0f) { + fingerDirection /= fingerLength; + } else { + fingerDirection = IDENTITY_UP; + } + + glm::quat particleEmitterRotation = rotationBetween(IDENTITY_UP, fingerDirection); + + _particleSystem.setEmitterPosition(_fingerParticleEmitter[f], particleEmitterPosition); + _particleSystem.setEmitterRotation(_fingerParticleEmitter[f], particleEmitterRotation); + + float radius = 0.005f; + const glm::vec4 color(1.0f, 0.6f, 0.0f, 0.5f); + const glm::vec3 velocity = fingerDirection * 0.002f; + const float lifespan = 1.0f; + _particleSystem.emitParticlesNow(_fingerParticleEmitter[f], 1, radius, color, velocity, lifespan); + } + } + } + } } _particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f)); diff --git a/interface/src/Hand.h b/interface/src/Hand.h index fb009ad04c..fb6b863458 100755 --- a/interface/src/Hand.h +++ b/interface/src/Hand.h @@ -19,8 +19,6 @@ #include #include -const int NUM_FINGERS_PER_HAND = 5; - class Avatar; class ProgramObject; @@ -56,9 +54,6 @@ public: const glm::vec3& getLeapBallPosition (int ball) const { return _leapBalls[ball].position;} bool isRaveGloveActive () const { return _isRaveGloveActive; } - // position conversion - glm::vec3 leapPositionToWorldPosition(const glm::vec3& leapPosition); - private: // disallow copies of the Hand, copy of owning Avatar is disallowed too Hand(const Hand&); @@ -71,8 +66,6 @@ private: bool _lookingInMirror; bool _isRaveGloveActive; glm::vec3 _ballColor; - glm::vec3 _position; - glm::quat _orientation; std::vector _leapBalls; bool _particleSystemInitialized; diff --git a/interface/src/ParticleSystem.cpp b/interface/src/ParticleSystem.cpp index fcc24a2663..66bb847226 100644 --- a/interface/src/ParticleSystem.cpp +++ b/interface/src/ParticleSystem.cpp @@ -11,34 +11,47 @@ #include "ParticleSystem.h" #include "Application.h" +const float DEFAULT_PARTICLE_RADIUS = 0.01f; const float DEFAULT_PARTICLE_BOUNCE = 1.0f; const float DEFAULT_PARTICLE_AIR_FRICTION = 2.0f; ParticleSystem::ParticleSystem() { - _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; + _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].visible = false; + _emitter[e].baseParticle.alive = false; + _emitter[e].baseParticle.age = 0.0f; + _emitter[e].baseParticle.lifespan = 0.0f; + _emitter[e].baseParticle.radius = 0.0f; + _emitter[e].baseParticle.emitterIndex = 0; + _emitter[e].baseParticle.position = glm::vec3(0.0f, 0.0f, 0.0f); + _emitter[e].baseParticle.velocity = glm::vec3(0.0f, 0.0f, 0.0f); + + + for (int s = 0; s _particle[p].lifespan) { + killParticle(p); + } else { + updateParticle(p, deltaTime); + } } } } + void ParticleSystem::updateEmitter(int e, float deltaTime) { _emitter[e].front = _emitter[e].rotation * IDENTITY_FRONT; @@ -106,9 +123,11 @@ void ParticleSystem::createParticle(int e, glm::vec3 position, glm::vec3 velocit _particle[p].age = 0.0f; _particle[p].position = position; _particle[p].velocity = velocity; - _particle[p].radius = radius; _particle[p].color = color; + _particle[p].radius = _emitter[e].particleAttributes[0].radius; + _particle[p].color = _emitter[e].particleAttributes[0].color; + _numParticles ++; assert(_numParticles <= MAX_PARTICLES); @@ -146,45 +165,72 @@ void ParticleSystem::setOrangeBlueColorPalette() { } -void ParticleSystem::setParticleAttributesForEmitter(int emitterIndex, ParticleAttributes attributes) { +void ParticleSystem::setParticleAttributes(int emitterIndex, ParticleAttributes attributes) { - _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; + for (int lifeStage = 0; lifeStage < NUM_PARTICLE_LIFE_STAGES; lifeStage ++ ) { + setParticleAttributes(emitterIndex, lifeStage, attributes); + } } +void ParticleSystem::setParticleAttributes(int emitterIndex, int lifeStage, ParticleAttributes attributes) { + + ParticleAttributes * a = &_emitter[emitterIndex].particleAttributes[lifeStage]; + + a->radius = attributes.radius; + a->color = attributes.color; + a->bounce = attributes.bounce; + a->gravity = attributes.gravity; + a->airFriction = attributes.airFriction; + a->jitter = attributes.jitter; + a->emitterAttraction = attributes.emitterAttraction; + a->tornadoForce = attributes.tornadoForce; + a->neighborAttraction = attributes.neighborAttraction; + a->neighborRepulsion = attributes.neighborRepulsion; + a->usingCollisionSphere = attributes.usingCollisionSphere; + a->collisionSpherePosition = attributes.collisionSpherePosition; + a->collisionSphereRadius = attributes.collisionSphereRadius; +} void ParticleSystem::updateParticle(int p, float deltaTime) { - _particle[p].age += deltaTime; + assert(_particle[p].age <= _particle[p].lifespan); - if (_particle[p].age > _particle[p].lifespan) { - killParticle(p); + float ageFraction = _particle[p].age / _particle[p].lifespan; + + int lifeStage = (int)( ageFraction * (NUM_PARTICLE_LIFE_STAGES-1) ); + + float lifeStageFraction = ageFraction * ( NUM_PARTICLE_LIFE_STAGES - 1 ) - lifeStage; + + /* + if ( p == 0 ) { + printf( "lifespan = %f ageFraction = %f lifeStage = %d lifeStageFraction = %f\n", _particle[p].lifespan, ageFraction, lifeStage, lifeStageFraction ); } + */ + _particle[p].radius + = _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage ].radius * (1.0f - lifeStageFraction) + + _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage+1].radius * lifeStageFraction; + + _particle[p].color + = _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage ].color * (1.0f - lifeStageFraction) + + _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage+1].color * lifeStageFraction; + Emitter myEmitter = _emitter[_particle[p].emitterIndex]; // apply random jitter + float j = myEmitter.particleAttributes[lifeStage].jitter; _particle[p].velocity += glm::vec3 ( - -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() + -j * ONE_HALF + j * randFloat(), + -j * ONE_HALF + j * randFloat(), + -j * ONE_HALF + j * randFloat() ) * deltaTime; // apply attraction to home position glm::vec3 vectorToHome = myEmitter.position - _particle[p].position; - _particle[p].velocity += vectorToHome * myEmitter.particleAttributes.emitterAttraction * deltaTime; + _particle[p].velocity += vectorToHome * myEmitter.particleAttributes[lifeStage].emitterAttraction * deltaTime; // apply neighbor attraction int neighbor = p + 1; @@ -195,20 +241,20 @@ void ParticleSystem::updateParticle(int p, float 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; + _particle[p].velocity -= vectorToNeighbor * myEmitter.particleAttributes[lifeStage].neighborAttraction * deltaTime; float distanceToNeighbor = glm::length(vectorToNeighbor); if (distanceToNeighbor > 0.0f) { - _particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * myEmitter.particleAttributes.neighborRepulsion * deltaTime; + _particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * myEmitter.particleAttributes[lifeStage].neighborRepulsion * deltaTime; } } // apply tornado force glm::vec3 tornadoDirection = glm::cross(vectorToHome, myEmitter.up); - _particle[p].velocity += tornadoDirection * myEmitter.particleAttributes.tornadoForce * deltaTime; + _particle[p].velocity += tornadoDirection * myEmitter.particleAttributes[lifeStage].tornadoForce * deltaTime; // apply air friction - float drag = 1.0 - myEmitter.particleAttributes.airFriction * deltaTime; + float drag = 1.0 - myEmitter.particleAttributes[lifeStage].airFriction * deltaTime; if (drag < 0.0f) { _particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f); } else { @@ -216,7 +262,7 @@ void ParticleSystem::updateParticle(int p, float deltaTime) { } // apply gravity - _particle[p].velocity -= _upDirection * myEmitter.particleAttributes.gravity * deltaTime; + _particle[p].velocity -= _upDirection * myEmitter.particleAttributes[lifeStage].gravity * deltaTime; // update position by velocity _particle[p].position += _particle[p].velocity; @@ -226,36 +272,66 @@ 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 *= -myEmitter.particleAttributes.bounce; + _particle[p].velocity.y *= -myEmitter.particleAttributes[lifeStage].bounce; } } // collision with sphere - if (myEmitter.particleAttributes.usingCollisionSphere) { - glm::vec3 vectorToSphereCenter = myEmitter.particleAttributes.collisionSpherePosition - _particle[p].position; + if (myEmitter.particleAttributes[lifeStage].usingCollisionSphere) { + glm::vec3 vectorToSphereCenter = myEmitter.particleAttributes[lifeStage].collisionSpherePosition - _particle[p].position; float distanceToSphereCenter = glm::length(vectorToSphereCenter); - float combinedRadius = myEmitter.particleAttributes.collisionSphereRadius + _particle[p].radius; + float combinedRadius = myEmitter.particleAttributes[lifeStage].collisionSphereRadius + _particle[p].radius; if (distanceToSphereCenter < combinedRadius) { if (distanceToSphereCenter > 0.0f){ glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter; - _particle[p].position = myEmitter.particleAttributes.collisionSpherePosition - directionToSphereCenter * combinedRadius; + _particle[p].position = myEmitter.particleAttributes[lifeStage].collisionSpherePosition - directionToSphereCenter * combinedRadius; } } } + + // do this at the end... + _particle[p].age += deltaTime; } 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; + + int lifeStage = 0; + + _emitter[e].particleAttributes[lifeStage].usingCollisionSphere = true; + _emitter[e].particleAttributes[lifeStage].collisionSpherePosition = position; + _emitter[e].particleAttributes[lifeStage].collisionSphereRadius = radius; } +void ParticleSystem::setEmitterBaseParticle(int emitterIndex, bool showing ) { + + _emitter[emitterIndex].baseParticle.alive = true; + _emitter[emitterIndex].baseParticle.emitterIndex = emitterIndex; +} + +void ParticleSystem::setEmitterBaseParticle(int emitterIndex, bool showing, float radius, glm::vec4 color ) { + + _emitter[emitterIndex].baseParticle.alive = true; + _emitter[emitterIndex].baseParticle.emitterIndex = emitterIndex; + _emitter[emitterIndex].baseParticle.radius = radius; + _emitter[emitterIndex].baseParticle.color = color; +} + + void ParticleSystem::render() { // render the emitters - for (unsigned int e = 0; e < _numEmitters; e++) { - if (_emitter[e].showingEmitter) { + for (int e = 0; e < _numEmitters; e++) { + + if (_emitter[e].baseParticle.alive) { + glColor4f(_emitter[e].baseParticle.color.r, _emitter[e].baseParticle.color.g, _emitter[e].baseParticle.color.b, _emitter[e].baseParticle.color.a ); + glPushMatrix(); + glTranslatef(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z); + glutSolidSphere(_emitter[e].baseParticle.radius, 6, 6); + glPopMatrix(); + } + + if (_emitter[e].visible) { renderEmitter(e, 0.2f); } }; @@ -271,19 +347,49 @@ void ParticleSystem::render() { 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(); + if (USE_BILLBOARD_RENDERING) { + glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); + glm::vec3 viewVector = _particle[p].position - cameraPosition; + float distance = glm::length(viewVector); + + if (distance >= 0.0f) { + viewVector /= distance; + glm::vec3 up = glm::vec3(viewVector.y, viewVector.z, viewVector.x); + glm::vec3 right = glm::vec3(viewVector.z, viewVector.x, viewVector.y); + + glm::vec3 p0 = _particle[p].position - right * _particle[p].radius - up * _particle[p].radius; + glm::vec3 p1 = _particle[p].position + right * _particle[p].radius - up * _particle[p].radius; + glm::vec3 p2 = _particle[p].position + right * _particle[p].radius + up * _particle[p].radius; + glm::vec3 p3 = _particle[p].position - right * _particle[p].radius + up * _particle[p].radius; + + glBegin(GL_TRIANGLES); + glVertex3f(p0.x, p0.y, p0.z); + glVertex3f(p1.x, p1.y, p1.z); + glVertex3f(p2.x, p2.y, p2.z); + glEnd(); + + glBegin(GL_TRIANGLES); + glVertex3f(p0.x, p0.y, p0.z); + glVertex3f(p2.x, p2.y, p2.z); + glVertex3f(p3.x, p3.y, p3.z); + glEnd(); + } + } else { + glPushMatrix(); + glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z); + glutSolidSphere(_particle[p].radius, 6, 6); + glPopMatrix(); + + if (SHOW_VELOCITY_TAILS) { + 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(); + } + } } diff --git a/interface/src/ParticleSystem.h b/interface/src/ParticleSystem.h index c94acd03ba..d6563cc1a5 100644 --- a/interface/src/ParticleSystem.h +++ b/interface/src/ParticleSystem.h @@ -11,13 +11,18 @@ #include -const int MAX_PARTICLES = 5000; -const int MAX_EMITTERS = 20; +const int MAX_PARTICLES = 5000; +const int MAX_EMITTERS = 20; +const int NUM_PARTICLE_LIFE_STAGES = 4; +const bool USE_BILLBOARD_RENDERING = false; +const bool SHOW_VELOCITY_TAILS = false; class ParticleSystem { public: struct ParticleAttributes { + float radius; + glm::vec4 color; float bounce; float gravity; float airFriction; @@ -34,31 +39,23 @@ public: 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 emitParticlesNow(int emitterIndex, int numParticles, float radius, glm::vec4 color, glm::vec3 velocity, float lifespan); void simulate(float deltaTime); void render(); + void setEmitterBaseParticle(int emitterIndex, bool showing ); + void setEmitterBaseParticle(int emitterIndex, bool showing, float radius, glm::vec4 color ); - 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 + void setParticleAttributes(int emitterIndex, ParticleAttributes attributes); + void setParticleAttributes(int emitterIndex, int lifeStage, ParticleAttributes attributes); + 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].visible = 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 { bool alive; // is the particle active? glm::vec3 position; // position @@ -70,8 +67,18 @@ private: int emitterIndex; // which emitter created this particle? }; + struct Emitter { + glm::vec3 position; + glm::quat rotation; + glm::vec3 right; // derived from rotation + glm::vec3 up; // derived from rotation + glm::vec3 front; // derived from rotation + bool visible; + Particle baseParticle; // a non-physical particle at the emitter position + ParticleAttributes particleAttributes[NUM_PARTICLE_LIFE_STAGES]; // the attributes of particles emitted from this emitter + }; + glm::vec3 _upDirection; - float _timer; Emitter _emitter[MAX_EMITTERS]; Particle _particle[MAX_PARTICLES]; int _numParticles; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 86895410dd..01907b92f4 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -18,6 +18,8 @@ using namespace std; +static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed + AvatarData::AvatarData(Node* owningNode) : NodeData(owningNode), _handPosition(0,0,0), @@ -127,36 +129,22 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { *destinationBuffer++ = bitItems; // leap hand data - // In order to make the hand data version-robust, hand data packing is just a series of vec3's, - // with conventions. If a client doesn't know the conventions, they can just get the vec3's - // and render them as balls, or ignore them, without crashing or disrupting anyone. - // Current convention: - // Zero or more fingetTip positions, followed by the same number of fingerRoot positions - - const std::vector& fingerTips = _handData->getFingerTips(); - const std::vector& fingerRoots = _handData->getFingerRoots(); - size_t numFingerVectors = fingerTips.size() + fingerRoots.size(); - if (numFingerVectors > 255) - numFingerVectors = 0; // safety. We shouldn't ever get over 255, so consider that invalid. + std::vector fingerVectors; + _handData->encodeRemoteData(fingerVectors); ///////////////////////////////// // Temporarily disable Leap finger sending, as it's causing a crash whenever someone's got a Leap connected - numFingerVectors = 0; + fingerVectors.clear(); ///////////////////////////////// - - *destinationBuffer++ = (unsigned char)numFingerVectors; - - if (numFingerVectors > 0) { - for (size_t i = 0; i < fingerTips.size(); ++i) { - destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerTips[i].x, 4); - destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerTips[i].y, 4); - destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerTips[i].z, 4); - } - for (size_t i = 0; i < fingerRoots.size(); ++i) { - destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerRoots[i].x, 4); - destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerRoots[i].y, 4); - destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerRoots[i].z, 4); - } + if (fingerVectors.size() > 255) + fingerVectors.clear(); // safety. We shouldn't ever get over 255, so consider that invalid. + + *destinationBuffer++ = (unsigned char)fingerVectors.size(); + + for (size_t i = 0; i < fingerVectors.size(); ++i) { + destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerVectors[i].x, fingerVectorRadix); + destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerVectors[i].y, fingerVectorRadix); + destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, fingerVectors[i].z, fingerVectorRadix); } // skeleton joints @@ -263,25 +251,16 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { // leap hand data if (sourceBuffer - startPosition < numBytes) // safety check { - std::vector fingerTips; - std::vector fingerRoots; unsigned int numFingerVectors = *sourceBuffer++; - unsigned int numFingerTips = numFingerVectors / 2; - unsigned int numFingerRoots = numFingerVectors - numFingerTips; - fingerTips.resize(numFingerTips); - fingerRoots.resize(numFingerRoots); - for (size_t i = 0; i < numFingerTips; ++i) { - sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerTips[i].x), 4); - sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerTips[i].y), 4); - sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerTips[i].z), 4); + if (numFingerVectors > 0) { + std::vector fingerVectors(numFingerVectors); + for (size_t i = 0; i < numFingerVectors; ++i) { + sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerVectors[i].x), fingerVectorRadix); + sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerVectors[i].y), fingerVectorRadix); + sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerVectors[i].z), fingerVectorRadix); + } + _handData->decodeRemoteData(fingerVectors); } - for (size_t i = 0; i < numFingerRoots; ++i) { - sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].x), 4); - sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].y), 4); - sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].z), 4); - } - _handData->setFingerTips(fingerTips); - _handData->setFingerRoots(fingerRoots); } // skeleton joints diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 986d030a3c..32e83b352d 100755 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -9,7 +9,74 @@ #include "HandData.h" HandData::HandData(AvatarData* owningAvatar) : + _basePosition(0.0f, 0.0f, 0.0f), + _baseOrientation(0.0f, 0.0f, 0.0f, 1.0f), _owningAvatarData(owningAvatar) { - + for (int i = 0; i < 2; ++i) { + _palms.push_back(PalmData(this)); + } } + +PalmData::PalmData(HandData* owningHandData) : +_rawPosition(0, 0, 0), +_rawNormal(0, 1, 0), +_isActive(false), +_owningHandData(owningHandData) +{ + for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) { + _fingers.push_back(FingerData(this, owningHandData)); + } +} + +FingerData::FingerData(PalmData* owningPalmData, HandData* owningHandData) : +_tipRawPosition(0, 0, 0), +_rootRawPosition(0, 0, 0), +_isActive(false), +_owningPalmData(owningPalmData), +_owningHandData(owningHandData) +{ +} + +void HandData::encodeRemoteData(std::vector& fingerVectors) { + fingerVectors.clear(); + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + fingerVectors.push_back(palm.getRawPosition()); + fingerVectors.push_back(palm.getRawNormal()); + for (size_t f = 0; f < palm.getNumFingers(); ++f) { + FingerData& finger = palm.getFingers()[f]; + if (finger.isActive()) { + fingerVectors.push_back(finger.getTipRawPosition()); + fingerVectors.push_back(finger.getRootRawPosition()); + } + else { + fingerVectors.push_back(glm::vec3(0,0,0)); + fingerVectors.push_back(glm::vec3(0,0,0)); + } + } + } +} + +void HandData::decodeRemoteData(const std::vector& fingerVectors) { + size_t vectorIndex = 0; + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + // If a palm is active, there will be + // 1 vector for its position + // 1 vector for normal + // 10 vectors for fingers (5 tip/root pairs) + bool palmActive = fingerVectors.size() >= i * 12; + palm.setActive(palmActive); + if (palmActive) { + palm.setRawPosition(fingerVectors[vectorIndex++]); + palm.setRawNormal(fingerVectors[vectorIndex++]); + for (size_t f = 0; f < NUM_FINGERS_PER_HAND; ++f) { + FingerData& finger = palm.getFingers()[i]; + finger.setRawTipPosition(fingerVectors[vectorIndex++]); + finger.setRawRootPosition(fingerVectors[vectorIndex++]); + } + } + } +} + diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 50a4843153..65b32ff5c6 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -13,8 +13,13 @@ #include #include +#include class AvatarData; +class FingerData; +class PalmData; + +const int NUM_FINGERS_PER_HAND = 5; class HandData { public: @@ -22,26 +27,79 @@ public: // These methods return the positions in Leap-relative space. // To convert to world coordinates, use Hand::leapPositionToWorldPosition. - const std::vector& getFingerTips() const { return _fingerTips; } - const std::vector& getFingerRoots() const { return _fingerRoots; } - const std::vector& getHandPositions() const { return _handPositions; } - const std::vector& getHandNormals() const { return _handNormals; } - void setFingerTips(const std::vector& fingerTips) { _fingerTips = fingerTips; } - void setFingerRoots(const std::vector& fingerRoots) { _fingerRoots = fingerRoots; } - void setHandPositions(const std::vector& handPositons) { _handPositions = handPositons; } - void setHandNormals(const std::vector& handNormals) { _handNormals = handNormals; } + + // position conversion + glm::vec3 leapPositionToWorldPosition(const glm::vec3& leapPosition) { + const float unitScale = 0.001; // convert mm to meters + return _basePosition + _baseOrientation * (leapPosition * unitScale); + } + glm::vec3 leapDirectionToWorldDirection(const glm::vec3& leapDirection) { + return glm::normalize(_baseOrientation * leapDirection); + } + + std::vector& getPalms() { return _palms; } + size_t getNumPalms() { return _palms.size(); } + + // Use these for sending and receiving hand data + void encodeRemoteData(std::vector& fingerVectors); + void decodeRemoteData(const std::vector& fingerVectors); friend class AvatarData; protected: - std::vector _fingerTips; - std::vector _fingerRoots; - std::vector _handPositions; - std::vector _handNormals; + glm::vec3 _basePosition; // Hands are placed relative to this + glm::quat _baseOrientation; // Hands are placed relative to this AvatarData* _owningAvatarData; + std::vector _palms; private: // privatize copy ctor and assignment operator so copies of this object cannot be made HandData(const HandData&); HandData& operator= (const HandData&); }; +class FingerData { +public: + FingerData(PalmData* owningPalmData, HandData* owningHandData); + + glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipRawPosition); } + glm::vec3 getRootPosition() const { return _owningHandData->leapPositionToWorldPosition(_rootRawPosition); } + const glm::vec3& getTipRawPosition() const { return _tipRawPosition; } + const glm::vec3& getRootRawPosition() const { return _rootRawPosition; } + bool isActive() const { return _isActive; } + + void setActive(bool active) { _isActive = active; } + void setRawTipPosition(const glm::vec3& pos) { _tipRawPosition = pos; } + void setRawRootPosition(const glm::vec3& pos) { _rootRawPosition = pos; } + +private: + glm::vec3 _tipRawPosition; + glm::vec3 _rootRawPosition; + bool _isActive; // This has current valid data + PalmData* _owningPalmData; + HandData* _owningHandData; +}; + +class PalmData { +public: + PalmData(HandData* owningHandData); + glm::vec3 getPosition() const { return _owningHandData->leapPositionToWorldPosition(_rawPosition); } + glm::vec3 getNormal() const { return _owningHandData->leapDirectionToWorldDirection(_rawNormal); } + const glm::vec3& getRawPosition() const { return _rawPosition; } + const glm::vec3& getRawNormal() const { return _rawNormal; } + bool isActive() const { return _isActive; } + + std::vector& getFingers() { return _fingers; } + size_t getNumFingers() { return _fingers.size(); } + + void setActive(bool active) { _isActive = active; } + void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } + void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; } + +private: + std::vector _fingers; + glm::vec3 _rawPosition; + glm::vec3 _rawNormal; + bool _isActive; // This has current valid data + HandData* _owningHandData; +}; + #endif /* defined(__hifi__HandData__) */ diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 7b4a0509f3..821a2d0247 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -15,7 +15,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { switch (type) { case PACKET_TYPE_HEAD_DATA: - return 1; + return 2; break; default: return 0;