mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 13:43:49 +02:00
First pass integration of new anim system into rig.
This commit is contained in:
parent
2401e6cc4b
commit
7b4cb8655c
11 changed files with 198 additions and 158 deletions
|
@ -433,6 +433,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, false,
|
||||
avatar, SLOT(setEnableRigAnimations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, false,
|
||||
avatar, SLOT(setEnableAnimGraph(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu,
|
||||
MenuOption::Connexion,
|
||||
|
|
|
@ -187,6 +187,7 @@ namespace MenuOption {
|
|||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableAnimGraph = "Enable Anim Graph";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString EnableRigAnimations = "Enable Rig Animations";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
|
|
|
@ -36,11 +36,6 @@
|
|||
|
||||
#include "devices/Faceshift.h"
|
||||
|
||||
#include "AnimDebugDraw.h"
|
||||
#include "AnimSkeleton.h"
|
||||
#include "AnimClip.h"
|
||||
#include "AnimBlendLinear.h"
|
||||
#include "AnimOverlay.h"
|
||||
#include "Application.h"
|
||||
#include "AvatarManager.h"
|
||||
#include "Environment.h"
|
||||
|
@ -163,27 +158,6 @@ void MyAvatar::update(float deltaTime) {
|
|||
_goToPending = false;
|
||||
}
|
||||
|
||||
if (_animNode) {
|
||||
static float t = 0.0f;
|
||||
_animVars.set("sine", 0.5f * sin(t) + 0.5f);
|
||||
|
||||
if (glm::length(getVelocity()) > 0.07f) {
|
||||
_animVars.set("isMoving", true);
|
||||
_animVars.set("isNotMoving", false);
|
||||
} else {
|
||||
_animVars.set("isMoving", false);
|
||||
_animVars.set("isNotMoving", true);
|
||||
}
|
||||
|
||||
t += deltaTime;
|
||||
AnimNode::Triggers triggers;
|
||||
_animNode->evaluate(_animVars, deltaTime, triggers);
|
||||
_animVars.clearTriggers();
|
||||
for (auto& trigger : triggers) {
|
||||
_animVars.setTrigger(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
|
@ -737,6 +711,13 @@ void MyAvatar::setEnableRigAnimations(bool isEnabled) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableAnimGraph(bool isEnabled) {
|
||||
_rig->setEnableAnimGraph(isEnabled);
|
||||
if (!isEnabled) {
|
||||
// AJT: TODO: FIXME: currently setEnableAnimGraph menu item only works on startup
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::loadData() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
|
@ -1035,7 +1016,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_billboardValid = false;
|
||||
_skeletonModel.setVisibleInScene(true, scene);
|
||||
_headBoneSet.clear();
|
||||
teardownNewAnimationSystem();
|
||||
}
|
||||
|
||||
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
|
||||
|
@ -1232,48 +1212,6 @@ void MyAvatar::initHeadBones() {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setupNewAnimationSystem() {
|
||||
|
||||
// create a skeleton
|
||||
auto geom = _skeletonModel.getGeometry()->getFBXGeometry();
|
||||
std::vector<FBXJoint> joints;
|
||||
for (auto& joint : geom.joints) {
|
||||
joints.push_back(joint);
|
||||
}
|
||||
AnimPose geometryOffset(_skeletonModel.getGeometry()->getFBXGeometry().offset);
|
||||
_animSkeleton = make_shared<AnimSkeleton>(joints, geometryOffset);
|
||||
|
||||
// add skeleton to the debug renderer, so we can see it.
|
||||
AnimDebugDraw::getInstance().addSkeleton("my-avatar-skeleton", _animSkeleton, AnimPose::identity);
|
||||
//_animSkeleton->dump();
|
||||
|
||||
qCDebug(interfaceapp) << "geomOffset =" << geometryOffset;
|
||||
|
||||
// load the anim graph
|
||||
// https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9
|
||||
// python2 -m SimpleHTTPServer&
|
||||
auto graphUrl = QUrl("http://localhost:8000/avatar.json");
|
||||
//auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/403651948de088ca4dcdda4f873e225b091c779a/avatar.json");
|
||||
_animLoader.reset(new AnimNodeLoader(graphUrl));
|
||||
connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) {
|
||||
_animNode = nodeIn;
|
||||
_animNode->setSkeleton(_animSkeleton);
|
||||
AnimPose xform(glm::vec3(1), glm::quat(), glm::vec3(0, 0, -1));
|
||||
AnimDebugDraw::getInstance().addAnimNode("my-avatar-animation", _animNode, xform);
|
||||
});
|
||||
connect(_animLoader.get(), &AnimNodeLoader::error, [this, graphUrl](int error, QString str) {
|
||||
qCCritical(interfaceapp) << "Error loading" << graphUrl << "code = " << error << "str =" << str;
|
||||
});
|
||||
}
|
||||
|
||||
void MyAvatar::teardownNewAnimationSystem() {
|
||||
AnimDebugDraw::getInstance().removeSkeleton("my-avatar-skeleton");
|
||||
AnimDebugDraw::getInstance().removeAnimNode("my-avatar-animation");
|
||||
_animSkeleton = nullptr;
|
||||
_animLoader = nullptr;
|
||||
_animNode = nullptr;
|
||||
}
|
||||
|
||||
void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||
|
||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||
|
@ -1281,11 +1219,15 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
|||
|
||||
if (_skeletonModel.initWhenReady(scene)) {
|
||||
initHeadBones();
|
||||
|
||||
_skeletonModel.setCauterizeBoneSet(_headBoneSet);
|
||||
|
||||
// AJT: SETUP DEBUG RENDERING OF NEW ANIMATION SYSTEM
|
||||
// https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9
|
||||
// python2 -m SimpleHTTPServer&
|
||||
auto graphUrl = QUrl("http://localhost:8000/avatar.json");
|
||||
//auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/403651948de088ca4dcdda4f873e225b091c779a/avatar.json");
|
||||
|
||||
setupNewAnimationSystem();
|
||||
_skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry());
|
||||
}
|
||||
|
||||
if (shouldDrawHead != _prevShouldDrawHead) {
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
#include <DynamicCharacterController.h>
|
||||
#include <Rig.h>
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimNodeLoader.h"
|
||||
|
||||
#include "Avatar.h"
|
||||
|
||||
class ModelItemID;
|
||||
|
@ -194,6 +191,7 @@ public slots:
|
|||
|
||||
virtual void rebuildSkeletonBody();
|
||||
void setEnableRigAnimations(bool isEnabled);
|
||||
void setEnableAnimGraph(bool isEnabled);
|
||||
|
||||
signals:
|
||||
void transformChanged();
|
||||
|
@ -201,8 +199,6 @@ signals:
|
|||
void collisionWithEntity(const Collision& collision);
|
||||
|
||||
private:
|
||||
void setupNewAnimationSystem();
|
||||
void teardownNewAnimationSystem();
|
||||
|
||||
glm::vec3 getWorldBodyPosition() const;
|
||||
glm::quat getWorldBodyOrientation() const;
|
||||
|
@ -317,11 +313,6 @@ private:
|
|||
std::unordered_set<int> _headBoneSet;
|
||||
RigPointer _rig;
|
||||
bool _prevShouldDrawHead;
|
||||
|
||||
std::shared_ptr<AnimNode> _animNode;
|
||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||
std::unique_ptr<AnimNodeLoader> _animLoader;
|
||||
AnimVariantMap _animVars;
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -571,3 +571,7 @@ bool SkeletonModel::hasSkeleton() {
|
|||
|
||||
void SkeletonModel::onInvalidate() {
|
||||
}
|
||||
|
||||
void SkeletonModel::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) {
|
||||
_rig->initAnimGraph(url, fbxGeometry);
|
||||
}
|
||||
|
|
|
@ -106,6 +106,8 @@ public:
|
|||
|
||||
virtual void onInvalidate() override;
|
||||
|
||||
void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry);
|
||||
|
||||
signals:
|
||||
|
||||
void skeletonLoaded();
|
||||
|
|
|
@ -161,4 +161,20 @@ void AnimSkeleton::dump() const {
|
|||
}
|
||||
qCDebug(animation) << "]";
|
||||
}
|
||||
|
||||
void AnimSkeleton::dump(const AnimPoseVec& poses) const {
|
||||
qCDebug(animation) << "[";
|
||||
for (int i = 0; i < getNumJoints(); i++) {
|
||||
qCDebug(animation) << " {";
|
||||
qCDebug(animation) << " name =" << getJointName(i);
|
||||
qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i);
|
||||
qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i);
|
||||
qCDebug(animation) << " pose =" << poses[i];
|
||||
if (getParentIndex(i) >= 0) {
|
||||
qCDebug(animation) << " parent =" << getJointName(getParentIndex(i));
|
||||
}
|
||||
qCDebug(animation) << " },";
|
||||
}
|
||||
qCDebug(animation) << "]";
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
|
||||
#ifndef NDEBUG
|
||||
void dump() const;
|
||||
void dump(const AnimPoseVec& poses) const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
#include "AnimationHandle.h"
|
||||
#include "AnimationLogging.h"
|
||||
|
||||
#include "AnimSkeleton.h"
|
||||
|
||||
#include "Rig.h"
|
||||
|
||||
void Rig::HeadParameters::dump() const {
|
||||
|
@ -183,6 +186,12 @@ void Rig::deleteAnimations() {
|
|||
removeAnimationHandle(animation);
|
||||
}
|
||||
_animationHandles.clear();
|
||||
|
||||
if (_enableAnimGraph) {
|
||||
_animSkeleton = nullptr;
|
||||
_animLoader = nullptr;
|
||||
_animNode = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
|
||||
|
@ -406,6 +415,22 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
|
|||
}
|
||||
|
||||
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
||||
|
||||
if (_enableAnimGraph) {
|
||||
static float t = 0.0f;
|
||||
_animVars.set("sine", 0.5f * sin(t) + 0.5f);
|
||||
|
||||
if (glm::length(worldVelocity) > 0.07f) {
|
||||
_animVars.set("isMoving", true);
|
||||
_animVars.set("isNotMoving", false);
|
||||
} else {
|
||||
_animVars.set("isMoving", false);
|
||||
_animVars.set("isNotMoving", true);
|
||||
}
|
||||
|
||||
t += deltaTime;
|
||||
}
|
||||
|
||||
if (!_enableRig) {
|
||||
return;
|
||||
}
|
||||
|
@ -442,55 +467,85 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
}
|
||||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
||||
|
||||
// First normalize the fades so that they sum to 1.0.
|
||||
// update the fade data in each animation (not normalized as they are an independent propert of animation)
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
float fadePerSecond = handle->getFadePerSecond();
|
||||
float fade = handle->getFade();
|
||||
if (fadePerSecond != 0.0f) {
|
||||
fade += fadePerSecond * deltaTime;
|
||||
if ((0.0f >= fade) || (fade >= 1.0f)) {
|
||||
fade = glm::clamp(fade, 0.0f, 1.0f);
|
||||
handle->setFadePerSecond(0.0f);
|
||||
|
||||
if (_enableAnimGraph) {
|
||||
if (!_animNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// evaluate the animation
|
||||
AnimNode::Triggers triggersOut;
|
||||
AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
|
||||
_animVars.clearTriggers();
|
||||
for (auto& trigger : triggersOut) {
|
||||
_animVars.setTrigger(trigger);
|
||||
}
|
||||
|
||||
// copy poses into jointStates
|
||||
const float PRIORITY = 1.0f;
|
||||
const float MIX = 1.0f;
|
||||
for (size_t i = 0; i < poses.size(); i++) {
|
||||
setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].resetTransformChanged();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// First normalize the fades so that they sum to 1.0.
|
||||
// update the fade data in each animation (not normalized as they are an independent propert of animation)
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
float fadePerSecond = handle->getFadePerSecond();
|
||||
float fade = handle->getFade();
|
||||
if (fadePerSecond != 0.0f) {
|
||||
fade += fadePerSecond * deltaTime;
|
||||
if ((0.0f >= fade) || (fade >= 1.0f)) {
|
||||
fade = glm::clamp(fade, 0.0f, 1.0f);
|
||||
handle->setFadePerSecond(0.0f);
|
||||
}
|
||||
handle->setFade(fade);
|
||||
if (fade <= 0.0f) { // stop any finished animations now
|
||||
handle->setRunning(false, false); // but do not restore joints as it causes a flicker
|
||||
}
|
||||
}
|
||||
handle->setFade(fade);
|
||||
if (fade <= 0.0f) { // stop any finished animations now
|
||||
handle->setRunning(false, false); // but do not restore joints as it causes a flicker
|
||||
}
|
||||
}
|
||||
}
|
||||
// sum the remaining fade data
|
||||
float fadeTotal = 0.0f;
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
fadeTotal += handle->getFade();
|
||||
}
|
||||
float fadeSumSoFar = 0.0f;
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
handle->setPriority(1.0f);
|
||||
// if no fadeTotal, everyone's (typically just one running) is starting at zero. In that case, blend equally.
|
||||
float normalizedFade = (fadeTotal != 0.0f) ? (handle->getFade() / fadeTotal) : (1.0f / _runningAnimations.count());
|
||||
assert(normalizedFade != 0.0f);
|
||||
// simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step.
|
||||
// i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result.
|
||||
// The formula here for mix is based on the idea that, at each step:
|
||||
// fadeSum is to normalizedFade, as (1 - mix) is to mix
|
||||
// i.e., fadeSumSoFar/normalizedFade = (1 - mix)/mix
|
||||
// Then we solve for mix.
|
||||
// Sanity check: For the first animation, fadeSum = 0, and the mix will always be 1.
|
||||
// Sanity check: For equal blending, the formula is equivalent to mix = 1 / nAnimationsSoFar++
|
||||
float mix = 1.0f / ((fadeSumSoFar / normalizedFade) + 1.0f);
|
||||
assert((0.0f <= mix) && (mix <= 1.0f));
|
||||
fadeSumSoFar += normalizedFade;
|
||||
handle->setMix(mix);
|
||||
handle->simulate(deltaTime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].resetTransformChanged();
|
||||
}
|
||||
// sum the remaining fade data
|
||||
float fadeTotal = 0.0f;
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
fadeTotal += handle->getFade();
|
||||
}
|
||||
float fadeSumSoFar = 0.0f;
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
handle->setPriority(1.0f);
|
||||
// if no fadeTotal, everyone's (typically just one running) is starting at zero. In that case, blend equally.
|
||||
float normalizedFade = (fadeTotal != 0.0f) ? (handle->getFade() / fadeTotal) : (1.0f / _runningAnimations.count());
|
||||
assert(normalizedFade != 0.0f);
|
||||
// simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step.
|
||||
// i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result.
|
||||
// The formula here for mix is based on the idea that, at each step:
|
||||
// fadeSum is to normalizedFade, as (1 - mix) is to mix
|
||||
// i.e., fadeSumSoFar/normalizedFade = (1 - mix)/mix
|
||||
// Then we solve for mix.
|
||||
// Sanity check: For the first animation, fadeSum = 0, and the mix will always be 1.
|
||||
// Sanity check: For equal blending, the formula is equivalent to mix = 1 / nAnimationsSoFar++
|
||||
float mix = 1.0f / ((fadeSumSoFar / normalizedFade) + 1.0f);
|
||||
assert((0.0f <= mix) && (mix <= 1.0f));
|
||||
fadeSumSoFar += normalizedFade;
|
||||
handle->setMix(mix);
|
||||
handle->simulate(deltaTime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].resetTransformChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -824,6 +879,7 @@ void Rig::updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3&
|
|||
updateEyeJoint(leftEyeIndex, modelTranslation, modelRotation, worldHeadOrientation, lookAtSpot, saccade);
|
||||
updateEyeJoint(rightEyeIndex, modelTranslation, modelRotation, worldHeadOrientation, lookAtSpot, saccade);
|
||||
}
|
||||
|
||||
void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) {
|
||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||
auto& state = _jointStates[index];
|
||||
|
@ -842,3 +898,38 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
state.getDefaultRotation(), DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) {
|
||||
if (!_enableAnimGraph) {
|
||||
return;
|
||||
}
|
||||
|
||||
// convert to std::vector of joints
|
||||
std::vector<FBXJoint> joints;
|
||||
joints.reserve(fbxGeometry.joints.size());
|
||||
for (auto& joint : fbxGeometry.joints) {
|
||||
joints.push_back(joint);
|
||||
}
|
||||
|
||||
// create skeleton
|
||||
AnimPose geometryOffset(fbxGeometry.offset);
|
||||
_animSkeleton = std::make_shared<AnimSkeleton>(joints, geometryOffset);
|
||||
|
||||
// add skeleton to the debug renderer, so we can see it.
|
||||
// AnimDebugDraw::getInstance().addSkeleton("my-avatar-skeleton", _animSkeleton, AnimPose::identity);
|
||||
// _animSkeleton->dump();
|
||||
|
||||
// load the anim graph
|
||||
_animLoader.reset(new AnimNodeLoader(url));
|
||||
connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) {
|
||||
_animNode = nodeIn;
|
||||
_animNode->setSkeleton(_animSkeleton);
|
||||
|
||||
// add node to debug renderer, for debugging
|
||||
// AnimPose xform(glm::vec3(1), glm::quat(), glm::vec3(0, 0, -1));
|
||||
// AnimDebugDraw::getInstance().addAnimNode("my-avatar-animation", _animNode, xform);
|
||||
});
|
||||
connect(_animLoader.get(), &AnimNodeLoader::error, [this, url](int error, QString str) {
|
||||
qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -40,6 +40,9 @@
|
|||
|
||||
#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimNodeLoader.h"
|
||||
|
||||
class AnimationHandle;
|
||||
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
|
||||
|
||||
|
@ -155,6 +158,7 @@ public:
|
|||
virtual void updateJointState(int index, glm::mat4 parentTransform) = 0;
|
||||
|
||||
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
|
||||
void setEnableAnimGraph(bool isEnabled) { _enableAnimGraph = isEnabled; }
|
||||
|
||||
void updateFromHeadParameters(const HeadParameters& params);
|
||||
void updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation,
|
||||
|
@ -163,6 +167,8 @@ public:
|
|||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority) = 0;
|
||||
|
||||
void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry);
|
||||
|
||||
protected:
|
||||
|
||||
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
||||
|
@ -184,8 +190,14 @@ public:
|
|||
QList<AnimationHandlePointer> _runningAnimations;
|
||||
|
||||
bool _enableRig;
|
||||
bool _enableAnimGraph;
|
||||
glm::vec3 _lastFront;
|
||||
glm::vec3 _lastPosition;
|
||||
|
||||
std::shared_ptr<AnimNode> _animNode;
|
||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||
std::unique_ptr<AnimNodeLoader> _animLoader;
|
||||
AnimVariantMap _animVars;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Rig__) */
|
||||
|
|
|
@ -27,37 +27,15 @@
|
|||
"children": [
|
||||
{
|
||||
"id": "idle",
|
||||
"type": "blendLinear",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"alpha": 0.5,
|
||||
"alphaVar": "sine"
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "normalIdle",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "otherIdle",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"startFrame": 20.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "walkFwd",
|
||||
|
|
Loading…
Reference in a new issue