diff --git a/interface/src/avatar/MuscleConstraint.cpp b/interface/src/avatar/MuscleConstraint.cpp new file mode 100644 index 0000000000..f4ba7975d0 --- /dev/null +++ b/interface/src/avatar/MuscleConstraint.cpp @@ -0,0 +1,34 @@ +// +// MuscleConstraint.cpp +// interface/src/avatar +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include "MuscleConstraint.h" + +const float DEFAULT_MUSCLE_STRENGTH = 0.5f * MAX_MUSCLE_STRENGTH; + +MuscleConstraint::MuscleConstraint(VerletPoint* parent, VerletPoint* child) + : _rootPoint(parent), _childPoint(child), + _parentIndex(-1), _childndex(-1), _strength(DEFAULT_MUSCLE_STRENGTH) { + _childOffset = child->_position - parent->_position; +} + +float MuscleConstraint::enforce() { + _childPoint->_position = (1.0f - _strength) * _childPoint->_position + _strength * (_rootPoint->_position + _childOffset); + return 0.0f; +} + +void MuscleConstraint::setIndices(int parent, int child) { + _parentIndex = parent; + _childndex = child; +} + diff --git a/interface/src/avatar/MuscleConstraint.h b/interface/src/avatar/MuscleConstraint.h new file mode 100644 index 0000000000..882b351b80 --- /dev/null +++ b/interface/src/avatar/MuscleConstraint.h @@ -0,0 +1,43 @@ +// +// MuscleConstraint.h +// interface/src/avatar +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MuscleConstraint_h +#define hifi_MuscleConstraint_h + +#include + +// MuscleConstraint is a simple constraint that pushes the child toward an offset relative to the parent. +// It does NOT push the parent. + +const float MAX_MUSCLE_STRENGTH = 0.5f; + +class MuscleConstraint : public Constraint { +public: + MuscleConstraint(VerletPoint* parent, VerletPoint* child); + MuscleConstraint(const MuscleConstraint& other); + float enforce(); + + void setIndices(int parent, int child); + int getParentIndex() const { return _parentIndex; } + int getChildIndex() const { return _childndex; } + void setChildOffset(const glm::vec3& offset) { _childOffset = offset; } + void setStrength(float strength) { _strength = glm::clamp(strength, 0.0f, MAX_MUSCLE_STRENGTH); } + float getStrength() const { return _strength; } +private: + VerletPoint* _rootPoint; + VerletPoint* _childPoint; + int _parentIndex; + int _childndex; + glm::vec3 _childOffset; + float _strength; // a value in range [0,MAX_MUSCLE_STRENGTH] +}; + +#endif // hifi_MuscleConstraint_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b81949d8c3..3caaad1391 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -14,11 +14,14 @@ #include #include +#include +#include #include "Application.h" #include "Avatar.h" #include "Hand.h" #include "Menu.h" +#include "MuscleConstraint.h" #include "SkeletonModel.h" SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : @@ -505,6 +508,7 @@ void SkeletonModel::renderRagdoll() { // virtual void SkeletonModel::initRagdollPoints() { clearRagdollConstraintsAndPoints(); + _muscleConstraints.clear(); // one point for each joint int numJoints = _jointStates.size(); @@ -512,8 +516,7 @@ void SkeletonModel::initRagdollPoints() { for (int i = 0; i < numJoints; ++i) { const JointState& state = _jointStates.at(i); glm::vec3 position = state.getPosition(); - _ragdollPoints[i]._position = position; - _ragdollPoints[i]._lastPosition = position; + _ragdollPoints[i].initPosition(position); } } @@ -523,11 +526,12 @@ void SkeletonModel::buildRagdollConstraints() { const int numPoints = _ragdollPoints.size(); assert(numPoints == _jointStates.size()); + float minBone = FLT_MAX; + float maxBone = -FLT_MAX; QMultiMap families; for (int i = 0; i < numPoints; ++i) { const JointState& state = _jointStates.at(i); - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; + int parentIndex = state.getParentIndex(); if (parentIndex == -1) { FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]), glm::vec3(0.0f)); _ragdollConstraints.push_back(anchor); @@ -537,20 +541,59 @@ void SkeletonModel::buildRagdollConstraints() { _ragdollConstraints.push_back(bone); families.insert(parentIndex, i); } + float boneLength = glm::length(state.getPositionInParentFrame()); + if (boneLength > maxBone) { + maxBone = boneLength; + } else if (boneLength < minBone) { + minBone = boneLength; + } } // Joints that have multiple children effectively have rigid constraints between the children - // in the parent frame, so we add constraints between children in the same family. + // in the parent frame, so we add DistanceConstraints between children in the same family. QMultiMap::iterator itr = families.begin(); while (itr != families.end()) { QList children = families.values(itr.key()); - if (children.size() > 1) { - for (int i = 1; i < children.size(); ++i) { + int numChildren = children.size(); + if (numChildren > 1) { + for (int i = 1; i < numChildren; ++i) { DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[children[i-1]]), &(_ragdollPoints[children[i]])); _ragdollConstraints.push_back(bone); } + if (numChildren > 2) { + DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[children[numChildren-1]]), &(_ragdollPoints[children[0]])); + _ragdollConstraints.push_back(bone); + } } ++itr; } + + float MAX_STRENGTH = 0.3f; + float MIN_STRENGTH = 0.005f; + // each joint gets a MuscleConstraint to its parent + for (int i = 1; i < numPoints; ++i) { + const JointState& state = _jointStates.at(i); + int p = state.getParentIndex(); + if (p == -1) { + continue; + } + MuscleConstraint* constraint = new MuscleConstraint(&(_ragdollPoints[p]), &(_ragdollPoints[i])); + _ragdollConstraints.push_back(constraint); + _muscleConstraints.push_back(constraint); + + // Short joints are more susceptible to wiggle so we modulate the strength based on the joint's length: + // long = weak and short = strong. + constraint->setIndices(p, i); + float boneLength = glm::length(state.getPositionInParentFrame()); + + float strength = MIN_STRENGTH + (MAX_STRENGTH - MIN_STRENGTH) * (maxBone - boneLength) / (maxBone - minBone); + if (!families.contains(i)) { + // Although muscles only pull on the children not parents, nevertheless those joints that have + // parents AND children are more stable than joints at the end such as fingers. For such joints we + // bestow maximum strength which helps reduce wiggle. + strength = MAX_MUSCLE_STRENGTH; + } + constraint->setStrength(strength); + } } @@ -598,7 +641,8 @@ void SkeletonModel::updateVisibleJointStates() { // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { - moveShapesTowardJoints(deltaTime); + Ragdoll::stepRagdollForward(deltaTime); + updateMuscles(); } float DENSITY_OF_WATER = 1000.0f; // kg/m^3 @@ -666,14 +710,16 @@ void SkeletonModel::buildShapes() { if (_ragdollPoints.size() == numStates) { int numJoints = _jointStates.size(); for (int i = 0; i < numJoints; ++i) { - _ragdollPoints[i]._lastPosition = _ragdollPoints.at(i)._position; - _ragdollPoints[i]._position = _jointStates.at(i).getPosition(); + _ragdollPoints[i].initPosition(_jointStates.at(i).getPosition()); } } enforceRagdollConstraints(); } void SkeletonModel::moveShapesTowardJoints(float deltaTime) { + // KEEP: although we don't currently use this method we may eventually need it to help + // unravel a skelton that has become tangled in its constraints. So let's keep this + // around for a while just in case. const int numStates = _jointStates.size(); assert(_jointStates.size() == _ragdollPoints.size()); if (_ragdollPoints.size() != numStates) { @@ -684,41 +730,26 @@ void SkeletonModel::moveShapesTowardJoints(float deltaTime) { 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(); - //} - // SIMPLE LINEAR SLAVING -- KEEP - - // parent-relative linear slaving + float oneMinusFraction = 1.0f - fraction; for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - _ragdollPoints[i]._lastPosition = _ragdollPoints.at(i)._position; + _ragdollPoints[i].initPosition(oneMinusFraction * _ragdollPoints[i]._position + + fraction * _jointStates.at(i).getPosition()); + } +} - int p = state.getParentIndex(); - if (p == -1) { - _ragdollPoints[i]._position = glm::vec3(0.0f); +void SkeletonModel::updateMuscles() { + int numConstraints = _muscleConstraints.size(); + for (int i = 0; i < numConstraints; ++i) { + MuscleConstraint* constraint = _muscleConstraints[i]; + int j = constraint->getParentIndex(); + if (j == -1) { continue; } - if (state.getDistanceToParent() < EPSILON) { - _ragdollPoints[i]._position = _ragdollPoints.at(p)._position; + int k = constraint->getChildIndex(); + if (k == -1) { 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 newBone = (1.0f - fraction) * bone + fraction * targetBone; - float boneLength = glm::length(newBone); - if (boneLength > EPSILON) { - // slam newBone's length to that of the joint helps maintain distance constraints - newBone *= state.getDistanceToParent() / boneLength; - } - // set the new position relative to parent's new position - _ragdollPoints[i]._position = _ragdollPoints.at(p)._position + newBone; + constraint->setChildOffset(_jointStates.at(k).getPosition() - _jointStates.at(j).getPosition()); } } @@ -738,8 +769,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { int parentIndex = joint.parentIndex; if (parentIndex == -1) { transforms[i] = _jointStates[i].getTransform(); - _ragdollPoints[i]._position = extractTranslation(transforms[i]); - _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; + _ragdollPoints[i].initPosition(extractTranslation(transforms[i])); continue; } @@ -747,8 +777,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; // setting the ragdollPoints here slams the VerletShapes into their default positions - _ragdollPoints[i]._position = extractTranslation(transforms[i]); - _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; + _ragdollPoints[i].initPosition(extractTranslation(transforms[i])); } // compute bounding box that encloses all shapes diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 7a88d890ac..5cb89f5e44 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -18,6 +18,7 @@ #include class Avatar; +class MuscleConstraint; /// A skeleton loaded from a model. class SkeletonModel : public Model, public Ragdoll { @@ -100,6 +101,7 @@ public: virtual void stepRagdollForward(float deltaTime); void moveShapesTowardJoints(float fraction); + void updateMuscles(); void computeBoundingShape(const FBXGeometry& geometry); void renderBoundingCollisionShapes(float alpha); @@ -144,6 +146,7 @@ private: CapsuleShape _boundingShape; glm::vec3 _boundingShapeLocalOffset; + QVector _muscleConstraints; }; #endif // hifi_SkeletonModel_h diff --git a/libraries/shared/src/Constraint.h b/libraries/shared/src/Constraint.h new file mode 100644 index 0000000000..422675b85d --- /dev/null +++ b/libraries/shared/src/Constraint.h @@ -0,0 +1,28 @@ +// +// Constraint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Constraint_h +#define hifi_Constraint_h + +class Constraint { +public: + Constraint() {} + virtual ~Constraint() {} + + /// Enforce contraint by moving relevant points. + /// \return max distance of point movement + virtual float enforce() = 0; + +protected: + int _type; +}; + +#endif // hifi_Constraint_h diff --git a/libraries/shared/src/DistanceConstraint.cpp b/libraries/shared/src/DistanceConstraint.cpp new file mode 100644 index 0000000000..50fa09d307 --- /dev/null +++ b/libraries/shared/src/DistanceConstraint.cpp @@ -0,0 +1,43 @@ +// +// DistanceConstraint.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DistanceConstraint.h" +#include "SharedUtil.h" // for EPSILON +#include "VerletPoint.h" + +DistanceConstraint::DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint) : _distance(-1.0f) { + _points[0] = startPoint; + _points[1] = endPoint; + _distance = glm::distance(_points[0]->_position, _points[1]->_position); +} + +DistanceConstraint::DistanceConstraint(const DistanceConstraint& other) { + _distance = other._distance; + _points[0] = other._points[0]; + _points[1] = other._points[1]; +} + +void DistanceConstraint::setDistance(float distance) { + _distance = fabsf(distance); +} + +float DistanceConstraint::enforce() { + // TODO: use a fast distance approximation + float newDistance = glm::distance(_points[0]->_position, _points[1]->_position); + glm::vec3 direction(0.0f, 1.0f, 0.0f); + if (newDistance > EPSILON) { + direction = (_points[0]->_position - _points[1]->_position) / newDistance; + } + glm::vec3 center = 0.5f * (_points[0]->_position + _points[1]->_position); + _points[0]->_position = center + (0.5f * _distance) * direction; + _points[1]->_position = center - (0.5f * _distance) * direction; + return glm::abs(newDistance - _distance); +} diff --git a/libraries/shared/src/DistanceConstraint.h b/libraries/shared/src/DistanceConstraint.h new file mode 100644 index 0000000000..c588807178 --- /dev/null +++ b/libraries/shared/src/DistanceConstraint.h @@ -0,0 +1,31 @@ +// +// DistanceConstraint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DistanceConstraint_h +#define hifi_DistanceConstraint_h + +#include "Constraint.h" + +class VerletPoint; + +class DistanceConstraint : public Constraint { +public: + DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint); + DistanceConstraint(const DistanceConstraint& other); + float enforce(); + void setDistance(float distance); + float getDistance() const { return _distance; } +private: + float _distance; + VerletPoint* _points[2]; +}; + +#endif // hifi_DistanceConstraint_h diff --git a/libraries/shared/src/FixedConstraint.cpp b/libraries/shared/src/FixedConstraint.cpp new file mode 100644 index 0000000000..099c6d7bac --- /dev/null +++ b/libraries/shared/src/FixedConstraint.cpp @@ -0,0 +1,35 @@ +// +// FixedConstraint.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FixedConstraint.h" +#include "Shape.h" // for MAX_SHAPE_MASS +#include "VerletPoint.h" + +FixedConstraint::FixedConstraint(VerletPoint* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) { +} + +float FixedConstraint::enforce() { + assert(_point != NULL); + // TODO: use fast approximate sqrt here + float distance = glm::distance(_anchor, _point->_position); + _point->_position = _anchor; + return distance; +} + +void FixedConstraint::setPoint(VerletPoint* point) { + assert(point); + _point = point; + _point->_mass = MAX_SHAPE_MASS; +} + +void FixedConstraint::setAnchor(const glm::vec3& anchor) { + _anchor = anchor; +} diff --git a/libraries/shared/src/FixedConstraint.h b/libraries/shared/src/FixedConstraint.h new file mode 100644 index 0000000000..050232a027 --- /dev/null +++ b/libraries/shared/src/FixedConstraint.h @@ -0,0 +1,32 @@ +// +// FixedConstraint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FixedConstraint_h +#define hifi_FixedConstraint_h + +#include + +#include "Constraint.h" + +class VerletPoint; + +class FixedConstraint : public Constraint { +public: + FixedConstraint(VerletPoint* point, const glm::vec3& anchor); + float enforce(); + void setPoint(VerletPoint* point); + void setAnchor(const glm::vec3& anchor); +private: + VerletPoint* _point; + glm::vec3 _anchor; +}; + +#endif // hifi_FixedConstraint_h diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 1d24e74864..6282db4dfb 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -11,86 +11,9 @@ #include "Ragdoll.h" -#include "CapsuleShape.h" -#include "CollisionInfo.h" -#include "SharedUtil.h" -#include "SphereShape.h" - -// ---------------------------------------------------------------------------- -// VerletPoint -// ---------------------------------------------------------------------------- -void VerletPoint::accumulateDelta(const glm::vec3& delta) { - _accumulatedDelta += delta; - ++_numDeltas; -} - -void VerletPoint::applyAccumulatedDelta() { - if (_numDeltas > 0) { - _position += _accumulatedDelta / (float)_numDeltas; - _accumulatedDelta = glm::vec3(0.0f); - _numDeltas = 0; - } -} - -// ---------------------------------------------------------------------------- -// FixedConstraint -// ---------------------------------------------------------------------------- -FixedConstraint::FixedConstraint(VerletPoint* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) { -} - -float FixedConstraint::enforce() { - assert(_point != NULL); - // TODO: use fast approximate sqrt here - float distance = glm::distance(_anchor, _point->_position); - _point->_position = _anchor; - return distance; -} - -void FixedConstraint::setPoint(VerletPoint* point) { - assert(point); - _point = point; - _point->_mass = MAX_SHAPE_MASS; -} - -void FixedConstraint::setAnchor(const glm::vec3& anchor) { - _anchor = anchor; -} - -// ---------------------------------------------------------------------------- -// DistanceConstraint -// ---------------------------------------------------------------------------- -DistanceConstraint::DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint) : _distance(-1.0f) { - _points[0] = startPoint; - _points[1] = endPoint; - _distance = glm::distance(_points[0]->_position, _points[1]->_position); -} - -DistanceConstraint::DistanceConstraint(const DistanceConstraint& other) { - _distance = other._distance; - _points[0] = other._points[0]; - _points[1] = other._points[1]; -} - -void DistanceConstraint::setDistance(float distance) { - _distance = fabsf(distance); -} - -float DistanceConstraint::enforce() { - // TODO: use a fast distance approximation - float newDistance = glm::distance(_points[0]->_position, _points[1]->_position); - glm::vec3 direction(0.0f, 1.0f, 0.0f); - if (newDistance > EPSILON) { - direction = (_points[0]->_position - _points[1]->_position) / newDistance; - } - glm::vec3 center = 0.5f * (_points[0]->_position + _points[1]->_position); - _points[0]->_position = center + (0.5f * _distance) * direction; - _points[1]->_position = center - (0.5f * _distance) * direction; - return glm::abs(newDistance - _distance); -} - -// ---------------------------------------------------------------------------- -// Ragdoll -// ---------------------------------------------------------------------------- +#include "Constraint.h" +#include "DistanceConstraint.h" +#include "FixedConstraint.h" Ragdoll::Ragdoll() { } @@ -98,6 +21,13 @@ Ragdoll::Ragdoll() { Ragdoll::~Ragdoll() { clearRagdollConstraintsAndPoints(); } + +void Ragdoll::stepRagdollForward(float deltaTime) { + int numPoints = _ragdollPoints.size(); + for (int i = 0; i < numPoints; ++i) { + _ragdollPoints[i].integrateForward(); + } +} void Ragdoll::clearRagdollConstraintsAndPoints() { int numConstraints = _ragdollConstraints.size(); @@ -118,4 +48,3 @@ float Ragdoll::enforceRagdollConstraints() { } return maxDistance; } - diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 682f0f8dae..91a7e7330e 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -14,67 +14,11 @@ #include #include +#include "VerletPoint.h" #include -class Shape; - -// TODO: Andrew to move VerletPoint class to its own file -class VerletPoint { -public: - VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(1.0f), _accumulatedDelta(0.0f), _numDeltas(0) {} - - void accumulateDelta(const glm::vec3& delta); - void applyAccumulatedDelta(); - - glm::vec3 getAccumulatedDelta() const { - return (_numDeltas > 0) ? _accumulatedDelta / (float)_numDeltas : glm::vec3(0.0f); - } - - glm::vec3 _position; - glm::vec3 _lastPosition; - float _mass; - -private: - glm::vec3 _accumulatedDelta; - int _numDeltas; -}; - -class Constraint { -public: - Constraint() {} - virtual ~Constraint() {} - - /// Enforce contraint by moving relevant points. - /// \return max distance of point movement - virtual float enforce() = 0; - -protected: - int _type; -}; - -class FixedConstraint : public Constraint { -public: - FixedConstraint(VerletPoint* point, const glm::vec3& anchor); - float enforce(); - void setPoint(VerletPoint* point); - void setAnchor(const glm::vec3& anchor); -private: - VerletPoint* _point; - glm::vec3 _anchor; -}; - -class DistanceConstraint : public Constraint { -public: - DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint); - DistanceConstraint(const DistanceConstraint& other); - float enforce(); - void setDistance(float distance); - float getDistance() const { return _distance; } -private: - float _distance; - VerletPoint* _points[2]; -}; +class Constraint; class Ragdoll { public: @@ -82,7 +26,7 @@ public: Ragdoll(); virtual ~Ragdoll(); - virtual void stepRagdollForward(float deltaTime) = 0; + virtual void stepRagdollForward(float deltaTime); /// \return max distance of point movement float enforceRagdollConstraints(); diff --git a/libraries/shared/src/VerletPoint.cpp b/libraries/shared/src/VerletPoint.cpp new file mode 100644 index 0000000000..641ac39341 --- /dev/null +++ b/libraries/shared/src/VerletPoint.cpp @@ -0,0 +1,31 @@ +// +// VerletPoint.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "VerletPoint.h" + +void VerletPoint::integrateForward() { + glm::vec3 oldPosition = _position; + _position += 0.6f * (_position - _lastPosition); + _lastPosition = oldPosition; +} + +void VerletPoint::accumulateDelta(const glm::vec3& delta) { + _accumulatedDelta += delta; + ++_numDeltas; +} + +void VerletPoint::applyAccumulatedDelta() { + if (_numDeltas > 0) { + _position += _accumulatedDelta / (float)_numDeltas; + _accumulatedDelta = glm::vec3(0.0f); + _numDeltas = 0; + } +} diff --git a/libraries/shared/src/VerletPoint.h b/libraries/shared/src/VerletPoint.h new file mode 100644 index 0000000000..076a624776 --- /dev/null +++ b/libraries/shared/src/VerletPoint.h @@ -0,0 +1,39 @@ +// +// VerletPoint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_VerletPoint_h +#define hifi_VerletPoint_h + +#include + +class VerletPoint { +public: + VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(1.0f), _accumulatedDelta(0.0f), _numDeltas(0) {} + + void initPosition(const glm::vec3& position) { _position = position; _lastPosition = position; } + void integrateForward(); + void accumulateDelta(const glm::vec3& delta); + void applyAccumulatedDelta(); + + glm::vec3 getAccumulatedDelta() const { + return (_numDeltas > 0) ? _accumulatedDelta / (float)_numDeltas : glm::vec3(0.0f); + } + + glm::vec3 _position; + glm::vec3 _lastPosition; + float _mass; + +private: + glm::vec3 _accumulatedDelta; + int _numDeltas; +}; + +#endif // hifi_VerletPoint_h