From 37b088ebfa5d457b66148fc423b4ee6339c7ca60 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 6 Mar 2014 12:38:07 -0800 Subject: [PATCH] Hands can collide with avatar head again --- interface/src/avatar/Avatar.cpp | 31 ++++++++------- interface/src/avatar/Avatar.h | 7 ++-- interface/src/avatar/Hand.cpp | 33 ++++------------ interface/src/avatar/Hand.h | 4 -- interface/src/avatar/Head.cpp | 35 +++++++---------- interface/src/avatar/Head.h | 3 +- interface/src/avatar/MyAvatar.cpp | 52 ++++++++++++++------------ interface/src/avatar/SkeletonModel.cpp | 26 ++++++++++--- interface/src/avatar/SkeletonModel.h | 5 ++- 9 files changed, 96 insertions(+), 100 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3963a84724..9c27d81dcb 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -489,13 +489,19 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet //return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); } -bool Avatar::findCollisions(const QVector& shapes, CollisionList& collisions) { +void Avatar::updateShapePositions() { _skeletonModel.updateShapePositions(); - bool collided = _skeletonModel.findCollisions(shapes, collisions); - Model& headModel = getHead()->getFaceModel(); headModel.updateShapePositions(); - collided = headModel.findCollisions(shapes, collisions); +} + +bool Avatar::findCollisions(const QVector& shapes, CollisionList& collisions) { + // TODO: Andrew to fix: also collide against _skeleton + //bool collided = _skeletonModel.findCollisions(shapes, collisions); + + Model& headModel = getHead()->getFaceModel(); + //collided = headModel.findCollisions(shapes, collisions) || collided; + bool collided = headModel.findCollisions(shapes, collisions); return collided; } @@ -710,15 +716,14 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const { return false; } -void Avatar::applyCollision(CollisionInfo& collision) { - if (!collision._data || collision._type != MODEL_COLLISION) { - return; - } - // TODO: make skeleton also respond to collisions - Model* model = static_cast(collision._data); - if (model == &(getHead()->getFaceModel())) { - getHead()->applyCollision(collision); - } +void Avatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { + // ATM we only support collision with head + getHead()->applyCollision(contactPoint, penetration); +} + +float Avatar::getBoundingRadius() const { + // TODO: also use head model when computing the avatar's bounding radius + return _skeletonModel.getBoundingRadius(); } float Avatar::getPelvisFloatingHeight() const { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 1bbf7df039..2aa7eac737 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -138,10 +138,11 @@ public: /// \return true if we expect the avatar would move as a result of the collision bool collisionWouldMoveAvatar(CollisionInfo& collision) const; - /// \param collision a data structure for storing info about collisions against Models - void applyCollision(CollisionInfo& collision); + void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); - float getBoundingRadius() const { return 0.5f * getSkeletonHeight(); } + /// \return bounding radius of avatar + virtual float getBoundingRadius() const; + void updateShapePositions(); public slots: void updateCollisionFlags(); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index ce2f2a242e..a154e76a05 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -145,10 +145,7 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { jointIndices[0] = skeletonModel.getLeftHandJointIndex(); jointIndices[1] = skeletonModel.getRightHandJointIndex(); - palmIndices[1] = -1; // adebug temporarily disable right hand - jointIndices[1] = -1; // adebug temporarily disable right hand - - for (size_t i = 0; i < 1; i++) { + for (size_t i = 0; i < 2; i++) { int palmIndex = palmIndices[i]; int jointIndex = jointIndices[i]; if (palmIndex == -1 || jointIndex == -1) { @@ -165,9 +162,8 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { handCollisions.clear(); QVector shapes; skeletonModel.getHandShapes(jointIndex, shapes); - bool collided = isMyHand ? avatar->findCollisions(shapes, handCollisions) : avatar->findCollisions(shapes, handCollisions); - if (collided) { - //if (avatar->findCollisions(shapes, handCollisions)) { + + if (avatar->findCollisions(shapes, handCollisions)) { glm::vec3 averagePenetration; glm::vec3 averageContactPoint; for (int j = 0; j < handCollisions.size(); ++j) { @@ -180,13 +176,14 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { // our hand against other avatar // for now we resolve it to test shapes/collisions // TODO: only partially resolve this penetration - palm.addToPosition(-averagePenetration); + palm.addToPenetration(averagePenetration); } else { // someone else's hand against MyAvatar // TODO: submit collision info to MyAvatar which should lean accordingly averageContactPoint /= float(handCollisions.size()); + avatar->applyCollision(averageContactPoint, averagePenetration); } - } + } } } @@ -218,7 +215,7 @@ void Hand::collideAgainstOurself() { totalPenetration = addPenetrations(totalPenetration, collision->_penetration); } // resolve penetration - palm.addToPosition(-totalPenetration); + palm.addToPenetration(totalPenetration); } } } @@ -428,19 +425,3 @@ void Hand::renderLeapHands(bool isMine) { glPopMatrix(); } - -void Hand::setLeapHands(const std::vector& handPositions, - const std::vector& 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); - } - } -} - diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index a1b1875424..6f64f62c9c 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -79,10 +79,6 @@ private: float _collisionAge; float _collisionDuration; - // private methods - void setLeapHands(const std::vector& handPositions, - const std::vector& handNormals); - void renderLeapHands(bool isMine); void renderLeapFingerTrails(); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 74bf983eb1..7e9d549509 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -213,32 +213,25 @@ float Head::getTweakedRoll() const { return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } -void Head::applyCollision(CollisionInfo& collision) { - // HACK: the collision proxies for the FaceModel are bad. As a temporary workaround - // we collide against a hard coded collision proxy. - // TODO: get a better collision proxy here. - const float HEAD_RADIUS = 0.15f; - const glm::vec3 HEAD_CENTER = _position; - - // collide the contactPoint against the collision proxy to obtain a new penetration - // NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere) - glm::vec3 penetration; - if (findPointSpherePenetration(collision._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) { - // compute lean angles - Avatar* owningAvatar = static_cast(_owningAvatar); - glm::quat bodyRotation = owningAvatar->getOrientation(); - glm::vec3 neckPosition; - if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { - glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); - glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); +void Head::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { + // compute lean angles + Avatar* owningAvatar = static_cast(_owningAvatar); + glm::quat bodyRotation = owningAvatar->getOrientation(); + glm::vec3 neckPosition; + if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { + glm::vec3 yAxis = bodyRotation * glm::vec3(0.f, 1.f, 0.f); + glm::vec3 leverArm = _position - neckPosition; + if (glm::dot(leverArm, yAxis) > 0.f) { float neckLength = glm::length(_position - neckPosition); if (neckLength > 0.f) { - float forward = glm::dot(collision._penetration, zAxis) / neckLength; - float sideways = - glm::dot(collision._penetration, xAxis) / neckLength; + glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); + glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); + float forward = glm::dot(penetration, zAxis) / neckLength; + float sideways = - glm::dot(penetration, xAxis) / neckLength; addLean(sideways, forward); } } - } + } } void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 7e7a96a3a7..d127912555 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -78,7 +78,8 @@ public: virtual float getTweakedYaw() const; virtual float getTweakedRoll() const; - void applyCollision(CollisionInfo& collisionInfo); + // move the head to avoid given collision info + void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); private: // disallow copies of the Head, copy of owning Avatar is disallowed too diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index de07ed55fa..da08f7a5ee 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -182,26 +182,6 @@ void MyAvatar::simulate(float deltaTime) { _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime); } - if (_collisionFlags != 0) { - Camera* myCamera = Application::getInstance()->getCamera(); - - float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE; - if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) { - radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f)); - radius *= COLLISION_RADIUS_SCALAR; - } - - if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) { - updateCollisionWithEnvironment(deltaTime, radius); - } - if (_collisionFlags & COLLISION_GROUP_VOXELS) { - updateCollisionWithVoxels(deltaTime, radius); - } - if (_collisionFlags & COLLISION_GROUP_AVATARS) { - updateCollisionWithAvatars(deltaTime); - } - } - // add thrust to velocity _velocity += _thrust * deltaTime; @@ -318,6 +298,30 @@ void MyAvatar::simulate(float deltaTime) { // Zero thrust out now that we've added it to velocity in this frame _thrust = glm::vec3(0, 0, 0); + + // now that we're done stepping the avatar forward in time, compute new collisions + if (_collisionFlags != 0) { + Camera* myCamera = Application::getInstance()->getCamera(); + + float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE; + if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) { + radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f)); + radius *= COLLISION_RADIUS_SCALAR; + } + + if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) { + updateCollisionWithEnvironment(deltaTime, radius); + } + if (_collisionFlags & COLLISION_GROUP_VOXELS) { + updateCollisionWithVoxels(deltaTime, radius); + } + if (_collisionFlags & COLLISION_GROUP_AVATARS) { + updateCollisionWithAvatars(deltaTime); + } + } + + // resolve collision results + _skeletonModel.syncToPalms(); // consider updating our billboard maybeUpdateBillboard(); @@ -919,14 +923,17 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // no need to compute a bunch of stuff if we have one or fewer avatars return; } + updateShapePositions(); float myBoundingRadius = getBoundingRadius(); + /* TODO: Andrew to fix Avatar-Avatar body collisions // HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis // TODO: make the collision work without assuming avatar orientation Extents myStaticExtents = _skeletonModel.getStaticExtents(); glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum; float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z); float myCapsuleHeight = staticScale.y; + */ CollisionInfo collisionInfo; foreach (const AvatarSharedPointer& avatarPointer, avatars) { @@ -935,16 +942,13 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // don't collide with ourselves continue; } + avatar->updateShapePositions(); float distance = glm::length(_position - avatar->getPosition()); if (_distanceToNearestAvatar > distance) { _distanceToNearestAvatar = distance; } float theirBoundingRadius = avatar->getBoundingRadius(); if (distance < myBoundingRadius + theirBoundingRadius) { - _skeletonModel.updateShapePositions(); - Model& headModel = getHead()->getFaceModel(); - headModel.updateShapePositions(); - /* TODO: Andrew to fix Avatar-Avatar body collisions Extents theirStaticExtents = _skeletonModel.getStaticExtents(); glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 7c7202566a..cf542a41d5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -62,6 +62,19 @@ void SkeletonModel::simulate(float deltaTime, bool delayLoad) { } } +void SkeletonModel::syncToPalms() { + int leftPalmIndex, rightPalmIndex; + Hand* hand = _owningAvatar->getHand(); + hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (leftPalmIndex != -1 && rightPalmIndex != -1) { + applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, + hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, + hand->getPalms()[rightPalmIndex]); + } +} + bool SkeletonModel::render(float alpha) { if (_jointStates.isEmpty()) { @@ -154,7 +167,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin // no point in continuing if there are no fingers if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) { - stretchArm(jointIndex, palm.getPosition()); + stretchArm(jointIndex, palm); return; } @@ -173,7 +186,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true); } - stretchArm(jointIndex, palm.getPosition()); + stretchArm(jointIndex, palm); } void SkeletonModel::updateJointState(int index) { @@ -196,7 +209,8 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const glm::angleAxis(-_owningAvatar->getHead()->getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation; } -void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) { +void SkeletonModel::stretchArm(int jointIndex, PalmData& palm) { +// const glm::vec3& position) { // find out where the hand is pointing glm::quat handRotation; getJointRotation(jointIndex, handRotation, true); @@ -212,11 +226,11 @@ void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) { glm::quat elbowRotation; getJointRotation(joint.parentIndex, elbowRotation, true); applyRotationDelta(joint.parentIndex, rotationBetween(elbowRotation * forwardVector, handVector), false); - + // set position according to normal length float scale = extractUniformScale(_scale); - glm::vec3 handPosition = position - _translation; - glm::vec3 elbowPosition = handPosition - handVector * joint.distanceToParent * scale; + palm.resolvePenetrations(); + glm::vec3 elbowPosition = palm.getPosition() - _translation; - handVector * (joint.distanceToParent * scale); // set shoulder orientation to point to elbow const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 3d95d805ea..1c6091a5cb 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -23,12 +23,13 @@ public: SkeletonModel(Avatar* owningAvatar); void simulate(float deltaTime, bool delayLoad = false); + void syncToPalms(); bool render(float alpha); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes void getHandShapes(int jointIndex, QVector& shapes) const; - + protected: void applyHandPosition(int jointIndex, const glm::vec3& position); @@ -45,7 +46,7 @@ private: /// Using the current position and rotation of the identified (hand) joint, computes a /// reasonable stretched configuration for the connected arm. - void stretchArm(int jointIndex, const glm::vec3& position); + void stretchArm(int jointIndex, PalmData& palm); Avatar* _owningAvatar; };