diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp new file mode 100644 index 0000000000..6a420cde43 --- /dev/null +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -0,0 +1,87 @@ +// +// ScriptableAvatar.cpp +// +// +// Created by Clement on 7/22/14. +// Copyright 2014 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 + +#include "ScriptableAvatar.h" + +ScriptableAvatar::ScriptableAvatar(ScriptEngine* scriptEngine) : _scriptEngine(scriptEngine), _animation(NULL) { + connect(_scriptEngine, SIGNAL(update(float)), this, SLOT(update(float))); +} + +void ScriptableAvatar::startAnimation(const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), + Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame), + Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); + return; + } + _animation = _scriptEngine->getAnimationCache()->getAnimation(url); + _animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame); +} + +void ScriptableAvatar::stopAnimation(const QString& url) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url)); + return; + } + _animation.clear(); +} + +AnimationDetails ScriptableAvatar::getAnimationDetails(const QString& url) { + if (QThread::currentThread() != thread()) { + AnimationDetails result; + QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AnimationDetails, result), + Q_ARG(const QString&, url)); + return result; + } + return _animationDetails; +} + +void ScriptableAvatar::update(float deltatime) { + // Run animation + if (_animation != NULL && _animation->isValid() && _animation->getFrames().size() > 0) { + QStringList modelJoints = getJointNames(); + QStringList animationJoints = _animation->getJointNames(); + + if (_jointData.size() != modelJoints.size()) { + _jointData.resize(modelJoints.size()); + } + + float frameIndex = _animationDetails.frameIndex + deltatime * _animationDetails.fps; + if (_animationDetails.loop || frameIndex < _animationDetails.lastFrame) { + while (frameIndex >= _animationDetails.lastFrame) { + frameIndex -= (_animationDetails.lastFrame - _animationDetails.firstFrame); + } + _animationDetails.frameIndex = frameIndex; + + const int frameCount = _animation->getFrames().size(); + const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(frameIndex) % frameCount); + const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(frameIndex) % frameCount); + const float frameFraction = glm::fract(frameIndex); + + for (int i = 0; i < modelJoints.size(); i++) { + int mapping = animationJoints.indexOf(modelJoints[i]); + if (mapping != -1) { + JointData& data = _jointData[i]; + data.valid = true; + data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + } else if (i < _jointData.size()) { + _jointData[i].valid = false; + } + } + } else { + _animation.clear(); + } + } +} diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h new file mode 100644 index 0000000000..5548ef5a32 --- /dev/null +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -0,0 +1,42 @@ +// +// ScriptableAvatar.h +// +// +// Created by Clement on 7/22/14. +// Copyright 2014 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_ScriptableAvatar_h +#define hifi_ScriptableAvatar_h + +#include +#include +#include + +class ScriptableAvatar : public AvatarData { + Q_OBJECT +public: + ScriptableAvatar(ScriptEngine* scriptEngine); + + /// Allows scripts to run animations. + Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, + bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + + /// Stops an animation as identified by a URL. + Q_INVOKABLE void stopAnimation(const QString& url); + + Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); + +private slots: + void update(float deltatime); + +private: + ScriptEngine* _scriptEngine; + AnimationPointer _animation; + AnimationDetails _animationDetails; +}; + +#endif // hifi_ScriptableAvatar_h \ No newline at end of file