mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 13:33:38 +02:00
Merge pull request #6934 from hyperlogic/tony/anim-pre-post-rot-support
Avatar Developer Menu: added animation debug options
This commit is contained in:
commit
0d257b82fa
10 changed files with 90 additions and 29 deletions
|
@ -482,6 +482,10 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false,
|
||||
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
|
||||
avatar, SLOT(setEnableInverseKinematics(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
|
|
|
@ -208,6 +208,7 @@ namespace MenuOption {
|
|||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString EnableInverseKinematics = "Enable Inverse Kinematics";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||
|
@ -302,6 +303,7 @@ namespace MenuOption {
|
|||
const QString UploadAsset = "Upload File to Asset Server";
|
||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||
const QString UseCamera = "Use Camera";
|
||||
const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
|
||||
const QString VelocityFilter = "Velocity Filter";
|
||||
const QString VisibleToEveryone = "Everyone";
|
||||
const QString VisibleToFriends = "Friends";
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <TextRenderer3D.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <AnimDebugDraw.h>
|
||||
#include <AnimClip.h>
|
||||
#include <recording/Deck.h>
|
||||
#include <recording/Recorder.h>
|
||||
#include <recording/Clip.h>
|
||||
|
@ -647,6 +648,15 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
|||
_skeletonModel.setVisibleInScene(isEnabled, scene);
|
||||
}
|
||||
|
||||
void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
|
||||
AnimClip::usePreAndPostPoseFromAnim = isEnabled;
|
||||
reset(true);
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
|
||||
_rig->setEnableInverseKinematics(isEnabled);
|
||||
}
|
||||
|
||||
void MyAvatar::loadData() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
|
|
|
@ -260,6 +260,8 @@ public slots:
|
|||
void setEnableDebugDrawPosition(bool isEnabled);
|
||||
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||
void setEnableInverseKinematics(bool isEnabled);
|
||||
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
||||
|
||||
glm::vec3 getPositionForAudio();
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
bool AnimClip::usePreAndPostPoseFromAnim = false;
|
||||
|
||||
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
|
||||
AnimNode(AnimNode::Type::Clip, id),
|
||||
_startFrame(startFrame),
|
||||
|
@ -109,6 +111,8 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
|
||||
for (int frame = 0; frame < frameCount; frame++) {
|
||||
|
||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||
|
||||
// init all joints in animation to default pose
|
||||
// this will give us a resonable result for bones in the model skeleton but not in the animation.
|
||||
_anim[frame].reserve(skeletonJointCount);
|
||||
|
@ -119,36 +123,41 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
|
||||
int skeletonJoint = jointMap[animJoint];
|
||||
|
||||
const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint];
|
||||
const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint];
|
||||
|
||||
// skip joints that are in the animation but not in the skeleton.
|
||||
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
|
||||
|
||||
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
|
||||
#ifdef USE_PRE_ROT_FROM_ANIM
|
||||
// TODO: This is the correct way to apply the pre rotations from maya, however
|
||||
// the current animation set has incorrect preRotations for the left wrist and thumb
|
||||
// so it looks wrong if we enable this code.
|
||||
glm::quat preRotation = animSkeleton.getPreRotation(animJoint);
|
||||
#else
|
||||
// TODO: This is the legacy approach, this does not work when animations and models do not
|
||||
// have the same set of pre rotations. For example when mixing maya models with blender animations.
|
||||
glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot;
|
||||
#endif
|
||||
const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint);
|
||||
AnimPose preRot, postRot;
|
||||
if (usePreAndPostPoseFromAnim) {
|
||||
preRot = animSkeleton.getPreRotationPose(animJoint);
|
||||
postRot = animSkeleton.getPostRotationPose(animJoint);
|
||||
} else {
|
||||
// In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations.
|
||||
preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3());
|
||||
postRot = AnimPose::identity;
|
||||
}
|
||||
|
||||
// used to adjust translation offsets, so large translation animatons on the reference skeleton
|
||||
// cancel out scale
|
||||
preRot.scale = glm::vec3(1.0f);
|
||||
postRot.scale = glm::vec3(1.0f);
|
||||
|
||||
AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3());
|
||||
|
||||
// adjust translation offsets, so large translation animatons on the reference skeleton
|
||||
// will be adjusted when played on a skeleton with short limbs.
|
||||
float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans));
|
||||
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
|
||||
const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint);
|
||||
float boneLengthScale = 1.0f;
|
||||
const float EPSILON = 0.0001f;
|
||||
if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) {
|
||||
boneLengthScale = glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans);
|
||||
}
|
||||
|
||||
AnimPose& pose = _anim[frame][skeletonJoint];
|
||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||
AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans));
|
||||
|
||||
// rotation in fbxAnimationFrame is a delta from its preRotation.
|
||||
pose.rot = preRotation * fbxAnimFrame.rotations[animJoint];
|
||||
|
||||
// translation in fbxAnimationFrame is not a delta.
|
||||
// convert it into a delta by subtracting from the first frame.
|
||||
const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint];
|
||||
pose.trans = relDefaultPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans);
|
||||
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ class AnimClip : public AnimNode {
|
|||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
static bool usePreAndPostPoseFromAnim;
|
||||
|
||||
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
|
||||
virtual ~AnimClip() override;
|
||||
|
||||
|
|
|
@ -59,8 +59,14 @@ const AnimPose& AnimSkeleton::getAbsoluteDefaultPose(int jointIndex) const {
|
|||
return _absoluteDefaultPoses[jointIndex];
|
||||
}
|
||||
|
||||
const glm::quat AnimSkeleton::getPreRotation(int jointIndex) const {
|
||||
return _joints[jointIndex].preRotation;
|
||||
// get pre multiplied transform which should include FBX pre potations
|
||||
const AnimPose& AnimSkeleton::getPreRotationPose(int jointIndex) const {
|
||||
return _relativePreRotationPoses[jointIndex];
|
||||
}
|
||||
|
||||
// get post multiplied transform which might include FBX offset transformations
|
||||
const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const {
|
||||
return _relativePostRotationPoses[jointIndex];
|
||||
}
|
||||
|
||||
int AnimSkeleton::getParentIndex(int jointIndex) const {
|
||||
|
@ -99,13 +105,20 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
|
|||
// build a chache of default poses
|
||||
_absoluteDefaultPoses.reserve(joints.size());
|
||||
_relativeDefaultPoses.reserve(joints.size());
|
||||
_relativePreRotationPoses.reserve(joints.size());
|
||||
_relativePostRotationPoses.reserve(joints.size());
|
||||
|
||||
// iterate over FBXJoints and extract the bind pose information.
|
||||
for (int i = 0; i < (int)joints.size(); i++) {
|
||||
|
||||
// build pre and post transforms
|
||||
glm::mat4 preRotationTransform = _joints[i].preTransform * glm::mat4_cast(_joints[i].preRotation);
|
||||
glm::mat4 postRotationTransform = glm::mat4_cast(_joints[i].postRotation) * _joints[i].postTransform;
|
||||
_relativePreRotationPoses.push_back(AnimPose(preRotationTransform));
|
||||
_relativePostRotationPoses.push_back(AnimPose(postRotationTransform));
|
||||
|
||||
// build relative and absolute default poses
|
||||
glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation);
|
||||
glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform;
|
||||
glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform;
|
||||
AnimPose relDefaultPose(relDefaultMat);
|
||||
_relativeDefaultPoses.push_back(relDefaultPose);
|
||||
int parentIndex = getParentIndex(i);
|
||||
|
|
|
@ -42,8 +42,11 @@ public:
|
|||
const AnimPose& getAbsoluteDefaultPose(int jointIndex) const;
|
||||
const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; }
|
||||
|
||||
// get pre-rotation aka Maya's joint orientation.
|
||||
const glm::quat getPreRotation(int jointIndex) const;
|
||||
// get pre transform which should include FBX pre potations
|
||||
const AnimPose& getPreRotationPose(int jointIndex) const;
|
||||
|
||||
// get post transform which might include FBX offset transformations
|
||||
const AnimPose& getPostRotationPose(int jointIndex) const;
|
||||
|
||||
int getParentIndex(int jointIndex) const;
|
||||
|
||||
|
@ -64,6 +67,8 @@ protected:
|
|||
AnimPoseVec _relativeBindPoses;
|
||||
AnimPoseVec _relativeDefaultPoses;
|
||||
AnimPoseVec _absoluteDefaultPoses;
|
||||
AnimPoseVec _relativePreRotationPoses;
|
||||
AnimPoseVec _relativePostRotationPoses;
|
||||
|
||||
// no copies
|
||||
AnimSkeleton(const AnimSkeleton&) = delete;
|
||||
|
|
|
@ -464,6 +464,10 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::setEnableInverseKinematics(bool enable) {
|
||||
_enableInverseKinematics = enable;
|
||||
}
|
||||
|
||||
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
|
||||
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
||||
return _absoluteDefaultPoses[index];
|
||||
|
@ -705,6 +709,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
}
|
||||
|
||||
t += deltaTime;
|
||||
|
||||
if (_enableInverseKinematics) {
|
||||
_animVars.set("ikOverlayAlpha", 1.0f);
|
||||
} else {
|
||||
_animVars.set("ikOverlayAlpha", 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
_lastFront = front;
|
||||
|
|
|
@ -208,6 +208,8 @@ public:
|
|||
|
||||
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
|
||||
|
||||
void setEnableInverseKinematics(bool enable);
|
||||
|
||||
protected:
|
||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||
void updateAnimationStateHandlers();
|
||||
|
@ -290,6 +292,8 @@ public:
|
|||
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
||||
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
||||
|
||||
bool _enableInverseKinematics { true };
|
||||
|
||||
private:
|
||||
QMap<int, StateHandler> _stateHandlers;
|
||||
int _nextStateHandlerId { 0 };
|
||||
|
|
Loading…
Reference in a new issue