mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 22:22:54 +02:00
Avatar Developer Menu: added animation debug options
This options are for for developers only and might help debug animation related issues. * Enable Inverse Kinematics: this can be toggled to disable IK for the avatar. * Enable Anim Pre and Post Rotations: this option can be used to use FBX pre-rotations from source avatar animations, instead of the current default, which is to use them from the source model. This only effects FBX files loaded by the animation system, it does not affect changing model orientations via JavaScript.
This commit is contained in:
parent
1bdeeeceeb
commit
3cee3cbb5a
8 changed files with 72 additions and 23 deletions
|
@ -479,6 +479,10 @@ Menu::Menu() {
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
|
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,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
|
||||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
|
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 EchoServerAudio = "Echo Server Audio";
|
||||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||||
const QString EnableCharacterController = "Enable avatar collisions";
|
const QString EnableCharacterController = "Enable avatar collisions";
|
||||||
|
const QString EnableInverseKinematics = "Enable Inverse Kinematics";
|
||||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||||
|
@ -302,6 +303,7 @@ namespace MenuOption {
|
||||||
const QString UploadAsset = "Upload File to Asset Server";
|
const QString UploadAsset = "Upload File to Asset Server";
|
||||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||||
const QString UseCamera = "Use Camera";
|
const QString UseCamera = "Use Camera";
|
||||||
|
const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
|
||||||
const QString VelocityFilter = "Velocity Filter";
|
const QString VelocityFilter = "Velocity Filter";
|
||||||
const QString VisibleToEveryone = "Everyone";
|
const QString VisibleToEveryone = "Everyone";
|
||||||
const QString VisibleToFriends = "Friends";
|
const QString VisibleToFriends = "Friends";
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <TextRenderer3D.h>
|
#include <TextRenderer3D.h>
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
#include <AnimDebugDraw.h>
|
#include <AnimDebugDraw.h>
|
||||||
|
#include <AnimClip.h>
|
||||||
#include <recording/Deck.h>
|
#include <recording/Deck.h>
|
||||||
#include <recording/Recorder.h>
|
#include <recording/Recorder.h>
|
||||||
#include <recording/Clip.h>
|
#include <recording/Clip.h>
|
||||||
|
@ -647,6 +648,15 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
||||||
_skeletonModel.setVisibleInScene(isEnabled, scene);
|
_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() {
|
void MyAvatar::loadData() {
|
||||||
Settings settings;
|
Settings settings;
|
||||||
settings.beginGroup("Avatar");
|
settings.beginGroup("Avatar");
|
||||||
|
|
|
@ -260,6 +260,8 @@ public slots:
|
||||||
void setEnableDebugDrawPosition(bool isEnabled);
|
void setEnableDebugDrawPosition(bool isEnabled);
|
||||||
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
||||||
void setEnableMeshVisible(bool isEnabled);
|
void setEnableMeshVisible(bool isEnabled);
|
||||||
|
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||||
|
void setEnableInverseKinematics(bool isEnabled);
|
||||||
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
||||||
|
|
||||||
glm::vec3 getPositionForAudio();
|
glm::vec3 getPositionForAudio();
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
|
bool AnimClip::usePreAndPostPoseFromAnim = false;
|
||||||
|
|
||||||
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
|
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
|
||||||
AnimNode(AnimNode::Type::Clip, id),
|
AnimNode(AnimNode::Type::Clip, id),
|
||||||
_startFrame(startFrame),
|
_startFrame(startFrame),
|
||||||
|
@ -109,6 +111,8 @@ void AnimClip::copyFromNetworkAnim() {
|
||||||
|
|
||||||
for (int frame = 0; frame < frameCount; frame++) {
|
for (int frame = 0; frame < frameCount; frame++) {
|
||||||
|
|
||||||
|
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
||||||
|
|
||||||
// init all joints in animation to default pose
|
// 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.
|
// this will give us a resonable result for bones in the model skeleton but not in the animation.
|
||||||
_anim[frame].reserve(skeletonJointCount);
|
_anim[frame].reserve(skeletonJointCount);
|
||||||
|
@ -119,36 +123,46 @@ void AnimClip::copyFromNetworkAnim() {
|
||||||
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
|
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
|
||||||
int skeletonJoint = jointMap[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.
|
// skip joints that are in the animation but not in the skeleton.
|
||||||
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
|
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
|
||||||
|
|
||||||
const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
|
AnimPose preRot, postRot;
|
||||||
#ifdef USE_PRE_ROT_FROM_ANIM
|
if (usePreAndPostPoseFromAnim) {
|
||||||
// TODO: This is the correct way to apply the pre rotations from maya, however
|
preRot = animSkeleton.getPreRotationPose(animJoint);
|
||||||
// the current animation set has incorrect preRotations for the left wrist and thumb
|
postRot = animSkeleton.getPostRotationPose(animJoint);
|
||||||
// so it looks wrong if we enable this code.
|
} else {
|
||||||
glm::quat preRotation = animSkeleton.getPreRotation(animJoint);
|
// In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations.
|
||||||
#else
|
preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3());
|
||||||
// TODO: This is the legacy approach, this does not work when animations and models do not
|
postRot = AnimPose::identity;
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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.
|
// 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];
|
AnimPose trans;
|
||||||
const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
|
if (usePreAndPostPoseFromAnim) {
|
||||||
|
trans = AnimPose(glm::vec3(1.0f), glm::quat(), boneLengthScale * (fbxAnimTrans + (fbxAnimTrans - fbxZeroTrans)));
|
||||||
|
} else {
|
||||||
|
trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans));
|
||||||
|
}
|
||||||
|
|
||||||
// rotation in fbxAnimationFrame is a delta from its preRotation.
|
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ class AnimClip : public AnimNode {
|
||||||
public:
|
public:
|
||||||
friend class AnimTests;
|
friend class AnimTests;
|
||||||
|
|
||||||
|
static bool usePreAndPostPoseFromAnim;
|
||||||
|
static bool useTransFromAnim;
|
||||||
|
|
||||||
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
|
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
|
||||||
virtual ~AnimClip() override;
|
virtual ~AnimClip() override;
|
||||||
|
|
||||||
|
|
|
@ -464,6 +464,10 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Rig::setEnableInverseKinematics(bool enable) {
|
||||||
|
_enableInverseKinematics = enable;
|
||||||
|
}
|
||||||
|
|
||||||
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
|
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
|
||||||
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
||||||
return _absoluteDefaultPoses[index];
|
return _absoluteDefaultPoses[index];
|
||||||
|
@ -705,6 +709,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
}
|
}
|
||||||
|
|
||||||
t += deltaTime;
|
t += deltaTime;
|
||||||
|
|
||||||
|
if (_enableInverseKinematics) {
|
||||||
|
_animVars.set("ikOverlayAlpha", 1.0f);
|
||||||
|
} else {
|
||||||
|
_animVars.set("ikOverlayAlpha", 0.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastFront = front;
|
_lastFront = front;
|
||||||
|
|
|
@ -208,6 +208,8 @@ public:
|
||||||
|
|
||||||
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
|
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
|
||||||
|
|
||||||
|
void setEnableInverseKinematics(bool enable);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||||
void updateAnimationStateHandlers();
|
void updateAnimationStateHandlers();
|
||||||
|
@ -290,6 +292,8 @@ public:
|
||||||
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
||||||
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
||||||
|
|
||||||
|
bool _enableInverseKinematics { true };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<int, StateHandler> _stateHandlers;
|
QMap<int, StateHandler> _stateHandlers;
|
||||||
int _nextStateHandlerId { 0 };
|
int _nextStateHandlerId { 0 };
|
||||||
|
|
Loading…
Reference in a new issue