mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 19:59:28 +02:00
Added CriticallyDampedSpringPoseHelper to help smooth out hips.
This commit is contained in:
parent
a776f7e55a
commit
0283c9fbdc
5 changed files with 79 additions and 28 deletions
|
@ -36,6 +36,7 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle
|
||||||
static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
||||||
glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||||
|
|
||||||
|
|
||||||
// check for pinned hips.
|
// check for pinned hips.
|
||||||
auto hipsIndex = myAvatar->getJointIndex("Hips");
|
auto hipsIndex = myAvatar->getJointIndex("Hips");
|
||||||
if (myAvatar->isJointPinned(hipsIndex)) {
|
if (myAvatar->isJointPinned(hipsIndex)) {
|
||||||
|
@ -199,49 +200,38 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
|
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
|
||||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
||||||
|
|
||||||
if (!_prevHipsValid) {
|
|
||||||
AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying);
|
|
||||||
_prevHips = hips;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying);
|
|
||||||
|
|
||||||
// timescale in seconds
|
// timescale in seconds
|
||||||
const float TRANS_HORIZ_TIMESCALE = 0.15f;
|
const float TRANS_HORIZ_TIMESCALE = 0.15f;
|
||||||
const float TRANS_VERT_TIMESCALE = 0.01f; // We want the vertical component of the hips to follow quickly to prevent spine squash/stretch.
|
const float TRANS_VERT_TIMESCALE = 0.01f; // We want the vertical component of the hips to follow quickly to prevent spine squash/stretch.
|
||||||
const float ROT_TIMESCALE = 0.15f;
|
const float ROT_TIMESCALE = 0.15f;
|
||||||
const float FLY_IDLE_TRANSITION_TIMESCALE = 0.25f;
|
const float FLY_IDLE_TRANSITION_TIMESCALE = 0.25f;
|
||||||
|
|
||||||
float transHorizAlpha, transVertAlpha, rotAlpha;
|
|
||||||
if (_flyIdleTimer < 0.0f) {
|
if (_flyIdleTimer < 0.0f) {
|
||||||
transHorizAlpha = glm::min(deltaTime / TRANS_HORIZ_TIMESCALE, 1.0f);
|
_smoothHipsHelper.setHorizontalTranslationTimescale(TRANS_HORIZ_TIMESCALE);
|
||||||
transVertAlpha = glm::min(deltaTime / TRANS_VERT_TIMESCALE, 1.0f);
|
_smoothHipsHelper.setVerticalTranslationTimescale(TRANS_VERT_TIMESCALE);
|
||||||
rotAlpha = glm::min(deltaTime / ROT_TIMESCALE, 1.0f);
|
_smoothHipsHelper.setRotationTimescale(ROT_TIMESCALE);
|
||||||
} else {
|
} else {
|
||||||
transHorizAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f);
|
_smoothHipsHelper.setHorizontalTranslationTimescale(FLY_IDLE_TRANSITION_TIMESCALE);
|
||||||
transVertAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f);
|
_smoothHipsHelper.setVerticalTranslationTimescale(FLY_IDLE_TRANSITION_TIMESCALE);
|
||||||
rotAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f);
|
_smoothHipsHelper.setRotationTimescale(FLY_IDLE_TRANSITION_TIMESCALE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// smootly lerp hips, in sensorframe, with different coeff for horiz and vertical translation.
|
AnimPose sensorHips = computeHipsInSensorFrame(myAvatar, isFlying);
|
||||||
float hipsY = hips.trans().y;
|
if (!_prevIsEstimatingHips) {
|
||||||
hips.trans() = lerp(_prevHips.trans(), hips.trans(), transHorizAlpha);
|
_smoothHipsHelper.teleport(sensorHips);
|
||||||
hips.trans().y = lerp(_prevHips.trans().y, hipsY, transVertAlpha);
|
}
|
||||||
hips.rot() = safeLerp(_prevHips.rot(), hips.rot(), rotAlpha);
|
sensorHips = _smoothHipsHelper.update(sensorHips, deltaTime);
|
||||||
|
|
||||||
_prevHips = hips;
|
|
||||||
_prevHipsValid = true;
|
|
||||||
|
|
||||||
glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180);
|
glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180);
|
||||||
AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix());
|
AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix());
|
||||||
|
|
||||||
params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips;
|
params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * sensorHips;
|
||||||
params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
|
params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
|
||||||
|
|
||||||
// set spine2 if we have hand controllers
|
// set spine2 if we have hand controllers
|
||||||
if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() &&
|
if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() &&
|
||||||
myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() &&
|
myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() &&
|
||||||
!(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) {
|
!(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) {
|
||||||
|
|
||||||
AnimPose currentSpine2Pose;
|
AnimPose currentSpine2Pose;
|
||||||
AnimPose currentHeadPose;
|
AnimPose currentHeadPose;
|
||||||
|
@ -267,8 +257,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_prevIsEstimatingHips = true;
|
||||||
} else {
|
} else {
|
||||||
_prevHipsValid = false;
|
_prevIsEstimatingHips = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
params.isTalking = head->getTimeWithoutTalking() <= 1.5f;
|
params.isTalking = head->getTimeWithoutTalking() <= 1.5f;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#define hifi_MySkeletonModel_h
|
#define hifi_MySkeletonModel_h
|
||||||
|
|
||||||
#include <avatars-renderer/SkeletonModel.h>
|
#include <avatars-renderer/SkeletonModel.h>
|
||||||
|
#include <AnimUtil.h>
|
||||||
#include "MyAvatar.h"
|
#include "MyAvatar.h"
|
||||||
|
|
||||||
/// A skeleton loaded from a model.
|
/// A skeleton loaded from a model.
|
||||||
|
@ -26,11 +27,12 @@ public:
|
||||||
private:
|
private:
|
||||||
void updateFingers();
|
void updateFingers();
|
||||||
|
|
||||||
AnimPose _prevHips; // sensor frame
|
CriticallyDampedSpringPoseHelper _smoothHipsHelper; // sensor frame
|
||||||
bool _prevHipsValid { false };
|
|
||||||
bool _prevIsFlying { false };
|
bool _prevIsFlying { false };
|
||||||
float _flyIdleTimer { 0.0f };
|
float _flyIdleTimer { 0.0f };
|
||||||
|
|
||||||
|
float _prevIsEstimatingHips { false };
|
||||||
|
|
||||||
std::map<int, int> _jointRotationFrameOffsetMap;
|
std::map<int, int> _jointRotationFrameOffsetMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,4 +38,57 @@ AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone);
|
||||||
// and returns a bodyRot that is also z-forward and y-up
|
// and returns a bodyRot that is also z-forward and y-up
|
||||||
glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& up);
|
glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& up);
|
||||||
|
|
||||||
|
|
||||||
|
// Uses a approximation of a critically damped spring to smooth full AnimPoses.
|
||||||
|
// It provides seperate timescales for horizontal, vertical and rotation components.
|
||||||
|
// The timescale is roughly how much time it will take the spring will reach halfway toward it's target.
|
||||||
|
class CriticallyDampedSpringPoseHelper {
|
||||||
|
public:
|
||||||
|
CriticallyDampedSpringPoseHelper() : _prevPoseValid(false) {}
|
||||||
|
|
||||||
|
void setHorizontalTranslationTimescale(float timescale) {
|
||||||
|
_horizontalTranslationTimescale = timescale;
|
||||||
|
}
|
||||||
|
void setVerticalTranslationTimescale(float timescale) {
|
||||||
|
_verticalTranslationTimescale = timescale;
|
||||||
|
}
|
||||||
|
void setRotationTimescale(float timescale) {
|
||||||
|
_rotationTimescale = timescale;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimPose update(const AnimPose& pose, float deltaTime) {
|
||||||
|
if (!_prevPoseValid) {
|
||||||
|
_prevPose = pose;
|
||||||
|
_prevPoseValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float horizontalTranslationAlpha = glm::min(deltaTime / _horizontalTranslationTimescale, 1.0f);
|
||||||
|
const float verticalTranslationAlpha = glm::min(deltaTime / _verticalTranslationTimescale, 1.0f);
|
||||||
|
const float rotationAlpha = glm::min(deltaTime / _rotationTimescale, 1.0f);
|
||||||
|
|
||||||
|
const float poseY = pose.trans().y;
|
||||||
|
AnimPose newPose = _prevPose;
|
||||||
|
newPose.trans() = lerp(_prevPose.trans(), pose.trans(), horizontalTranslationAlpha);
|
||||||
|
newPose.trans().y = lerp(_prevPose.trans().y, poseY, verticalTranslationAlpha);
|
||||||
|
newPose.rot() = safeLerp(_prevPose.rot(), pose.rot(), rotationAlpha);
|
||||||
|
|
||||||
|
_prevPose = newPose;
|
||||||
|
_prevPoseValid = true;
|
||||||
|
|
||||||
|
return newPose;
|
||||||
|
}
|
||||||
|
|
||||||
|
void teleport(const AnimPose& pose) {
|
||||||
|
_prevPoseValid = true;
|
||||||
|
_prevPose = pose;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AnimPose _prevPose;
|
||||||
|
float _horizontalTranslationTimescale { 0.15f };
|
||||||
|
float _verticalTranslationTimescale { 0.15f };
|
||||||
|
float _rotationTimescale { 0.15f };
|
||||||
|
bool _prevPoseValid;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1609,6 +1609,7 @@ glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int up
|
||||||
|
|
||||||
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {
|
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {
|
||||||
if (!_animSkeleton || !_animNode) {
|
if (!_animSkeleton || !_animNode) {
|
||||||
|
_previousControllerParameters = params;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1700,6 +1701,8 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_previousControllerParameters = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::initAnimGraph(const QUrl& url) {
|
void Rig::initAnimGraph(const QUrl& url) {
|
||||||
|
|
|
@ -394,6 +394,8 @@ protected:
|
||||||
|
|
||||||
AnimContext _lastContext;
|
AnimContext _lastContext;
|
||||||
AnimVariantMap _lastAnimVars;
|
AnimVariantMap _lastAnimVars;
|
||||||
|
|
||||||
|
ControllerParameters _previousControllerParameters;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__Rig__) */
|
#endif /* defined(__hifi__Rig__) */
|
||||||
|
|
Loading…
Reference in a new issue