diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6c8a177c99..3f585d8da4 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -355,6 +355,55 @@ bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float pene return false; } +bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { + // TODO: provide an early exit using bounding sphere of entire avatar + + const HandData* handData = getHandData(); + if (handData) { + int jointIndices[2] = { _skeletonModel.getLeftHandJointIndex(), _skeletonModel.getRightHandJointIndex() }; + for (int i = 0; i < 2; i++) { + const PalmData* palm = handData->getPalm(i); + if (palm) { + // create a disk collision proxy where the hand is + glm::vec3 fingerAxis(0.f); + for (size_t f = 0; f < palm->getNumFingers(); ++f) { + const FingerData& finger = (palm->getFingers())[f]; + if (finger.isActive()) { + // compute finger axis + glm::vec3 fingerTip = finger.getTipPosition(); + glm::vec3 fingerRoot = finger.getRootPosition(); + fingerAxis = glm::normalize(fingerTip - fingerRoot); + break; + } + } + glm::vec3 handPosition; + if (i == 0) { + _skeletonModel.getLeftHandPosition(handPosition); + } + else { + _skeletonModel.getRightHandPosition(handPosition); + } + glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis; + glm::vec3 diskNormal = palm->getNormal(); + + // collide against the disk + if (findSphereDiskPenetration(sphereCenter, sphereRadius, + diskCenter, HAND_PADDLE_RADIUS, diskNormal, collision._penetration)) { + collision._addedVelocity = palm->getVelocity(); + return true; + } + } + } + } + + if (_skeletonModel.findSpherePenetration(sphereCenter, sphereRadius, collision._penetration)) { + collision._penetration /= (float)(TREE_SCALE); + collision._addedVelocity = getVelocity(); + return true; + } + return false; +} + int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { // change in position implies movement glm::vec3 oldPosition = _position; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 91f5e75ffc..e02931d2ec 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -167,6 +167,13 @@ public: bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, int skeletonSkipIndex = -1) const; + /// Checks for collision between the a sphere and the avatar. + /// \param collisionCenter the center of the penetration test sphere + /// \param collisionRadius the radius of the penetration test sphere + /// \param collision[out] the details of the collision point + /// \return whether or not the sphere collided + virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision); + virtual int parseData(unsigned char* sourceBuffer, int numBytes); static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 2784e0a115..b56e2c0c3d 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -616,23 +616,37 @@ void Hand::renderLeapHands(bool isMine) { } } - // Draw the palms - for (size_t i = 0; i < getNumPalms(); ++i) { - PalmData& palm = getPalms()[i]; - if (palm.isActive()) { - const float palmThickness = 0.02f; - if (palm.getIsCollidingWithPalm()) { - glColor4f(1, 0, 0, 0.50); - } else { - glColor4f(handColor.r, handColor.g, handColor.b, 0.25); + // Draw the hand paddles + int MAX_NUM_PADDLES = 2; // one for left and one for right + glColor4f(handColor.r, handColor.g, handColor.b, 0.3f); + for (int i = 0; i < MAX_NUM_PADDLES; i++) { + const PalmData* palm = getPalm(i); + if (palm) { + // compute finger axis + glm::vec3 fingerAxis(0.f); + for (size_t f = 0; f < palm->getNumFingers(); ++f) { + const FingerData& finger = (palm->getFingers())[f]; + if (finger.isActive()) { + glm::vec3 fingerTip = finger.getTipPosition(); + glm::vec3 fingerRoot = finger.getRootPosition(); + fingerAxis = glm::normalize(fingerTip - fingerRoot); + break; + } } - glm::vec3 tip = palm.getPosition(); - glm::vec3 root = palm.getPosition() + palm.getNormal() * palmThickness; - const float radiusA = 0.05f; - const float radiusB = 0.03f; - Avatar::renderJointConnectingCone(root, tip, radiusA, radiusB); + // compute paddle position + glm::vec3 handPosition; + if (i == SIXENSE_CONTROLLER_ID_LEFT_HAND) { + _owningAvatar->getSkeletonModel().getLeftHandPosition(handPosition); + } else if (i == SIXENSE_CONTROLLER_ID_RIGHT_HAND) { + _owningAvatar->getSkeletonModel().getRightHandPosition(handPosition); + } + glm::vec3 tip = handPosition + HAND_PADDLE_OFFSET * fingerAxis; + glm::vec3 root = tip + palm->getNormal() * HAND_PADDLE_THICKNESS; + // render a very shallow cone as the paddle + Avatar::renderJointConnectingCone(root, tip, HAND_PADDLE_RADIUS, 0.f); } } + glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 8a672fc1d9..bc671d1926 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -30,6 +30,10 @@ class Avatar; class ProgramObject; +const float HAND_PADDLE_OFFSET = 0.1f; +const float HAND_PADDLE_THICKNESS = 0.05f; +const float HAND_PADDLE_RADIUS = 0.15f; + class Hand : public HandData { public: Hand(Avatar* owningAvatar); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1cfdfbca88..c8fa1e3f14 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -8,6 +8,8 @@ #include +#include + #include "Application.h" #include "Avatar.h" #include "Menu.h" diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 776eb29e3b..e5ca5c7623 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -9,7 +9,6 @@ #ifndef __interface__SkeletonModel__ #define __interface__SkeletonModel__ -#include #include "renderer/Model.h" diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 3eb1256f0f..3509357b0c 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -488,6 +488,22 @@ bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePos return getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && getJointPosition(geometry.rightEyeJointIndex, secondEyePosition); } + +bool Model::getLeftHandPosition(glm::vec3& position) const { + return getJointPosition(getLeftHandJointIndex(), position); +} + +bool Model::getLeftHandRotation(glm::quat& rotation) const { + return getJointRotation(getLeftHandJointIndex(), rotation); +} + +bool Model::getRightHandPosition(glm::vec3& position) const { + return getJointPosition(getRightHandJointIndex(), position); +} + +bool Model::getRightHandRotation(glm::quat& rotation) const { + return getJointRotation(getRightHandJointIndex(), rotation); +} bool Model::setLeftHandPosition(const glm::vec3& position) { return setJointPosition(getLeftHandJointIndex(), position); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index fc3a0687b8..ae4303caea 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -82,6 +82,22 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + /// Retrieve the position of the left hand + /// \return true whether or not the position was found + bool getLeftHandPosition(glm::vec3& position) const; + + /// Retrieve the rotation of the left hand + /// \return true whether or not the rotation was found + bool getLeftHandRotation(glm::quat& rotation) const; + + /// Retrieve the position of the right hand + /// \return true whether or not the position was found + bool getRightHandPosition(glm::vec3& position) const; + + /// Retrieve the rotation of the right hand + /// \return true whether or not the rotation was found + bool getRightHandRotation(glm::quat& rotation) const; + /// Sets the position of the left hand using inverse kinematics. /// \return whether or not the left hand joint was found bool setLeftHandPosition(const glm::vec3& position); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c417bfb534..daf299380b 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -20,9 +20,10 @@ #include #include +#include #include - #include + #include "HeadData.h" #include "HandData.h" @@ -118,6 +119,10 @@ public: /// \return whether or not the sphere penetrated virtual bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, int skeletonSkipIndex = -1) const { return false; } + + virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { + return false; + } protected: QUuid _uuid; diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index e8b2c97ff0..7b0be95abe 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -38,8 +38,17 @@ PalmData& HandData::addNewPalm() { return _palms.back(); } -const int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0; -const int SIXENSE_CONTROLLER_ID_RIGHT_HAND = 1; +const PalmData* HandData::getPalm(int sixSenseID) const { + // the palms are not necessarily added in left-right order, + // so we have to search for the right SixSenseID + for (int i = 0; i < _palms.size(); i++) { + const PalmData* palm = &(_palms[i]); + if (palm->getSixenseID() == sixSenseID) { + return palm->isActive() ? palm : NULL; + } + } + return NULL; +} void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { leftPalmIndex = -1; diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index a0c8fed980..1aa60da927 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -35,6 +35,9 @@ const int BUTTON_FWD = 128; const float LEAP_UNIT_SCALE = 0.001f; ///< convert mm to meters +const int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0; +const int SIXENSE_CONTROLLER_ID_RIGHT_HAND = 1; + class HandData { public: HandData(AvatarData* owningAvatar); @@ -55,6 +58,7 @@ public: std::vector& getPalms() { return _palms; } const std::vector& getPalms() const { return _palms; } + const PalmData* getPalm(int sixSenseID) const; size_t getNumPalms() const { return _palms.size(); } PalmData& addNewPalm(); diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 0bf5db90f0..78b1402461 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -136,15 +137,14 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { - // particles that are in hand, don't collide with other avatar parts + // particles that are in hand, don't collide with avatars if (particle->getInHand()) { return; } - //printf("updateCollisionWithAvatars()...\n"); glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); float radius = particle->getRadius() * (float)(TREE_SCALE); - const float ELASTICITY = 0.4f; + const float ELASTICITY = 0.95f; const float DAMPING = 0.0f; const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; @@ -153,77 +153,40 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // first check the selfAvatar if set... if (_selfAvatar) { AvatarData* avatar = (AvatarData*)_selfAvatar; - //printf("updateCollisionWithAvatars()..._selfAvatar=%p\n", avatar); - - // check hands... - const HandData* handData = avatar->getHandData(); - - // TODO: combine hand and collision check into one. Note: would need to supply - // CollisionInfo class rather than just vec3 (penetration) so we can get back - // added velocity. - - if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) { - // TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart. - // apply a hard collision when ball collides with hand - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - - // determine if the palm that collided was moving, if so, then we add that palm velocity as well... - glm::vec3 addedVelocity = NO_ADDED_VELOCITY; - if (collidingPalm) { - glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)(TREE_SCALE); - //printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z); - addedVelocity = palmVelocity; + CollisionInfo collision; + if (avatar->findSphereCollision(center, radius, collision)) { + if (glm::dot(particle->getVelocity(), collision._addedVelocity) < 0.f) { + // only collide when particle and collision point are moving toward each other + collision._penetration /= (float)(TREE_SCALE); + collision._addedVelocity /= (float)(TREE_SCALE); + updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY); + applyHardCollision(particle, collision._penetration, ELASTICITY, DAMPING, collision._addedVelocity); } - - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); - } else if (avatar->findSpherePenetration(center, radius, penetration)) { - // apply hard collision when particle collides with avatar - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - glm::vec3 addedVelocity = avatar->getVelocity(); - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); } } // loop through all the other avatars for potential interactions... - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { //qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n"; if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { - // TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart. AvatarData* avatar = static_cast(node->getLinkedData()); - //printf("updateCollisionWithAvatars()...avatar=%p\n", avatar); - - // check hands... - const HandData* handData = avatar->getHandData(); - - if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) { - // apply a hard collision when ball collides with hand - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - - // determine if the palm that collided was moving, if so, then we add that palm velocity as well... - glm::vec3 addedVelocity = NO_ADDED_VELOCITY; - if (collidingPalm) { - glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)(TREE_SCALE); - //printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z); - addedVelocity = palmVelocity; + + CollisionInfo collision; + if (avatar->findSphereCollision(center, radius, collision)) { + if (glm::dot(particle->getVelocity(), collision._addedVelocity) < 0.f) { + // only collide when particle and collision point are moving toward each other + collision._penetration /= (float)(TREE_SCALE); + collision._addedVelocity /= (float)(TREE_SCALE); + updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY); + applyHardCollision(particle, collision._penetration, ELASTICITY, DAMPING, collision._addedVelocity); } - - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); - - } else if (avatar->findSpherePenetration(center, radius, penetration)) { - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - glm::vec3 addedVelocity = avatar->getVelocity(); - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); } } } } +// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, float elasticity, float damping, const glm::vec3& addedVelocity) { // diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index 920b89c053..13766a0264 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -61,4 +61,4 @@ private: AvatarData* _selfAvatar; }; -#endif /* defined(__hifi__ParticleCollisionSystem__) */ \ No newline at end of file +#endif /* defined(__hifi__ParticleCollisionSystem__) */ diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h new file mode 100644 index 0000000000..4f46a6bfd3 --- /dev/null +++ b/libraries/shared/src/CollisionInfo.h @@ -0,0 +1,26 @@ +// +// CollisionInfo.h +// hifi +// +// Created by Andrew Meadows on 2014.01.13 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__CollisionInfo__ +#define __hifi__CollisionInfo__ + +#include + +class CollisionInfo { +public: + CollisionInfo() : _penetration(0.f), _addedVelocity(0.f) { } + ~CollisionInfo() {} + + //glm::vec3 _point; + //glm::vec3 _normal; + glm::vec3 _penetration; + glm::vec3 _addedVelocity; +}; + + +#endif /* defined(__hifi__CollisionInfo__) */ diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 5d6e216ad5..2627a9035a 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -114,6 +114,30 @@ bool findSpherePlanePenetration(const glm::vec3& sphereCenter, float sphereRadiu return false; } +bool findSphereDiskPenetration(const glm::vec3& sphereCenter, float sphereRadius, + const glm::vec3& diskCenter, float diskRadius, const glm::vec3& diskNormal, + glm::vec3& penetration) { + glm::vec3 localCenter = sphereCenter - diskCenter; + float verticalDistance = glm::dot(localCenter, diskNormal); + + + if (abs(verticalDistance) < sphereRadius) { + // sphere hit the plane, but does it hit the disk? + // Note: this algorithm ignores edge hits. + glm::vec3 verticalOffset = verticalDistance * diskNormal; + if (glm::length(localCenter - verticalOffset) < diskRadius) { + // yes, hit the disk + penetration = (sphereRadius - abs(verticalDistance)) * diskNormal; + if (verticalDistance < 0.f) { + // hit the backside of the disk, so negate penetration vector + penetration *= -1.f; + } + return true; + } + } + return false; +} + bool findCapsuleSpherePenetration(const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius, const glm::vec3& sphereCenter, float sphereRadius, glm::vec3& penetration) { if (findSphereCapsulePenetration(sphereCenter, sphereRadius, diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 2b782b43ae..59ad055445 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -47,6 +47,17 @@ bool findSphereCapsuleConePenetration(const glm::vec3& sphereCenter, float spher bool findSpherePlanePenetration(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec4& plane, glm::vec3& penetration); +/// Computes the penetration between a sphere and a disk. +/// \param sphereCenter center of sphere +/// \param sphereRadius radius of sphere +/// \param diskCenter center of disk +/// \param diskRadius radius of disk +/// \param diskNormal normal of disk plan +/// \return true if sphere touches disk (does not handle collisions with disk edge) +bool findSphereDiskPenetration(const glm::vec3& sphereCenter, float sphereRadius, + const glm::vec3& diskCenter, float diskRadius, const glm::vec3& diskNormal, + glm::vec3& penetration); + bool findCapsuleSpherePenetration(const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius, const glm::vec3& sphereCenter, float sphereRadius, glm::vec3& penetration);