From 35196a00593db15b03f13b3c507ee93d250f0ce1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 30 Jul 2015 22:07:05 -0700 Subject: [PATCH] bare-bones AnimClip implementation with tests! It accumulates time and handles looping, and should handle onDone and onLoop events in the future. --- libraries/animation/src/AnimClip.cpp | 77 ++++++++++++++++++++++++ libraries/animation/src/AnimClip.h | 38 +++++++++--- libraries/animation/src/AnimNode.h | 7 ++- tests/animation/CMakeLists.txt | 2 +- tests/animation/src/AnimClipTests.cpp | 85 +++++++++++++++++++++++++++ tests/animation/src/AnimClipTests.h | 27 +++++++++ 6 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 libraries/animation/src/AnimClip.cpp create mode 100644 tests/animation/src/AnimClipTests.cpp create mode 100644 tests/animation/src/AnimClipTests.h diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp new file mode 100644 index 0000000000..5b0284c08b --- /dev/null +++ b/libraries/animation/src/AnimClip.cpp @@ -0,0 +1,77 @@ +// +// AnimClip.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 "AnimClip.h" +#include "AnimationLogging.h" + +AnimClip::AnimClip(const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : + _url(url), + _startFrame(startFrame), + _endFrame(endFrame), + _timeScale(timeScale), + _frame(startFrame), + _loopFlag(loopFlag) +{ + +} + +AnimClip::~AnimClip() { + +} + +void AnimClip::setURL(const std::string& url) { + // TODO: + _url = url; +} + +void AnimClip::setStartFrame(float startFrame) { + _startFrame = startFrame; +} + +void AnimClip::setEndFrame(float endFrame) { + _endFrame = endFrame; +} + +void AnimClip::setLoopFlag(bool loopFlag) { + _loopFlag = loopFlag; +} + +const AnimPose& AnimClip::evaluate(float dt) { + const float startFrame = std::min(_startFrame, _endFrame); + if (startFrame == _endFrame) { + // when startFrame >= endFrame + _frame = _endFrame; + } else if (_timeScale > 0.0f) { + // accumulate time, keeping track of loops and end of animation events. + const float FRAMES_PER_SECOND = 30.0f; + float framesRemaining = (dt * _timeScale) * FRAMES_PER_SECOND; + while (framesRemaining > 0.0f) { + float framesTillEnd = _endFrame - _frame; + if (framesRemaining >= framesTillEnd) { + if (_loopFlag) { + // anim loop + // TODO: trigger onLoop event + framesRemaining -= framesTillEnd; + _frame = startFrame; + } else { + // anim end + // TODO: trigger onDone event + _frame = _endFrame; + framesRemaining = 0.0f; + } + } else { + _frame += framesRemaining; + framesRemaining = 0.0f; + } + } + } + // TODO: eval animation + + return _frame; +} diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 15c6dffae9..60ef8e74f2 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -10,17 +10,41 @@ #ifndef hifi_AnimClip_h #define hifi_AnimClip_h +#include +#include "AnimationCache.h" +#include "AnimNode.h" + class AnimClip : public AnimNode { +public: + friend class AnimClipTests; + + AnimClip(const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); + virtual ~AnimClip(); void setURL(const std::string& url); - void setStartFrame(AnimFrame startFrame); - void setEndFrame(AnimFrame startFrame); - void setLoopFlag(bool loopFlag); - void setTimeScale(float timeScale); + const std::string& getURL() const { return _url; } -public: - virtual const float getEnd() const; - virtual const AnimPose& evaluate(float t); + void setStartFrame(float startFrame); + float getStartFrame() const { return _startFrame; } + + void setEndFrame(float endFrame); + float getEndFrame() const { return _endFrame; } + + void setTimeScale(float timeScale) { _timeScale = timeScale; } + float getTimeScale() const { return _timeScale; } + + void setLoopFlag(bool loopFlag); + bool getLoopFlag() const { return _loopFlag; } + + virtual const AnimPose& evaluate(float dt); +protected: + AnimationPointer _anim; + std::string _url; + float _startFrame; + float _endFrame; + float _timeScale; + float _frame; + bool _loopFlag; }; #endif // hifi_AnimClip_h diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 648138d06b..8e78012865 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -10,10 +10,13 @@ #ifndef hifi_AnimNode_h #define hifi_AnimNode_h +typedef float AnimPose; + class AnimNode { public: - virtual float getEnd() const = 0; - virtual const AnimPose& evaluate(float t) = 0; + virtual ~AnimNode() {} + + virtual const AnimPose& evaluate(float dt) = 0; }; #endif // hifi_AnimNode_h diff --git a/tests/animation/CMakeLists.txt b/tests/animation/CMakeLists.txt index 2e9dbc9424..a66e391f69 100644 --- a/tests/animation/CMakeLists.txt +++ b/tests/animation/CMakeLists.txt @@ -1,7 +1,7 @@ # Declare dependencies macro (setup_testcase_dependencies) # link in the shared libraries - link_hifi_libraries(shared animation gpu fbx model) + link_hifi_libraries(shared animation gpu fbx model networking) copy_dlls_beside_windows_executable() endmacro () diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimClipTests.cpp new file mode 100644 index 0000000000..68f92afb2f --- /dev/null +++ b/tests/animation/src/AnimClipTests.cpp @@ -0,0 +1,85 @@ +// +// AnimClipTests.cpp +// tests/rig/src +// +// 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 "AnimClipTests.h" +#include "AnimClip.h" +#include "AnimationLogging.h" + +#include <../QTestExtensions.h> + +QTEST_MAIN(AnimClipTests) + +const float EPSILON = 0.001f; + +void AnimClipTests::testAccessors() { + std::string url = "foo"; + float startFrame = 2.0f; + float endFrame = 20.0f; + float timeScale = 1.1f; + float loopFlag = true; + + AnimClip clip(url, startFrame, endFrame, timeScale, loopFlag); + QVERIFY(clip.getURL() == url); + QVERIFY(clip.getStartFrame() == startFrame); + QVERIFY(clip.getEndFrame() == endFrame); + QVERIFY(clip.getTimeScale() == timeScale); + QVERIFY(clip.getLoopFlag() == loopFlag); + + std::string url2 = "bar"; + float startFrame2 = 22.0f; + float endFrame2 = 100.0f; + float timeScale2 = 1.2f; + float loopFlag2 = false; + + clip.setURL(url2); + clip.setStartFrame(startFrame2); + clip.setEndFrame(endFrame2); + clip.setTimeScale(timeScale2); + clip.setLoopFlag(loopFlag2); + + QVERIFY(clip.getURL() == url2); + QVERIFY(clip.getStartFrame() == startFrame2); + QVERIFY(clip.getEndFrame() == endFrame2); + QVERIFY(clip.getTimeScale() == timeScale2); + QVERIFY(clip.getLoopFlag() == loopFlag2); +} + +static float secsToFrames(float secs) { + const float FRAMES_PER_SECOND = 30.0f; + return secs * FRAMES_PER_SECOND; +} + +static float framesToSec(float secs) { + const float FRAMES_PER_SECOND = 30.0f; + return secs / FRAMES_PER_SECOND; +} + +void AnimClipTests::testEvaulate() { + std::string url = "foo"; + float startFrame = 2.0f; + float endFrame = 22.0f; + float timeScale = 1.0f; + float loopFlag = true; + + AnimClip clip(url, startFrame, endFrame, timeScale, loopFlag); + + clip.evaluate(framesToSec(10.0f)); + QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, EPSILON); + + // does it loop? + clip.evaluate(framesToSec(11.0f)); + QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); + + // does it pause at end? + clip.setLoopFlag(false); + clip.evaluate(framesToSec(20.0f)); + QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, EPSILON); +} + diff --git a/tests/animation/src/AnimClipTests.h b/tests/animation/src/AnimClipTests.h new file mode 100644 index 0000000000..f49690d400 --- /dev/null +++ b/tests/animation/src/AnimClipTests.h @@ -0,0 +1,27 @@ +// +// AnimClipTests.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 +// + +#ifndef hifi_AnimClipTests_h +#define hifi_AnimClipTests_h + +#include +#include + +inline float getErrorDifference(float a, float b) { + return fabs(a - b); +} + +class AnimClipTests : public QObject { + Q_OBJECT +private slots: + void testAccessors(); + void testEvaulate(); +}; + +#endif // hifi_TransformTests_h