From d4199e6092800672a72b8b977775c6272562d355 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 23 Jul 2014 13:59:21 -0700 Subject: [PATCH 1/6] Edit properties tweaks --- examples/editModels.js | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 458ddf7b4a..6a692394ba 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -748,18 +748,19 @@ function Tooltip() { this.updateText = function(properties) { var angles = Quat.safeEulerAngles(properties.modelRotation); var text = "Model Properties:\n" - text += "x: " + properties.position.x.toFixed(this.decimals) + "\n" - text += "y: " + properties.position.y.toFixed(this.decimals) + "\n" - text += "z: " + properties.position.z.toFixed(this.decimals) + "\n" - text += "pitch: " + angles.x.toFixed(this.decimals) + "\n" - text += "yaw: " + angles.y.toFixed(this.decimals) + "\n" - text += "roll: " + angles.z.toFixed(this.decimals) + "\n" + text += "X: " + properties.position.x.toFixed(this.decimals) + "\n" + text += "Y: " + properties.position.y.toFixed(this.decimals) + "\n" + text += "Z: " + properties.position.z.toFixed(this.decimals) + "\n" + text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n" + text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n" + text += "Roll: " + angles.z.toFixed(this.decimals) + "\n" text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n" text += "ID: " + properties.id + "\n" - text += "model url: " + properties.modelURL + "\n" - text += "animation url: " + properties.animationURL + "\n" + text += "Model URL: " + properties.modelURL + "\n" + text += "Animation URL: " + properties.animationURL + "\n" + text += "Animation is playing: " + properties.animationIsPlaying + "\n" if (properties.sittingPoints.length > 0) { - text += properties.sittingPoints.length + " sitting points: " + text += properties.sittingPoints.length + " Sitting points: " for (var i = 0; i < properties.sittingPoints.length; ++i) { text += properties.sittingPoints[i].name + " " } @@ -1146,6 +1147,7 @@ function handeMenuEvent(menuItem){ var decimals = 3; array.push({ label: "Model URL:", value: selectedModelProperties.modelURL }); array.push({ label: "Animation URL:", value: selectedModelProperties.animationURL }); + array.push({ label: "Animation is playing:", value: selectedModelProperties.animationIsPlaying }); array.push({ label: "X:", value: selectedModelProperties.position.x.toFixed(decimals) }); array.push({ label: "Y:", value: selectedModelProperties.position.y.toFixed(decimals) }); array.push({ label: "Z:", value: selectedModelProperties.position.z.toFixed(decimals) }); @@ -1158,16 +1160,18 @@ function handeMenuEvent(menuItem){ var propertyName = Window.form("Edit Properties", array); modelSelected = false; - selectedModelProperties.modelURL = array[0].value; - selectedModelProperties.animationURL = array[1].value; - selectedModelProperties.position.x = array[2].value; - selectedModelProperties.position.y = array[3].value; - selectedModelProperties.position.z = array[4].value; - angles.x = array[5].value; - angles.y = array[6].value; - angles.z = array[7].value; + var index = 0; + selectedModelProperties.modelURL = array[index++].value; + selectedModelProperties.animationURL = array[index++].value; + selectedModelProperties.animationIsPlaying = array[index++].value; + selectedModelProperties.position.x = array[index++].value; + selectedModelProperties.position.y = array[index++].value; + selectedModelProperties.position.z = array[index++].value; + angles.x = array[index++].value; + angles.y = array[index++].value; + angles.z = array[index++].value; selectedModelProperties.modelRotation = Quat.fromVec3Degrees(angles); - selectedModelProperties.radius = array[8].value / 2; + selectedModelProperties.radius = array[9].value / 2; print(selectedModelProperties.radius); Models.editModel(selectedModelID, selectedModelProperties); From 4b867de8640dc84a4c9035b2a54d58fdd281aa10 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 23 Jul 2014 14:00:13 -0700 Subject: [PATCH 2/6] Created ScriptableAvatar class --- .../src/avatars/ScriptableAvatar.cpp | 87 +++++++++++++++++++ .../src/avatars/ScriptableAvatar.h | 42 +++++++++ 2 files changed, 129 insertions(+) create mode 100644 assignment-client/src/avatars/ScriptableAvatar.cpp create mode 100644 assignment-client/src/avatars/ScriptableAvatar.h 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 From 95c98203a9f201a4a17c278b24db56a8ca06830c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 23 Jul 2014 14:00:49 -0700 Subject: [PATCH 3/6] Replaced the Avatar in Agent by a scriptable avatar. --- assignment-client/src/Agent.cpp | 4 +++- libraries/script-engine/src/ScriptEngine.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0449e0d682..0a182e7917 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -29,6 +29,8 @@ #include // TODO: consider moving to scriptengine.h #include // TODO: consider moving to scriptengine.h +#include "avatars/ScriptableAvatar.h" + #include "Agent.h" Agent::Agent(const QByteArray& packet) : @@ -228,7 +230,7 @@ void Agent::run() { qDebug() << "Downloaded script:" << scriptContents; // setup an Avatar for the script to use - AvatarData scriptedAvatar; + ScriptableAvatar scriptedAvatar(&_scriptEngine); // call model URL setters with empty URLs so our avatar, if user, will have the default models scriptedAvatar.setFaceModelURL(QUrl()); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 0eda74914f..f516a439c7 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -59,6 +59,7 @@ public: QScriptEngine* getEngine() { return &_engine; } ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } + AnimationCache* getAnimationCache() { return &_animationCache; } /// sets the script contents, will return false if failed, will fail if script is already running bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString("")); From cde025c8ad885cc19e68b2d4da448e60fce708f8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 24 Jul 2014 13:47:56 -0700 Subject: [PATCH 4/6] Remove unnecessary arguments --- .../src/avatars/ScriptableAvatar.cpp | 15 ++++++++------- assignment-client/src/avatars/ScriptableAvatar.h | 8 +++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 6a420cde43..150e364ed7 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -17,6 +17,7 @@ ScriptableAvatar::ScriptableAvatar(ScriptEngine* scriptEngine) : _scriptEngine(s connect(_scriptEngine, SIGNAL(update(float)), this, SLOT(update(float))); } +// hold and priority unused but kept so that client side JS can run. 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()) { @@ -27,22 +28,22 @@ void ScriptableAvatar::startAnimation(const QString& url, float fps, float prior } _animation = _scriptEngine->getAnimationCache()->getAnimation(url); _animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame); + _maskedJoints = maskedJoints; } -void ScriptableAvatar::stopAnimation(const QString& url) { +void ScriptableAvatar::stopAnimation() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url)); + QMetaObject::invokeMethod(this, "stopAnimation"); return; } _animation.clear(); } -AnimationDetails ScriptableAvatar::getAnimationDetails(const QString& url) { +AnimationDetails ScriptableAvatar::getAnimationDetails() { if (QThread::currentThread() != thread()) { AnimationDetails result; QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(AnimationDetails, result), - Q_ARG(const QString&, url)); + Q_RETURN_ARG(AnimationDetails, result)); return result; } return _animationDetails; @@ -72,11 +73,11 @@ void ScriptableAvatar::update(float deltatime) { for (int i = 0; i < modelJoints.size(); i++) { int mapping = animationJoints.indexOf(modelJoints[i]); - if (mapping != -1) { + if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) { JointData& data = _jointData[i]; data.valid = true; data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); - } else if (i < _jointData.size()) { + } else { _jointData[i].valid = false; } } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 5548ef5a32..5a99c8c8da 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -24,11 +24,8 @@ public: /// 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); + Q_INVOKABLE void stopAnimation(); + Q_INVOKABLE AnimationDetails getAnimationDetails(); private slots: void update(float deltatime); @@ -37,6 +34,7 @@ private: ScriptEngine* _scriptEngine; AnimationPointer _animation; AnimationDetails _animationDetails; + QStringList _maskedJoints; }; #endif // hifi_ScriptableAvatar_h \ No newline at end of file From f7c675f3e6365f724dd47d7ad5e4158b4edd6858 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 25 Jul 2014 11:35:25 -0700 Subject: [PATCH 5/6] CR missing index++ --- examples/editModels.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/editModels.js b/examples/editModels.js index 6a692394ba..dbd0e8b2a1 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -1171,7 +1171,7 @@ function handeMenuEvent(menuItem){ angles.y = array[index++].value; angles.z = array[index++].value; selectedModelProperties.modelRotation = Quat.fromVec3Degrees(angles); - selectedModelProperties.radius = array[9].value / 2; + selectedModelProperties.radius = array[index++].value / 2; print(selectedModelProperties.radius); Models.editModel(selectedModelID, selectedModelProperties); @@ -1205,6 +1205,7 @@ Controller.keyPressEvent.connect(function(event) { somethingChanged = true; } }); + Controller.keyReleaseEvent.connect(function(event) { if (event.text == "z" || event.text == "Z") { zIsPressed = false; From 3c7375daadd205d0fcc52b707ce45485e11efc67 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 28 Jul 2014 10:17:04 -0700 Subject: [PATCH 6/6] Removed unused variable --- interface/src/avatar/MyAvatar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index aaeb99388a..4d2d679956 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1878,8 +1878,6 @@ void MyAvatar::renderLaserPointers() { //Gets the tip position for the laser pointer glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); - const float PALM_TIP_ROD_LENGTH_MULT = 40.0f; - glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); glm::vec3 position = palm->getPosition();