mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 09:48:44 +02:00
Added AnimBlendLinear + tests.
MyAvatar now does a sine wave blend between a walk and a walk animation.
This commit is contained in:
parent
da3d35cdfc
commit
5d83976e2a
12 changed files with 211 additions and 44 deletions
|
@ -52,6 +52,7 @@
|
||||||
#include "AnimDebugDraw.h"
|
#include "AnimDebugDraw.h"
|
||||||
#include "AnimSkeleton.h"
|
#include "AnimSkeleton.h"
|
||||||
#include "AnimClip.h"
|
#include "AnimClip.h"
|
||||||
|
#include "AnimBlendLinear.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -150,6 +151,10 @@ void MyAvatar::reset() {
|
||||||
void MyAvatar::update(float deltaTime) {
|
void MyAvatar::update(float deltaTime) {
|
||||||
|
|
||||||
if (_animNode) {
|
if (_animNode) {
|
||||||
|
static float t = 0.0f;
|
||||||
|
auto blend = std::static_pointer_cast<AnimBlendLinear>(_animNode);
|
||||||
|
blend->setAlpha(0.5f * sin(t) + 0.5f);
|
||||||
|
t += deltaTime;
|
||||||
_animNode->evaluate(deltaTime);
|
_animNode->evaluate(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1203,16 +1208,7 @@ void MyAvatar::initHeadBones() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::preRender(RenderArgs* renderArgs) {
|
void MyAvatar::setupNewAnimationSystem() {
|
||||||
|
|
||||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
|
||||||
const bool shouldDrawHead = shouldRenderHead(renderArgs);
|
|
||||||
|
|
||||||
if (_skeletonModel.initWhenReady(scene)) {
|
|
||||||
initHeadBones();
|
|
||||||
_skeletonModel.setCauterizeBoneSet(_headBoneSet);
|
|
||||||
|
|
||||||
// AJT: SETUP DEBUG RENDERING OF NEW ANIMATION SYSTEM
|
|
||||||
|
|
||||||
// create a skeleton and hand it over to the debug draw instance
|
// create a skeleton and hand it over to the debug draw instance
|
||||||
auto geom = _skeletonModel.getGeometry()->getFBXGeometry();
|
auto geom = _skeletonModel.getGeometry()->getFBXGeometry();
|
||||||
|
@ -1224,10 +1220,30 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||||
AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset());
|
AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset());
|
||||||
AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform);
|
AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform);
|
||||||
|
|
||||||
_animNode = make_shared<AnimClip>("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true);
|
// create a blend node
|
||||||
|
auto blend = make_shared<AnimBlendLinear>("blend", 0.5f);
|
||||||
|
auto idle = make_shared<AnimClip>("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true);
|
||||||
|
auto walk = make_shared<AnimClip>("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", 0.0f, 29.0f, 1.0f, true);
|
||||||
|
blend->addChild(idle);
|
||||||
|
blend->addChild(walk);
|
||||||
|
_animNode = blend;
|
||||||
_animNode->setSkeleton(skeleton);
|
_animNode->setSkeleton(skeleton);
|
||||||
xform.trans.z += 1.0f;
|
xform.trans.z += 1.0f;
|
||||||
AnimDebugDraw::getInstance().addAnimNode("clip", _animNode, xform);
|
AnimDebugDraw::getInstance().addAnimNode("blend", _animNode, xform);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||||
|
|
||||||
|
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||||
|
const bool shouldDrawHead = shouldRenderHead(renderArgs);
|
||||||
|
|
||||||
|
if (_skeletonModel.initWhenReady(scene)) {
|
||||||
|
initHeadBones();
|
||||||
|
_skeletonModel.setCauterizeBoneSet(_headBoneSet);
|
||||||
|
|
||||||
|
// AJT: SETUP DEBUG RENDERING OF NEW ANIMATION SYSTEM
|
||||||
|
|
||||||
|
setupNewAnimationSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldDrawHead != _prevShouldDrawHead) {
|
if (shouldDrawHead != _prevShouldDrawHead) {
|
||||||
|
|
|
@ -189,6 +189,7 @@ signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void setupNewAnimationSystem();
|
||||||
QByteArray toByteArray();
|
QByteArray toByteArray();
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
void updateFromTrackers(float deltaTime);
|
void updateFromTrackers(float deltaTime);
|
||||||
|
|
59
libraries/animation/src/AnimBlendLinear.cpp
Normal file
59
libraries/animation/src/AnimBlendLinear.cpp
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//
|
||||||
|
// AnimBlendLinear.cpp
|
||||||
|
//
|
||||||
|
// Copyright 2015 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 "AnimBlendLinear.h"
|
||||||
|
#include "GLMHelpers.h"
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
|
||||||
|
AnimBlendLinear::AnimBlendLinear(const std::string& id, float alpha) :
|
||||||
|
AnimNode(AnimNode::BlendLinearType, id),
|
||||||
|
_alpha(alpha) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimBlendLinear::~AnimBlendLinear() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<AnimPose>& AnimBlendLinear::evaluate(float dt) {
|
||||||
|
|
||||||
|
if (_children.size() == 0) {
|
||||||
|
for (auto&& pose : _poses) {
|
||||||
|
pose = AnimPose::identity;
|
||||||
|
}
|
||||||
|
} else if (_children.size() == 1) {
|
||||||
|
_poses = _children[0]->evaluate(dt);
|
||||||
|
} else {
|
||||||
|
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
||||||
|
size_t prevPoseIndex = glm::floor(clampedAlpha);
|
||||||
|
size_t nextPoseIndex = glm::ceil(clampedAlpha);
|
||||||
|
float alpha = glm::fract(clampedAlpha);
|
||||||
|
if (prevPoseIndex != nextPoseIndex) {
|
||||||
|
auto prevPoses = _children[prevPoseIndex]->evaluate(dt);
|
||||||
|
auto nextPoses = _children[nextPoseIndex]->evaluate(dt);
|
||||||
|
|
||||||
|
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
|
||||||
|
_poses.resize(prevPoses.size());
|
||||||
|
for (size_t i = 0; i < _poses.size(); i++) {
|
||||||
|
const AnimPose& prevPose = prevPoses[i];
|
||||||
|
const AnimPose& nextPose = nextPoses[i];
|
||||||
|
_poses[i].scale = lerp(prevPose.scale, nextPose.scale, alpha);
|
||||||
|
_poses[i].rot = glm::normalize(glm::lerp(prevPose.rot, nextPose.rot, alpha));
|
||||||
|
_poses[i].trans = lerp(prevPose.trans, nextPose.trans, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
const std::vector<AnimPose>& AnimBlendLinear::getPosesInternal() const {
|
||||||
|
return _poses;
|
||||||
|
}
|
39
libraries/animation/src/AnimBlendLinear.h
Normal file
39
libraries/animation/src/AnimBlendLinear.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// AnimBlendLinear.h
|
||||||
|
//
|
||||||
|
// Copyright 2015 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 "AnimNode.h"
|
||||||
|
|
||||||
|
#ifndef hifi_AnimBlendLinear_h
|
||||||
|
#define hifi_AnimBlendLinear_h
|
||||||
|
|
||||||
|
class AnimBlendLinear : public AnimNode {
|
||||||
|
public:
|
||||||
|
|
||||||
|
AnimBlendLinear(const std::string& id, float alpha);
|
||||||
|
virtual ~AnimBlendLinear() override;
|
||||||
|
|
||||||
|
virtual const std::vector<AnimPose>& evaluate(float dt) override;
|
||||||
|
|
||||||
|
void setAlpha(float alpha) { _alpha = alpha; }
|
||||||
|
float getAlpha() const { return _alpha; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
virtual const std::vector<AnimPose>& getPosesInternal() const override;
|
||||||
|
|
||||||
|
std::vector<AnimPose> _poses;
|
||||||
|
|
||||||
|
float _alpha;
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimBlendLinear(const AnimBlendLinear&) = delete;
|
||||||
|
AnimBlendLinear& operator=(const AnimBlendLinear&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimBlendLinear_h
|
|
@ -103,11 +103,11 @@ const std::vector<AnimPose>& AnimClip::evaluate(float dt) {
|
||||||
float alpha = glm::fract(_frame);
|
float alpha = glm::fract(_frame);
|
||||||
|
|
||||||
for (size_t i = 0; i < _poses.size(); i++) {
|
for (size_t i = 0; i < _poses.size(); i++) {
|
||||||
const AnimPose& prevBone = prevFrame[i];
|
const AnimPose& prevPose = prevFrame[i];
|
||||||
const AnimPose& nextBone = nextFrame[i];
|
const AnimPose& nextPose = nextFrame[i];
|
||||||
_poses[i].scale = lerp(prevBone.scale, nextBone.scale, alpha);
|
_poses[i].scale = lerp(prevPose.scale, nextPose.scale, alpha);
|
||||||
_poses[i].rot = glm::normalize(glm::lerp(prevBone.rot, nextBone.rot, alpha));
|
_poses[i].rot = glm::normalize(glm::lerp(prevPose.rot, nextPose.rot, alpha));
|
||||||
_poses[i].trans = lerp(prevBone.trans, nextBone.trans, alpha);
|
_poses[i].trans = lerp(prevPose.trans, nextPose.trans, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
ClipType = 0,
|
ClipType = 0,
|
||||||
|
BlendLinearType,
|
||||||
NumTypes
|
NumTypes
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<AnimNode> Pointer;
|
typedef std::shared_ptr<AnimNode> Pointer;
|
||||||
|
@ -49,7 +50,13 @@ public:
|
||||||
}
|
}
|
||||||
int getChildCount() const { return (int)_children.size(); }
|
int getChildCount() const { return (int)_children.size(); }
|
||||||
|
|
||||||
void setSkeleton(AnimSkeleton::Pointer skeleton) { _skeleton = skeleton; }
|
void setSkeleton(AnimSkeleton::Pointer skeleton) {
|
||||||
|
setSkeletonInternal(skeleton);
|
||||||
|
for (auto&& child : _children) {
|
||||||
|
child->setSkeletonInternal(skeleton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AnimSkeleton::Pointer getSkeleton() const { return _skeleton; }
|
AnimSkeleton::Pointer getSkeleton() const { return _skeleton; }
|
||||||
|
|
||||||
virtual ~AnimNode() {}
|
virtual ~AnimNode() {}
|
||||||
|
@ -57,6 +64,11 @@ public:
|
||||||
virtual const std::vector<AnimPose>& evaluate(float dt) = 0;
|
virtual const std::vector<AnimPose>& evaluate(float dt) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
virtual void setSkeletonInternal(AnimSkeleton::Pointer skeleton) {
|
||||||
|
_skeleton = skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
virtual const std::vector<AnimPose>& getPosesInternal() const = 0;
|
virtual const std::vector<AnimPose>& getPosesInternal() const = 0;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "AnimNode.h"
|
#include "AnimNode.h"
|
||||||
#include "AnimClip.h"
|
#include "AnimClip.h"
|
||||||
|
#include "AnimBlendLinear.h"
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimNodeLoader.h"
|
#include "AnimNodeLoader.h"
|
||||||
|
|
||||||
|
@ -25,15 +26,18 @@ struct TypeInfo {
|
||||||
// This will result in a compile error if someone adds a new
|
// This will result in a compile error if someone adds a new
|
||||||
// item to the AnimNode::Type enum. This is by design.
|
// item to the AnimNode::Type enum. This is by design.
|
||||||
static TypeInfo typeInfoArray[AnimNode::NumTypes] = {
|
static TypeInfo typeInfoArray[AnimNode::NumTypes] = {
|
||||||
{ AnimNode::ClipType, "clip" }
|
{ AnimNode::ClipType, "clip" },
|
||||||
|
{ AnimNode::BlendLinearType, "blendLinear" }
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl);
|
typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl);
|
||||||
|
|
||||||
static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl);
|
static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl);
|
||||||
|
static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl);
|
||||||
|
|
||||||
static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = {
|
static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = {
|
||||||
loadClipNode
|
loadClipNode,
|
||||||
|
loadBlendLinearNode
|
||||||
};
|
};
|
||||||
|
|
||||||
#define READ_STRING(NAME, JSON_OBJ, ID, URL) \
|
#define READ_STRING(NAME, JSON_OBJ, ID, URL) \
|
||||||
|
@ -132,6 +136,17 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
|
||||||
return std::make_shared<AnimClip>(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag);
|
return std::make_shared<AnimClip>(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) {
|
||||||
|
|
||||||
|
READ_FLOAT(alpha, jsonObj, id, jsonUrl);
|
||||||
|
|
||||||
|
return std::make_shared<AnimBlendLinear>(id.toStdString(), alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimNodeLoader::AnimNodeLoader() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const {
|
AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const {
|
||||||
// load entire file into a string.
|
// load entire file into a string.
|
||||||
QString jsonUrl = QString::fromStdString(filename);
|
QString jsonUrl = QString::fromStdString(filename);
|
||||||
|
@ -145,7 +160,12 @@ AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const {
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
// convert string into a json doc
|
// convert string into a json doc
|
||||||
auto doc = QJsonDocument::fromJson(contents.toUtf8());
|
QJsonParseError error;
|
||||||
|
auto doc = QJsonDocument::fromJson(contents.toUtf8(), &error);
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, failed to parse json, error =" << error.errorString() << ", url =" << jsonUrl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
QJsonObject obj = doc.object();
|
QJsonObject obj = doc.object();
|
||||||
|
|
||||||
// version
|
// version
|
||||||
|
|
|
@ -16,8 +16,13 @@ class AnimNode;
|
||||||
|
|
||||||
class AnimNodeLoader {
|
class AnimNodeLoader {
|
||||||
public:
|
public:
|
||||||
|
AnimNodeLoader();
|
||||||
// TODO: load from url
|
// TODO: load from url
|
||||||
std::shared_ptr<AnimNode> load(const std::string& filename) const;
|
std::shared_ptr<AnimNode> load(const std::string& filename) const;
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimNodeLoader(const AnimNodeLoader&) = delete;
|
||||||
|
AnimNodeLoader& operator=(const AnimNodeLoader&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AnimNodeLoader
|
#endif // hifi_AnimNodeLoader
|
||||||
|
|
|
@ -56,6 +56,10 @@ protected:
|
||||||
std::vector<FBXJoint> _joints;
|
std::vector<FBXJoint> _joints;
|
||||||
std::vector<AnimPose> _absoluteBindPoses;
|
std::vector<AnimPose> _absoluteBindPoses;
|
||||||
std::vector<AnimPose> _relativeBindPoses;
|
std::vector<AnimPose> _relativeBindPoses;
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimSkeleton(const AnimSkeleton&) = delete;
|
||||||
|
AnimSkeleton& operator=(const AnimSkeleton&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -45,6 +45,10 @@ protected:
|
||||||
|
|
||||||
std::unordered_map<std::string, SkeletonInfo> _skeletons;
|
std::unordered_map<std::string, SkeletonInfo> _skeletons;
|
||||||
std::unordered_map<std::string, AnimNodeInfo> _animNodes;
|
std::unordered_map<std::string, AnimNodeInfo> _animNodes;
|
||||||
|
|
||||||
|
// no copies
|
||||||
|
AnimDebugDraw(const AnimDebugDraw&) = delete;
|
||||||
|
AnimDebugDraw& operator=(const AnimDebugDraw&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AnimDebugDraw
|
#endif // hifi_AnimDebugDraw
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "AnimClipTests.h"
|
#include "AnimClipTests.h"
|
||||||
#include "AnimNodeLoader.h"
|
#include "AnimNodeLoader.h"
|
||||||
#include "AnimClip.h"
|
#include "AnimClip.h"
|
||||||
|
#include "AnimBlendLinear.h"
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
|
|
||||||
#include <../QTestExtensions.h>
|
#include <../QTestExtensions.h>
|
||||||
|
@ -103,24 +104,34 @@ void AnimClipTests::testLoader() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QVERIFY((bool)node);
|
QVERIFY((bool)node);
|
||||||
QVERIFY(node->getID() == "idle");
|
QVERIFY(node->getID() == "blend");
|
||||||
QVERIFY(node->getType() == AnimNode::ClipType);
|
QVERIFY(node->getType() == AnimNode::BlendLinearType);
|
||||||
|
|
||||||
auto clip = std::static_pointer_cast<AnimClip>(node);
|
auto blend = std::static_pointer_cast<AnimBlendLinear>(node);
|
||||||
|
QVERIFY(blend->getAlpha() == 0.5f);
|
||||||
|
|
||||||
QVERIFY(clip->getURL() == "idle.fbx");
|
QVERIFY(node->getChildCount() == 3);
|
||||||
QVERIFY(clip->getStartFrame() == 0.0f);
|
|
||||||
QVERIFY(clip->getEndFrame() == 30.0f);
|
|
||||||
QVERIFY(clip->getTimeScale() == 1.0f);
|
|
||||||
QVERIFY(clip->getLoopFlag() == true);
|
|
||||||
|
|
||||||
QVERIFY(clip->getChildCount() == 3);
|
std::shared_ptr<AnimNode> nodes[3] = { node->getChild(0), node->getChild(1), node->getChild(2) };
|
||||||
|
|
||||||
std::shared_ptr<AnimNode> nodes[3] = { clip->getChild(0), clip->getChild(1), clip->getChild(2) };
|
|
||||||
QVERIFY(nodes[0]->getID() == "test01");
|
QVERIFY(nodes[0]->getID() == "test01");
|
||||||
QVERIFY(nodes[0]->getChildCount() == 0);
|
QVERIFY(nodes[0]->getChildCount() == 0);
|
||||||
QVERIFY(nodes[1]->getID() == "test02");
|
QVERIFY(nodes[1]->getID() == "test02");
|
||||||
QVERIFY(nodes[1]->getChildCount() == 0);
|
QVERIFY(nodes[1]->getChildCount() == 0);
|
||||||
QVERIFY(nodes[2]->getID() == "test03");
|
QVERIFY(nodes[2]->getID() == "test03");
|
||||||
QVERIFY(nodes[2]->getChildCount() == 0);
|
QVERIFY(nodes[2]->getChildCount() == 0);
|
||||||
|
|
||||||
|
auto test01 = std::static_pointer_cast<AnimClip>(nodes[0]);
|
||||||
|
QVERIFY(test01->getURL() == "test01.fbx");
|
||||||
|
QVERIFY(test01->getStartFrame() == 1.0f);
|
||||||
|
QVERIFY(test01->getEndFrame() == 20.0f);
|
||||||
|
QVERIFY(test01->getTimeScale() == 1.0f);
|
||||||
|
QVERIFY(test01->getLoopFlag() == false);
|
||||||
|
|
||||||
|
auto test02 = std::static_pointer_cast<AnimClip>(nodes[1]);
|
||||||
|
QVERIFY(test02->getURL() == "test02.fbx");
|
||||||
|
QVERIFY(test02->getStartFrame() == 2.0f);
|
||||||
|
QVERIFY(test02->getEndFrame() == 21.0f);
|
||||||
|
QVERIFY(test02->getTimeScale() == 0.9f);
|
||||||
|
QVERIFY(test02->getLoopFlag() == true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
{
|
{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"root": {
|
"root": {
|
||||||
"id": "idle",
|
"id": "blend",
|
||||||
"type": "clip",
|
"type": "blendLinear",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "idle.fbx",
|
"alpha": 0.5
|
||||||
"startFrame": 0.0,
|
|
||||||
"endFrame": 30.0,
|
|
||||||
"timeScale": 1.0,
|
|
||||||
"loopFlag": true
|
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
|
@ -29,9 +25,9 @@
|
||||||
"data": {
|
"data": {
|
||||||
"url": "test02.fbx",
|
"url": "test02.fbx",
|
||||||
"startFrame": 2.0,
|
"startFrame": 2.0,
|
||||||
"endFrame": 20.0,
|
"endFrame": 21.0,
|
||||||
"timeScale": 1.0,
|
"timeScale": 0.9,
|
||||||
"loopFlag": false
|
"loopFlag": true
|
||||||
},
|
},
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue