From c0e02107c56ac6d68f11d0c2ba44c6f09c6b142b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 23 Jul 2014 16:51:26 -0700 Subject: [PATCH 1/6] minor cleanup --- libraries/shared/src/Ragdoll.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 59c1291725..682f0f8dae 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -28,11 +28,7 @@ public: void applyAccumulatedDelta(); glm::vec3 getAccumulatedDelta() const { - glm::vec3 foo(0.0f); - if (_numDeltas > 0) { - foo = _accumulatedDelta / (float)_numDeltas; - } - return foo; + return (_numDeltas > 0) ? _accumulatedDelta / (float)_numDeltas : glm::vec3(0.0f); } glm::vec3 _position; From b62f04e9fe498e411ac448307f89c475571255d5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 23 Jul 2014 16:51:53 -0700 Subject: [PATCH 2/6] check for nan's coming out of rotationBetween() --- libraries/shared/src/SharedUtil.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index b5be502ed5..e795ea746c 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -733,6 +733,13 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { } } else { axis = glm::normalize(glm::cross(v1, v2)); + // It is possible for axis to be nan even when angle is not less than EPSILON. + // For example when angle is small but not tiny but v1 and v2 and have very short lengths. + if (glm::isnan(glm::dot(axis, axis))) { + // set angle and axis to values that will generate an identity rotation + angle = 0.0f; + axis = glm::vec3(1.0f, 0.0f, 0.0f); + } } return glm::angleAxis(angle, axis); } From 72c74984540a6f6bc593be7136871e9d6de06d28 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 23 Jul 2014 16:56:40 -0700 Subject: [PATCH 3/6] add JointState::getDistanceToParent() --- interface/src/renderer/JointState.cpp | 4 ++++ interface/src/renderer/JointState.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 865036df8e..26fa29c4a9 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -20,6 +20,7 @@ JointState::JointState() : _animationPriority(0.0f), _positionInParentFrame(0.0f), + _distanceToParent(0.0f), _fbxJoint(NULL), _constraint(NULL) { } @@ -29,6 +30,7 @@ JointState::JointState(const JointState& other) : _constraint(NULL) { _rotation = other._rotation; _rotationInConstrainedFrame = other._rotationInConstrainedFrame; _positionInParentFrame = other._positionInParentFrame; + _distanceToParent = other._distanceToParent; _animationPriority = other._animationPriority; _fbxJoint = other._fbxJoint; // DO NOT copy _constraint @@ -72,6 +74,7 @@ void JointState::copyState(const JointState& state) { _rotation = extractRotation(_transform); _rotationInConstrainedFrame = state._rotationInConstrainedFrame; _positionInParentFrame = state._positionInParentFrame; + _distanceToParent = state._distanceToParent; _visibleTransform = state._visibleTransform; _visibleRotation = extractRotation(_visibleTransform); @@ -82,6 +85,7 @@ void JointState::copyState(const JointState& state) { void JointState::initTransform(const glm::mat4& parentTransform) { computeTransform(parentTransform); _positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform)); + _distanceToParent = glm::length(_positionInParentFrame); } void JointState::computeTransform(const glm::mat4& parentTransform) { diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index 86ee5d01b7..a3b792abc4 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -51,6 +51,7 @@ public: glm::quat getRotationInParentFrame() const; glm::quat getVisibleRotationInParentFrame() const; const glm::vec3& getPositionInParentFrame() const { return _positionInParentFrame; } + float getDistanceToParent() const { return _distanceToParent; } int getParentIndex() const { return _fbxJoint->parentIndex; } @@ -104,6 +105,7 @@ private: glm::quat _rotation; // joint- to model-frame glm::quat _rotationInConstrainedFrame; // rotation in frame where angular constraints would be applied glm::vec3 _positionInParentFrame; // only changes when the Model is scaled + float _distanceToParent; glm::mat4 _visibleTransform; glm::quat _visibleRotation; From 559188caced5c9cb3c2bb3eeb993588c1e018605 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 23 Jul 2014 17:00:17 -0700 Subject: [PATCH 4/6] parent-relative linear slaving of verlet shapes --- interface/src/avatar/SkeletonModel.cpp | 57 ++++++++++++++++++++------ 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 59f39834e3..1a68b86a14 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -533,6 +533,7 @@ void SkeletonModel::buildRagdollConstraints() { _ragdollConstraints.push_back(anchor); } else { DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex])); + bone->setDistance(state.getDistanceToParent()); _ragdollConstraints.push_back(bone); families.insert(parentIndex, i); } @@ -597,11 +598,7 @@ void SkeletonModel::updateVisibleJointStates() { // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { - // NOTE: increasing this timescale reduces vibrations in the ragdoll solution and reduces tunneling - // but makes the shapes slower to follow the body (introduces lag). - const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.05f; - float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); - moveShapesTowardJoints(fraction); + moveShapesTowardJoints(deltaTime); } float DENSITY_OF_WATER = 1000.0f; // kg/m^3 @@ -676,17 +673,51 @@ void SkeletonModel::buildShapes() { enforceRagdollConstraints(); } -void SkeletonModel::moveShapesTowardJoints(float fraction) { +void SkeletonModel::moveShapesTowardJoints(float deltaTime) { const int numStates = _jointStates.size(); assert(_jointStates.size() == _ragdollPoints.size()); - assert(fraction >= 0.0f && fraction <= 1.0f); - if (_ragdollPoints.size() == numStates) { - float oneMinusFraction = 1.0f - fraction; - int numJoints = _jointStates.size(); - for (int i = 0; i < numJoints; ++i) { - _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; - _ragdollPoints[i]._position = oneMinusFraction * _ragdollPoints[i]._position + fraction * _jointStates.at(i).getPosition(); + if (_ragdollPoints.size() != numStates) { + return; + } + + const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.05f; + float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); + + // SIMPLE LINEAR_SLAVING -- KEEP this implementation for reference + //float oneMinusFraction = 1.0f - fraction; + //for (int i = 0; i < numStates; ++i) { + // _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; + // _ragdollPoints[i]._position = oneMinusFraction * _ragdollPoints[i]._position + fraction * _jointStates.at(i).getPosition(); + //} + // LINEAR_SLAVING -- KEEP + + // parent-relative linear slaving + for (int i = 0; i < numStates; ++i) { + JointState& state = _jointStates[i]; + _ragdollPoints[i]._lastPosition = _ragdollPoints.at(i)._position; + + int p = state.getParentIndex(); + if (p == -1) { + _ragdollPoints[i]._position = glm::vec3(0.0f); + continue; } + if (state.getDistanceToParent() < EPSILON) { + _ragdollPoints[i]._position = _ragdollPoints.at(p)._position; + continue; + } + + const JointState& parentState = _jointStates.at(p); + glm::vec3 targetBone = state.getPosition() - parentState.getPosition(); + glm::vec3 bone = _ragdollPoints.at(i)._lastPosition - _ragdollPoints.at(p)._lastPosition; + + glm::vec3 newBone = (1.0f - fraction) * bone + fraction * targetBone; + float boneLength = glm::length(newBone); + if (boneLength > EPSILON) { + // slam newBone's lenght to that of the joint, which helps maintain distance constraints + newBone *= state.getDistanceToParent() / boneLength; + } + // we set the new position relative to parent + _ragdollPoints[i]._position = _ragdollPoints.at(p)._position + newBone; } } From 2aaa628a6727096c6f1f9a406bd98504caeaca74 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 24 Jul 2014 08:42:34 -0700 Subject: [PATCH 5/6] send visible joint state when colliding as ragdoll --- interface/src/avatar/MyAvatar.cpp | 13 ++++++++++--- interface/src/renderer/JointState.cpp | 8 ++++++++ interface/src/renderer/JointState.h | 3 +++ interface/src/renderer/Model.cpp | 18 ++++++++++++------ interface/src/renderer/Model.h | 4 ++++ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 556f9dfc68..37558adbc7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -165,9 +165,16 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model _jointData.resize(_skeletonModel.getJointStateCount()); - for (int i = 0; i < _jointData.size(); i++) { - JointData& data = _jointData[i]; - data.valid = _skeletonModel.getJointState(i, data.rotation); + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + for (int i = 0; i < _jointData.size(); i++) { + JointData& data = _jointData[i]; + data.valid = _skeletonModel.getVisibleJointState(i, data.rotation); + } + } else { + for (int i = 0; i < _jointData.size(); i++) { + JointData& data = _jointData[i]; + data.valid = _skeletonModel.getJointState(i, data.rotation); + } } } diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 26fa29c4a9..2a4372401e 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -218,6 +218,14 @@ void JointState::setVisibleRotationInConstrainedFrame(const glm::quat& targetRot _visibleRotation = parentRotation * _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; } +const bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) const { + glm::quat defaultRotation = _fbxJoint->rotation; + return glm::abs(rotation.x - defaultRotation.x) < tolerance && + glm::abs(rotation.y - defaultRotation.y) < tolerance && + glm::abs(rotation.z - defaultRotation.z) < tolerance && + glm::abs(rotation.w - defaultRotation.w) < tolerance; +} + const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const { assert(_fbxJoint != NULL); return _fbxJoint->translation; diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index a3b792abc4..94811fe13c 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -82,6 +82,9 @@ public: void setRotationInConstrainedFrame(const glm::quat& targetRotation); void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } + const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; } + + const bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const; const glm::vec3& getDefaultTranslationInConstrainedFrame() const; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 8fac5d3d03..8c19c11ed3 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -676,12 +676,18 @@ bool Model::getJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; } - rotation = _jointStates.at(index).getRotationInConstrainedFrame(); - const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation; - return glm::abs(rotation.x - defaultRotation.x) >= EPSILON || - glm::abs(rotation.y - defaultRotation.y) >= EPSILON || - glm::abs(rotation.z - defaultRotation.z) >= EPSILON || - glm::abs(rotation.w - defaultRotation.w) >= EPSILON; + const JointState& state = _jointStates.at(index); + rotation = state.getRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); +} + +bool Model::getVisibleJointState(int index, glm::quat& rotation) const { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getVisibleRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); } void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index a4eae8fd9a..cbed941791 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -113,6 +113,10 @@ public: /// Fetches the joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) bool getJointState(int index, glm::quat& rotation) const; + + /// Fetches the visible joint state at the specified index. + /// \return whether or not the joint state is "valid" (that is, non-default) + bool getVisibleJointState(int index, glm::quat& rotation) const; /// Sets the joint state at the specified index. void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); From 1d124ebc61e3df0942d81a25cabf58518a9dad6d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 24 Jul 2014 08:49:41 -0700 Subject: [PATCH 6/6] fix comments --- interface/src/avatar/SkeletonModel.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1a68b86a14..b81949d8c3 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -680,16 +680,17 @@ void SkeletonModel::moveShapesTowardJoints(float deltaTime) { return; } + // fraction = 0 means keep old position, = 1 means slave 100% to target position const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.05f; float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); - // SIMPLE LINEAR_SLAVING -- KEEP this implementation for reference + // SIMPLE LINEAR SLAVING -- KEEP this implementation for reference //float oneMinusFraction = 1.0f - fraction; //for (int i = 0; i < numStates; ++i) { // _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; // _ragdollPoints[i]._position = oneMinusFraction * _ragdollPoints[i]._position + fraction * _jointStates.at(i).getPosition(); //} - // LINEAR_SLAVING -- KEEP + // SIMPLE LINEAR SLAVING -- KEEP // parent-relative linear slaving for (int i = 0; i < numStates; ++i) { @@ -706,17 +707,17 @@ void SkeletonModel::moveShapesTowardJoints(float deltaTime) { continue; } + glm::vec3 bone = _ragdollPoints.at(i)._lastPosition - _ragdollPoints.at(p)._lastPosition; const JointState& parentState = _jointStates.at(p); glm::vec3 targetBone = state.getPosition() - parentState.getPosition(); - glm::vec3 bone = _ragdollPoints.at(i)._lastPosition - _ragdollPoints.at(p)._lastPosition; glm::vec3 newBone = (1.0f - fraction) * bone + fraction * targetBone; float boneLength = glm::length(newBone); if (boneLength > EPSILON) { - // slam newBone's lenght to that of the joint, which helps maintain distance constraints + // slam newBone's length to that of the joint helps maintain distance constraints newBone *= state.getDistanceToParent() / boneLength; } - // we set the new position relative to parent + // set the new position relative to parent's new position _ragdollPoints[i]._position = _ragdollPoints.at(p)._position + newBone; } }