From 836cdeb1038d65dddd0da7c06bd2c21db66aa155 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 30 Jul 2015 18:40:48 -0700 Subject: [PATCH 001/117] Baby steps toward new animation system * AnimNode pure virtual base class for all animation nodes. * AnimClip playback of a single FBX animation. --- libraries/animation/src/AnimClip.h | 26 ++++++++++++++++++++++++++ libraries/animation/src/AnimNode.h | 19 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 libraries/animation/src/AnimClip.h create mode 100644 libraries/animation/src/AnimNode.h diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h new file mode 100644 index 0000000000..15c6dffae9 --- /dev/null +++ b/libraries/animation/src/AnimClip.h @@ -0,0 +1,26 @@ +// +// AnimClip.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_AnimClip_h +#define hifi_AnimClip_h + +class AnimClip : public AnimNode { + + void setURL(const std::string& url); + void setStartFrame(AnimFrame startFrame); + void setEndFrame(AnimFrame startFrame); + void setLoopFlag(bool loopFlag); + void setTimeScale(float timeScale); + +public: + virtual const float getEnd() const; + virtual const AnimPose& evaluate(float t); +}; + +#endif // hifi_AnimClip_h diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h new file mode 100644 index 0000000000..648138d06b --- /dev/null +++ b/libraries/animation/src/AnimNode.h @@ -0,0 +1,19 @@ +// +// AnimInterface.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_AnimNode_h +#define hifi_AnimNode_h + +class AnimNode { +public: + virtual float getEnd() const = 0; + virtual const AnimPose& evaluate(float t) = 0; +}; + +#endif // hifi_AnimNode_h From 35196a00593db15b03f13b3c507ee93d250f0ce1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 30 Jul 2015 22:07:05 -0700 Subject: [PATCH 002/117] 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 From 343b2ccf9d491a7d94fd50c9d6ddd656c6f3f95f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 31 Jul 2015 15:17:47 -0700 Subject: [PATCH 003/117] Added AnimNodeLoader which loads AnimClip nodes from json * added tests and sample json file --- libraries/animation/src/AnimClip.cpp | 3 +- libraries/animation/src/AnimClip.h | 11 +- libraries/animation/src/AnimNode.h | 38 +++++ libraries/animation/src/AnimNodeLoader.cpp | 173 +++++++++++++++++++++ libraries/animation/src/AnimNodeLoader.h | 23 +++ tests/animation/src/AnimClipTests.cpp | 40 ++++- tests/animation/src/AnimClipTests.h | 1 + tests/animation/src/test.json | 52 +++++++ 8 files changed, 334 insertions(+), 7 deletions(-) create mode 100644 libraries/animation/src/AnimNodeLoader.cpp create mode 100644 libraries/animation/src/AnimNodeLoader.h create mode 100644 tests/animation/src/test.json diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 5b0284c08b..a819304d18 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -10,7 +10,8 @@ #include "AnimClip.h" #include "AnimationLogging.h" -AnimClip::AnimClip(const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : +AnimClip::AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : + AnimNode(AnimNode::ClipType, id), _url(url), _startFrame(startFrame), _endFrame(endFrame), diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 60ef8e74f2..db3419332b 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -18,7 +18,7 @@ class AnimClip : public AnimNode { public: friend class AnimClipTests; - AnimClip(const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); + AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip(); void setURL(const std::string& url); @@ -37,14 +37,21 @@ public: 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; + + float _frame; + + // no copies + AnimClip(const AnimClip&) = delete; + AnimClip& operator=(const AnimClip&) = delete; }; #endif // hifi_AnimClip_h diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 8e78012865..af1fd8d5d5 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -10,13 +10,51 @@ #ifndef hifi_AnimNode_h #define hifi_AnimNode_h +#include +#include +#include +#include + typedef float AnimPose; +class QJsonObject; class AnimNode { public: + enum Type { + ClipType = 0, + NumTypes + }; + + AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} + + const std::string& getID() const { return _id; } + Type getType() const { return _type; } + + void addChild(std::shared_ptr child) { _children.push_back(child); } + void removeChild(std::shared_ptr child) { + auto iter = std::find(_children.begin(), _children.end(), child); + if (iter != _children.end()) { + _children.erase(iter); + } + } + const std::shared_ptr& getChild(int i) const { + assert(i >= 0 && i < (int)_children.size()); + return _children[i]; + } + int getChildCount() const { return (int)_children.size(); } + virtual ~AnimNode() {} virtual const AnimPose& evaluate(float dt) = 0; + +protected: + std::string _id; + Type _type; + std::vector> _children; + + // no copies + AnimNode(const AnimNode&) = delete; + AnimNode& operator=(const AnimNode&) = delete; }; #endif // hifi_AnimNode_h diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp new file mode 100644 index 0000000000..c2cb75364d --- /dev/null +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -0,0 +1,173 @@ +// +// AnimNodeLoader.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 +#include +#include +#include + +#include "AnimNode.h" +#include "AnimClip.h" +#include "AnimationLogging.h" +#include "AnimNodeLoader.h" + +struct TypeInfo { + AnimNode::Type type; + const char* str; +}; + +// This will result in a compile error if someone adds a new +// item to the AnimNode::Type enum. This is by design. +static TypeInfo typeInfoArray[AnimNode::NumTypes] = { + { AnimNode::ClipType, "clip" } +}; + +typedef std::shared_ptr (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); + +static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); + +static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { + loadClipNode +}; + +#define READ_STRING(NAME, JSON_OBJ, ID, URL) \ + auto NAME##_VAL = JSON_OBJ.value(#NAME); \ + if (!NAME##_VAL.isString()) { \ + qCCritical(animation) << "AnimNodeLoader, error reading string" \ + << #NAME << ", id =" << ID \ + << ", url =" << URL; \ + return nullptr; \ + } \ + QString NAME = NAME##_VAL.toString() + +#define READ_BOOL(NAME, JSON_OBJ, ID, URL) \ + auto NAME##_VAL = JSON_OBJ.value(#NAME); \ + if (!NAME##_VAL.isBool()) { \ + qCCritical(animation) << "AnimNodeLoader, error reading bool" \ + << #NAME << ", id =" << ID \ + << ", url =" << URL; \ + return nullptr; \ + } \ + bool NAME = NAME##_VAL.toBool() + +#define READ_FLOAT(NAME, JSON_OBJ, ID, URL) \ + auto NAME##_VAL = JSON_OBJ.value(#NAME); \ + if (!NAME##_VAL.isDouble()) { \ + qCCritical(animation) << "AnimNodeLoader, error reading double" \ + << #NAME << "id =" << ID \ + << ", url =" << URL; \ + return nullptr; \ + } \ + float NAME = (float)NAME##_VAL.toDouble() + +static AnimNode::Type stringToEnum(const QString& str) { + for (int i = 0; i < AnimNode::NumTypes; i++ ) { + if (str == typeInfoArray[i].str) { + return typeInfoArray[i].type; + } + } + return AnimNode::NumTypes; +} + +static std::shared_ptr loadNode(const QJsonObject& jsonObj, const QString& jsonUrl) { + auto idVal = jsonObj.value("id"); + if (!idVal.isString()) { + qCCritical(animation) << "AnimNodeLoader, bad string \"id\", url =" << jsonUrl; + return nullptr; + } + QString id = idVal.toString(); + + auto typeVal = jsonObj.value("type"); + if (!typeVal.isString()) { + qCCritical(animation) << "AnimNodeLoader, bad object \"type\", id =" << id << ", url =" << jsonUrl; + return nullptr; + } + QString typeStr = typeVal.toString(); + AnimNode::Type type = stringToEnum(typeStr); + if (type == AnimNode::NumTypes) { + qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id << ", url =" << jsonUrl; + return nullptr; + } + + auto dataValue = jsonObj.value("data"); + if (!dataValue.isObject()) { + qCCritical(animation) << "AnimNodeLoader, bad string \"data\", id =" << id << ", url =" << jsonUrl; + return nullptr; + } + auto dataObj = dataValue.toObject(); + + assert((int)type >= 0 && type < AnimNode::NumTypes); + auto node = nodeLoaderFuncs[type](dataObj, id, jsonUrl); + + auto childrenValue = jsonObj.value("children"); + if (!childrenValue.isArray()) { + qCCritical(animation) << "AnimNodeLoader, bad array \"children\", id =" << id << ", url =" << jsonUrl; + return nullptr; + } + auto childrenAry = childrenValue.toArray(); + for (auto& childValue : childrenAry) { + if (!childValue.isObject()) { + qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id << ", url =" << jsonUrl; + return nullptr; + } + node->addChild(loadNode(childValue.toObject(), jsonUrl)); + } + return node; +} + +static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { + + READ_STRING(url, jsonObj, id, jsonUrl); + READ_FLOAT(startFrame, jsonObj, id, jsonUrl); + READ_FLOAT(endFrame, jsonObj, id, jsonUrl); + READ_FLOAT(timeScale, jsonObj, id, jsonUrl); + READ_BOOL(loopFlag, jsonObj, id, jsonUrl); + + return std::make_shared(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag); +} + +std::shared_ptr AnimNodeLoader::load(const std::string& filename) const { + // load entire file into a string. + QString jsonUrl = QString::fromStdString(filename); + QFile file; + file.setFileName(jsonUrl); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCCritical(animation) << "AnimNodeLoader, could not open url =" << jsonUrl; + return nullptr; + } + QString contents = file.readAll(); + file.close(); + + // convert string into a json doc + auto doc = QJsonDocument::fromJson(contents.toUtf8()); + QJsonObject obj = doc.object(); + + // version + QJsonValue versionVal = obj.value("version"); + if (!versionVal.isString()) { + qCCritical(animation) << "AnimNodeLoader, bad string \"version\", url =" << jsonUrl; + return nullptr; + } + QString version = versionVal.toString(); + + // check version + if (version != "1.0") { + qCCritical(animation) << "AnimNodeLoader, bad version number" << version << "expected \"1.0\", url =" << jsonUrl; + return nullptr; + } + + // root + QJsonValue rootVal = obj.value("root"); + if (!rootVal.isObject()) { + qCCritical(animation) << "AnimNodeLoader, bad object \"root\", url =" << jsonUrl; + return nullptr; + } + + return loadNode(rootVal.toObject(), jsonUrl); +} diff --git a/libraries/animation/src/AnimNodeLoader.h b/libraries/animation/src/AnimNodeLoader.h new file mode 100644 index 0000000000..941ea32f02 --- /dev/null +++ b/libraries/animation/src/AnimNodeLoader.h @@ -0,0 +1,23 @@ +// +// AnimNodeLoader.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_AnimNodeLoader_h +#define hifi_AnimNodeLoader_h + +#include + +class AnimNode; + +class AnimNodeLoader { +public: + // TODO: load from url + std::shared_ptr load(const std::string& filename) const; +}; + +#endif // hifi_AnimNodeLoader diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimClipTests.cpp index 68f92afb2f..bab52f17a3 100644 --- a/tests/animation/src/AnimClipTests.cpp +++ b/tests/animation/src/AnimClipTests.cpp @@ -9,6 +9,7 @@ // #include "AnimClipTests.h" +#include "AnimNodeLoader.h" #include "AnimClip.h" #include "AnimationLogging.h" @@ -19,13 +20,18 @@ QTEST_MAIN(AnimClipTests) const float EPSILON = 0.001f; void AnimClipTests::testAccessors() { + std::string id = "my anim clip"; std::string url = "foo"; float startFrame = 2.0f; float endFrame = 20.0f; float timeScale = 1.1f; - float loopFlag = true; + bool loopFlag = true; + + AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); + + QVERIFY(clip.getID() == id); + QVERIFY(clip.getType() == AnimNode::ClipType); - AnimClip clip(url, startFrame, endFrame, timeScale, loopFlag); QVERIFY(clip.getURL() == url); QVERIFY(clip.getStartFrame() == startFrame); QVERIFY(clip.getEndFrame() == endFrame); @@ -36,7 +42,7 @@ void AnimClipTests::testAccessors() { float startFrame2 = 22.0f; float endFrame2 = 100.0f; float timeScale2 = 1.2f; - float loopFlag2 = false; + bool loopFlag2 = false; clip.setURL(url2); clip.setStartFrame(startFrame2); @@ -62,13 +68,14 @@ static float framesToSec(float secs) { } void AnimClipTests::testEvaulate() { + std::string id = "my clip node"; 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); + AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); clip.evaluate(framesToSec(10.0f)); QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, EPSILON); @@ -83,3 +90,28 @@ void AnimClipTests::testEvaulate() { QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, EPSILON); } +void AnimClipTests::testLoader() { + AnimNodeLoader loader; + auto node = loader.load("../../../tests/animation/src/test.json"); + QVERIFY((bool)node); + QVERIFY(node->getID() == "idle"); + QVERIFY(node->getType() == AnimNode::ClipType); + + auto clip = std::static_pointer_cast(node); + + QVERIFY(clip->getURL() == "idle.fbx"); + 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 nodes[3] = { clip->getChild(0), clip->getChild(1), clip->getChild(2) }; + QVERIFY(nodes[0]->getID() == "test01"); + QVERIFY(nodes[0]->getChildCount() == 0); + QVERIFY(nodes[1]->getID() == "test02"); + QVERIFY(nodes[1]->getChildCount() == 0); + QVERIFY(nodes[2]->getID() == "test03"); + QVERIFY(nodes[2]->getChildCount() == 0); +} diff --git a/tests/animation/src/AnimClipTests.h b/tests/animation/src/AnimClipTests.h index f49690d400..08f25c324f 100644 --- a/tests/animation/src/AnimClipTests.h +++ b/tests/animation/src/AnimClipTests.h @@ -22,6 +22,7 @@ class AnimClipTests : public QObject { private slots: void testAccessors(); void testEvaulate(); + void testLoader(); }; #endif // hifi_TransformTests_h diff --git a/tests/animation/src/test.json b/tests/animation/src/test.json new file mode 100644 index 0000000000..0bcf91201a --- /dev/null +++ b/tests/animation/src/test.json @@ -0,0 +1,52 @@ +{ + "version": "1.0", + "root": { + "id": "idle", + "type": "clip", + "data": { + "url": "idle.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [ + { + "id": "test01", + "type": "clip", + "data": { + "url": "test01.fbx", + "startFrame": 1.0, + "endFrame": 20.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "test02", + "type": "clip", + "data": { + "url": "test02.fbx", + "startFrame": 2.0, + "endFrame": 20.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "test03", + "type": "clip", + "data": { + "url": "test03.fbx", + "startFrame": 3.0, + "endFrame": 20.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + } + ] + } +} From da809efcd6d60f9531d392b23232397ad6a2bc4f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 31 Jul 2015 19:06:50 -0700 Subject: [PATCH 004/117] WIP commit, DOES NOT BUILD. * Added AnimSkeleton class * Attempt to copy animation frames when _networkAnimation has finished loading. Fill in the holes with bind pose. --- libraries/animation/src/AnimClip.cpp | 92 +++++++++++++++++++--- libraries/animation/src/AnimClip.h | 11 ++- libraries/animation/src/AnimNode.h | 29 +++++-- libraries/animation/src/AnimNodeLoader.cpp | 10 +-- libraries/animation/src/AnimSkeleton.cpp | 36 +++++++++ libraries/animation/src/AnimSkeleton.h | 30 +++++++ libraries/shared/src/GLMHelpers.cpp | 1 - 7 files changed, 185 insertions(+), 24 deletions(-) create mode 100644 libraries/animation/src/AnimSkeleton.cpp create mode 100644 libraries/animation/src/AnimSkeleton.h diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index a819304d18..51b01560fb 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -27,7 +27,7 @@ AnimClip::~AnimClip() { } void AnimClip::setURL(const std::string& url) { - // TODO: + _networkAnim = DependencyManager::get()->getAnimation(QString::fromStdString(url)); _url = url; } @@ -43,11 +43,11 @@ void AnimClip::setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; } -const AnimPose& AnimClip::evaluate(float dt) { +float AnimClip::accumulateTime(float frame, float dt) const { const float startFrame = std::min(_startFrame, _endFrame); if (startFrame == _endFrame) { // when startFrame >= endFrame - _frame = _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; @@ -59,20 +59,94 @@ const AnimPose& AnimClip::evaluate(float dt) { // anim loop // TODO: trigger onLoop event framesRemaining -= framesTillEnd; - _frame = startFrame; + frame = startFrame; } else { // anim end // TODO: trigger onDone event - _frame = _endFrame; + frame = _endFrame; framesRemaining = 0.0f; } } else { - _frame += framesRemaining; + frame += framesRemaining; framesRemaining = 0.0f; } } } - // TODO: eval animation - - return _frame; + return frame; +} + +const std::vector& AnimClip::evaluate(float dt) { + _frame = accumulateTime(_frame, dt); + + if (!_anim && _networkAnim && _networkAnim->isLoaded() && _skeleton) { + copyFramesFromNetworkAnim(); + _networkAnim = nullptr; + } + + if (_anim) { + int frameCount = _anim.size(); + + int prevIndex = (int)glm::floor(_frame); + int nextIndex = (int)glm::ceil(_frame); + if (_loopFlag && nextIndex >= frameCount) { + nextIndex = 0; + } + + // It can be quite possible for the user to set _startFrame and _endFrame to + // values before or past valid ranges. We clamp the frames here. + prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); + nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); + + const std::vector& prevFrame = _anim[prevIndex]; + const std::vector& nextFrame = _anim[nextIndex]; + float alpha = glm::fract(_frame); + + for (size_t i = 0; i < _bones.size(); i++) { + const AnimBone& prevBone = prevFrame[i]; + const AnimBone& nextBone = nextFrame[i]; + _bones[i].scale = glm::lerp(prevBone.scale, nextBone.scale, alpha); + _bones[i].rot = glm::normalize(glm::lerp(prevBone.rot, nextBone.rot, alpha)); + _bones[i].trans = glm::lerp(prevBone.trans, nextBone.trans, alpha); + } + } + + return _bones; +} + +void AnimClip::copyFromNetworkAnim() { + assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); + _anim.clear(); + + // build a mapping from animation joint indices to skeleton joint indices. + // by matching joints with the same name. + const FBXGeometry& geom = _networkAnim->getGeometry(); + const QVector& joints = geom.joints; + std::vector jointMap; + const int animJointCount = joints.count(); + jointMap.reserve(animJointCount); + for (int i = 0; i < animJointCount; i++) { + int skeletonJoint _skeleton.nameToJointIndex(joints.at(i).name); + jointMap.push_back(skeletonJoint); + } + + const int frameCount = geom.animationFrames.size(); + const int skeletonJointCount = _skeleton.jointCount(); + _anim.resize(frameCount); + for (int i = 0; i < frameCount; i++) { + + // init all joints in animation to bind pose + _anim[i].reserve(skeletonJointCount); + for (int j = 0; j < skeletonJointCount; j++) { + _anim[i].push_back(_skeleton.getBindPose(j)); + } + + // init over all joint animations + for (int j = 0; j < animJointCount; j++) { + int k = jointMap[j]; + if (k >= 0 && k < skeletonJointCount) { + // currently animations only have rotation. + _anim[i][k].rot = geom.animationFrames[i].rotations[j]; + } + } + } } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index db3419332b..a43ee98194 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -36,10 +36,17 @@ public: void setLoopFlag(bool loopFlag); bool getLoopFlag() const { return _loopFlag; } - virtual const AnimPose& evaluate(float dt); + virtual const std::vector& evaluate(float dt); protected: - AnimationPointer _anim; + float accumulateTime(float frame, float dt) const; + void copyFromNetworkAnim(); + + AnimationPointer _networkAnim; + std::vector _bones; + + // _anim[frame][joint] + std::vector> _anim; std::string _url; float _startFrame; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index af1fd8d5d5..36f5c29ef3 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -1,5 +1,5 @@ // -// AnimInterface.h +// AnimNode.h // // Copyright 2015 High Fidelity, Inc. // @@ -14,8 +14,18 @@ #include #include #include +#include +#include + +#include "AnimSkeleton.h" + +struct AnimBone { + AnimBone() {} + glm::vec3 scale; + glm::quat rot; + glm::vec3 trans; +}; -typedef float AnimPose; class QJsonObject; class AnimNode { @@ -24,33 +34,38 @@ public: ClipType = 0, NumTypes }; + typedef std::shared_ptr Pointer; AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} const std::string& getID() const { return _id; } Type getType() const { return _type; } - void addChild(std::shared_ptr child) { _children.push_back(child); } - void removeChild(std::shared_ptr child) { + void addChild(Pointer child) { _children.push_back(child); } + void removeChild(Pointer child) { auto iter = std::find(_children.begin(), _children.end(), child); if (iter != _children.end()) { _children.erase(iter); } } - const std::shared_ptr& getChild(int i) const { + Pointer getChild(int i) const { assert(i >= 0 && i < (int)_children.size()); return _children[i]; } int getChildCount() const { return (int)_children.size(); } + void setSkeleton(AnimSkeleton::Pointer skeleton) { _skeleton = skeleton; } + AnimSkeleton::Pointer getSkeleton() const { return _skeleton; } + virtual ~AnimNode() {} - virtual const AnimPose& evaluate(float dt) = 0; + virtual const std::vector& evaluate(float dt) = 0; protected: std::string _id; Type _type; - std::vector> _children; + std::vector _children; + AnimSkeleton::Pointer _skeleton; // no copies AnimNode(const AnimNode&) = delete; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index c2cb75364d..d9b34e3c86 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -28,9 +28,9 @@ static TypeInfo typeInfoArray[AnimNode::NumTypes] = { { AnimNode::ClipType, "clip" } }; -typedef std::shared_ptr (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); +typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); -static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); +static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { loadClipNode @@ -75,7 +75,7 @@ static AnimNode::Type stringToEnum(const QString& str) { return AnimNode::NumTypes; } -static std::shared_ptr loadNode(const QJsonObject& jsonObj, const QString& jsonUrl) { +static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QString& jsonUrl) { auto idVal = jsonObj.value("id"); if (!idVal.isString()) { qCCritical(animation) << "AnimNodeLoader, bad string \"id\", url =" << jsonUrl; @@ -121,7 +121,7 @@ static std::shared_ptr loadNode(const QJsonObject& jsonObj, const QStr return node; } -static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { +static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { READ_STRING(url, jsonObj, id, jsonUrl); READ_FLOAT(startFrame, jsonObj, id, jsonUrl); @@ -132,7 +132,7 @@ static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const return std::make_shared(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag); } -std::shared_ptr AnimNodeLoader::load(const std::string& filename) const { +AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const { // load entire file into a string. QString jsonUrl = QString::fromStdString(filename); QFile file; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp new file mode 100644 index 0000000000..fbd31d55b9 --- /dev/null +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -0,0 +1,36 @@ +// +// AnimSkeleton.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 "AnimSkeleton.h" + +AnimSkeleton::AnimSkeleton(const std::vector& joints) { + _joints = joints; +} + +int AnimSkeltion::nameToJointIndex(const QString& jointName) const { + for (int i = 0; i < _joints.size(); i++) { + if (_joints.name == jointName) { + return i; + } + } + return -1; +} + +int AnimSkeleton::getNumJoints() const { + return _joints.size(); +} + +AnimBone getBindPose(int jointIndex) const { + // TODO: what coordinate frame is the bindTransform in? local to the bones parent frame? or model? + return AnimBone bone(glm::vec3(1.0f, 1.0f, 1.0f), + glm::quat_cast(_joints[jointIndex].bindTransform), + glm::vec3(0.0f, 0.0f, 0.0f)); +} + + diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h new file mode 100644 index 0000000000..a48d7dfa50 --- /dev/null +++ b/libraries/animation/src/AnimSkeleton.h @@ -0,0 +1,30 @@ +// +// AnimSkeleton.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_AnimSkeleton +#define hifi_AnimSkeleton + +#include + +#include "FBXReader.h" + +class AnimSkeleton { +public: + typedef std::shared_ptr Pointer; + AnimSkeleton(const std::vector& joints); + int nameToJointIndex(const QString& jointName) const; + int getNumJoints() const; + AnimBone getBindPose(int jointIndex) const; +} + +protected: + std::vector _joints; +}; + +#endif diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index d5b2917369..6084bf9354 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -347,4 +347,3 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { return result; } - From f5dee717a1d64a677fe71b7e9a4596dfd0667b52 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 31 Jul 2015 22:08:39 -0700 Subject: [PATCH 005/117] Added fbx loading via animation cache. * added AnimPose::copyFromNetworkAnim() which should, re-map bone ids to match the current skeleton, and fill in missing bones with bind pose frames. * added ability to set a skeleton on a node. I might need to add a recursive version of this. * it compiles! * tests run! --- libraries/animation/src/AnimClip.cpp | 42 +++++++++++----------- libraries/animation/src/AnimClip.h | 6 ++-- libraries/animation/src/AnimNode.h | 11 ++---- libraries/animation/src/AnimNodeLoader.cpp | 2 +- libraries/animation/src/AnimSkeleton.cpp | 14 ++++---- libraries/animation/src/AnimSkeleton.h | 13 +++++-- libraries/shared/src/GLMHelpers.h | 15 ++++++++ tests/animation/src/AnimClipTests.cpp | 22 ++++++++---- tests/animation/src/AnimClipTests.h | 2 ++ tests/animation/src/{ => data}/test.json | 0 10 files changed, 79 insertions(+), 48 deletions(-) rename tests/animation/src/{ => data}/test.json (100%) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 51b01560fb..d10430dec3 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -7,6 +7,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "GLMHelpers.h" #include "AnimClip.h" #include "AnimationLogging.h" @@ -16,8 +17,8 @@ AnimClip::AnimClip(const std::string& id, const std::string& url, float startFra _startFrame(startFrame), _endFrame(endFrame), _timeScale(timeScale), - _frame(startFrame), - _loopFlag(loopFlag) + _loopFlag(loopFlag), + _frame(startFrame) { } @@ -27,7 +28,8 @@ AnimClip::~AnimClip() { } void AnimClip::setURL(const std::string& url) { - _networkAnim = DependencyManager::get()->getAnimation(QString::fromStdString(url)); + auto animCache = DependencyManager::get(); + _networkAnim = animCache->getAnimation(QString::fromStdString(url)); _url = url; } @@ -75,15 +77,15 @@ float AnimClip::accumulateTime(float frame, float dt) const { return frame; } -const std::vector& AnimClip::evaluate(float dt) { +const std::vector& AnimClip::evaluate(float dt) { _frame = accumulateTime(_frame, dt); - if (!_anim && _networkAnim && _networkAnim->isLoaded() && _skeleton) { - copyFramesFromNetworkAnim(); - _networkAnim = nullptr; + if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { + copyFromNetworkAnim(); + _networkAnim.reset(); } - if (_anim) { + if (_anim.size()) { int frameCount = _anim.size(); int prevIndex = (int)glm::floor(_frame); @@ -97,20 +99,20 @@ const std::vector& AnimClip::evaluate(float dt) { prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); - const std::vector& prevFrame = _anim[prevIndex]; - const std::vector& nextFrame = _anim[nextIndex]; + const std::vector& prevFrame = _anim[prevIndex]; + const std::vector& nextFrame = _anim[nextIndex]; float alpha = glm::fract(_frame); - for (size_t i = 0; i < _bones.size(); i++) { - const AnimBone& prevBone = prevFrame[i]; - const AnimBone& nextBone = nextFrame[i]; - _bones[i].scale = glm::lerp(prevBone.scale, nextBone.scale, alpha); - _bones[i].rot = glm::normalize(glm::lerp(prevBone.rot, nextBone.rot, alpha)); - _bones[i].trans = glm::lerp(prevBone.trans, nextBone.trans, alpha); + for (size_t i = 0; i < _poses.size(); i++) { + const AnimPose& prevBone = prevFrame[i]; + const AnimPose& nextBone = nextFrame[i]; + _poses[i].scale = lerp(prevBone.scale, nextBone.scale, alpha); + _poses[i].rot = glm::normalize(glm::lerp(prevBone.rot, nextBone.rot, alpha)); + _poses[i].trans = lerp(prevBone.trans, nextBone.trans, alpha); } } - return _bones; + return _poses; } void AnimClip::copyFromNetworkAnim() { @@ -125,19 +127,19 @@ void AnimClip::copyFromNetworkAnim() { const int animJointCount = joints.count(); jointMap.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { - int skeletonJoint _skeleton.nameToJointIndex(joints.at(i).name); + int skeletonJoint = _skeleton->nameToJointIndex(joints.at(i).name); jointMap.push_back(skeletonJoint); } const int frameCount = geom.animationFrames.size(); - const int skeletonJointCount = _skeleton.jointCount(); + const int skeletonJointCount = _skeleton->getNumJoints(); _anim.resize(frameCount); for (int i = 0; i < frameCount; i++) { // init all joints in animation to bind pose _anim[i].reserve(skeletonJointCount); for (int j = 0; j < skeletonJointCount; j++) { - _anim[i].push_back(_skeleton.getBindPose(j)); + _anim[i].push_back(_skeleton->getBindPose(j)); } // init over all joint animations diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index a43ee98194..9858e5b524 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -36,17 +36,17 @@ public: void setLoopFlag(bool loopFlag); bool getLoopFlag() const { return _loopFlag; } - virtual const std::vector& evaluate(float dt); + virtual const std::vector& evaluate(float dt); protected: float accumulateTime(float frame, float dt) const; void copyFromNetworkAnim(); AnimationPointer _networkAnim; - std::vector _bones; + std::vector _poses; // _anim[frame][joint] - std::vector> _anim; + std::vector> _anim; std::string _url; float _startFrame; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 36f5c29ef3..a801b8b2bc 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -19,13 +19,6 @@ #include "AnimSkeleton.h" -struct AnimBone { - AnimBone() {} - glm::vec3 scale; - glm::quat rot; - glm::vec3 trans; -}; - class QJsonObject; class AnimNode { @@ -59,11 +52,11 @@ public: virtual ~AnimNode() {} - virtual const std::vector& evaluate(float dt) = 0; + virtual const std::vector& evaluate(float dt) = 0; protected: - std::string _id; Type _type; + std::string _id; std::vector _children; AnimSkeleton::Pointer _skeleton; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index d9b34e3c86..2d3a5a010c 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -111,7 +111,7 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QString& jso return nullptr; } auto childrenAry = childrenValue.toArray(); - for (auto& childValue : childrenAry) { + for (const auto& childValue : childrenAry) { if (!childValue.isObject()) { qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id << ", url =" << jsonUrl; return nullptr; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index fbd31d55b9..695dea0752 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -13,9 +13,9 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; } -int AnimSkeltion::nameToJointIndex(const QString& jointName) const { - for (int i = 0; i < _joints.size(); i++) { - if (_joints.name == jointName) { +int AnimSkeleton::nameToJointIndex(const QString& jointName) const { + for (size_t i = 0; i < _joints.size(); i++) { + if (_joints[i].name == jointName) { return i; } } @@ -26,11 +26,11 @@ int AnimSkeleton::getNumJoints() const { return _joints.size(); } -AnimBone getBindPose(int jointIndex) const { +AnimPose AnimSkeleton::getBindPose(int jointIndex) const { // TODO: what coordinate frame is the bindTransform in? local to the bones parent frame? or model? - return AnimBone bone(glm::vec3(1.0f, 1.0f, 1.0f), - glm::quat_cast(_joints[jointIndex].bindTransform), - glm::vec3(0.0f, 0.0f, 0.0f)); + return AnimPose(glm::vec3(1.0f, 1.0f, 1.0f), + glm::quat_cast(_joints[jointIndex].bindTransform), + glm::vec3(0.0f, 0.0f, 0.0f)); } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index a48d7dfa50..0966c2b5f7 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -14,14 +14,23 @@ #include "FBXReader.h" +struct AnimPose { + AnimPose() {} + AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} + + glm::vec3 scale; + glm::quat rot; + glm::vec3 trans; +}; + class AnimSkeleton { public: typedef std::shared_ptr Pointer; + AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; int getNumJoints() const; - AnimBone getBindPose(int jointIndex) const; -} + AnimPose getBindPose(int jointIndex) const; protected: std::vector _joints; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 6874f3b391..1a61c426e5 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -150,4 +150,19 @@ T toNormalizedDeviceScale(const T& value, const T& size) { #define PITCH(euler) euler.x #define ROLL(euler) euler.z +template +glm::detail::tvec2 lerp(const glm::detail::tvec2& x, const glm::detail::tvec2& y, T a) { + return x * (T(1) - a) + (y * a); +} + +template +glm::detail::tvec3 lerp(const glm::detail::tvec3& x, const glm::detail::tvec3& y, T a) { + return x * (T(1) - a) + (y * a); +} + +template +glm::detail::tvec4 lerp(const glm::detail::tvec4& x, const glm::detail::tvec4& y, T a) { + return x * (T(1) - a) + (y * a); +} + #endif // hifi_GLMHelpers_h diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimClipTests.cpp index bab52f17a3..aab49aa30d 100644 --- a/tests/animation/src/AnimClipTests.cpp +++ b/tests/animation/src/AnimClipTests.cpp @@ -19,6 +19,15 @@ QTEST_MAIN(AnimClipTests) const float EPSILON = 0.001f; +void AnimClipTests::initTestCase() { + auto animationCache = DependencyManager::set(); + auto resourceCacheSharedItems = DependencyManager::set(); +} + +void AnimClipTests::cleanupTestCase() { + DependencyManager::destroy(); +} + void AnimClipTests::testAccessors() { std::string id = "my anim clip"; std::string url = "foo"; @@ -57,11 +66,6 @@ void AnimClipTests::testAccessors() { 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; @@ -92,7 +96,13 @@ void AnimClipTests::testEvaulate() { void AnimClipTests::testLoader() { AnimNodeLoader loader; - auto node = loader.load("../../../tests/animation/src/test.json"); + +#ifdef Q_OS_WIN + auto node = loader.load("../../../tests/animation/src/data/test.json"); +#else + auto node = loader.load("../../../../tests/animation/src/data/test.json"); +#endif + QVERIFY((bool)node); QVERIFY(node->getID() == "idle"); QVERIFY(node->getType() == AnimNode::ClipType); diff --git a/tests/animation/src/AnimClipTests.h b/tests/animation/src/AnimClipTests.h index 08f25c324f..f70ee31aa1 100644 --- a/tests/animation/src/AnimClipTests.h +++ b/tests/animation/src/AnimClipTests.h @@ -20,6 +20,8 @@ inline float getErrorDifference(float a, float b) { class AnimClipTests : public QObject { Q_OBJECT private slots: + void initTestCase(); + void cleanupTestCase(); void testAccessors(); void testEvaulate(); void testLoader(); diff --git a/tests/animation/src/test.json b/tests/animation/src/data/test.json similarity index 100% rename from tests/animation/src/test.json rename to tests/animation/src/data/test.json From 91ca13c66daef7863c99e96ecf0487a6201b47e7 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 1 Aug 2015 12:43:03 -0700 Subject: [PATCH 006/117] Added AnimDebugDraw to render-utils --- interface/src/Application.cpp | 4 + libraries/render-utils/src/AnimDebugDraw.cpp | 144 +++++++++++++++++++ libraries/render-utils/src/AnimDebugDraw.h | 36 +++++ libraries/render-utils/src/animdebugdraw.slf | 17 +++ libraries/render-utils/src/animdebugdraw.slv | 24 ++++ tests/animation/src/AnimClipTests.cpp | 1 - tests/animation/src/AnimClipTests.h | 2 +- 7 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 libraries/render-utils/src/AnimDebugDraw.cpp create mode 100644 libraries/render-utils/src/AnimDebugDraw.h create mode 100644 libraries/render-utils/src/animdebugdraw.slf create mode 100644 libraries/render-utils/src/animdebugdraw.slv diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index acdfd8cfc9..e43465ad75 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -142,6 +142,8 @@ #include "ui/AddressBarDialog.h" #include "ui/UpdateDialog.h" +#include "AnimDebugDraw.h" + // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU #if defined(Q_OS_WIN) @@ -645,6 +647,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); + + AnimDebugDraw& add = AnimDebugDraw::getInstance(); } void Application::aboutToQuit() { diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp new file mode 100644 index 0000000000..80a8824d5a --- /dev/null +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -0,0 +1,144 @@ +// +// AnimDebugDraw.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 "animdebugdraw_vert.h" +#include "animdebugdraw_frag.h" +#include + +#include "AnimDebugDraw.h" +#include "AbstractViewStateInterface.h" + +struct Vertex { + glm::vec3 pos; + uint32_t rgba; +}; + +class AnimDebugDrawData { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + AnimDebugDrawData() { + + _vertexFormat = std::make_shared(); + _vertexBuffer = std::make_shared(); + _indexBuffer = std::make_shared(); + + _vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, 0); + _vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, offsetof(Vertex, rgba)); + } + + void render(RenderArgs* args) { + auto& batch = *args->_batch; + batch.setPipeline(_pipeline); + auto transform = Transform{}; + batch.setModelTransform(transform); + + batch.setInputFormat(_vertexFormat); + batch.setInputBuffer(0, _vertexBuffer, 0, sizeof(Vertex)); + batch.setIndexBuffer(gpu::UINT16, _indexBuffer, 0); + + auto numIndices = _indexBuffer->getSize() / sizeof(uint16_t); + batch.drawIndexed(gpu::LINES, numIndices); + } + + gpu::PipelinePointer _pipeline; + render::ItemID _item; + gpu::Stream::FormatPointer _vertexFormat; + gpu::BufferPointer _vertexBuffer; + gpu::BufferPointer _indexBuffer; +}; + +typedef render::Payload AnimDebugDrawPayload; + +namespace render { + template <> const ItemKey payloadGetKey(const AnimDebugDrawData::Pointer& data) { return ItemKey::Builder::transparentShape(); } + template <> const Item::Bound payloadGetBound(const AnimDebugDrawData::Pointer& data) { return Item::Bound(); } + template <> void payloadRender(const AnimDebugDrawData::Pointer& data, RenderArgs* args) { + data->render(args); + } +} + +static AnimDebugDraw* instance = nullptr; + +AnimDebugDraw& AnimDebugDraw::getInstance() { + if (!instance) { + instance = new AnimDebugDraw(); + } + return *instance; +} + +static uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24); +} + +gpu::PipelinePointer AnimDebugDraw::_pipeline; + +AnimDebugDraw::AnimDebugDraw() : + _itemID(0) { + + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(false, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, + gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, + gpu::State::BLEND_OP_ADD, gpu::State::ONE); + auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(animdebugdraw_vert))); + auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(animdebugdraw_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + + _animDebugDrawData = std::make_shared(); + _animDebugDrawPayload = std::make_shared(_animDebugDrawData); + + _animDebugDrawData->_pipeline = _pipeline; + + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + if (scene) { + _itemID = scene->allocateID(); + render::PendingChanges pendingChanges; + pendingChanges.resetItem(_itemID, _animDebugDrawPayload); + scene->enqueuePendingChanges(pendingChanges); + } + + // HACK: add red, green and blue axis at (1,1,1) + _animDebugDrawData->_vertexBuffer->resize(sizeof(Vertex) * 6); + Vertex* data = (Vertex*)_animDebugDrawData->_vertexBuffer->editData(); + + data[0].pos = glm::vec3(1.0, 1.0f, 1.0f); + data[0].rgba = toRGBA(255, 0, 0, 255); + data[1].pos = glm::vec3(2.0, 1.0f, 1.0f); + data[1].rgba = toRGBA(255, 0, 0, 255); + + data[2].pos = glm::vec3(1.0, 1.0f, 1.0f); + data[2].rgba = toRGBA(0, 255, 0, 255); + data[3].pos = glm::vec3(1.0, 2.0f, 1.0f); + data[3].rgba = toRGBA(0, 255, 0, 255); + + data[4].pos = glm::vec3(1.0, 1.0f, 1.0f); + data[4].rgba = toRGBA(0, 0, 255, 255); + data[5].pos = glm::vec3(1.0, 1.0f, 2.0f); + data[5].rgba = toRGBA(0, 0, 255, 255); + + _animDebugDrawData->_indexBuffer->resize(sizeof(uint16_t) * 6); + uint16_t* indices = (uint16_t*)_animDebugDrawData->_indexBuffer->editData(); + for (int i = 0; i < 6; i++) { + indices[i] = i; + } + +} + +AnimDebugDraw::~AnimDebugDraw() { + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + if (scene && _itemID) { + render::PendingChanges pendingChanges; + pendingChanges.removeItem(_itemID); + scene->enqueuePendingChanges(pendingChanges); + } +} diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h new file mode 100644 index 0000000000..ea3ac9ced1 --- /dev/null +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -0,0 +1,36 @@ +// +// AnimDebugDraw.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_AnimDebugDraw_h +#define hifi_AnimDebugDraw_h + +#include "AnimNode.h" +#include +#include + +class AnimDebugDrawData; +typedef render::Payload AnimDebugDrawPayload; + + +class AnimDebugDraw { +public: + static AnimDebugDraw& getInstance(); + + AnimDebugDraw(); + ~AnimDebugDraw(); + +protected: + std::shared_ptr _animDebugDrawData; + std::shared_ptr _animDebugDrawPayload; + render::ItemID _itemID; + + static gpu::PipelinePointer _pipeline; +}; + +#endif // hifi_AnimDebugDraw diff --git a/libraries/render-utils/src/animdebugdraw.slf b/libraries/render-utils/src/animdebugdraw.slf new file mode 100644 index 0000000000..aa34c9bfba --- /dev/null +++ b/libraries/render-utils/src/animdebugdraw.slf @@ -0,0 +1,17 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// unlit untextured fragment shader +// +// 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 +// + +varying vec4 varColor; + +void main(void) { + gl_FragColor = varColor; +} diff --git a/libraries/render-utils/src/animdebugdraw.slv b/libraries/render-utils/src/animdebugdraw.slv new file mode 100644 index 0000000000..68749ec7cc --- /dev/null +++ b/libraries/render-utils/src/animdebugdraw.slv @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// unlit untextured vertex shader +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +varying vec4 varColor; + +void main(void) { + // pass along the diffuse color + varColor = gl_Color; + + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> +} diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimClipTests.cpp index aab49aa30d..58c7311b2d 100644 --- a/tests/animation/src/AnimClipTests.cpp +++ b/tests/animation/src/AnimClipTests.cpp @@ -1,6 +1,5 @@ // // AnimClipTests.cpp -// tests/rig/src // // Copyright 2015 High Fidelity, Inc. // diff --git a/tests/animation/src/AnimClipTests.h b/tests/animation/src/AnimClipTests.h index f70ee31aa1..d4590ef27d 100644 --- a/tests/animation/src/AnimClipTests.h +++ b/tests/animation/src/AnimClipTests.h @@ -27,4 +27,4 @@ private slots: void testLoader(); }; -#endif // hifi_TransformTests_h +#endif // hifi_AnimClipTests_h From b8bae7cc3f9b19273cd05e2280255b17ba9436c0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 1 Aug 2015 15:01:25 -0700 Subject: [PATCH 007/117] =?UTF-8?q?Debug=20rendering=20of=20MyAvatar?= =?UTF-8?q?=E2=80=99s=20skeletonModel=20skeleton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interface/src/Application.cpp | 7 +- interface/src/avatar/MyAvatar.cpp | 11 +++ libraries/animation/src/AnimSkeleton.cpp | 12 ++- libraries/animation/src/AnimSkeleton.h | 1 + libraries/render-utils/src/AnimDebugDraw.cpp | 87 ++++++++++++++++++++ libraries/render-utils/src/AnimDebugDraw.h | 15 +++- libraries/shared/src/GLMHelpers.h | 3 + libraries/shared/src/Transform.h | 7 ++ 8 files changed, 135 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e43465ad75..ed5cba6ac1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -647,8 +647,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); - - AnimDebugDraw& add = AnimDebugDraw::getInstance(); } void Application::aboutToQuit() { @@ -2622,6 +2620,11 @@ void Application::update(float deltaTime) { loadViewFrustum(_myCamera, _viewFrustum); } + // Update animation debug draw renderer + { + AnimDebugDraw::getInstance().update(); + } + quint64 now = usecTimestampNow(); // Update my voxel servers with my current voxel query... diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2d7cf4ca5e..80da07b966 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -49,6 +49,8 @@ #include "gpu/GLBackend.h" +#include "AnimDebugDraw.h" +#include "AnimSkeleton.h" using namespace std; @@ -1203,6 +1205,15 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { if (_skeletonModel.initWhenReady(scene)) { initHeadBones(); _skeletonModel.setCauterizeBoneSet(_headBoneSet); + + // create a skeleton and hand it over to the debug draw instance + auto geom = _skeletonModel.getGeometry()->getFBXGeometry(); + std::vector joints; + for (auto& joint : geom.joints) { + joints.push_back(joint); + } + auto skeleton = make_shared(joints); + AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, Transform()); } if (shouldDrawHead != _prevShouldDrawHead) { diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 695dea0752..96b90b8f46 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -8,6 +8,7 @@ // #include "AnimSkeleton.h" +#include "glmHelpers.h" AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; @@ -27,10 +28,15 @@ int AnimSkeleton::getNumJoints() const { } AnimPose AnimSkeleton::getBindPose(int jointIndex) const { - // TODO: what coordinate frame is the bindTransform in? local to the bones parent frame? or model? - return AnimPose(glm::vec3(1.0f, 1.0f, 1.0f), + // TODO: perhaps cache these, it's expensive to de-compose the matrix + // on every call. + return AnimPose(extractScale(_joints[jointIndex].bindTransform), glm::quat_cast(_joints[jointIndex].bindTransform), - glm::vec3(0.0f, 0.0f, 0.0f)); + extractTranslation(_joints[jointIndex].bindTransform)); +} + +int AnimSkeleton::getParentIndex(int jointIndex) const { + return _joints[jointIndex].parentIndex; } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 0966c2b5f7..0b50e60b65 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -31,6 +31,7 @@ public: int nameToJointIndex(const QString& jointName) const; int getNumJoints() const; AnimPose getBindPose(int jointIndex) const; + int getParentIndex(int jointIndex) const; protected: std::vector _joints; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 80a8824d5a..263241ca0e 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -142,3 +142,90 @@ AnimDebugDraw::~AnimDebugDraw() { scene->enqueuePendingChanges(pendingChanges); } } + +void AnimDebugDraw::addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const Transform& worldTransform) { + _skeletons[key] = SkeletonInfo(skeleton, worldTransform); +} + +void AnimDebugDraw::removeSkeleton(std::string key) { + _skeletons.erase(key); +} + +void AnimDebugDraw::update() { + + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + if (!scene) { + return; + } + + render::PendingChanges pendingChanges; + pendingChanges.updateItem(_itemID, [&](AnimDebugDrawData& data) { + + // figure out how many verts we will need. + int numVerts = 0; + for (auto&& iter : _skeletons) { + AnimSkeleton::Pointer& skeleton = iter.second.first; + numVerts += skeleton->getNumJoints() * 6; + for (int i = 0; i < skeleton->getNumJoints(); i++) { + if (skeleton->getParentIndex(i) >= 0) { + numVerts += 2; + } + } + } + + const uint32_t red = toRGBA(255, 0, 0, 255); + const uint32_t green = toRGBA(0, 255, 0, 255); + const uint32_t blue = toRGBA(0, 0, 255, 255); + const uint32_t gray = toRGBA(64, 64, 64, 255); + + data._vertexBuffer->resize(sizeof(Vertex) * numVerts); + Vertex* verts = (Vertex*)data._vertexBuffer->editData(); + Vertex* v = verts; + for (auto&& iter : _skeletons) { + AnimSkeleton::Pointer& skeleton = iter.second.first; + Transform& xform = iter.second.second; + for (int i = 0; i < skeleton->getNumJoints(); i++) { + AnimPose pose = skeleton->getBindPose(i); + + v->pos = xform.transform(pose.trans); + v->rgba = red; + v++; + v->pos = xform.transform(pose.trans + pose.rot * glm::vec3(pose.scale.x, 0.0f, 0.0f)); + v->rgba = red; + v++; + + v->pos = xform.transform(pose.trans); + v->rgba = green; + v++; + v->pos = xform.transform(pose.trans + pose.rot * glm::vec3(0.0f, pose.scale.y, 0.0f)); + v->rgba = green; + v++; + + v->pos = xform.transform(pose.trans); + v->rgba = blue; + v++; + v->pos = xform.transform(pose.trans + pose.rot * glm::vec3(0.0f, 0.0f, pose.scale.z)); + v->rgba = blue; + v++; + + if (skeleton->getParentIndex(i) >= 0) { + AnimPose parentPose = skeleton->getBindPose(skeleton->getParentIndex(i)); + v->pos = xform.transform(pose.trans); + v->rgba = gray; + v++; + v->pos = xform.transform(parentPose.trans); + v->rgba = gray; + v++; + } + } + } + + data._indexBuffer->resize(sizeof(uint16_t) * numVerts); + uint16_t* indices = (uint16_t*)data._indexBuffer->editData(); + for (int i = 0; i < numVerts; i++) { + indices[i] = i; + } + + }); + scene->enqueuePendingChanges(pendingChanges); +} diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index ea3ac9ced1..136c27d1aa 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -10,14 +10,14 @@ #ifndef hifi_AnimDebugDraw_h #define hifi_AnimDebugDraw_h +#include "render/Scene.h" +#include "gpu/Pipeline.h" #include "AnimNode.h" -#include -#include +#include "AnimSkeleton.h" class AnimDebugDrawData; typedef render::Payload AnimDebugDrawPayload; - class AnimDebugDraw { public: static AnimDebugDraw& getInstance(); @@ -25,12 +25,21 @@ public: AnimDebugDraw(); ~AnimDebugDraw(); + void addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const Transform& worldTransform); + void removeSkeleton(std::string key); + + void update(); + protected: std::shared_ptr _animDebugDrawData; std::shared_ptr _animDebugDrawPayload; render::ItemID _itemID; static gpu::PipelinePointer _pipeline; + + typedef std::pair SkeletonInfo; + + std::unordered_map _skeletons; }; #endif // hifi_AnimDebugDraw diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 1a61c426e5..accf4246c6 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -150,16 +150,19 @@ T toNormalizedDeviceScale(const T& value, const T& size) { #define PITCH(euler) euler.x #define ROLL(euler) euler.z +// vec2 lerp - linear interpolate template glm::detail::tvec2 lerp(const glm::detail::tvec2& x, const glm::detail::tvec2& y, T a) { return x * (T(1) - a) + (y * a); } +// vec3 lerp - linear interpolate template glm::detail::tvec3 lerp(const glm::detail::tvec3& x, const glm::detail::tvec3& y, T a) { return x * (T(1) - a) + (y * a); } +// vec4 lerp - linear interpolate template glm::detail::tvec4 lerp(const glm::detail::tvec4& x, const glm::detail::tvec4& y, T a) { return x * (T(1) - a) + (y * a); diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 3064e18471..699eb8ef0a 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -129,6 +129,7 @@ public: static Transform& inverseMult(Transform& result, const Transform& left, const Transform& right); Vec4 transform(const Vec4& pos) const; + Vec3 transform(const Vec3& pos) const; protected: @@ -504,6 +505,12 @@ inline Transform::Vec4 Transform::transform(const Vec4& pos) const { return m * pos; } +inline Transform::Vec3 Transform::transform(const Vec3& pos) const { + Mat4 m; + getMatrix(m); + Vec4 result = m * Vec4(pos, 1.0f); + return Vec3(result.x / result.w, result.y / result.w, result.z / result.w); +} inline Transform::Mat4& Transform::getCachedMatrix(Transform::Mat4& result) const { updateCache(); From d1fdbe32d2591a2fcb2ce86dc2eb907455a7e6fe Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 1 Aug 2015 15:24:58 -0700 Subject: [PATCH 008/117] optimizations for debug rendering of AnimSkeleton --- libraries/animation/src/AnimSkeleton.cpp | 14 +++++++++----- libraries/animation/src/AnimSkeleton.h | 1 + libraries/render-utils/src/AnimDebugDraw.cpp | 18 ++++++++++++------ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 96b90b8f46..e30be784ca 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -12,6 +12,14 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; + + // build a cache of bind poses + _bindPoses.reserve(joints.size()); + for (size_t i = 0; i < joints.size(); i++) { + _bindPoses.push_back(AnimPose(extractScale(_joints[i].bindTransform), + glm::quat_cast(_joints[i].bindTransform), + extractTranslation(_joints[i].bindTransform))); + } } int AnimSkeleton::nameToJointIndex(const QString& jointName) const { @@ -28,11 +36,7 @@ int AnimSkeleton::getNumJoints() const { } AnimPose AnimSkeleton::getBindPose(int jointIndex) const { - // TODO: perhaps cache these, it's expensive to de-compose the matrix - // on every call. - return AnimPose(extractScale(_joints[jointIndex].bindTransform), - glm::quat_cast(_joints[jointIndex].bindTransform), - extractTranslation(_joints[jointIndex].bindTransform)); + return _bindPoses[jointIndex]; } int AnimSkeleton::getParentIndex(int jointIndex) const { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 0b50e60b65..7afc419e6b 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -35,6 +35,7 @@ public: protected: std::vector _joints; + std::vector _bindPoses; }; #endif diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 263241ca0e..9439e8314f 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -187,30 +187,36 @@ void AnimDebugDraw::update() { for (int i = 0; i < skeleton->getNumJoints(); i++) { AnimPose pose = skeleton->getBindPose(i); - v->pos = xform.transform(pose.trans); + glm::vec3 base = xform.transform(pose.trans); + + // x-axis + v->pos = base; v->rgba = red; v++; - v->pos = xform.transform(pose.trans + pose.rot * glm::vec3(pose.scale.x, 0.0f, 0.0f)); + v->pos = base + pose.rot * glm::vec3(1.0f, 0.0f, 0.0f); v->rgba = red; v++; + // y-axis v->pos = xform.transform(pose.trans); v->rgba = green; v++; - v->pos = xform.transform(pose.trans + pose.rot * glm::vec3(0.0f, pose.scale.y, 0.0f)); + v->pos = base + pose.rot * glm::vec3(0.0f, 1.0f, 0.0f); v->rgba = green; v++; - v->pos = xform.transform(pose.trans); + // z-axis + v->pos = base; v->rgba = blue; v++; - v->pos = xform.transform(pose.trans + pose.rot * glm::vec3(0.0f, 0.0f, pose.scale.z)); + v->pos = base + pose.rot * glm::vec3(0.0f, 0.0f, 1.0f); v->rgba = blue; v++; + // line to parent. if (skeleton->getParentIndex(i) >= 0) { AnimPose parentPose = skeleton->getBindPose(skeleton->getParentIndex(i)); - v->pos = xform.transform(pose.trans); + v->pos = base; v->rgba = gray; v++; v->pos = xform.transform(parentPose.trans); From df794637504e4693830cdc65d9e686a9efb2245a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 2 Aug 2015 12:50:14 -0700 Subject: [PATCH 009/117] WIP, animNode rendering --- interface/src/avatar/MyAvatar.cpp | 14 ++- interface/src/avatar/MyAvatar.h | 3 + libraries/animation/src/AnimClip.cpp | 12 +- libraries/animation/src/AnimClip.h | 7 +- libraries/animation/src/AnimNode.h | 5 + libraries/animation/src/AnimSkeleton.cpp | 4 + libraries/animation/src/AnimSkeleton.h | 5 + libraries/render-utils/src/AnimDebugDraw.cpp | 123 +++++++++++++------ libraries/render-utils/src/AnimDebugDraw.h | 9 +- 9 files changed, 136 insertions(+), 46 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 80da07b966..86d5d06719 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -51,6 +51,7 @@ #include "AnimDebugDraw.h" #include "AnimSkeleton.h" +#include "AnimClip.h" using namespace std; @@ -147,6 +148,11 @@ void MyAvatar::reset() { } void MyAvatar::update(float deltaTime) { + + if (_animNode) { + _animNode->evaluate(deltaTime); + } + if (_referential) { _referential->update(); } @@ -1206,6 +1212,8 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { initHeadBones(); _skeletonModel.setCauterizeBoneSet(_headBoneSet); + // AJT: SETUP DEBUG RENDERING OF NEW ANIMATION SYSTEM + // create a skeleton and hand it over to the debug draw instance auto geom = _skeletonModel.getGeometry()->getFBXGeometry(); std::vector joints; @@ -1213,7 +1221,11 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { joints.push_back(joint); } auto skeleton = make_shared(joints); - AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, Transform()); + //AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, AnimPose::identity); + + _animNode = make_shared("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true); + _animNode->setSkeleton(skeleton); + AnimDebugDraw::getInstance().addAnimNode("clip", _animNode, AnimPose::identity); } if (shouldDrawHead != _prevShouldDrawHead) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 129a05f93b..dc7c9e164f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -19,6 +19,7 @@ #include "Avatar.h" class ModelItemID; +class AnimNode; enum eyeContactTarget { LEFT_EYE, @@ -281,6 +282,8 @@ private: RigPointer _rig; bool _prevShouldDrawHead; std::unordered_set _headBoneSet; + + std::shared_ptr _animNode; }; #endif // hifi_MyAvatar_h diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index d10430dec3..b76c8d8ef3 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,14 +13,13 @@ AnimClip::AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : AnimNode(AnimNode::ClipType, id), - _url(url), _startFrame(startFrame), _endFrame(endFrame), _timeScale(timeScale), _loopFlag(loopFlag), _frame(startFrame) { - + setURL(url); } AnimClip::~AnimClip() { @@ -128,6 +127,9 @@ void AnimClip::copyFromNetworkAnim() { jointMap.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { int skeletonJoint = _skeleton->nameToJointIndex(joints.at(i).name); + if (skeletonJoint == -1) { + qCWarning(animation) << "animation contains joint =" << joints.at(i).name << " which is not in the skeleton, url =" << _url.c_str(); + } jointMap.push_back(skeletonJoint); } @@ -151,4 +153,10 @@ void AnimClip::copyFromNetworkAnim() { } } } + _poses.resize(skeletonJointCount); +} + + +const std::vector& AnimClip::getPosesInternal() const { + return _poses; } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 9858e5b524..59b286b95f 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -19,7 +19,7 @@ public: friend class AnimClipTests; AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); - virtual ~AnimClip(); + virtual ~AnimClip() override; void setURL(const std::string& url); const std::string& getURL() const { return _url; } @@ -36,12 +36,15 @@ public: void setLoopFlag(bool loopFlag); bool getLoopFlag() const { return _loopFlag; } - virtual const std::vector& evaluate(float dt); + virtual const std::vector& evaluate(float dt) override; protected: float accumulateTime(float frame, float dt) const; void copyFromNetworkAnim(); + // for AnimDebugDraw rendering + virtual const std::vector& getPosesInternal() const override; + AnimationPointer _networkAnim; std::vector _poses; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index a801b8b2bc..97249e2649 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -23,6 +23,8 @@ class QJsonObject; class AnimNode { public: + friend class AnimDebugDraw; + enum Type { ClipType = 0, NumTypes @@ -55,6 +57,9 @@ public: virtual const std::vector& evaluate(float dt) = 0; protected: + // for AnimDebugDraw rendering + virtual const std::vector& getPosesInternal() const = 0; + Type _type; std::string _id; std::vector _children; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index e30be784ca..9112ae301c 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -10,6 +10,10 @@ #include "AnimSkeleton.h" #include "glmHelpers.h" +const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), + glm::quat(), + glm::vec3(0.0f)); + AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 7afc419e6b..4a1124901e 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -17,6 +17,11 @@ struct AnimPose { AnimPose() {} AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} + static const AnimPose identity; + + glm::vec3 operator*(const glm::vec3& pos) const { + return trans + (rot * (scale * pos)); + } glm::vec3 scale; glm::quat rot; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 9439e8314f..13c71ec2e9 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -58,7 +58,7 @@ public: typedef render::Payload AnimDebugDrawPayload; namespace render { - template <> const ItemKey payloadGetKey(const AnimDebugDrawData::Pointer& data) { return ItemKey::Builder::transparentShape(); } + template <> const ItemKey payloadGetKey(const AnimDebugDrawData::Pointer& data) { return ItemKey::Builder::opaqueShape(); } template <> const Item::Bound payloadGetBound(const AnimDebugDrawData::Pointer& data) { return Item::Bound(); } template <> void payloadRender(const AnimDebugDrawData::Pointer& data, RenderArgs* args) { data->render(args); @@ -143,14 +143,65 @@ AnimDebugDraw::~AnimDebugDraw() { } } -void AnimDebugDraw::addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const Transform& worldTransform) { - _skeletons[key] = SkeletonInfo(skeleton, worldTransform); +void AnimDebugDraw::addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const AnimPose& rootPose) { + _skeletons[key] = SkeletonInfo(skeleton, rootPose); } void AnimDebugDraw::removeSkeleton(std::string key) { _skeletons.erase(key); } +void AnimDebugDraw::addAnimNode(std::string key, AnimNode::Pointer animNode, const AnimPose& rootPose) { + _animNodes[key] = AnimNodeInfo(animNode, rootPose); +} + +void AnimDebugDraw::removeAnimNode(std::string key) { + _animNodes.erase(key); +} + +static const uint32_t red = toRGBA(255, 0, 0, 255); +static const uint32_t green = toRGBA(0, 255, 0, 255); +static const uint32_t blue = toRGBA(0, 0, 255, 255); +static const uint32_t cyan = toRGBA(0, 128, 128, 255); + +static void addWireframeSphereWithAxes(const AnimPose& rootPose, const AnimPose& pose, float radius, Vertex*& v) { + + // x-axis + v->pos = rootPose * pose.trans; + v->rgba = red; + v++; + v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(radius * 2.0f, 0.0f, 0.0f)); + v->rgba = red; + v++; + + // y-axis + v->pos = rootPose * pose.trans; + v->rgba = green; + v++; + v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, radius * 2.0f, 0.0f)); + v->rgba = green; + v++; + + // z-axis + v->pos = rootPose * pose.trans; + v->rgba = blue; + v++; + v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, 0.0f, radius * 2.0f)); + v->rgba = blue; + v++; +} + +static void addWireframeBoneAxis(const AnimPose& rootPose, const AnimPose& pose, + const AnimPose& parentPose, float radius, Vertex*& v) { + v->pos = rootPose * pose.trans; + v->rgba = cyan; + v++; + v->pos = rootPose * parentPose.trans; + v->rgba = cyan; + v++; +} + + void AnimDebugDraw::update() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); @@ -173,59 +224,53 @@ void AnimDebugDraw::update() { } } - const uint32_t red = toRGBA(255, 0, 0, 255); - const uint32_t green = toRGBA(0, 255, 0, 255); - const uint32_t blue = toRGBA(0, 0, 255, 255); - const uint32_t gray = toRGBA(64, 64, 64, 255); + for (auto&& iter : _animNodes) { + AnimNode::Pointer& animNode = iter.second.first; + auto poses = animNode->getPosesInternal(); + numVerts += poses.size() * 6; + + /* + for (int i = 0; i < poses.size(); i++) { + // TODO: need skeleton! + if (skeleton->getParentIndex(i) >= 0) { + numVerts += 2; + } + } + */ + } data._vertexBuffer->resize(sizeof(Vertex) * numVerts); Vertex* verts = (Vertex*)data._vertexBuffer->editData(); Vertex* v = verts; for (auto&& iter : _skeletons) { AnimSkeleton::Pointer& skeleton = iter.second.first; - Transform& xform = iter.second.second; + AnimPose& rootPose = iter.second.second; for (int i = 0; i < skeleton->getNumJoints(); i++) { AnimPose pose = skeleton->getBindPose(i); - glm::vec3 base = xform.transform(pose.trans); - - // x-axis - v->pos = base; - v->rgba = red; - v++; - v->pos = base + pose.rot * glm::vec3(1.0f, 0.0f, 0.0f); - v->rgba = red; - v++; - - // y-axis - v->pos = xform.transform(pose.trans); - v->rgba = green; - v++; - v->pos = base + pose.rot * glm::vec3(0.0f, 1.0f, 0.0f); - v->rgba = green; - v++; - - // z-axis - v->pos = base; - v->rgba = blue; - v++; - v->pos = base + pose.rot * glm::vec3(0.0f, 0.0f, 1.0f); - v->rgba = blue; - v++; + // draw axes + const float radius = 1.0f; + addWireframeSphereWithAxes(rootPose, pose, radius, v); // line to parent. if (skeleton->getParentIndex(i) >= 0) { AnimPose parentPose = skeleton->getBindPose(skeleton->getParentIndex(i)); - v->pos = base; - v->rgba = gray; - v++; - v->pos = xform.transform(parentPose.trans); - v->rgba = gray; - v++; + addWireframeBoneAxis(rootPose, pose, parentPose, radius, v); } } } + for (auto&& iter : _animNodes) { + AnimNode::Pointer& animNode = iter.second.first; + AnimPose& rootPose = iter.second.second; + auto poses = animNode->getPosesInternal(); + for (size_t i = 0; i < poses.size(); i++) { + // draw axes + const float radius = 1.0f; + addWireframeSphereWithAxes(rootPose, poses[i], radius, v); + } + } + data._indexBuffer->resize(sizeof(uint16_t) * numVerts); uint16_t* indices = (uint16_t*)data._indexBuffer->editData(); for (int i = 0; i < numVerts; i++) { diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index 136c27d1aa..6957696184 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -25,9 +25,12 @@ public: AnimDebugDraw(); ~AnimDebugDraw(); - void addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const Transform& worldTransform); + void addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const AnimPose& rootPose); void removeSkeleton(std::string key); + void addAnimNode(std::string key, AnimNode::Pointer skeleton, const AnimPose& rootPose); + void removeAnimNode(std::string key); + void update(); protected: @@ -37,9 +40,11 @@ protected: static gpu::PipelinePointer _pipeline; - typedef std::pair SkeletonInfo; + typedef std::pair SkeletonInfo; + typedef std::pair AnimNodeInfo; std::unordered_map _skeletons; + std::unordered_map _animNodes; }; #endif // hifi_AnimDebugDraw From 55da34f713c263d2c4365edbe98132ce95c25683 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 2 Aug 2015 15:22:30 -0700 Subject: [PATCH 010/117] Better debug rendering of animations. * added mat4 cast and mat4 ctors to AnimPose. --- interface/src/avatar/MyAvatar.cpp | 6 ++- libraries/animation/src/AnimClip.cpp | 8 ++-- libraries/animation/src/AnimSkeleton.cpp | 39 +++++++++++++++++--- libraries/animation/src/AnimSkeleton.h | 22 +++++++++-- libraries/render-utils/src/AnimDebugDraw.cpp | 31 ++++++++++++---- 5 files changed, 83 insertions(+), 23 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 86d5d06719..18d01e5d2c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1221,11 +1221,13 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { joints.push_back(joint); } auto skeleton = make_shared(joints); - //AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, AnimPose::identity); + AnimPose xform(glm::vec3(0.01f), glm::quat(), glm::vec3(0.0)); + AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); _animNode = make_shared("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true); _animNode->setSkeleton(skeleton); - AnimDebugDraw::getInstance().addAnimNode("clip", _animNode, AnimPose::identity); + xform.trans.z += 1.0f; + AnimDebugDraw::getInstance().addAnimNode("clip", _animNode, xform); } if (shouldDrawHead != _prevShouldDrawHead) { diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index b76c8d8ef3..6f16a8d884 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -138,18 +138,18 @@ void AnimClip::copyFromNetworkAnim() { _anim.resize(frameCount); for (int i = 0; i < frameCount; i++) { - // init all joints in animation to bind pose + // init all joints in animation to relative bind pose _anim[i].reserve(skeletonJointCount); for (int j = 0; j < skeletonJointCount; j++) { - _anim[i].push_back(_skeleton->getBindPose(j)); + _anim[i].push_back(_skeleton->getRelativeBindPose(j)); } // init over all joint animations for (int j = 0; j < animJointCount; j++) { int k = jointMap[j]; if (k >= 0 && k < skeletonJointCount) { - // currently animations only have rotation. - _anim[i][k].rot = geom.animationFrames[i].rotations[j]; + // currently FBX animations only have rotation. + _anim[i][k].rot = _skeleton->getRelativeBindPose(k).rot * geom.animationFrames[i].rotations[j]; } } } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 9112ae301c..e966203d22 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -14,15 +14,38 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), glm::quat(), glm::vec3(0.0f)); +AnimPose::AnimPose(const glm::mat4& mat) { + scale = extractScale(mat); + rot = glm::quat_cast(mat); + trans = extractTranslation(mat); +} + +AnimPose::operator glm::mat4() const { + glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); + glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); + return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), + glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); +} + AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; // build a cache of bind poses - _bindPoses.reserve(joints.size()); + _absoluteBindPoses.reserve(joints.size()); + _relativeBindPoses.reserve(joints.size()); for (size_t i = 0; i < joints.size(); i++) { - _bindPoses.push_back(AnimPose(extractScale(_joints[i].bindTransform), - glm::quat_cast(_joints[i].bindTransform), - extractTranslation(_joints[i].bindTransform))); + glm::mat4 absBindMat = _joints[i].bindTransform; + AnimPose absBindPose(_joints[i].bindTransform); + _absoluteBindPoses.push_back(absBindPose); + int parentIndex = getParentIndex(i); + if (parentIndex >= 0) { + glm::mat4 invParentAbsBindMat = glm::inverse(_joints[parentIndex].bindTransform); + glm::mat4 relBindMat = invParentAbsBindMat * absBindMat; + _relativeBindPoses.push_back(AnimPose(relBindMat)); + } else { + _relativeBindPoses.push_back(absBindPose); + } } } @@ -39,8 +62,12 @@ int AnimSkeleton::getNumJoints() const { return _joints.size(); } -AnimPose AnimSkeleton::getBindPose(int jointIndex) const { - return _bindPoses[jointIndex]; +AnimPose AnimSkeleton::getAbsoluteBindPose(int jointIndex) const { + return _absoluteBindPoses[jointIndex]; +} + +AnimPose AnimSkeleton::getRelativeBindPose(int jointIndex) const { + return _relativeBindPoses[jointIndex]; } int AnimSkeleton::getParentIndex(int jointIndex) const { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 4a1124901e..9019a1b356 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -16,13 +16,20 @@ struct AnimPose { AnimPose() {} + AnimPose(const glm::mat4& mat); AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} static const AnimPose identity; - glm::vec3 operator*(const glm::vec3& pos) const { - return trans + (rot * (scale * pos)); + glm::vec3 operator*(const glm::vec3& rhs) const { + return trans + (rot * (scale * rhs)); } + AnimPose operator*(const AnimPose& rhs) const { + return AnimPose(static_cast(*this) * static_cast(rhs)); + } + + operator glm::mat4() const; + glm::vec3 scale; glm::quat rot; glm::vec3 trans; @@ -35,12 +42,19 @@ public: AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; int getNumJoints() const; - AnimPose getBindPose(int jointIndex) const; + + // absolute pose, not relative to parent + AnimPose getAbsoluteBindPose(int jointIndex) const; + + // relative to parent pose + AnimPose getRelativeBindPose(int jointIndex) const; + int getParentIndex(int jointIndex) const; protected: std::vector _joints; - std::vector _bindPoses; + std::vector _absoluteBindPoses; + std::vector _relativeBindPoses; }; #endif diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 13c71ec2e9..5c646d9052 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -228,15 +228,12 @@ void AnimDebugDraw::update() { AnimNode::Pointer& animNode = iter.second.first; auto poses = animNode->getPosesInternal(); numVerts += poses.size() * 6; - - /* + auto skeleton = animNode->getSkeleton(); for (int i = 0; i < poses.size(); i++) { - // TODO: need skeleton! if (skeleton->getParentIndex(i) >= 0) { numVerts += 2; } } - */ } data._vertexBuffer->resize(sizeof(Vertex) * numVerts); @@ -245,8 +242,9 @@ void AnimDebugDraw::update() { for (auto&& iter : _skeletons) { AnimSkeleton::Pointer& skeleton = iter.second.first; AnimPose& rootPose = iter.second.second; + for (int i = 0; i < skeleton->getNumJoints(); i++) { - AnimPose pose = skeleton->getBindPose(i); + AnimPose pose = skeleton->getAbsoluteBindPose(i); // draw axes const float radius = 1.0f; @@ -254,7 +252,7 @@ void AnimDebugDraw::update() { // line to parent. if (skeleton->getParentIndex(i) >= 0) { - AnimPose parentPose = skeleton->getBindPose(skeleton->getParentIndex(i)); + AnimPose parentPose = skeleton->getAbsoluteBindPose(skeleton->getParentIndex(i)); addWireframeBoneAxis(rootPose, pose, parentPose, radius, v); } } @@ -264,10 +262,29 @@ void AnimDebugDraw::update() { AnimNode::Pointer& animNode = iter.second.first; AnimPose& rootPose = iter.second.second; auto poses = animNode->getPosesInternal(); + + auto skeleton = animNode->getSkeleton(); + + std::vector absAnimPose; + absAnimPose.reserve(skeleton->getNumJoints()); + for (size_t i = 0; i < poses.size(); i++) { + + auto parentIndex = skeleton->getParentIndex(i); + if (parentIndex >= 0) { + absAnimPose[i] = absAnimPose[parentIndex] * poses[i]; + } else { + absAnimPose[i] = poses[i]; + } + // draw axes const float radius = 1.0f; - addWireframeSphereWithAxes(rootPose, poses[i], radius, v); + addWireframeSphereWithAxes(rootPose, absAnimPose[i], radius, v); + + if (parentIndex >= 0) { + // draw line to parent + addWireframeBoneAxis(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, v); + } } } From 69cc27080220296fd0d94614cfed5239c3812bfa Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 3 Aug 2015 10:02:05 -0700 Subject: [PATCH 011/117] removed scale for fight club model --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/render-utils/src/AnimDebugDraw.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 18d01e5d2c..99b634dec1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1221,7 +1221,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { joints.push_back(joint); } auto skeleton = make_shared(joints); - AnimPose xform(glm::vec3(0.01f), glm::quat(), glm::vec3(0.0)); + AnimPose xform(glm::vec3(1.0f), glm::quat(), glm::vec3(0.0)); AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); _animNode = make_shared("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true); diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 5c646d9052..6e3bb4bc8a 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -247,7 +247,7 @@ void AnimDebugDraw::update() { AnimPose pose = skeleton->getAbsoluteBindPose(i); // draw axes - const float radius = 1.0f; + const float radius = 0.01f; addWireframeSphereWithAxes(rootPose, pose, radius, v); // line to parent. @@ -278,7 +278,7 @@ void AnimDebugDraw::update() { } // draw axes - const float radius = 1.0f; + const float radius = 0.01f; addWireframeSphereWithAxes(rootPose, absAnimPose[i], radius, v); if (parentIndex >= 0) { From d8a20340a043cf8840d254a006799217abe7b308 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 3 Aug 2015 18:31:32 -0700 Subject: [PATCH 012/117] Found and fix source of memory corruption. std::vector.reserve() and raw access do not mix. raw access will only work if you push_back elements onto the vector first. However this worked fine on MacOSX, probably due to differences in STL implementations. Some code clean up and some commented out debugging lines. Debug rendering of animaions of fight club model is not working. Not sure what frame these transformations are in. --- libraries/animation/src/AnimClip.cpp | 14 +++--- libraries/animation/src/AnimSkeleton.cpp | 49 ++++++++++++++++---- libraries/animation/src/AnimSkeleton.h | 19 ++++---- libraries/render-utils/src/AnimDebugDraw.cpp | 25 +++++++--- 4 files changed, 75 insertions(+), 32 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 6f16a8d884..dbb241d484 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -121,14 +121,14 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. const FBXGeometry& geom = _networkAnim->getGeometry(); - const QVector& joints = geom.joints; + const QVector& animJoints = geom.joints; std::vector jointMap; - const int animJointCount = joints.count(); + const int animJointCount = animJoints.count(); jointMap.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { - int skeletonJoint = _skeleton->nameToJointIndex(joints.at(i).name); + int skeletonJoint = _skeleton->nameToJointIndex(animJoints.at(i).name); if (skeletonJoint == -1) { - qCWarning(animation) << "animation contains joint =" << joints.at(i).name << " which is not in the skeleton, url =" << _url.c_str(); + qCWarning(animation) << "animation contains joint =" << animJoints.at(i).name << " which is not in the skeleton, url =" << _url.c_str(); } jointMap.push_back(skeletonJoint); } @@ -138,10 +138,10 @@ void AnimClip::copyFromNetworkAnim() { _anim.resize(frameCount); for (int i = 0; i < frameCount; i++) { - // init all joints in animation to relative bind pose + // init all joints in animation to identity _anim[i].reserve(skeletonJointCount); for (int j = 0; j < skeletonJointCount; j++) { - _anim[i].push_back(_skeleton->getRelativeBindPose(j)); + _anim[i].push_back(AnimPose::identity); } // init over all joint animations @@ -149,7 +149,7 @@ void AnimClip::copyFromNetworkAnim() { int k = jointMap[j]; if (k >= 0 && k < skeletonJointCount) { // currently FBX animations only have rotation. - _anim[i][k].rot = _skeleton->getRelativeBindPose(k).rot * geom.animationFrames[i].rotations[j]; + _anim[i][k].rot = geom.animationFrames[i].rotations[j]; } } } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index e966203d22..feaa1828ce 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -8,6 +8,7 @@ // #include "AnimSkeleton.h" +#include "AnimationLogging.h" #include "glmHelpers.h" const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), @@ -16,10 +17,22 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), AnimPose::AnimPose(const glm::mat4& mat) { scale = extractScale(mat); - rot = glm::quat_cast(mat); + rot = glm::normalize(glm::quat_cast(mat)); trans = extractTranslation(mat); } +glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const { + return trans + (rot * (scale * rhs)); +} + +AnimPose AnimPose::operator*(const AnimPose& rhs) const { + return AnimPose(static_cast(*this) * static_cast(rhs)); +} + +AnimPose AnimPose::inverse() const { + return AnimPose(glm::inverse(static_cast(*this))); +} + AnimPose::operator glm::mat4() const { glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); @@ -28,6 +41,7 @@ AnimPose::operator glm::mat4() const { glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); } + AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; @@ -35,17 +49,32 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints) { _absoluteBindPoses.reserve(joints.size()); _relativeBindPoses.reserve(joints.size()); for (size_t i = 0; i < joints.size(); i++) { - glm::mat4 absBindMat = _joints[i].bindTransform; - AnimPose absBindPose(_joints[i].bindTransform); - _absoluteBindPoses.push_back(absBindPose); + + AnimPose absoluteBindPose(_joints[i].bindTransform); + _absoluteBindPoses.push_back(absoluteBindPose); + int parentIndex = getParentIndex(i); if (parentIndex >= 0) { - glm::mat4 invParentAbsBindMat = glm::inverse(_joints[parentIndex].bindTransform); - glm::mat4 relBindMat = invParentAbsBindMat * absBindMat; - _relativeBindPoses.push_back(AnimPose(relBindMat)); + AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); + _relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose); } else { - _relativeBindPoses.push_back(absBindPose); + _relativeBindPoses.push_back(absoluteBindPose); } + + // AJT: + // Attempt to use relative bind pose.. but it's not working. + /* + AnimPose relBindPose(glm::vec3(1.0f), _joints[i].rotation, _joints[i].translation); + _relativeBindPoses.push_back(relBindPose); + + int parentIndex = getParentIndex(i); + if (parentIndex >= 0) { + AnimPose parentAbsBindPose = _absoluteBindPoses[parentIndex]; + _absoluteBindPoses.push_back(parentAbsBindPose * relBindPose); + } else { + _absoluteBindPoses.push_back(relBindPose); + } + */ } } @@ -74,4 +103,6 @@ int AnimSkeleton::getParentIndex(int jointIndex) const { return _joints[jointIndex].parentIndex; } - +const QString& AnimSkeleton::getJointName(int jointIndex) const { + return _joints[jointIndex].name; +} diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 9019a1b356..b51daadae7 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -16,18 +16,13 @@ struct AnimPose { AnimPose() {} - AnimPose(const glm::mat4& mat); + explicit AnimPose(const glm::mat4& mat); AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} static const AnimPose identity; - glm::vec3 operator*(const glm::vec3& rhs) const { - return trans + (rot * (scale * rhs)); - } - - AnimPose operator*(const AnimPose& rhs) const { - return AnimPose(static_cast(*this) * static_cast(rhs)); - } - + glm::vec3 operator*(const glm::vec3& rhs) const; + AnimPose operator*(const AnimPose& rhs) const; + AnimPose inverse() const; operator glm::mat4() const; glm::vec3 scale; @@ -35,12 +30,18 @@ struct AnimPose { glm::vec3 trans; }; +inline QDebug operator<<(QDebug debug, const AnimPose& pose) { + debug << "AnimPose, trans = (" << pose.trans.x << pose.trans.y << pose.trans.z << "), rot = (" << pose.rot.x << pose.rot.y << pose.rot.z << pose.rot.w << "), scale = (" << pose.scale.x << pose.scale.y << pose.scale.z << ")"; + return debug; +} + class AnimSkeleton { public: typedef std::shared_ptr Pointer; AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; + const QString& getJointName(int jointIndex) const; int getNumJoints() const; // absolute pose, not relative to parent diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 6e3bb4bc8a..8a9877ca12 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -13,6 +13,7 @@ #include "AnimDebugDraw.h" #include "AbstractViewStateInterface.h" +#include "RenderUtilsLogging.h" struct Vertex { glm::vec3 pos; @@ -218,7 +219,8 @@ void AnimDebugDraw::update() { AnimSkeleton::Pointer& skeleton = iter.second.first; numVerts += skeleton->getNumJoints() * 6; for (int i = 0; i < skeleton->getNumJoints(); i++) { - if (skeleton->getParentIndex(i) >= 0) { + auto parentIndex = skeleton->getParentIndex(i); + if (parentIndex >= 0) { numVerts += 2; } } @@ -229,8 +231,9 @@ void AnimDebugDraw::update() { auto poses = animNode->getPosesInternal(); numVerts += poses.size() * 6; auto skeleton = animNode->getSkeleton(); - for (int i = 0; i < poses.size(); i++) { - if (skeleton->getParentIndex(i) >= 0) { + for (size_t i = 0; i < poses.size(); i++) { + auto parentIndex = skeleton->getParentIndex(i); + if (parentIndex >= 0) { numVerts += 2; } } @@ -251,8 +254,14 @@ void AnimDebugDraw::update() { addWireframeSphereWithAxes(rootPose, pose, radius, v); // line to parent. - if (skeleton->getParentIndex(i) >= 0) { - AnimPose parentPose = skeleton->getAbsoluteBindPose(skeleton->getParentIndex(i)); + auto parentIndex = skeleton->getParentIndex(i); + //qCDebug(renderutils) << skeleton->getJointName(i) << " index = " << i; + //qCDebug(renderutils) << " absPose =" << skeleton->getAbsoluteBindPose(i); + //qCDebug(renderutils) << " relPose =" << skeleton->getRelativeBindPose(i); + if (parentIndex >= 0) { + //qCDebug(renderutils) << " parent =" << parentIndex; + assert(parentIndex < skeleton->getNumJoints()); + AnimPose parentPose = skeleton->getAbsoluteBindPose(parentIndex); addWireframeBoneAxis(rootPose, pose, parentPose, radius, v); } } @@ -266,7 +275,7 @@ void AnimDebugDraw::update() { auto skeleton = animNode->getSkeleton(); std::vector absAnimPose; - absAnimPose.reserve(skeleton->getNumJoints()); + absAnimPose.resize(skeleton->getNumJoints()); for (size_t i = 0; i < poses.size(); i++) { @@ -282,18 +291,20 @@ void AnimDebugDraw::update() { addWireframeSphereWithAxes(rootPose, absAnimPose[i], radius, v); if (parentIndex >= 0) { + assert((size_t)parentIndex < poses.size()); // draw line to parent addWireframeBoneAxis(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, v); } } } + assert(numVerts == (v - verts)); + data._indexBuffer->resize(sizeof(uint16_t) * numVerts); uint16_t* indices = (uint16_t*)data._indexBuffer->editData(); for (int i = 0; i < numVerts; i++) { indices[i] = i; } - }); scene->enqueuePendingChanges(pendingChanges); } From 559367db4a138ba789493ec3a47f0e0b01c7da3f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 4 Aug 2015 16:03:08 -0700 Subject: [PATCH 013/117] Got to the bottom of the bind pose question. When TRUST_BIND_TRANSFORM is defined in AnimSkeleton, the bind pose is taken from the FBXJoint::bindTransform field, which is correct for all joints bound to actual geometry. Basically it's not trust worthy for bones NOT bound to anything. When TRUST_BIND_TRANSFORM is not defined, the bind pose is taken from the other FBXJoint fields. Unfortunatly these poses are NOT the bind pose, but instead are taken from the FBX files 'resting' pose. i.e. frame 0. --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimClip.cpp | 6 +-- libraries/animation/src/AnimSkeleton.cpp | 45 +++++++++++++++----- libraries/render-utils/src/AnimDebugDraw.cpp | 22 +++++----- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 99b634dec1..88f8a64830 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1221,7 +1221,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { joints.push_back(joint); } auto skeleton = make_shared(joints); - AnimPose xform(glm::vec3(1.0f), glm::quat(), glm::vec3(0.0)); + AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); _animNode = make_shared("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index dbb241d484..9f97602ed1 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -138,10 +138,10 @@ void AnimClip::copyFromNetworkAnim() { _anim.resize(frameCount); for (int i = 0; i < frameCount; i++) { - // init all joints in animation to identity + // init all joints in animation to bind pose _anim[i].reserve(skeletonJointCount); for (int j = 0; j < skeletonJointCount; j++) { - _anim[i].push_back(AnimPose::identity); + _anim[i].push_back(_skeleton->getRelativeBindPose(j)); } // init over all joint animations @@ -149,7 +149,7 @@ void AnimClip::copyFromNetworkAnim() { int k = jointMap[j]; if (k >= 0 && k < skeletonJointCount) { // currently FBX animations only have rotation. - _anim[i][k].rot = geom.animationFrames[i].rotations[j]; + _anim[i][k].rot = _skeleton->getRelativeBindPose(j).rot * geom.animationFrames[i].rotations[j]; } } } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index feaa1828ce..27556c6930 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -10,6 +10,8 @@ #include "AnimSkeleton.h" #include "AnimationLogging.h" #include "glmHelpers.h" +#include +#include const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), glm::quat(), @@ -17,7 +19,7 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), AnimPose::AnimPose(const glm::mat4& mat) { scale = extractScale(mat); - rot = glm::normalize(glm::quat_cast(mat)); + rot = extractRotation(mat); trans = extractTranslation(mat); } @@ -41,6 +43,7 @@ AnimPose::operator glm::mat4() const { glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); } +//#define TRUST_BIND_TRANSFORM AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; @@ -50,9 +53,31 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints) { _relativeBindPoses.reserve(joints.size()); for (size_t i = 0; i < joints.size(); i++) { + /* + // AJT: dump the skeleton, because wtf. + qCDebug(animation) << getJointName(i); + qCDebug(animation) << " isFree =" << _joints[i].isFree; + qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; + qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; + qCDebug(animation) << " boneRadius =" << _joints[i].boneRadius; + qCDebug(animation) << " translation =" << _joints[i].translation; + qCDebug(animation) << " preTransform =" << _joints[i].preTransform; + qCDebug(animation) << " preRotation =" << _joints[i].preRotation; + qCDebug(animation) << " rotation =" << _joints[i].rotation; + qCDebug(animation) << " postRotation =" << _joints[i].postRotation; + qCDebug(animation) << " postTransform =" << _joints[i].postTransform; + qCDebug(animation) << " transform =" << _joints[i].transform; + qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax; + qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation; + qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation; + qCDebug(animation) << " bindTransform" << _joints[i].bindTransform; + qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint; + */ + +#ifdef TRUST_BIND_TRANSFORM + // trust FBXJoint::bindTransform (which is wrong for joints NOT bound to anything) AnimPose absoluteBindPose(_joints[i].bindTransform); _absoluteBindPoses.push_back(absoluteBindPose); - int parentIndex = getParentIndex(i); if (parentIndex >= 0) { AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); @@ -60,24 +85,24 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints) { } else { _relativeBindPoses.push_back(absoluteBindPose); } - - // AJT: - // Attempt to use relative bind pose.. but it's not working. - /* - AnimPose relBindPose(glm::vec3(1.0f), _joints[i].rotation, _joints[i].translation); +#else + // trust FBXJoint's local transforms (which is not really the bind pose, but the default pose in the fbx) + glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation); + glm::mat4 relBindMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform; + AnimPose relBindPose(relBindMat); _relativeBindPoses.push_back(relBindPose); int parentIndex = getParentIndex(i); if (parentIndex >= 0) { - AnimPose parentAbsBindPose = _absoluteBindPoses[parentIndex]; - _absoluteBindPoses.push_back(parentAbsBindPose * relBindPose); + _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relBindPose); } else { _absoluteBindPoses.push_back(relBindPose); } - */ +#endif } } + int AnimSkeleton::nameToJointIndex(const QString& jointName) const { for (size_t i = 0; i < _joints.size(); i++) { if (_joints[i].name == jointName) { diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 8a9877ca12..7bfe7be98d 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -167,27 +167,29 @@ static const uint32_t cyan = toRGBA(0, 128, 128, 255); static void addWireframeSphereWithAxes(const AnimPose& rootPose, const AnimPose& pose, float radius, Vertex*& v) { + glm::vec3 base = rootPose * pose.trans; + // x-axis - v->pos = rootPose * pose.trans; + v->pos = base; v->rgba = red; v++; - v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(radius * 2.0f, 0.0f, 0.0f)); + v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(radius, 0.0f, 0.0f)); v->rgba = red; v++; // y-axis - v->pos = rootPose * pose.trans; + v->pos = base; v->rgba = green; v++; - v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, radius * 2.0f, 0.0f)); + v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, radius, 0.0f)); v->rgba = green; v++; // z-axis - v->pos = rootPose * pose.trans; + v->pos = base; v->rgba = blue; v++; - v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, 0.0f, radius * 2.0f)); + v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, 0.0f, radius)); v->rgba = blue; v++; } @@ -250,16 +252,12 @@ void AnimDebugDraw::update() { AnimPose pose = skeleton->getAbsoluteBindPose(i); // draw axes - const float radius = 0.01f; + const float radius = 0.1f; addWireframeSphereWithAxes(rootPose, pose, radius, v); // line to parent. auto parentIndex = skeleton->getParentIndex(i); - //qCDebug(renderutils) << skeleton->getJointName(i) << " index = " << i; - //qCDebug(renderutils) << " absPose =" << skeleton->getAbsoluteBindPose(i); - //qCDebug(renderutils) << " relPose =" << skeleton->getRelativeBindPose(i); if (parentIndex >= 0) { - //qCDebug(renderutils) << " parent =" << parentIndex; assert(parentIndex < skeleton->getNumJoints()); AnimPose parentPose = skeleton->getAbsoluteBindPose(parentIndex); addWireframeBoneAxis(rootPose, pose, parentPose, radius, v); @@ -287,7 +285,7 @@ void AnimDebugDraw::update() { } // draw axes - const float radius = 0.01f; + const float radius = 0.1f; addWireframeSphereWithAxes(rootPose, absAnimPose[i], radius, v); if (parentIndex >= 0) { From da3d35cdfcebd2567fe7d50fc589b7302aa6cc77 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 4 Aug 2015 16:06:40 -0700 Subject: [PATCH 014/117] Added scaleOffset to FBXJoint::postTransform as it was missing. Also added some documentation/comments to the FBXJoint struct. --- libraries/fbx/src/FBXReader.cpp | 14 ++++++++------ libraries/fbx/src/FBXReader.h | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 3b7d18f1d8..bd3f050a55 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1706,7 +1706,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, glm::vec3 rotationOffset; glm::vec3 preRotation, rotation, postRotation; glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f); - glm::vec3 scalePivot, rotationPivot; + glm::vec3 scalePivot, rotationPivot, scaleOffset; bool rotationMinX = false, rotationMinY = false, rotationMinZ = false; bool rotationMaxX = false, rotationMaxY = false, rotationMaxZ = false; glm::vec3 rotationMin, rotationMax; @@ -1755,12 +1755,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } else if (property.properties.at(0) == "Lcl Scaling") { scale = getVec3(property.properties, index); + } else if (property.properties.at(0) == "ScalingOffset") { + scaleOffset = getVec3(property.properties, index); + + // NOTE: these rotation limits are stored in degrees (NOT radians) } else if (property.properties.at(0) == "RotationMin") { rotationMin = getVec3(property.properties, index); - } - // NOTE: these rotation limits are stored in degrees (NOT radians) - else if (property.properties.at(0) == "RotationMax") { + } else if (property.properties.at(0) == "RotationMax") { rotationMax = getVec3(property.properties, index); } else if (property.properties.at(0) == "RotationMinX") { @@ -1827,8 +1829,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, model.preRotation = glm::quat(glm::radians(preRotation)); model.rotation = glm::quat(glm::radians(rotation)); model.postRotation = glm::quat(glm::radians(postRotation)); - model.postTransform = glm::translate(-rotationPivot) * glm::translate(scalePivot) * - glm::scale(scale) * glm::translate(-scalePivot); + model.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) * + glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); // NOTE: angles from the FBX file are in degrees // so we convert them to radians for the FBXModel class model.rotationMin = glm::radians(glm::vec3(rotationMinX ? rotationMin.x : -180.0f, diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index b8a22b0b80..aaefdae252 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -64,12 +64,18 @@ public: int parentIndex; float distanceToParent; float boneRadius; - glm::vec3 translation; - glm::mat4 preTransform; - glm::quat preRotation; - glm::quat rotation; - glm::quat postRotation; - glm::mat4 postTransform; + + // http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html + + glm::vec3 translation; // T + glm::mat4 preTransform; // Roff * Rp + glm::quat preRotation; // Rpre + glm::quat rotation; // R + glm::quat postRotation; // Rpost + glm::mat4 postTransform; // Rp-1 * Soff * Sp * S * Sp-1 + + // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) + glm::mat4 transform; glm::vec3 rotationMin; // radians glm::vec3 rotationMax; // radians From 5d83976e2abd6c46a094483b6b6a810206b0ee12 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 4 Aug 2015 18:13:13 -0700 Subject: [PATCH 015/117] Added AnimBlendLinear + tests. MyAvatar now does a sine wave blend between a walk and a walk animation. --- interface/src/avatar/MyAvatar.cpp | 46 ++++++++++------ interface/src/avatar/MyAvatar.h | 1 + libraries/animation/src/AnimBlendLinear.cpp | 59 +++++++++++++++++++++ libraries/animation/src/AnimBlendLinear.h | 39 ++++++++++++++ libraries/animation/src/AnimClip.cpp | 10 ++-- libraries/animation/src/AnimNode.h | 14 ++++- libraries/animation/src/AnimNodeLoader.cpp | 26 +++++++-- libraries/animation/src/AnimNodeLoader.h | 5 ++ libraries/animation/src/AnimSkeleton.h | 4 ++ libraries/render-utils/src/AnimDebugDraw.h | 4 ++ tests/animation/src/AnimClipTests.cpp | 31 +++++++---- tests/animation/src/data/test.json | 16 +++--- 12 files changed, 211 insertions(+), 44 deletions(-) create mode 100644 libraries/animation/src/AnimBlendLinear.cpp create mode 100644 libraries/animation/src/AnimBlendLinear.h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 88f8a64830..ecfa848630 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -52,6 +52,7 @@ #include "AnimDebugDraw.h" #include "AnimSkeleton.h" #include "AnimClip.h" +#include "AnimBlendLinear.h" using namespace std; @@ -150,7 +151,11 @@ void MyAvatar::reset() { void MyAvatar::update(float deltaTime) { if (_animNode) { - _animNode->evaluate(deltaTime); + static float t = 0.0f; + auto blend = std::static_pointer_cast(_animNode); + blend->setAlpha(0.5f * sin(t) + 0.5f); + t += deltaTime; + _animNode->evaluate(deltaTime); } if (_referential) { @@ -1203,6 +1208,30 @@ void MyAvatar::initHeadBones() { } } +void MyAvatar::setupNewAnimationSystem() { + + // create a skeleton and hand it over to the debug draw instance + auto geom = _skeletonModel.getGeometry()->getFBXGeometry(); + std::vector joints; + for (auto& joint : geom.joints) { + joints.push_back(joint); + } + auto skeleton = make_shared(joints); + AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); + AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); + + // create a blend node + auto blend = make_shared("blend", 0.5f); + auto idle = make_shared("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("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); + xform.trans.z += 1.0f; + AnimDebugDraw::getInstance().addAnimNode("blend", _animNode, xform); +} + void MyAvatar::preRender(RenderArgs* renderArgs) { render::ScenePointer scene = Application::getInstance()->getMain3DScene(); @@ -1214,20 +1243,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { // AJT: SETUP DEBUG RENDERING OF NEW ANIMATION SYSTEM - // create a skeleton and hand it over to the debug draw instance - auto geom = _skeletonModel.getGeometry()->getFBXGeometry(); - std::vector joints; - for (auto& joint : geom.joints) { - joints.push_back(joint); - } - auto skeleton = make_shared(joints); - AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); - AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); - - _animNode = make_shared("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true); - _animNode->setSkeleton(skeleton); - xform.trans.z += 1.0f; - AnimDebugDraw::getInstance().addAnimNode("clip", _animNode, xform); + setupNewAnimationSystem(); } if (shouldDrawHead != _prevShouldDrawHead) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index dc7c9e164f..7da36a6884 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -189,6 +189,7 @@ signals: private: + void setupNewAnimationSystem(); QByteArray toByteArray(); void simulate(float deltaTime); void updateFromTrackers(float deltaTime); diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp new file mode 100644 index 0000000000..3471e4e8d1 --- /dev/null +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -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& 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& AnimBlendLinear::getPosesInternal() const { + return _poses; +} diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h new file mode 100644 index 0000000000..db217a25ee --- /dev/null +++ b/libraries/animation/src/AnimBlendLinear.h @@ -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& evaluate(float dt) override; + + void setAlpha(float alpha) { _alpha = alpha; } + float getAlpha() const { return _alpha; } + +protected: + // for AnimDebugDraw rendering + virtual const std::vector& getPosesInternal() const override; + + std::vector _poses; + + float _alpha; + + // no copies + AnimBlendLinear(const AnimBlendLinear&) = delete; + AnimBlendLinear& operator=(const AnimBlendLinear&) = delete; +}; + +#endif // hifi_AnimBlendLinear_h diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 9f97602ed1..8c223013bf 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -103,11 +103,11 @@ const std::vector& AnimClip::evaluate(float dt) { float alpha = glm::fract(_frame); for (size_t i = 0; i < _poses.size(); i++) { - const AnimPose& prevBone = prevFrame[i]; - const AnimPose& nextBone = nextFrame[i]; - _poses[i].scale = lerp(prevBone.scale, nextBone.scale, alpha); - _poses[i].rot = glm::normalize(glm::lerp(prevBone.rot, nextBone.rot, alpha)); - _poses[i].trans = lerp(prevBone.trans, nextBone.trans, alpha); + const AnimPose& prevPose = prevFrame[i]; + const AnimPose& nextPose = nextFrame[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); } } diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 97249e2649..6ee7ff89c8 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -27,6 +27,7 @@ public: enum Type { ClipType = 0, + BlendLinearType, NumTypes }; typedef std::shared_ptr Pointer; @@ -49,7 +50,13 @@ public: } 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; } virtual ~AnimNode() {} @@ -57,6 +64,11 @@ public: virtual const std::vector& evaluate(float dt) = 0; protected: + + virtual void setSkeletonInternal(AnimSkeleton::Pointer skeleton) { + _skeleton = skeleton; + } + // for AnimDebugDraw rendering virtual const std::vector& getPosesInternal() const = 0; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 2d3a5a010c..811b2f76db 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -14,6 +14,7 @@ #include "AnimNode.h" #include "AnimClip.h" +#include "AnimBlendLinear.h" #include "AnimationLogging.h" #include "AnimNodeLoader.h" @@ -25,15 +26,18 @@ struct TypeInfo { // This will result in a compile error if someone adds a new // item to the AnimNode::Type enum. This is by design. 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); 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] = { - loadClipNode + loadClipNode, + loadBlendLinearNode }; #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(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(id.toStdString(), alpha); +} + +AnimNodeLoader::AnimNodeLoader() { + +} + AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const { // load entire file into a string. QString jsonUrl = QString::fromStdString(filename); @@ -145,7 +160,12 @@ AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const { file.close(); // 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(); // version diff --git a/libraries/animation/src/AnimNodeLoader.h b/libraries/animation/src/AnimNodeLoader.h index 941ea32f02..26fb4fc9d5 100644 --- a/libraries/animation/src/AnimNodeLoader.h +++ b/libraries/animation/src/AnimNodeLoader.h @@ -16,8 +16,13 @@ class AnimNode; class AnimNodeLoader { public: + AnimNodeLoader(); // TODO: load from url std::shared_ptr load(const std::string& filename) const; + + // no copies + AnimNodeLoader(const AnimNodeLoader&) = delete; + AnimNodeLoader& operator=(const AnimNodeLoader&) = delete; }; #endif // hifi_AnimNodeLoader diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index b51daadae7..ee3e55c115 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -56,6 +56,10 @@ protected: std::vector _joints; std::vector _absoluteBindPoses; std::vector _relativeBindPoses; + + // no copies + AnimSkeleton(const AnimSkeleton&) = delete; + AnimSkeleton& operator=(const AnimSkeleton&) = delete; }; #endif diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index 6957696184..34ee9c50c0 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -45,6 +45,10 @@ protected: std::unordered_map _skeletons; std::unordered_map _animNodes; + + // no copies + AnimDebugDraw(const AnimDebugDraw&) = delete; + AnimDebugDraw& operator=(const AnimDebugDraw&) = delete; }; #endif // hifi_AnimDebugDraw diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimClipTests.cpp index 58c7311b2d..5c91d4a617 100644 --- a/tests/animation/src/AnimClipTests.cpp +++ b/tests/animation/src/AnimClipTests.cpp @@ -10,6 +10,7 @@ #include "AnimClipTests.h" #include "AnimNodeLoader.h" #include "AnimClip.h" +#include "AnimBlendLinear.h" #include "AnimationLogging.h" #include <../QTestExtensions.h> @@ -103,24 +104,34 @@ void AnimClipTests::testLoader() { #endif QVERIFY((bool)node); - QVERIFY(node->getID() == "idle"); - QVERIFY(node->getType() == AnimNode::ClipType); + QVERIFY(node->getID() == "blend"); + QVERIFY(node->getType() == AnimNode::BlendLinearType); - auto clip = std::static_pointer_cast(node); + auto blend = std::static_pointer_cast(node); + QVERIFY(blend->getAlpha() == 0.5f); - QVERIFY(clip->getURL() == "idle.fbx"); - QVERIFY(clip->getStartFrame() == 0.0f); - QVERIFY(clip->getEndFrame() == 30.0f); - QVERIFY(clip->getTimeScale() == 1.0f); - QVERIFY(clip->getLoopFlag() == true); + QVERIFY(node->getChildCount() == 3); - QVERIFY(clip->getChildCount() == 3); + std::shared_ptr nodes[3] = { node->getChild(0), node->getChild(1), node->getChild(2) }; - std::shared_ptr nodes[3] = { clip->getChild(0), clip->getChild(1), clip->getChild(2) }; QVERIFY(nodes[0]->getID() == "test01"); QVERIFY(nodes[0]->getChildCount() == 0); QVERIFY(nodes[1]->getID() == "test02"); QVERIFY(nodes[1]->getChildCount() == 0); QVERIFY(nodes[2]->getID() == "test03"); QVERIFY(nodes[2]->getChildCount() == 0); + + auto test01 = std::static_pointer_cast(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(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); } diff --git a/tests/animation/src/data/test.json b/tests/animation/src/data/test.json index 0bcf91201a..765617fa2f 100644 --- a/tests/animation/src/data/test.json +++ b/tests/animation/src/data/test.json @@ -1,14 +1,10 @@ { "version": "1.0", "root": { - "id": "idle", - "type": "clip", + "id": "blend", + "type": "blendLinear", "data": { - "url": "idle.fbx", - "startFrame": 0.0, - "endFrame": 30.0, - "timeScale": 1.0, - "loopFlag": true + "alpha": 0.5 }, "children": [ { @@ -29,9 +25,9 @@ "data": { "url": "test02.fbx", "startFrame": 2.0, - "endFrame": 20.0, - "timeScale": 1.0, - "loopFlag": false + "endFrame": 21.0, + "timeScale": 0.9, + "loopFlag": true }, "children": [] }, From 2154f762029c33bd6efb79bac9a8dfcc2a28f064 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 5 Aug 2015 12:03:20 -0700 Subject: [PATCH 016/117] Added some comments to AnimNode, AnimClip & AnimBlendLinear. --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimBlendLinear.h | 10 ++++++++++ libraries/animation/src/AnimClip.h | 6 ++++++ libraries/animation/src/AnimNode.h | 13 +++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ecfa848630..b50e550deb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1223,7 +1223,7 @@ void MyAvatar::setupNewAnimationSystem() { // create a blend node auto blend = make_shared("blend", 0.5f); auto idle = make_shared("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("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", 0.0f, 29.0f, 1.0f, true); + auto walk = make_shared("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", 0.0f, 28.0f, 1.0f, true); blend->addChild(idle); blend->addChild(walk); _animNode = blend; diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index db217a25ee..c1c1c928e5 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -12,6 +12,16 @@ #ifndef hifi_AnimBlendLinear_h #define hifi_AnimBlendLinear_h +// Linear blend between two AnimNodes. +// the amount of blending is determined by the alpha parameter. +// If the number of children is 2, then the alpha parameters should be between +// 0 and 1. The first animation will have a (1 - alpha) factor, and the second +// will have factor of alpha. +// This node supports more then 2 children. In this case the alpha should be +// between 0 and n - 1. This alpha can be used to linearly interpolate between +// the closest two children poses. This can be used to sweep through a series +// of animation poses. + class AnimBlendLinear : public AnimNode { public: diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 59b286b95f..1868ac0e03 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -14,6 +14,12 @@ #include "AnimationCache.h" #include "AnimNode.h" +// Playback a single animation timeline. +// url determines the location of the fbx file to use within this clip. +// startFrame and endFrame are in frames 1/30th of a second. +// timescale can be used to speed-up or slow-down the animation. +// loop flag, when true, will loop the animation as it reaches the end frame. + class AnimClip : public AnimNode { public: friend class AnimClipTests; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 6ee7ff89c8..debc423e20 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -21,6 +21,15 @@ class QJsonObject; +// Base class for all elements in the animation blend tree. +// It provides the following categories of functions: +// +// * id getter, id is a string name useful for debugging and searching. +// * type getter, helpful for determining the derived type of this node. +// * hierarchy accessors, for adding, removing and iterating over child AnimNodes +// * skeleton accessors, the skeleton is from the model whose bones we are going to manipulate +// * evaluate method, perform actual joint manipulations here and return result by reference. + class AnimNode { public: friend class AnimDebugDraw; @@ -37,6 +46,7 @@ public: const std::string& getID() const { return _id; } Type getType() const { return _type; } + // hierarchy accessors void addChild(Pointer child) { _children.push_back(child); } void removeChild(Pointer child) { auto iter = std::find(_children.begin(), _children.end(), child); @@ -62,6 +72,9 @@ public: virtual ~AnimNode() {} virtual const std::vector& evaluate(float dt) = 0; + virtual const std::vector& overlay(float dt, const std::vector& underPoses) { + return evaluate(dt); + } protected: From 2d0315978ed6b4bbcb78d53ca828ec0187146ce4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 5 Aug 2015 12:32:15 -0700 Subject: [PATCH 017/117] Fix for compilation on linux. --- libraries/animation/src/AnimSkeleton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 27556c6930..7c813a053e 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -9,7 +9,7 @@ #include "AnimSkeleton.h" #include "AnimationLogging.h" -#include "glmHelpers.h" +#include "GLMHelpers.h" #include #include From 1f8c8adbd652e58087ac7d9e8e138ca6ff266ece Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 5 Aug 2015 17:05:53 -0700 Subject: [PATCH 018/117] Added AnimOverlay node, moved blend sub-routine into AnimUtil. --- libraries/animation/src/AnimBlendLinear.cpp | 10 ++-- libraries/animation/src/AnimClip.cpp | 9 +--- libraries/animation/src/AnimNode.h | 1 + libraries/animation/src/AnimNodeLoader.cpp | 43 ++++++++++++++- libraries/animation/src/AnimOverlay.cpp | 36 +++++++++++++ libraries/animation/src/AnimOverlay.h | 58 +++++++++++++++++++++ libraries/animation/src/AnimUtil.cpp | 21 ++++++++ libraries/animation/src/AnimUtil.h | 23 ++++++++ 8 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 libraries/animation/src/AnimOverlay.cpp create mode 100644 libraries/animation/src/AnimOverlay.h create mode 100644 libraries/animation/src/AnimUtil.cpp create mode 100644 libraries/animation/src/AnimUtil.h diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 3471e4e8d1..76d92cd264 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -10,6 +10,7 @@ #include "AnimBlendLinear.h" #include "GLMHelpers.h" #include "AnimationLogging.h" +#include "AnimUtil.h" AnimBlendLinear::AnimBlendLinear(const std::string& id, float alpha) : AnimNode(AnimNode::BlendLinearType, id), @@ -40,13 +41,8 @@ const std::vector& AnimBlendLinear::evaluate(float 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); - } + + blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); } } } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 8c223013bf..9863d71882 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -10,6 +10,7 @@ #include "GLMHelpers.h" #include "AnimClip.h" #include "AnimationLogging.h" +#include "AnimUtil.h" AnimClip::AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : AnimNode(AnimNode::ClipType, id), @@ -102,13 +103,7 @@ const std::vector& AnimClip::evaluate(float dt) { const std::vector& nextFrame = _anim[nextIndex]; float alpha = glm::fract(_frame); - for (size_t i = 0; i < _poses.size(); i++) { - const AnimPose& prevPose = prevFrame[i]; - const AnimPose& nextPose = nextFrame[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); - } + blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); } return _poses; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index debc423e20..d1adc1fc6d 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -37,6 +37,7 @@ public: enum Type { ClipType = 0, BlendLinearType, + OverlayType, NumTypes }; typedef std::shared_ptr Pointer; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 811b2f76db..9a4634a462 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -16,6 +16,7 @@ #include "AnimClip.h" #include "AnimBlendLinear.h" #include "AnimationLogging.h" +#include "AnimOverlay.h" #include "AnimNodeLoader.h" struct TypeInfo { @@ -27,17 +28,20 @@ struct TypeInfo { // item to the AnimNode::Type enum. This is by design. static TypeInfo typeInfoArray[AnimNode::NumTypes] = { { AnimNode::ClipType, "clip" }, - { AnimNode::BlendLinearType, "blendLinear" } + { AnimNode::BlendLinearType, "blendLinear" }, + { AnimNode::OverlayType, "overlay" } }; 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 loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); +static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { loadClipNode, - loadBlendLinearNode + loadBlendLinearNode, + loadOverlayNode }; #define READ_STRING(NAME, JSON_OBJ, ID, URL) \ @@ -143,6 +147,41 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q return std::make_shared(id.toStdString(), alpha); } +static const char* boneSetStrings[AnimOverlay::NumBoneSets] = { + "fullBody", + "upperBody", + "lowerBody", + "rightArm", + "leftArm", + "aboveTheHead", + "belowTheHead", + "headOnly", + "spineOnly", + "empty" +}; + +static AnimOverlay::BoneSet stringToBoneSetEnum(const QString& str) { + for (int i = 0; i < (int)AnimOverlay::NumBoneSets; i++) { + if (str == boneSetStrings[i]) { + return (AnimOverlay::BoneSet)i; + } + } + return AnimOverlay::NumBoneSets; +} + +static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { + + READ_STRING(boneSet, jsonObj, id, jsonUrl); + + auto boneSetEnum = stringToBoneSetEnum(boneSet); + if (boneSetEnum == AnimOverlay::NumBoneSets) { + qCCritical(animation) << "AnimNodeLoader, unknown bone set =" << boneSet << ", defaulting to \"fullBody\", url =" << jsonUrl; + boneSetEnum = AnimOverlay::FullBody; + } + + return std::make_shared(id.toStdString(), boneSetEnum); +} + AnimNodeLoader::AnimNodeLoader() { } diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp new file mode 100644 index 0000000000..6f39ab8876 --- /dev/null +++ b/libraries/animation/src/AnimOverlay.cpp @@ -0,0 +1,36 @@ +// +// AnimOverlay.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 "AnimOverlay.h" +#include "AnimUtil.h" + +AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet) : + AnimNode(AnimNode::OverlayType, id), + _boneSet(boneSet) { +} + +AnimOverlay::~AnimOverlay() { +} + +const std::vector& AnimOverlay::evaluate(float dt) { + if (_children.size() >= 2) { + auto underPoses = _children[1]->evaluate(dt); + auto overPoses = _children[0]->overlay(dt, underPoses); + + if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) { + _poses.resize(underPoses.size()); + + for (size_t i = 0; i < _poses.size(); i++) { + float alpha = 1.0f; // TODO: PULL from boneSet + blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]); + } + } + } + return _poses; +} diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h new file mode 100644 index 0000000000..7f38f53297 --- /dev/null +++ b/libraries/animation/src/AnimOverlay.h @@ -0,0 +1,58 @@ +// +// AnimOverlay.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_AnimOverlay_h +#define hifi_AnimOverlay_h + +#include "AnimNode.h" + +// Overlay the AnimPoses from one AnimNode on top of another AnimNode. +// child[0] is overlayed on top of child[1]. The boneset is used +// to control blending on a per-bone bases. + +class AnimOverlay : public AnimNode { +public: + + enum BoneSet { + FullBody = 0, + UpperBodyBoneSet, + LowerBodyBoneSet, + RightArmBoneSet, + LeftArmBoneSet, + AboveTheHeadBoneSet, + BelowTheHeadBoneSet, + HeadOnlyBoneSet, + SpineOnlyBoneSet, + EmptyBoneSet, + NumBoneSets, + }; + + AnimOverlay(const std::string& id, BoneSet boneSet); + virtual ~AnimOverlay() override; + + void setBoneSet(BoneSet boneSet) { _boneSet = boneSet; } + BoneSet getBoneSet() const { return _boneSet; } + + virtual const std::vector& evaluate(float dt) override; + +protected: + // for AnimDebugDraw rendering + virtual const std::vector& getPosesInternal() const override; + + std::vector _poses; + BoneSet _boneSet; + + // no copies + AnimOverlay(const AnimOverlay&) = delete; + AnimOverlay& operator=(const AnimOverlay&) = delete; +}; + +#endif // hifi_AnimOverlay_h diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp new file mode 100644 index 0000000000..4423b5c933 --- /dev/null +++ b/libraries/animation/src/AnimUtil.cpp @@ -0,0 +1,21 @@ +// +// AnimUtil.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 "AnimUtil.h" +#include "GLMHelpers.h" + +void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { + for (size_t i = 0; i < numPoses; i++) { + const AnimPose& aPose = a[i]; + const AnimPose& bPose = b[i]; + result[i].scale = lerp(aPose.scale, bPose.scale, alpha); + result[i].rot = glm::normalize(glm::lerp(aPose.rot, bPose.rot, alpha)); + result[i].trans = lerp(aPose.trans, bPose.trans, alpha); + } +} diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h new file mode 100644 index 0000000000..556eba8989 --- /dev/null +++ b/libraries/animation/src/AnimUtil.h @@ -0,0 +1,23 @@ +// +// AnimUtil.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_AnimUtil_h +#define hifi_AnimUtil_h + +#include "AnimNode.h" + +// TODO: use restrict keyword +// TODO: excellent candidate for simd vectorization. + +// this is where the magic happens +void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); + +#endif + + From c3fc37df18d3e36100dc087032911ef715049a1a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 5 Aug 2015 18:07:56 -0700 Subject: [PATCH 019/117] Added true boneSets to AnimOverlay. --- interface/src/avatar/MyAvatar.cpp | 15 ++- libraries/animation/src/AnimNode.h | 8 +- libraries/animation/src/AnimNodeLoader.cpp | 2 +- libraries/animation/src/AnimOverlay.cpp | 141 ++++++++++++++++++++- libraries/animation/src/AnimOverlay.h | 17 ++- libraries/animation/src/AnimSkeleton.h | 1 + 6 files changed, 168 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b50e550deb..a7416e05f0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -53,6 +53,7 @@ #include "AnimSkeleton.h" #include "AnimClip.h" #include "AnimBlendLinear.h" +#include "AnimOverlay.h" using namespace std; @@ -152,10 +153,12 @@ void MyAvatar::update(float deltaTime) { if (_animNode) { static float t = 0.0f; + /* auto blend = std::static_pointer_cast(_animNode); blend->setAlpha(0.5f * sin(t) + 0.5f); + */ t += deltaTime; - _animNode->evaluate(deltaTime); + _animNode->evaluate(deltaTime); } if (_referential) { @@ -1220,13 +1223,13 @@ void MyAvatar::setupNewAnimationSystem() { AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); - // create a blend node - auto blend = make_shared("blend", 0.5f); + // create a overlay node + auto overlay = make_shared("overlay", AnimOverlay::UpperBodyBoneSet); auto idle = make_shared("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("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", 0.0f, 28.0f, 1.0f, true); - blend->addChild(idle); - blend->addChild(walk); - _animNode = blend; + overlay->addChild(idle); + overlay->addChild(walk); + _animNode = overlay; _animNode->setSkeleton(skeleton); xform.trans.z += 1.0f; AnimDebugDraw::getInstance().addAnimNode("blend", _animNode, xform); diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index d1adc1fc6d..cae3d1805f 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -61,14 +61,14 @@ public: } int getChildCount() const { return (int)_children.size(); } - void setSkeleton(AnimSkeleton::Pointer skeleton) { + void setSkeleton(const AnimSkeleton::Pointer skeleton) { setSkeletonInternal(skeleton); for (auto&& child : _children) { child->setSkeletonInternal(skeleton); } } - AnimSkeleton::Pointer getSkeleton() const { return _skeleton; } + AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; } virtual ~AnimNode() {} @@ -79,7 +79,7 @@ public: protected: - virtual void setSkeletonInternal(AnimSkeleton::Pointer skeleton) { + virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; } @@ -89,7 +89,7 @@ protected: Type _type; std::string _id; std::vector _children; - AnimSkeleton::Pointer _skeleton; + AnimSkeleton::ConstPointer _skeleton; // no copies AnimNode(const AnimNode&) = delete; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 9a4634a462..cd635af8df 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -176,7 +176,7 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri auto boneSetEnum = stringToBoneSetEnum(boneSet); if (boneSetEnum == AnimOverlay::NumBoneSets) { qCCritical(animation) << "AnimNodeLoader, unknown bone set =" << boneSet << ", defaulting to \"fullBody\", url =" << jsonUrl; - boneSetEnum = AnimOverlay::FullBody; + boneSetEnum = AnimOverlay::FullBodyBoneSet; } return std::make_shared(id.toStdString(), boneSetEnum); diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 6f39ab8876..c02c83d69f 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -9,13 +9,32 @@ #include "AnimOverlay.h" #include "AnimUtil.h" +#include AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet) : - AnimNode(AnimNode::OverlayType, id), - _boneSet(boneSet) { + AnimNode(AnimNode::OverlayType, id), _boneSet(boneSet) { } AnimOverlay::~AnimOverlay() { + +} + +void AnimOverlay::setBoneSet(BoneSet boneSet) { + _boneSet = boneSet; + assert(_skeleton); + switch (boneSet) { + case FullBodyBoneSet: buildFullBodyBoneSet(); break; + case UpperBodyBoneSet: buildUpperBodyBoneSet(); break; + case LowerBodyBoneSet: buildLowerBodyBoneSet(); break; + case RightArmBoneSet: buildRightArmBoneSet(); break; + case LeftArmBoneSet: buildLeftArmBoneSet(); break; + case AboveTheHeadBoneSet: buildAboveTheHeadBoneSet(); break; + case BelowTheHeadBoneSet: buildBelowTheHeadBoneSet(); break; + case HeadOnlyBoneSet: buildHeadOnlyBoneSet(); break; + case SpineOnlyBoneSet: buildSpineOnlyBoneSet(); break; + default: + case EmptyBoneSet: buildEmptyBoneSet(); break; + } } const std::vector& AnimOverlay::evaluate(float dt) { @@ -25,12 +44,128 @@ const std::vector& AnimOverlay::evaluate(float dt) { if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) { _poses.resize(underPoses.size()); + assert(_boneSetVec.size() == _poses.size()); for (size_t i = 0; i < _poses.size(); i++) { - float alpha = 1.0f; // TODO: PULL from boneSet + float alpha = _boneSetVec[i]; // TODO: PULL from boneSet blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]); } } } return _poses; } + +template +void for_each_child_joint(AnimSkeleton::ConstPointer skeleton, int startJoint, Func f) { + std::queue q; + q.push(startJoint); + while(q.size() > 0) { + int jointIndex = q.front(); + for (int i = 0; i < skeleton->getNumJoints(); i++) { + if (jointIndex == skeleton->getParentIndex(i)) { + f(i); + q.push(i); + } + } + q.pop(); + } +} + +void AnimOverlay::buildFullBodyBoneSet() { + assert(_skeleton); + _boneSetVec.resize(_skeleton->getNumJoints()); + for (int i = 0; i < _skeleton->getNumJoints(); i++) { + _boneSetVec[i] = 1.0f; + } +} + +void AnimOverlay::buildUpperBodyBoneSet() { + assert(_skeleton); + buildEmptyBoneSet(); + int spineJoint = _skeleton->nameToJointIndex("Spine"); + for_each_child_joint(_skeleton, spineJoint, [&](int i) { + _boneSetVec[i] = 1.0f; + }); +} + +void AnimOverlay::buildLowerBodyBoneSet() { + assert(_skeleton); + buildFullBodyBoneSet(); + int hipsJoint = _skeleton->nameToJointIndex("Hips"); + int spineJoint = _skeleton->nameToJointIndex("Spine"); + _boneSetVec.resize(_skeleton->getNumJoints()); + for_each_child_joint(_skeleton, spineJoint, [&](int i) { + _boneSetVec[i] = 0.0f; + }); + _boneSetVec[hipsJoint] = 0.0f; +} + +void AnimOverlay::buildRightArmBoneSet() { + assert(_skeleton); + buildEmptyBoneSet(); + int rightShoulderJoint = _skeleton->nameToJointIndex("RightShoulder"); + for_each_child_joint(_skeleton, rightShoulderJoint, [&](int i) { + _boneSetVec[i] = 1.0f; + }); +} + +void AnimOverlay::buildLeftArmBoneSet() { + assert(_skeleton); + buildEmptyBoneSet(); + int leftShoulderJoint = _skeleton->nameToJointIndex("LeftShoulder"); + for_each_child_joint(_skeleton, leftShoulderJoint, [&](int i) { + _boneSetVec[i] = 1.0f; + }); +} + +void AnimOverlay::buildAboveTheHeadBoneSet() { + assert(_skeleton); + buildEmptyBoneSet(); + int headJoint = _skeleton->nameToJointIndex("Head"); + for_each_child_joint(_skeleton, headJoint, [&](int i) { + _boneSetVec[i] = 1.0f; + }); +} + +void AnimOverlay::buildBelowTheHeadBoneSet() { + assert(_skeleton); + buildFullBodyBoneSet(); + int headJoint = _skeleton->nameToJointIndex("Head"); + for_each_child_joint(_skeleton, headJoint, [&](int i) { + _boneSetVec[i] = 0.0f; + }); +} + +void AnimOverlay::buildHeadOnlyBoneSet() { + assert(_skeleton); + buildEmptyBoneSet(); + int headJoint = _skeleton->nameToJointIndex("Head"); + _boneSetVec[headJoint] = 1.0f; +} + +void AnimOverlay::buildSpineOnlyBoneSet() { + assert(_skeleton); + buildEmptyBoneSet(); + int spineJoint = _skeleton->nameToJointIndex("Spine"); + _boneSetVec[spineJoint] = 1.0f; +} + +void AnimOverlay::buildEmptyBoneSet() { + assert(_skeleton); + _boneSetVec.resize(_skeleton->getNumJoints()); + for (int i = 0; i < _skeleton->getNumJoints(); i++) { + _boneSetVec[i] = 0.0f; + } +} + +// for AnimDebugDraw rendering +const std::vector& AnimOverlay::getPosesInternal() const { + return _poses; +} + +void AnimOverlay::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { + _skeleton = skeleton; + + // we have to re-build the bone set when the skeleton changes. + setBoneSet(_boneSet); +} diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 7f38f53297..82990514af 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -22,7 +22,7 @@ class AnimOverlay : public AnimNode { public: enum BoneSet { - FullBody = 0, + FullBodyBoneSet = 0, UpperBodyBoneSet, LowerBodyBoneSet, RightArmBoneSet, @@ -38,7 +38,7 @@ public: AnimOverlay(const std::string& id, BoneSet boneSet); virtual ~AnimOverlay() override; - void setBoneSet(BoneSet boneSet) { _boneSet = boneSet; } + void setBoneSet(BoneSet boneSet); BoneSet getBoneSet() const { return _boneSet; } virtual const std::vector& evaluate(float dt) override; @@ -46,9 +46,22 @@ public: protected: // for AnimDebugDraw rendering virtual const std::vector& getPosesInternal() const override; + virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; std::vector _poses; BoneSet _boneSet; + std::vector _boneSetVec; + + void buildFullBodyBoneSet(); + void buildUpperBodyBoneSet(); + void buildLowerBodyBoneSet(); + void buildRightArmBoneSet(); + void buildLeftArmBoneSet(); + void buildAboveTheHeadBoneSet(); + void buildBelowTheHeadBoneSet(); + void buildHeadOnlyBoneSet(); + void buildSpineOnlyBoneSet(); + void buildEmptyBoneSet(); // no copies AnimOverlay(const AnimOverlay&) = delete; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index ee3e55c115..468abdc359 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -38,6 +38,7 @@ inline QDebug operator<<(QDebug debug, const AnimPose& pose) { class AnimSkeleton { public: typedef std::shared_ptr Pointer; + typedef std::shared_ptr ConstPointer; AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; From 9457794d9e73082bf1fcfc4d1574851ce7852f33 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 24 Aug 2015 14:50:02 -0700 Subject: [PATCH 020/117] fix whitespace offset due to merge conflict --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1ee3a3beff..1e296c1066 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -155,7 +155,7 @@ void MyAvatar::reset() { setOrientation(glm::quat(eulers)); } - void MyAvatar::update(float deltaTime) { +void MyAvatar::update(float deltaTime) { if (_goToPending) { setPosition(_goToPosition); From 5a73aef1f8beba1b22d027dd68368c471e48828b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 24 Aug 2015 16:19:16 -0700 Subject: [PATCH 021/117] AnimSkeleton more accurate bind pose generation. --- libraries/animation/src/AnimSkeleton.cpp | 74 +++++++++--------------- libraries/fbx/src/FBXReader.cpp | 7 ++- libraries/fbx/src/FBXReader.h | 1 + 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 7c813a053e..38c8bf8166 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -43,7 +43,10 @@ AnimPose::operator glm::mat4() const { glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); } -//#define TRUST_BIND_TRANSFORM +static const mat4 IDENTITY = mat4(); +static bool matrixIsIdentity(const glm::mat4& m) { + return m == IDENTITY; +} AnimSkeleton::AnimSkeleton(const std::vector& joints) { _joints = joints; @@ -51,58 +54,37 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints) { // build a cache of bind poses _absoluteBindPoses.reserve(joints.size()); _relativeBindPoses.reserve(joints.size()); + for (size_t i = 0; i < joints.size(); i++) { - - /* - // AJT: dump the skeleton, because wtf. - qCDebug(animation) << getJointName(i); - qCDebug(animation) << " isFree =" << _joints[i].isFree; - qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; - qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; - qCDebug(animation) << " boneRadius =" << _joints[i].boneRadius; - qCDebug(animation) << " translation =" << _joints[i].translation; - qCDebug(animation) << " preTransform =" << _joints[i].preTransform; - qCDebug(animation) << " preRotation =" << _joints[i].preRotation; - qCDebug(animation) << " rotation =" << _joints[i].rotation; - qCDebug(animation) << " postRotation =" << _joints[i].postRotation; - qCDebug(animation) << " postTransform =" << _joints[i].postTransform; - qCDebug(animation) << " transform =" << _joints[i].transform; - qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax; - qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation; - qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation; - qCDebug(animation) << " bindTransform" << _joints[i].bindTransform; - qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint; - */ - -#ifdef TRUST_BIND_TRANSFORM - // trust FBXJoint::bindTransform (which is wrong for joints NOT bound to anything) - AnimPose absoluteBindPose(_joints[i].bindTransform); - _absoluteBindPoses.push_back(absoluteBindPose); - int parentIndex = getParentIndex(i); - if (parentIndex >= 0) { - AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); - _relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose); + if (_joints[i].bindTransformIsValid) { + // Use the FBXJoint::bindTransform, which is absolute model coordinates + // i.e. not relative to it's parent. + AnimPose absoluteBindPose(_joints[i].bindTransform); + _absoluteBindPoses.push_back(absoluteBindPose); + int parentIndex = getParentIndex(i); + if (parentIndex >= 0) { + AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); + _relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose); + } else { + _relativeBindPoses.push_back(absoluteBindPose); + } } else { - _relativeBindPoses.push_back(absoluteBindPose); - } -#else - // trust FBXJoint's local transforms (which is not really the bind pose, but the default pose in the fbx) - glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation); - glm::mat4 relBindMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform; - AnimPose relBindPose(relBindMat); - _relativeBindPoses.push_back(relBindPose); + // use FBXJoint's local transform, instead + glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation); + glm::mat4 relBindMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform; + AnimPose relBindPose(relBindMat); + _relativeBindPoses.push_back(relBindPose); - int parentIndex = getParentIndex(i); - if (parentIndex >= 0) { - _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relBindPose); - } else { - _absoluteBindPoses.push_back(relBindPose); + int parentIndex = getParentIndex(i); + if (parentIndex >= 0) { + _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relBindPose); + } else { + _absoluteBindPoses.push_back(relBindPose); + } } -#endif } } - int AnimSkeleton::nameToJointIndex(const QString& jointName) const { for (size_t i = 0; i < _joints.size(); i++) { if (_joints[i].name == jointName) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 7d4979e08f..47a8ee2f07 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2307,7 +2307,9 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping break; } } - + + joint.bindTransformIsValid = false; + geometry.joints.append(joint); geometry.jointIndices.insert(model.name, geometry.joints.size()); @@ -2535,7 +2537,8 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping FBXJoint& joint = geometry.joints[fbxCluster.jointIndex]; joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); joint.bindTransform = cluster.transformLink; - + joint.bindTransformIsValid = true; + // update the bind pose extents glm::vec3 bindTranslation = extractTranslation(geometry.offset * joint.bindTransform); geometry.bindExtents.addPoint(bindTranslation); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index ae8adeb348..d8e1ae59e6 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -84,6 +84,7 @@ public: glm::mat4 bindTransform; QString name; bool isSkeletonJoint; + bool bindTransformIsValid; }; From bde75e9e516da4e8bfd5ef9b61c32e337d5be160 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 24 Aug 2015 17:36:21 -0700 Subject: [PATCH 022/117] AnimDebugDraw rendering works again. I had to port AnimDebugDraw shader to GLSL 4.1. --- libraries/render-utils/src/animdebugdraw.slf | 6 ++++-- libraries/render-utils/src/animdebugdraw.slv | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/animdebugdraw.slf b/libraries/render-utils/src/animdebugdraw.slf index aa34c9bfba..8a3aca055e 100644 --- a/libraries/render-utils/src/animdebugdraw.slf +++ b/libraries/render-utils/src/animdebugdraw.slf @@ -10,8 +10,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -varying vec4 varColor; +in vec4 _color; + +out vec4 _fragColor; void main(void) { - gl_FragColor = varColor; + _fragColor = _color; } diff --git a/libraries/render-utils/src/animdebugdraw.slv b/libraries/render-utils/src/animdebugdraw.slv index 68749ec7cc..f3117714b0 100644 --- a/libraries/render-utils/src/animdebugdraw.slv +++ b/libraries/render-utils/src/animdebugdraw.slv @@ -8,17 +8,19 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include gpu/Inputs.slh@> + <@include gpu/Transform.slh@> <$declareStandardTransform()$> -varying vec4 varColor; +out vec4 _color; void main(void) { // pass along the diffuse color - varColor = gl_Color; + _color = inColor.rgba; TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> } From 7a2ca047cbcde94f5e87c0aa54e0dbf9dd79f976 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 24 Aug 2015 19:00:12 -0700 Subject: [PATCH 023/117] Added network resource download support to AnimNodeLoader. --- libraries/animation/src/AnimNodeLoader.cpp | 74 ++++++++++--------- libraries/animation/src/AnimNodeLoader.h | 33 +++++++-- tests/animation/src/AnimClipTests.cpp | 85 +++++++++++++--------- tests/animation/src/AnimClipTests.h | 4 - tests/animation/src/RigTests.cpp | 13 ++-- 5 files changed, 125 insertions(+), 84 deletions(-) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index cd635af8df..615dde1627 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -32,11 +32,11 @@ static TypeInfo typeInfoArray[AnimNode::NumTypes] = { { AnimNode::OverlayType, "overlay" } }; -typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); +typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QUrl& 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 AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); +static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { loadClipNode, @@ -49,7 +49,7 @@ static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { if (!NAME##_VAL.isString()) { \ qCCritical(animation) << "AnimNodeLoader, error reading string" \ << #NAME << ", id =" << ID \ - << ", url =" << URL; \ + << ", url =" << URL.toDisplayString(); \ return nullptr; \ } \ QString NAME = NAME##_VAL.toString() @@ -59,7 +59,7 @@ static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { if (!NAME##_VAL.isBool()) { \ qCCritical(animation) << "AnimNodeLoader, error reading bool" \ << #NAME << ", id =" << ID \ - << ", url =" << URL; \ + << ", url =" << URL.toDisplayString(); \ return nullptr; \ } \ bool NAME = NAME##_VAL.toBool() @@ -69,7 +69,7 @@ static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { if (!NAME##_VAL.isDouble()) { \ qCCritical(animation) << "AnimNodeLoader, error reading double" \ << #NAME << "id =" << ID \ - << ", url =" << URL; \ + << ", url =" << URL.toDisplayString(); \ return nullptr; \ } \ float NAME = (float)NAME##_VAL.toDouble() @@ -83,29 +83,29 @@ static AnimNode::Type stringToEnum(const QString& str) { return AnimNode::NumTypes; } -static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QString& jsonUrl) { +static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) { auto idVal = jsonObj.value("id"); if (!idVal.isString()) { - qCCritical(animation) << "AnimNodeLoader, bad string \"id\", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad string \"id\", url =" << jsonUrl.toDisplayString(); return nullptr; } QString id = idVal.toString(); auto typeVal = jsonObj.value("type"); if (!typeVal.isString()) { - qCCritical(animation) << "AnimNodeLoader, bad object \"type\", id =" << id << ", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad object \"type\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } QString typeStr = typeVal.toString(); AnimNode::Type type = stringToEnum(typeStr); if (type == AnimNode::NumTypes) { - qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id << ", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } auto dataValue = jsonObj.value("data"); if (!dataValue.isObject()) { - qCCritical(animation) << "AnimNodeLoader, bad string \"data\", id =" << id << ", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad string \"data\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } auto dataObj = dataValue.toObject(); @@ -115,13 +115,13 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QString& jso auto childrenValue = jsonObj.value("children"); if (!childrenValue.isArray()) { - qCCritical(animation) << "AnimNodeLoader, bad array \"children\", id =" << id << ", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad array \"children\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } auto childrenAry = childrenValue.toArray(); for (const auto& childValue : childrenAry) { if (!childValue.isObject()) { - qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id << ", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } node->addChild(loadNode(childValue.toObject(), jsonUrl)); @@ -129,7 +129,7 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QString& jso return node; } -static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { +static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_STRING(url, jsonObj, id, jsonUrl); READ_FLOAT(startFrame, jsonObj, id, jsonUrl); @@ -140,7 +140,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& return std::make_shared(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag); } -static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { +static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl); @@ -169,40 +169,35 @@ static AnimOverlay::BoneSet stringToBoneSetEnum(const QString& str) { return AnimOverlay::NumBoneSets; } -static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { +static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_STRING(boneSet, jsonObj, id, jsonUrl); auto boneSetEnum = stringToBoneSetEnum(boneSet); if (boneSetEnum == AnimOverlay::NumBoneSets) { - qCCritical(animation) << "AnimNodeLoader, unknown bone set =" << boneSet << ", defaulting to \"fullBody\", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, unknown bone set =" << boneSet << ", defaulting to \"fullBody\", url =" << jsonUrl.toDisplayString(); boneSetEnum = AnimOverlay::FullBodyBoneSet; } return std::make_shared(id.toStdString(), boneSetEnum); } -AnimNodeLoader::AnimNodeLoader() { +AnimNodeLoader::AnimNodeLoader(const QUrl& url) : + _url(url), + _resource(nullptr) { + _resource = new Resource(url); + connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(onRequestDone(QNetworkReply&))); + connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(onRequestError(QNetworkReply::NetworkError))); } -AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const { - // load entire file into a string. - QString jsonUrl = QString::fromStdString(filename); - QFile file; - file.setFileName(jsonUrl); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qCCritical(animation) << "AnimNodeLoader, could not open url =" << jsonUrl; - return nullptr; - } - QString contents = file.readAll(); - file.close(); +AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& jsonUrl) { // convert string into a json doc QJsonParseError error; - auto doc = QJsonDocument::fromJson(contents.toUtf8(), &error); + auto doc = QJsonDocument::fromJson(contents, &error); if (error.error != QJsonParseError::NoError) { - qCCritical(animation) << "AnimNodeLoader, failed to parse json, error =" << error.errorString() << ", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, failed to parse json, error =" << error.errorString() << ", url =" << jsonUrl.toDisplayString(); return nullptr; } QJsonObject obj = doc.object(); @@ -210,23 +205,32 @@ AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const { // version QJsonValue versionVal = obj.value("version"); if (!versionVal.isString()) { - qCCritical(animation) << "AnimNodeLoader, bad string \"version\", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad string \"version\", url =" << jsonUrl.toDisplayString(); return nullptr; } QString version = versionVal.toString(); // check version if (version != "1.0") { - qCCritical(animation) << "AnimNodeLoader, bad version number" << version << "expected \"1.0\", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad version number" << version << "expected \"1.0\", url =" << jsonUrl.toDisplayString(); return nullptr; } // root QJsonValue rootVal = obj.value("root"); if (!rootVal.isObject()) { - qCCritical(animation) << "AnimNodeLoader, bad object \"root\", url =" << jsonUrl; + qCCritical(animation) << "AnimNodeLoader, bad object \"root\", url =" << jsonUrl.toDisplayString(); return nullptr; } return loadNode(rootVal.toObject(), jsonUrl); } + +void AnimNodeLoader::onRequestDone(QNetworkReply& request) { + auto node = load(request.readAll(), _url); + emit success(node); +} + +void AnimNodeLoader::onRequestError(QNetworkReply::NetworkError netError) { + emit error((int)netError, "Resource download error"); +} diff --git a/libraries/animation/src/AnimNodeLoader.h b/libraries/animation/src/AnimNodeLoader.h index 26fb4fc9d5..095c05cf7e 100644 --- a/libraries/animation/src/AnimNodeLoader.h +++ b/libraries/animation/src/AnimNodeLoader.h @@ -12,13 +12,36 @@ #include -class AnimNode; +#include +#include +#include + +#include "AnimNode.h" + +class Resource; + +class AnimNodeLoader : public QObject { + Q_OBJECT -class AnimNodeLoader { public: - AnimNodeLoader(); - // TODO: load from url - std::shared_ptr load(const std::string& filename) const; + AnimNodeLoader(const QUrl& url); + +signals: + void success(AnimNode::Pointer node); + void error(int error, QString str); + +protected: + // synchronous + static AnimNode::Pointer load(const QByteArray& contents, const QUrl& jsonUrl); + +protected slots: + void onRequestDone(QNetworkReply& request); + void onRequestError(QNetworkReply::NetworkError error); + +protected: + QUrl _url; + Resource* _resource; +private: // no copies AnimNodeLoader(const AnimNodeLoader&) = delete; diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimClipTests.cpp index 5c91d4a617..9a7841947b 100644 --- a/tests/animation/src/AnimClipTests.cpp +++ b/tests/animation/src/AnimClipTests.cpp @@ -30,7 +30,7 @@ void AnimClipTests::cleanupTestCase() { void AnimClipTests::testAccessors() { std::string id = "my anim clip"; - std::string url = "foo"; + std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 20.0f; float timeScale = 1.1f; @@ -47,7 +47,7 @@ void AnimClipTests::testAccessors() { QVERIFY(clip.getTimeScale() == timeScale); QVERIFY(clip.getLoopFlag() == loopFlag); - std::string url2 = "bar"; + std::string url2 = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx"; float startFrame2 = 22.0f; float endFrame2 = 100.0f; float timeScale2 = 1.2f; @@ -73,7 +73,7 @@ static float framesToSec(float secs) { void AnimClipTests::testEvaulate() { std::string id = "my clip node"; - std::string url = "foo"; + std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 22.0f; float timeScale = 1.0f; @@ -95,43 +95,60 @@ void AnimClipTests::testEvaulate() { } void AnimClipTests::testLoader() { - AnimNodeLoader loader; + auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json"); + AnimNodeLoader loader(url); -#ifdef Q_OS_WIN - auto node = loader.load("../../../tests/animation/src/data/test.json"); -#else - auto node = loader.load("../../../../tests/animation/src/data/test.json"); -#endif + const int timeout = 1000; + QEventLoop loop; + QTimer timer; + timer.setInterval(timeout); + timer.setSingleShot(true); - QVERIFY((bool)node); - QVERIFY(node->getID() == "blend"); - QVERIFY(node->getType() == AnimNode::BlendLinearType); + bool done = false; + connect(&loader, &AnimNodeLoader::success, [&](AnimNode::Pointer node) { + QVERIFY((bool)node); + QVERIFY(node->getID() == "blend"); + QVERIFY(node->getType() == AnimNode::BlendLinearType); - auto blend = std::static_pointer_cast(node); - QVERIFY(blend->getAlpha() == 0.5f); + QVERIFY((bool)node); + QVERIFY(node->getID() == "blend"); + QVERIFY(node->getType() == AnimNode::BlendLinearType); - QVERIFY(node->getChildCount() == 3); + auto blend = std::static_pointer_cast(node); + QVERIFY(blend->getAlpha() == 0.5f); - std::shared_ptr nodes[3] = { node->getChild(0), node->getChild(1), node->getChild(2) }; + QVERIFY(node->getChildCount() == 3); - QVERIFY(nodes[0]->getID() == "test01"); - QVERIFY(nodes[0]->getChildCount() == 0); - QVERIFY(nodes[1]->getID() == "test02"); - QVERIFY(nodes[1]->getChildCount() == 0); - QVERIFY(nodes[2]->getID() == "test03"); - QVERIFY(nodes[2]->getChildCount() == 0); + std::shared_ptr nodes[3] = { node->getChild(0), node->getChild(1), node->getChild(2) }; - auto test01 = std::static_pointer_cast(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); + QVERIFY(nodes[0]->getID() == "test01"); + QVERIFY(nodes[0]->getChildCount() == 0); + QVERIFY(nodes[1]->getID() == "test02"); + QVERIFY(nodes[1]->getChildCount() == 0); + QVERIFY(nodes[2]->getID() == "test03"); + QVERIFY(nodes[2]->getChildCount() == 0); - auto test02 = std::static_pointer_cast(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); + auto test01 = std::static_pointer_cast(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(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); + done = true; + }); + + loop.connect(&loader, SIGNAL(success(AnimNode::Pointer)), SLOT(quit())); + loop.connect(&loader, SIGNAL(error(int, QString)), SLOT(quit())); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(); + loop.exec(); + + QVERIFY(done); } diff --git a/tests/animation/src/AnimClipTests.h b/tests/animation/src/AnimClipTests.h index d4590ef27d..6239a88be8 100644 --- a/tests/animation/src/AnimClipTests.h +++ b/tests/animation/src/AnimClipTests.h @@ -13,10 +13,6 @@ #include #include -inline float getErrorDifference(float a, float b) { - return fabs(a - b); -} - class AnimClipTests : public QObject { Q_OBJECT private slots: diff --git a/tests/animation/src/RigTests.cpp b/tests/animation/src/RigTests.cpp index b0e0a53ee5..ff457ff804 100644 --- a/tests/animation/src/RigTests.cpp +++ b/tests/animation/src/RigTests.cpp @@ -78,24 +78,25 @@ void RigTests::initTestCase() { #ifdef FROM_FILE QFile file(FROM_FILE); QCOMPARE(file.open(QIODevice::ReadOnly), true); - FBXGeometry geometry = readFBX(file.readAll(), QVariantHash()); + FBXGeometry* geometry = readFBX(file.readAll(), QVariantHash()); #else QUrl fbxUrl("https://s3.amazonaws.com/hifi-public/models/skeletons/Zack/Zack.fbx"); QNetworkReply* reply = OBJReader().request(fbxUrl, false); // Just a convenience hack for synchronoud http request auto fbxHttpCode = !reply->isFinished() ? -1 : reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); QCOMPARE(fbxHttpCode, 200); - FBXGeometry geometry = readFBX(reply->readAll(), QVariantHash()); + FBXGeometry* geometry = readFBX(reply->readAll(), QVariantHash()); #endif - + QVERIFY((bool)geometry); + QVector jointStates; - for (int i = 0; i < geometry.joints.size(); ++i) { - JointState state(geometry.joints[i]); + for (int i = 0; i < geometry->joints.size(); ++i) { + JointState state(geometry->joints[i]); jointStates.append(state); } _rig = std::make_shared(); _rig->initJointStates(jointStates, glm::mat4(), 0, 41, 40, 39, 17, 16, 15); // FIXME? get by name? do we really want to exclude the shoulder blades? - std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; + std::cout << "Rig is ready " << geometry->joints.count() << " joints " << std::endl; reportAll(_rig); } From 4bdb00bbc5f3d0ccec115ba6a2f71c91088b8a58 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Aug 2015 09:58:36 -0700 Subject: [PATCH 024/117] Added setCurrentFrame interface to AnimClip. This will recurse the tree and call setCurrentFrameInternal on each node. This method can be overriden, currently the only meaningful implementation is AnimClip. --- libraries/animation/src/AnimClip.cpp | 69 +++++++++++++++------------- libraries/animation/src/AnimClip.h | 2 + libraries/animation/src/AnimNode.h | 9 +++- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 9863d71882..dca43cb735 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -45,38 +45,6 @@ void AnimClip::setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; } -float AnimClip::accumulateTime(float frame, float dt) const { - 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; - } - } - } - return frame; -} - const std::vector& AnimClip::evaluate(float dt) { _frame = accumulateTime(_frame, dt); @@ -109,6 +77,43 @@ const std::vector& AnimClip::evaluate(float dt) { return _poses; } +void AnimClip::setCurrentFrameInternal(float frame) { + const float dt = 0.0f; + _frame = accumulateTime(frame, dt); +} + +float AnimClip::accumulateTime(float frame, float dt) const { + 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; + } + } + } + return frame; +} + void AnimClip::copyFromNetworkAnim() { assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); _anim.clear(); diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 1868ac0e03..3480fdc3cf 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -45,6 +45,8 @@ public: virtual const std::vector& evaluate(float dt) override; protected: + virtual void setCurrentFrameInternal(float frame) override; + float accumulateTime(float frame, float dt) const; void copyFromNetworkAnim(); diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index cae3d1805f..9063488e9b 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -43,6 +43,7 @@ public: typedef std::shared_ptr Pointer; AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} + virtual ~AnimNode() {} const std::string& getID() const { return _id; } Type getType() const { return _type; } @@ -70,7 +71,12 @@ public: AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; } - virtual ~AnimNode() {} + void setCurrentFrame(float frame) { + setCurrentFrameInternal(frame); + for (auto&& child : _children) { + child->setCurrentFrameInternal(frame); + } + } virtual const std::vector& evaluate(float dt) = 0; virtual const std::vector& overlay(float dt, const std::vector& underPoses) { @@ -79,6 +85,7 @@ public: protected: + virtual void setCurrentFrameInternal(float frame) {} virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; } From 62f86e6a462cbba67cf7c70760c3af361ebcf460 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Aug 2015 11:33:57 -0700 Subject: [PATCH 025/117] Added AnimVariant, renamed AnimClipTests to AnimTests. * Added test for AnimVariant. --- libraries/animation/src/AnimClip.h | 2 +- libraries/animation/src/AnimVariant.h | 63 +++++++++ .../src/{AnimClipTests.cpp => AnimTests.cpp} | 133 +++++++++++------- .../src/{AnimClipTests.h => AnimTests.h} | 13 +- 4 files changed, 156 insertions(+), 55 deletions(-) create mode 100644 libraries/animation/src/AnimVariant.h rename tests/animation/src/{AnimClipTests.cpp => AnimTests.cpp} (53%) rename tests/animation/src/{AnimClipTests.h => AnimTests.h} (65%) diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 3480fdc3cf..2cce40d98a 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -22,7 +22,7 @@ class AnimClip : public AnimNode { public: - friend class AnimClipTests; + friend class AnimTests; AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip() override; diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h new file mode 100644 index 0000000000..13e364045b --- /dev/null +++ b/libraries/animation/src/AnimVariant.h @@ -0,0 +1,63 @@ +// +// AnimVariant.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_AnimVariant_h +#define hifi_AnimVariant_h + +#include +#include +#include + +class AnimVariant { +public: + enum Type { + BoolType = 0, + FloatType, + Vec3Type, + QuatType, + Mat4Type, + NumTypes + }; + + AnimVariant() : _type(BoolType) { memset(&_val, 0, sizeof(_val)); } + AnimVariant(bool value) : _type(BoolType) { _val.boolVal = value; } + AnimVariant(float value) : _type(FloatType) { _val.floats[0] = value; } + AnimVariant(const glm::vec3& value) : _type(Vec3Type) { *reinterpret_cast(&_val) = value; } + AnimVariant(const glm::quat& value) : _type(QuatType) { *reinterpret_cast(&_val) = value; } + AnimVariant(const glm::mat4& value) : _type(Mat4Type) { *reinterpret_cast(&_val) = value; } + + bool isBool() const { return _type == BoolType; } + bool isFloat() const { return _type == FloatType; } + bool isVec3() const { return _type == Vec3Type; } + bool isQuat() const { return _type == QuatType; } + bool isMat4() const { return _type == Mat4Type; } + + void setBool(bool value) { assert(_type == BoolType); _val.boolVal = value; } + void setFloat(float value) { assert(_type == FloatType); _val.floats[0] = value; } + void setVec3(const glm::vec3& value) { assert(_type == Vec3Type); *reinterpret_cast(&_val) = value; } + void setQuat(const glm::quat& value) { assert(_type == QuatType); *reinterpret_cast(&_val) = value; } + void setMat4(const glm::mat4& value) { assert(_type == Mat4Type); *reinterpret_cast(&_val) = value; } + + bool getBool() { assert(_type == BoolType); return _val.boolVal; } + float getFloat() { assert(_type == FloatType); return _val.floats[0]; } + const glm::vec3& getVec3() { assert(_type == Vec3Type); return *reinterpret_cast(&_val); } + const glm::quat& getQuat() { assert(_type == QuatType); return *reinterpret_cast(&_val); } + const glm::mat4& getMat4() { assert(_type == Mat4Type); return *reinterpret_cast(&_val); } + +protected: + Type _type; + union { + bool boolVal; + float floats[16]; + } _val; +}; + +typedef std::map AnimVarantMap; + +#endif // hifi_AnimVariant_h diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimTests.cpp similarity index 53% rename from tests/animation/src/AnimClipTests.cpp rename to tests/animation/src/AnimTests.cpp index 9a7841947b..82d805d515 100644 --- a/tests/animation/src/AnimClipTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -1,5 +1,5 @@ // -// AnimClipTests.cpp +// AnimTests.cpp // // Copyright 2015 High Fidelity, Inc. // @@ -7,28 +7,29 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AnimClipTests.h" +#include "AnimTests.h" #include "AnimNodeLoader.h" #include "AnimClip.h" #include "AnimBlendLinear.h" #include "AnimationLogging.h" +#include "AnimVariant.h" #include <../QTestExtensions.h> -QTEST_MAIN(AnimClipTests) +QTEST_MAIN(AnimTests) const float EPSILON = 0.001f; -void AnimClipTests::initTestCase() { +void AnimTests::initTestCase() { auto animationCache = DependencyManager::set(); auto resourceCacheSharedItems = DependencyManager::set(); } -void AnimClipTests::cleanupTestCase() { +void AnimTests::cleanupTestCase() { DependencyManager::destroy(); } -void AnimClipTests::testAccessors() { +void AnimTests::testAccessors() { std::string id = "my anim clip"; std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; @@ -71,7 +72,7 @@ static float framesToSec(float secs) { return secs / FRAMES_PER_SECOND; } -void AnimClipTests::testEvaulate() { +void AnimTests::testEvaulate() { std::string id = "my clip node"; std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; @@ -94,7 +95,7 @@ void AnimClipTests::testEvaulate() { QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, EPSILON); } -void AnimClipTests::testLoader() { +void AnimTests::testLoader() { auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json"); AnimNodeLoader loader(url); @@ -104,45 +105,8 @@ void AnimClipTests::testLoader() { timer.setInterval(timeout); timer.setSingleShot(true); - bool done = false; - connect(&loader, &AnimNodeLoader::success, [&](AnimNode::Pointer node) { - QVERIFY((bool)node); - QVERIFY(node->getID() == "blend"); - QVERIFY(node->getType() == AnimNode::BlendLinearType); - - QVERIFY((bool)node); - QVERIFY(node->getID() == "blend"); - QVERIFY(node->getType() == AnimNode::BlendLinearType); - - auto blend = std::static_pointer_cast(node); - QVERIFY(blend->getAlpha() == 0.5f); - - QVERIFY(node->getChildCount() == 3); - - std::shared_ptr nodes[3] = { node->getChild(0), node->getChild(1), node->getChild(2) }; - - QVERIFY(nodes[0]->getID() == "test01"); - QVERIFY(nodes[0]->getChildCount() == 0); - QVERIFY(nodes[1]->getID() == "test02"); - QVERIFY(nodes[1]->getChildCount() == 0); - QVERIFY(nodes[2]->getID() == "test03"); - QVERIFY(nodes[2]->getChildCount() == 0); - - auto test01 = std::static_pointer_cast(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(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); - done = true; - }); + AnimNode::Pointer node = nullptr; + connect(&loader, &AnimNodeLoader::success, [&](AnimNode::Pointer nodeIn) { node = nodeIn; }); loop.connect(&loader, SIGNAL(success(AnimNode::Pointer)), SLOT(quit())); loop.connect(&loader, SIGNAL(error(int, QString)), SLOT(quit())); @@ -150,5 +114,78 @@ void AnimClipTests::testLoader() { timer.start(); loop.exec(); - QVERIFY(done); + QVERIFY((bool)node); + + QVERIFY(node->getID() == "blend"); + QVERIFY(node->getType() == AnimNode::BlendLinearType); + + QVERIFY((bool)node); + QVERIFY(node->getID() == "blend"); + QVERIFY(node->getType() == AnimNode::BlendLinearType); + + auto blend = std::static_pointer_cast(node); + QVERIFY(blend->getAlpha() == 0.5f); + + QVERIFY(node->getChildCount() == 3); + + std::shared_ptr nodes[3] = { node->getChild(0), node->getChild(1), node->getChild(2) }; + + QVERIFY(nodes[0]->getID() == "test01"); + QVERIFY(nodes[0]->getChildCount() == 0); + QVERIFY(nodes[1]->getID() == "test02"); + QVERIFY(nodes[1]->getChildCount() == 0); + QVERIFY(nodes[2]->getID() == "test03"); + QVERIFY(nodes[2]->getChildCount() == 0); + + auto test01 = std::static_pointer_cast(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(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); +} + +void AnimTests::testVariant() { + auto defaultVar = AnimVariant(); + auto boolVar = AnimVariant(true); + auto floatVar = AnimVariant(1.0f); + auto vec3Var = AnimVariant(glm::vec3(1.0f, 2.0f, 3.0f)); + auto quatVar = AnimVariant(glm::quat(1.0f, 2.0f, 3.0f, 4.0f)); + auto mat4Var = AnimVariant(glm::mat4(glm::vec4(1.0f, 2.0f, 3.0f, 4.0f), + glm::vec4(5.0f, 6.0f, 7.0f, 8.0f), + glm::vec4(9.0f, 10.0f, 11.0f, 12.0f), + glm::vec4(13.0f, 14.0f, 15.0f, 16.0f))); + QVERIFY(defaultVar.isBool()); + QVERIFY(defaultVar.getBool() == false); + + QVERIFY(boolVar.isBool()); + QVERIFY(boolVar.getBool() == true); + + QVERIFY(floatVar.isFloat()); + QVERIFY(floatVar.getFloat() == 1.0f); + + QVERIFY(vec3Var.isVec3()); + auto v = vec3Var.getVec3(); + QVERIFY(v.x == 1.0f); + QVERIFY(v.y == 2.0f); + QVERIFY(v.z == 3.0f); + + QVERIFY(quatVar.isQuat()); + auto q = quatVar.getQuat(); + QVERIFY(q.w == 1.0f); + QVERIFY(q.x == 2.0f); + QVERIFY(q.y == 3.0f); + QVERIFY(q.z == 4.0f); + + QVERIFY(mat4Var.isMat4()); + auto m = mat4Var.getMat4(); + QVERIFY(m[0].x == 1.0f); + QVERIFY(m[3].w == 16.0f); } diff --git a/tests/animation/src/AnimClipTests.h b/tests/animation/src/AnimTests.h similarity index 65% rename from tests/animation/src/AnimClipTests.h rename to tests/animation/src/AnimTests.h index 6239a88be8..460caa067d 100644 --- a/tests/animation/src/AnimClipTests.h +++ b/tests/animation/src/AnimTests.h @@ -1,19 +1,19 @@ // -// AnimClipTests.h +// AnimTests.h // -// Copyright 2015 High Fidelity, Inc. +// 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 +#ifndef hifi_AnimTests_h +#define hifi_AnimTests_h #include #include -class AnimClipTests : public QObject { +class AnimTests : public QObject { Q_OBJECT private slots: void initTestCase(); @@ -21,6 +21,7 @@ private slots: void testAccessors(); void testEvaulate(); void testLoader(); + void testVariant(); }; -#endif // hifi_AnimClipTests_h +#endif // hifi_AnimTests_h From 496c706bba9783af051380a3e26cdc9db1879c2e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Aug 2015 11:59:24 -0700 Subject: [PATCH 026/117] Added AnimVariantMap argument to evaluate. --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/MyAvatar.h | 2 ++ libraries/animation/src/AnimBlendLinear.cpp | 10 ++++++---- libraries/animation/src/AnimBlendLinear.h | 2 +- libraries/animation/src/AnimClip.cpp | 5 ++++- libraries/animation/src/AnimClip.h | 2 +- libraries/animation/src/AnimNode.h | 7 ++++--- libraries/animation/src/AnimOverlay.cpp | 9 +++++---- libraries/animation/src/AnimOverlay.h | 2 +- libraries/animation/src/AnimVariant.h | 8 +++++++- tests/animation/src/AnimTests.cpp | 12 +++++++++--- 11 files changed, 41 insertions(+), 20 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1e296c1066..15bde7fe2c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -170,7 +170,7 @@ void MyAvatar::update(float deltaTime) { blend->setAlpha(0.5f * sin(t) + 0.5f); */ t += deltaTime; - _animNode->evaluate(deltaTime); + _animNode->evaluate(_animVars, deltaTime); } if (_referential) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c1812a5e96..0adc901398 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -17,6 +17,7 @@ #include #include "Avatar.h" +#include "AnimVariant.h" class ModelItemID; class AnimNode; @@ -315,6 +316,7 @@ private: bool _prevShouldDrawHead; std::shared_ptr _animNode; + AnimVariantMap _animVars; }; #endif // hifi_MyAvatar_h diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 76d92cd264..8ce6845e6f 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -22,22 +22,24 @@ AnimBlendLinear::~AnimBlendLinear() { } -const std::vector& AnimBlendLinear::evaluate(float dt) { +const std::vector& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt) { + + // TODO: update _alpha from animVars if (_children.size() == 0) { for (auto&& pose : _poses) { pose = AnimPose::identity; } } else if (_children.size() == 1) { - _poses = _children[0]->evaluate(dt); + _poses = _children[0]->evaluate(animVars, 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); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index c1c1c928e5..784d89b04c 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -28,7 +28,7 @@ public: AnimBlendLinear(const std::string& id, float alpha); virtual ~AnimBlendLinear() override; - virtual const std::vector& evaluate(float dt) override; + virtual const std::vector& evaluate(const AnimVariantMap& animVars, float dt) override; void setAlpha(float alpha) { _alpha = alpha; } float getAlpha() const { return _alpha; } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index dca43cb735..7653aabde8 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -45,7 +45,10 @@ void AnimClip::setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; } -const std::vector& AnimClip::evaluate(float dt) { +const std::vector& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) { + + // TODO: update _frame, _startFrame, _endFrame, _timeScale, _loopFlag from animVars. + _frame = accumulateTime(_frame, dt); if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 2cce40d98a..0d4f104678 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -42,7 +42,7 @@ public: void setLoopFlag(bool loopFlag); bool getLoopFlag() const { return _loopFlag; } - virtual const std::vector& evaluate(float dt) override; + virtual const std::vector& evaluate(const AnimVariantMap& animVars, float dt) override; protected: virtual void setCurrentFrameInternal(float frame) override; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 9063488e9b..c4edd9bb06 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -18,6 +18,7 @@ #include #include "AnimSkeleton.h" +#include "AnimVariant.h" class QJsonObject; @@ -78,9 +79,9 @@ public: } } - virtual const std::vector& evaluate(float dt) = 0; - virtual const std::vector& overlay(float dt, const std::vector& underPoses) { - return evaluate(dt); + virtual const std::vector& evaluate(const AnimVariantMap& animVars, float dt) = 0; + virtual const std::vector& overlay(const AnimVariantMap& animVars, float dt, const std::vector& underPoses) { + return evaluate(animVars, dt); } protected: diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index c02c83d69f..3a76903d0f 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -37,17 +37,18 @@ void AnimOverlay::setBoneSet(BoneSet boneSet) { } } -const std::vector& AnimOverlay::evaluate(float dt) { +const std::vector& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt) { + if (_children.size() >= 2) { - auto underPoses = _children[1]->evaluate(dt); - auto overPoses = _children[0]->overlay(dt, underPoses); + auto underPoses = _children[1]->evaluate(animVars, dt); + auto overPoses = _children[0]->overlay(animVars, dt, underPoses); if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) { _poses.resize(underPoses.size()); assert(_boneSetVec.size() == _poses.size()); for (size_t i = 0; i < _poses.size(); i++) { - float alpha = _boneSetVec[i]; // TODO: PULL from boneSet + float alpha = _boneSetVec[i]; blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]); } } diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 82990514af..87d05e2b96 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -41,7 +41,7 @@ public: void setBoneSet(BoneSet boneSet); BoneSet getBoneSet() const { return _boneSet; } - virtual const std::vector& evaluate(float dt) override; + virtual const std::vector& evaluate(const AnimVariantMap& animVars, float dt) override; protected: // for AnimDebugDraw rendering diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 13e364045b..627e3210f4 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -18,6 +18,7 @@ class AnimVariant { public: enum Type { BoolType = 0, + IntType, FloatType, Vec3Type, QuatType, @@ -27,24 +28,28 @@ public: AnimVariant() : _type(BoolType) { memset(&_val, 0, sizeof(_val)); } AnimVariant(bool value) : _type(BoolType) { _val.boolVal = value; } + AnimVariant(int value) : _type(IntType) { _val.intVal = value; } AnimVariant(float value) : _type(FloatType) { _val.floats[0] = value; } AnimVariant(const glm::vec3& value) : _type(Vec3Type) { *reinterpret_cast(&_val) = value; } AnimVariant(const glm::quat& value) : _type(QuatType) { *reinterpret_cast(&_val) = value; } AnimVariant(const glm::mat4& value) : _type(Mat4Type) { *reinterpret_cast(&_val) = value; } bool isBool() const { return _type == BoolType; } + bool isInt() const { return _type == IntType; } bool isFloat() const { return _type == FloatType; } bool isVec3() const { return _type == Vec3Type; } bool isQuat() const { return _type == QuatType; } bool isMat4() const { return _type == Mat4Type; } void setBool(bool value) { assert(_type == BoolType); _val.boolVal = value; } + void setInt(int value) { assert(_type == IntType); _val.intVal = value; } void setFloat(float value) { assert(_type == FloatType); _val.floats[0] = value; } void setVec3(const glm::vec3& value) { assert(_type == Vec3Type); *reinterpret_cast(&_val) = value; } void setQuat(const glm::quat& value) { assert(_type == QuatType); *reinterpret_cast(&_val) = value; } void setMat4(const glm::mat4& value) { assert(_type == Mat4Type); *reinterpret_cast(&_val) = value; } bool getBool() { assert(_type == BoolType); return _val.boolVal; } + int getInt() { assert(_type == IntType); return _val.intVal; } float getFloat() { assert(_type == FloatType); return _val.floats[0]; } const glm::vec3& getVec3() { assert(_type == Vec3Type); return *reinterpret_cast(&_val); } const glm::quat& getQuat() { assert(_type == QuatType); return *reinterpret_cast(&_val); } @@ -54,10 +59,11 @@ protected: Type _type; union { bool boolVal; + int intVal; float floats[16]; } _val; }; -typedef std::map AnimVarantMap; +typedef std::map AnimVariantMap; #endif // hifi_AnimVariant_h diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 82d805d515..a25ce4bc0a 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -80,18 +80,20 @@ void AnimTests::testEvaulate() { float timeScale = 1.0f; float loopFlag = true; + auto vars = AnimVariantMap(); + AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); - clip.evaluate(framesToSec(10.0f)); + clip.evaluate(vars, framesToSec(10.0f)); QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, EPSILON); // does it loop? - clip.evaluate(framesToSec(11.0f)); + clip.evaluate(vars, 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)); + clip.evaluate(vars, framesToSec(20.0f)); QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, EPSILON); } @@ -155,6 +157,7 @@ void AnimTests::testLoader() { void AnimTests::testVariant() { auto defaultVar = AnimVariant(); auto boolVar = AnimVariant(true); + auto intVar = AnimVariant(1); auto floatVar = AnimVariant(1.0f); auto vec3Var = AnimVariant(glm::vec3(1.0f, 2.0f, 3.0f)); auto quatVar = AnimVariant(glm::quat(1.0f, 2.0f, 3.0f, 4.0f)); @@ -168,6 +171,9 @@ void AnimTests::testVariant() { QVERIFY(boolVar.isBool()); QVERIFY(boolVar.getBool() == true); + QVERIFY(intVar.isInt()); + QVERIFY(intVar.getInt() == 1); + QVERIFY(floatVar.isFloat()); QVERIFY(floatVar.getFloat() == 1.0f); From 4abf0cbd63a5c7f178632cf493bdff892cba8450 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Aug 2015 20:28:17 -0700 Subject: [PATCH 027/117] AnimVariantMap is used in eval, MyAvatar loads avatar.json via url --- interface/src/avatar/MyAvatar.cpp | 37 +++++---- interface/src/avatar/MyAvatar.h | 6 +- libraries/animation/src/AnimBlendLinear.cpp | 12 ++- libraries/animation/src/AnimBlendLinear.h | 20 +++-- libraries/animation/src/AnimClip.cpp | 45 +++++----- libraries/animation/src/AnimClip.h | 38 ++++----- libraries/animation/src/AnimNode.h | 17 ++-- libraries/animation/src/AnimOverlay.cpp | 24 ++++-- libraries/animation/src/AnimOverlay.h | 26 ++++-- libraries/animation/src/AnimSkeleton.h | 6 +- libraries/animation/src/AnimStateMachine.h | 66 +++++++++++++++ libraries/animation/src/AnimVariant.h | 81 ++++++++++++++++-- tests/animation/src/AnimTests.cpp | 92 ++++++++++++--------- tests/animation/src/AnimTests.h | 5 +- tests/animation/src/data/avatar.json | 37 +++++++++ 15 files changed, 361 insertions(+), 151 deletions(-) create mode 100644 libraries/animation/src/AnimStateMachine.h create mode 100644 tests/animation/src/data/avatar.json diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 15bde7fe2c..48fb2c1801 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -165,10 +165,7 @@ void MyAvatar::update(float deltaTime) { if (_animNode) { static float t = 0.0f; - /* - auto blend = std::static_pointer_cast(_animNode); - blend->setAlpha(0.5f * sin(t) + 0.5f); - */ + _animVars.set("sine", 0.5f * sin(t) + 0.5f); t += deltaTime; _animNode->evaluate(_animVars, deltaTime); } @@ -1222,26 +1219,30 @@ void MyAvatar::initHeadBones() { void MyAvatar::setupNewAnimationSystem() { - // create a skeleton and hand it over to the debug draw instance + // create a skeleton auto geom = _skeletonModel.getGeometry()->getFBXGeometry(); std::vector joints; for (auto& joint : geom.joints) { joints.push_back(joint); } - auto skeleton = make_shared(joints); - AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); - AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); + _animSkeleton = make_shared(joints); - // create a overlay node - auto overlay = make_shared("overlay", AnimOverlay::UpperBodyBoneSet); - auto idle = make_shared("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("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", 0.0f, 28.0f, 1.0f, true); - overlay->addChild(idle); - overlay->addChild(walk); - _animNode = overlay; - _animNode->setSkeleton(skeleton); - xform.trans.z += 1.0f; - AnimDebugDraw::getInstance().addAnimNode("blend", _animNode, xform); + // add it to the debug renderer, so we can see it. + //AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); + //AnimDebugDraw::getInstance().addSkeleton("my-avatar", _animSkeleton, xform); + + // load the anim graph + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/a939eadee4f36248776913d42891954a8d009158/avatar.json"); + _animLoader.reset(new AnimNodeLoader(graphUrl)); + connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { + _animNode = nodeIn; + _animNode->setSkeleton(_animSkeleton); + AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset() + glm::vec3(0, 0, 1)); + AnimDebugDraw::getInstance().addAnimNode("node", _animNode, xform); + }); + connect(_animLoader.get(), &AnimNodeLoader::error, [this, graphUrl](int error, QString str) { + qCCritical(interfaceapp) << "Error loading" << graphUrl << "code = " << error << "str =" << str; + }); } void MyAvatar::preRender(RenderArgs* renderArgs) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0adc901398..3946edbaba 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -16,8 +16,10 @@ #include #include +#include "AnimNode.h" +#include "AnimNodeLoader.h" + #include "Avatar.h" -#include "AnimVariant.h" class ModelItemID; class AnimNode; @@ -316,6 +318,8 @@ private: bool _prevShouldDrawHead; std::shared_ptr _animNode; + std::shared_ptr _animSkeleton; + std::unique_ptr _animLoader; AnimVariantMap _animVars; }; diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 8ce6845e6f..90dd85f313 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -22,9 +22,9 @@ AnimBlendLinear::~AnimBlendLinear() { } -const std::vector& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt) { +const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt) { - // TODO: update _alpha from animVars + _alpha = animVars.lookup(_alphaVar, _alpha); if (_children.size() == 0) { for (auto&& pose : _poses) { @@ -37,7 +37,11 @@ const std::vector& AnimBlendLinear::evaluate(const AnimVariantMap& ani size_t prevPoseIndex = glm::floor(clampedAlpha); size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - if (prevPoseIndex != nextPoseIndex) { + if (prevPoseIndex == nextPoseIndex) { + // this can happen if alpha is on an integer boundary + _poses = _children[prevPoseIndex]->evaluate(animVars, dt); + } else { + // need to eval and blend between two children. auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt); auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt); @@ -52,6 +56,6 @@ const std::vector& AnimBlendLinear::evaluate(const AnimVariantMap& ani } // for AnimDebugDraw rendering -const std::vector& AnimBlendLinear::getPosesInternal() const { +const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { return _poses; } diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 784d89b04c..85799ad3e3 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -7,11 +7,11 @@ // 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 +#include "AnimNode.h" + // Linear blend between two AnimNodes. // the amount of blending is determined by the alpha parameter. // If the number of children is 2, then the alpha parameters should be between @@ -24,23 +24,25 @@ class AnimBlendLinear : public AnimNode { public: + friend class AnimTests; AnimBlendLinear(const std::string& id, float alpha); virtual ~AnimBlendLinear() override; - virtual const std::vector& evaluate(const AnimVariantMap& animVars, float dt) override; - - void setAlpha(float alpha) { _alpha = alpha; } - float getAlpha() const { return _alpha; } + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; protected: - // for AnimDebugDraw rendering - virtual const std::vector& getPosesInternal() const override; + void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } - std::vector _poses; + // for AnimDebugDraw rendering + virtual const AnimPoseVec& getPosesInternal() const override; + + AnimPoseVec _poses; float _alpha; + std::string _alphaVar; + // no copies AnimBlendLinear(const AnimBlendLinear&) = delete; AnimBlendLinear& operator=(const AnimBlendLinear&) = delete; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 7653aabde8..51e32143be 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -20,38 +20,25 @@ AnimClip::AnimClip(const std::string& id, const std::string& url, float startFra _loopFlag(loopFlag), _frame(startFrame) { - setURL(url); + loadURL(url); } AnimClip::~AnimClip() { } -void AnimClip::setURL(const std::string& url) { - auto animCache = DependencyManager::get(); - _networkAnim = animCache->getAnimation(QString::fromStdString(url)); - _url = url; -} +const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) { -void AnimClip::setStartFrame(float startFrame) { - _startFrame = startFrame; -} - -void AnimClip::setEndFrame(float endFrame) { - _endFrame = endFrame; -} - -void AnimClip::setLoopFlag(bool loopFlag) { - _loopFlag = loopFlag; -} - -const std::vector& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) { - - // TODO: update _frame, _startFrame, _endFrame, _timeScale, _loopFlag from animVars. - - _frame = accumulateTime(_frame, dt); + // lookup parameters from animVars, using current instance variables as defaults. + _startFrame = animVars.lookup(_startFrameVar, _startFrame); + _endFrame = animVars.lookup(_endFrameVar, _endFrame); + _timeScale = animVars.lookup(_timeScaleVar, _timeScale); + _loopFlag = animVars.lookup(_loopFlagVar, _loopFlag); + _frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt); + // poll network anim to see if it's finished loading yet. if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { + // loading is complete, copy animation frames from network animation, then throw it away. copyFromNetworkAnim(); _networkAnim.reset(); } @@ -70,8 +57,8 @@ const std::vector& AnimClip::evaluate(const AnimVariantMap& animVars, prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); - const std::vector& prevFrame = _anim[prevIndex]; - const std::vector& nextFrame = _anim[nextIndex]; + const AnimPoseVec& prevFrame = _anim[prevIndex]; + const AnimPoseVec& nextFrame = _anim[nextIndex]; float alpha = glm::fract(_frame); blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); @@ -80,6 +67,12 @@ const std::vector& AnimClip::evaluate(const AnimVariantMap& animVars, return _poses; } +void AnimClip::loadURL(const std::string& url) { + auto animCache = DependencyManager::get(); + _networkAnim = animCache->getAnimation(QString::fromStdString(url)); + _url = url; +} + void AnimClip::setCurrentFrameInternal(float frame) { const float dt = 0.0f; _frame = accumulateTime(frame, dt); @@ -160,6 +153,6 @@ void AnimClip::copyFromNetworkAnim() { } -const std::vector& AnimClip::getPosesInternal() const { +const AnimPoseVec& AnimClip::getPosesInternal() const { return _poses; } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 0d4f104678..e4651e0ece 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -27,46 +27,44 @@ public: AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip() override; - void setURL(const std::string& url); - const std::string& getURL() const { return _url; } - - 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 std::vector& evaluate(const AnimVariantMap& animVars, float dt) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; protected: + void loadURL(const std::string& url); + + void setStartFrameVar(const std::string& startFrameVar) { _startFrameVar = startFrameVar; } + void setEndFrameVar(const std::string& endFrameVar) { _endFrameVar = endFrameVar; } + void setTimeScaleVar(const std::string& timeScaleVar) { _timeScaleVar = timeScaleVar; } + void setLoopFlagVar(const std::string& loopFlagVar) { _loopFlagVar = loopFlagVar; } + void setFrameVar(const std::string& frameVar) { _frameVar = frameVar; } + virtual void setCurrentFrameInternal(float frame) override; float accumulateTime(float frame, float dt) const; void copyFromNetworkAnim(); // for AnimDebugDraw rendering - virtual const std::vector& getPosesInternal() const override; + virtual const AnimPoseVec& getPosesInternal() const override; AnimationPointer _networkAnim; - std::vector _poses; + AnimPoseVec _poses; // _anim[frame][joint] - std::vector> _anim; + std::vector _anim; std::string _url; float _startFrame; float _endFrame; float _timeScale; bool _loopFlag; - float _frame; + std::string _startFrameVar; + std::string _endFrameVar; + std::string _timeScaleVar; + std::string _loopFlagVar; + std::string _frameVar; + // no copies AnimClip(const AnimClip&) = delete; AnimClip& operator=(const AnimClip&) = delete; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index c4edd9bb06..b7c7826540 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -63,6 +63,7 @@ public: } int getChildCount() const { return (int)_children.size(); } + // pair this AnimNode graph with a skeleton. void setSkeleton(const AnimSkeleton::Pointer skeleton) { setSkeletonInternal(skeleton); for (auto&& child : _children) { @@ -72,6 +73,13 @@ public: AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; } + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) = 0; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, const AnimPoseVec& underPoses) { + return evaluate(animVars, dt); + } + +protected: + void setCurrentFrame(float frame) { setCurrentFrameInternal(frame); for (auto&& child : _children) { @@ -79,20 +87,13 @@ public: } } - virtual const std::vector& evaluate(const AnimVariantMap& animVars, float dt) = 0; - virtual const std::vector& overlay(const AnimVariantMap& animVars, float dt, const std::vector& underPoses) { - return evaluate(animVars, dt); - } - -protected: - virtual void setCurrentFrameInternal(float frame) {} virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; } // for AnimDebugDraw rendering - virtual const std::vector& getPosesInternal() const = 0; + virtual const AnimPoseVec& getPosesInternal() const = 0; Type _type; std::string _id; diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 3a76903d0f..97db1711ca 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -11,16 +11,15 @@ #include "AnimUtil.h" #include -AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet) : - AnimNode(AnimNode::OverlayType, id), _boneSet(boneSet) { +AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet, float alpha) : + AnimNode(AnimNode::OverlayType, id), _boneSet(boneSet), _alpha(alpha) { } AnimOverlay::~AnimOverlay() { } -void AnimOverlay::setBoneSet(BoneSet boneSet) { - _boneSet = boneSet; +void AnimOverlay::buildBoneSet(BoneSet boneSet) { assert(_skeleton); switch (boneSet) { case FullBodyBoneSet: buildFullBodyBoneSet(); break; @@ -37,7 +36,16 @@ void AnimOverlay::setBoneSet(BoneSet boneSet) { } } -const std::vector& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt) { +const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt) { + + // lookup parameters from animVars, using current instance variables as defaults. + // NOTE: switching bonesets can be an expensive operation, let's try to avoid it. + auto prevBoneSet = _boneSet; + _boneSet = (BoneSet)animVars.lookup(_boneSetVar, (int)_boneSet); + if (_boneSet != prevBoneSet && _skeleton) { + buildBoneSet(_boneSet); + } + _alpha = animVars.lookup(_alphaVar, _alpha); if (_children.size() >= 2) { auto underPoses = _children[1]->evaluate(animVars, dt); @@ -48,7 +56,7 @@ const std::vector& AnimOverlay::evaluate(const AnimVariantMap& animVar assert(_boneSetVec.size() == _poses.size()); for (size_t i = 0; i < _poses.size(); i++) { - float alpha = _boneSetVec[i]; + float alpha = _boneSetVec[i] * _alpha; blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]); } } @@ -160,7 +168,7 @@ void AnimOverlay::buildEmptyBoneSet() { } // for AnimDebugDraw rendering -const std::vector& AnimOverlay::getPosesInternal() const { +const AnimPoseVec& AnimOverlay::getPosesInternal() const { return _poses; } @@ -168,5 +176,5 @@ void AnimOverlay::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; // we have to re-build the bone set when the skeleton changes. - setBoneSet(_boneSet); + buildBoneSet(_boneSet); } diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 87d05e2b96..2fe25d430e 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -7,8 +7,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AnimNode.h" - #ifndef hifi_AnimOverlay_h #define hifi_AnimOverlay_h @@ -17,9 +15,13 @@ // Overlay the AnimPoses from one AnimNode on top of another AnimNode. // child[0] is overlayed on top of child[1]. The boneset is used // to control blending on a per-bone bases. +// alpha gives the ability to fade in and fade out overlays. +// alpha of 0, will have no overlay, final pose will be 100% from child[1]. +// alpha of 1, will be a full overlay. class AnimOverlay : public AnimNode { public: + friend class AnimDebugDraw; enum BoneSet { FullBodyBoneSet = 0, @@ -35,23 +37,29 @@ public: NumBoneSets, }; - AnimOverlay(const std::string& id, BoneSet boneSet); + AnimOverlay(const std::string& id, BoneSet boneSet, float alpha); virtual ~AnimOverlay() override; - void setBoneSet(BoneSet boneSet); - BoneSet getBoneSet() const { return _boneSet; } + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; - virtual const std::vector& evaluate(const AnimVariantMap& animVars, float dt) override; + protected: + void buildBoneSet(BoneSet boneSet); + + void setBoneSetVar(const std::string& boneSetVar) { _boneSetVar = boneSetVar; } + void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } -protected: // for AnimDebugDraw rendering - virtual const std::vector& getPosesInternal() const override; + virtual const AnimPoseVec& getPosesInternal() const override; virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; - std::vector _poses; + AnimPoseVec _poses; BoneSet _boneSet; + float _alpha; std::vector _boneSetVec; + std::string _boneSetVar; + std::string _alphaVar; + void buildFullBodyBoneSet(); void buildUpperBodyBoneSet(); void buildLowerBodyBoneSet(); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 468abdc359..02ecd5ea7e 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -35,6 +35,8 @@ inline QDebug operator<<(QDebug debug, const AnimPose& pose) { return debug; } +typedef std::vector AnimPoseVec; + class AnimSkeleton { public: typedef std::shared_ptr Pointer; @@ -55,8 +57,8 @@ public: protected: std::vector _joints; - std::vector _absoluteBindPoses; - std::vector _relativeBindPoses; + AnimPoseVec _absoluteBindPoses; + AnimPoseVec _relativeBindPoses; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h new file mode 100644 index 0000000000..65c1bafdca --- /dev/null +++ b/libraries/animation/src/AnimStateMachine.h @@ -0,0 +1,66 @@ + +#ifndef hifi_AnimStateMachine_h +#define hifi_AnimStateMachine_h + +// AJT: post-pone state machine work. +#if 0 +class AnimStateMachine : public AnimNode { +public: + friend class AnimDebugDraw; + + AnimStateMachine(const std::string& id, float alpha); + virtual ~AnimStateMachine() override; + + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; + + class AnimTransition; + + class AnimState { + public: + typedef std::shared_ptr Pointer; + AnimState(const std::string& name, AnimNode::Pointer node, float interpFrame, float interpDuration) : + _name(name), + _node(node), + _interpFrame(interpFrame), + _interpDuration(interpDuration) {} + protected: + std::string _name; + AnimNode::Pointer _node; + float _interpFrame; + float _interpDuration; + std::vector _transitions; + private: + // no copies + AnimState(const AnimState&) = delete; + AnimState& operator=(const AnimState&) = delete; + }; + + class AnimTransition { + AnimTransition(const std::string& trigger, AnimState::Pointer state) : _trigger(trigger), _state(state) {} + protected: + std::string _trigger; + AnimState::Pointer _state; + }; + + void addState(AnimState::Pointer state); + void removeState(AnimState::Pointer state); + void setCurrentState(AnimState::Pointer state); + +protected: + // for AnimDebugDraw rendering + virtual const AnimPoseVec& getPosesInternal() const override; + + AnimPoseVec _poses; + + std::vector _states; + AnimState::Pointer _currentState; + AnimState::Pointer _defaultState; + +private: + // no copies + AnimStateMachine(const AnimBlendLinear&) = delete; + AnimStateMachine& operator=(const AnimBlendLinear&) = delete; +}; +#endif + +#endif // hifi_AnimBlendLinear_h diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 627e3210f4..36d189f85c 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -13,6 +13,7 @@ #include #include #include +#include class AnimVariant { public: @@ -48,12 +49,12 @@ public: void setQuat(const glm::quat& value) { assert(_type == QuatType); *reinterpret_cast(&_val) = value; } void setMat4(const glm::mat4& value) { assert(_type == Mat4Type); *reinterpret_cast(&_val) = value; } - bool getBool() { assert(_type == BoolType); return _val.boolVal; } - int getInt() { assert(_type == IntType); return _val.intVal; } - float getFloat() { assert(_type == FloatType); return _val.floats[0]; } - const glm::vec3& getVec3() { assert(_type == Vec3Type); return *reinterpret_cast(&_val); } - const glm::quat& getQuat() { assert(_type == QuatType); return *reinterpret_cast(&_val); } - const glm::mat4& getMat4() { assert(_type == Mat4Type); return *reinterpret_cast(&_val); } + bool getBool() const { assert(_type == BoolType); return _val.boolVal; } + int getInt() const { assert(_type == IntType); return _val.intVal; } + float getFloat() const { assert(_type == FloatType); return _val.floats[0]; } + const glm::vec3& getVec3() const { assert(_type == Vec3Type); return *reinterpret_cast(&_val); } + const glm::quat& getQuat() const { assert(_type == QuatType); return *reinterpret_cast(&_val); } + const glm::mat4& getMat4() const { assert(_type == Mat4Type); return *reinterpret_cast(&_val); } protected: Type _type; @@ -64,6 +65,72 @@ protected: } _val; }; -typedef std::map AnimVariantMap; +class AnimVariantMap { +public: + + bool lookup(const std::string& key, bool defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getBool() : defaultValue; + } + } + + int lookup(const std::string& key, int defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getInt() : defaultValue; + } + } + + float lookup(const std::string& key, float defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getFloat() : defaultValue; + } + } + + const glm::vec3& lookup(const std::string& key, const glm::vec3& defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getVec3() : defaultValue; + } + } + + const glm::quat& lookup(const std::string& key, const glm::quat& defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getQuat() : defaultValue; + } + } + + const glm::mat4& lookup(const std::string& key, const glm::mat4& defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getMat4() : defaultValue; + } + } + + void set(const std::string& key, bool value) { _map[key] = AnimVariant(value); } + void set(const std::string& key, int value) { _map[key] = AnimVariant(value); } + void set(const std::string& key, float value) { _map[key] = AnimVariant(value); } + void set(const std::string& key, const glm::vec3& value) { _map[key] = AnimVariant(value); } + void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); } + void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } + +protected: + std::map _map; +}; #endif // hifi_AnimVariant_h diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index a25ce4bc0a..91bb1ce5bb 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -29,7 +29,7 @@ void AnimTests::cleanupTestCase() { DependencyManager::destroy(); } -void AnimTests::testAccessors() { +void AnimTests::testClipInternalState() { std::string id = "my anim clip"; std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; @@ -42,29 +42,11 @@ void AnimTests::testAccessors() { QVERIFY(clip.getID() == id); QVERIFY(clip.getType() == AnimNode::ClipType); - QVERIFY(clip.getURL() == url); - QVERIFY(clip.getStartFrame() == startFrame); - QVERIFY(clip.getEndFrame() == endFrame); - QVERIFY(clip.getTimeScale() == timeScale); - QVERIFY(clip.getLoopFlag() == loopFlag); - - std::string url2 = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx"; - float startFrame2 = 22.0f; - float endFrame2 = 100.0f; - float timeScale2 = 1.2f; - bool 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); + QVERIFY(clip._url == url); + QVERIFY(clip._startFrame == startFrame); + QVERIFY(clip._endFrame == endFrame); + QVERIFY(clip._timeScale == timeScale); + QVERIFY(clip._loopFlag == loopFlag); } static float framesToSec(float secs) { @@ -72,7 +54,7 @@ static float framesToSec(float secs) { return secs / FRAMES_PER_SECOND; } -void AnimTests::testEvaulate() { +void AnimTests::testClipEvaulate() { std::string id = "my clip node"; std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; @@ -81,6 +63,7 @@ void AnimTests::testEvaulate() { float loopFlag = true; auto vars = AnimVariantMap(); + vars.set("FalseVar", false); AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); @@ -92,11 +75,46 @@ void AnimTests::testEvaulate() { QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); // does it pause at end? - clip.setLoopFlag(false); + clip.setLoopFlagVar("FalseVar"); clip.evaluate(vars, framesToSec(20.0f)); QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, EPSILON); } +void AnimTests::testClipEvaulateWithVars() { + std::string id = "my clip node"; + std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; + float startFrame = 2.0f; + float endFrame = 22.0f; + float timeScale = 1.0f; + float loopFlag = true; + + float startFrame2 = 22.0f; + float endFrame2 = 100.0f; + float timeScale2 = 1.2f; + bool loopFlag2 = false; + + auto vars = AnimVariantMap(); + vars.set("startFrame2", startFrame2); + vars.set("endFrame2", endFrame2); + vars.set("timeScale2", timeScale2); + vars.set("loopFlag2", loopFlag2); + + AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); + clip.setStartFrameVar("startFrame2"); + clip.setEndFrameVar("endFrame2"); + clip.setTimeScaleVar("timeScale2"); + clip.setLoopFlagVar("loopFlag2"); + + clip.evaluate(vars, framesToSec(0.1f)); + + // verify that the values from the AnimVariantMap made it into the clipNode's + // internal state + QVERIFY(clip._startFrame == startFrame2); + QVERIFY(clip._endFrame == endFrame2); + QVERIFY(clip._timeScale == timeScale2); + QVERIFY(clip._loopFlag == loopFlag2); +} + void AnimTests::testLoader() { auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json"); AnimNodeLoader loader(url); @@ -126,7 +144,7 @@ void AnimTests::testLoader() { QVERIFY(node->getType() == AnimNode::BlendLinearType); auto blend = std::static_pointer_cast(node); - QVERIFY(blend->getAlpha() == 0.5f); + QVERIFY(blend->_alpha == 0.5f); QVERIFY(node->getChildCount() == 3); @@ -140,18 +158,18 @@ void AnimTests::testLoader() { QVERIFY(nodes[2]->getChildCount() == 0); auto test01 = std::static_pointer_cast(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); + QVERIFY(test01->_url == "test01.fbx"); + QVERIFY(test01->_startFrame == 1.0f); + QVERIFY(test01->_endFrame == 20.0f); + QVERIFY(test01->_timeScale == 1.0f); + QVERIFY(test01->_loopFlag == false); auto test02 = std::static_pointer_cast(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); + QVERIFY(test02->_url == "test02.fbx"); + QVERIFY(test02->_startFrame == 2.0f); + QVERIFY(test02->_endFrame == 21.0f); + QVERIFY(test02->_timeScale == 0.9f); + QVERIFY(test02->_loopFlag == true); } void AnimTests::testVariant() { diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index 460caa067d..e667444657 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -18,8 +18,9 @@ class AnimTests : public QObject { private slots: void initTestCase(); void cleanupTestCase(); - void testAccessors(); - void testEvaulate(); + void testClipInternalState(); + void testClipEvaulate(); + void testClipEvaulateWithVars(); void testLoader(); void testVariant(); }; diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json new file mode 100644 index 0000000000..5dca9d3e16 --- /dev/null +++ b/tests/animation/src/data/avatar.json @@ -0,0 +1,37 @@ +{ + "version": "1.0", + "root": { + "id": "root", + "type": "overlay", + "data": { + "boneSet": "upperBody", + "alpha": 1.0 + }, + "children": [ + { + "id": "idle", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walk", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", + "startFrame": 0.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] + } +} From 0c02a338f220dbcceb607cca29563393ad45e80b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Aug 2015 20:57:01 -0700 Subject: [PATCH 028/117] Added support for setting Variants in the json file. For example: the avatar.json file was updated to use the "sine" Variant to drive the Overlay alpha parameter. --- interface/src/avatar/MyAvatar.cpp | 3 +- libraries/animation/src/AnimBlendLinear.h | 2 +- libraries/animation/src/AnimClip.h | 6 +-- libraries/animation/src/AnimNodeLoader.cpp | 54 ++++++++++++++++++++-- libraries/animation/src/AnimOverlay.h | 8 ++-- tests/animation/src/data/avatar.json | 3 +- 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 48fb2c1801..d19254892d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1232,7 +1232,8 @@ void MyAvatar::setupNewAnimationSystem() { //AnimDebugDraw::getInstance().addSkeleton("my-avatar", _animSkeleton, xform); // load the anim graph - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/a939eadee4f36248776913d42891954a8d009158/avatar.json"); + // https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9 + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/c4a9223e97b1d00b423b87542a2a57895ca72d21/avatar.json"); _animLoader.reset(new AnimNodeLoader(graphUrl)); connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { _animNode = nodeIn; diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 85799ad3e3..2df1965064 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -31,9 +31,9 @@ public: virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; -protected: void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } +protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index e4651e0ece..45924c1eed 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -29,15 +29,15 @@ public: virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; -protected: - void loadURL(const std::string& url); - void setStartFrameVar(const std::string& startFrameVar) { _startFrameVar = startFrameVar; } void setEndFrameVar(const std::string& endFrameVar) { _endFrameVar = endFrameVar; } void setTimeScaleVar(const std::string& timeScaleVar) { _timeScaleVar = timeScaleVar; } void setLoopFlagVar(const std::string& loopFlagVar) { _loopFlagVar = loopFlagVar; } void setFrameVar(const std::string& frameVar) { _frameVar = frameVar; } +protected: + void loadURL(const std::string& url); + virtual void setCurrentFrameInternal(float frame) override; float accumulateTime(float frame, float dt) const; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 615dde1627..74bafb8630 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -54,6 +54,13 @@ static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { } \ QString NAME = NAME##_VAL.toString() +#define READ_OPTIONAL_STRING(NAME, JSON_OBJ) \ + auto NAME##_VAL = JSON_OBJ.value(#NAME); \ + QString NAME; \ + if (NAME##_VAL.isString()) { \ + NAME = NAME##_VAL.toString(); \ + } + #define READ_BOOL(NAME, JSON_OBJ, ID, URL) \ auto NAME##_VAL = JSON_OBJ.value(#NAME); \ if (!NAME##_VAL.isBool()) { \ @@ -137,14 +144,42 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_FLOAT(timeScale, jsonObj, id, jsonUrl); READ_BOOL(loopFlag, jsonObj, id, jsonUrl); - return std::make_shared(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag); + READ_OPTIONAL_STRING(startFrameVar, jsonObj); + READ_OPTIONAL_STRING(endFrameVar, jsonObj); + READ_OPTIONAL_STRING(timeScaleVar, jsonObj); + READ_OPTIONAL_STRING(loopFlagVar, jsonObj); + + auto node = std::make_shared(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag); + + if (!startFrameVar.isEmpty()) { + node->setStartFrameVar(startFrameVar.toStdString()); + } + if (!endFrameVar.isEmpty()) { + node->setEndFrameVar(endFrameVar.toStdString()); + } + if (!timeScaleVar.isEmpty()) { + node->setTimeScaleVar(timeScaleVar.toStdString()); + } + if (!loopFlagVar.isEmpty()) { + node->setLoopFlagVar(loopFlagVar.toStdString()); + } + + return node; } static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl); - return std::make_shared(id.toStdString(), alpha); + READ_OPTIONAL_STRING(alphaVar, jsonObj); + + auto node = std::make_shared(id.toStdString(), alpha); + + if (!alphaVar.isEmpty()) { + node->setAlphaVar(alphaVar.toStdString()); + } + + return node; } static const char* boneSetStrings[AnimOverlay::NumBoneSets] = { @@ -172,6 +207,7 @@ static AnimOverlay::BoneSet stringToBoneSetEnum(const QString& str) { static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_STRING(boneSet, jsonObj, id, jsonUrl); + READ_FLOAT(alpha, jsonObj, id, jsonUrl); auto boneSetEnum = stringToBoneSetEnum(boneSet); if (boneSetEnum == AnimOverlay::NumBoneSets) { @@ -179,7 +215,19 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri boneSetEnum = AnimOverlay::FullBodyBoneSet; } - return std::make_shared(id.toStdString(), boneSetEnum); + READ_OPTIONAL_STRING(boneSetVar, jsonObj); + READ_OPTIONAL_STRING(alphaVar, jsonObj); + + auto node = std::make_shared(id.toStdString(), boneSetEnum, alpha); + + if (!boneSetVar.isEmpty()) { + node->setBoneSetVar(boneSetVar.toStdString()); + } + if (!alphaVar.isEmpty()) { + node->setAlphaVar(alphaVar.toStdString()); + } + + return node; } AnimNodeLoader::AnimNodeLoader(const QUrl& url) : diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 2fe25d430e..5940f0b2b3 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -21,7 +21,7 @@ class AnimOverlay : public AnimNode { public: - friend class AnimDebugDraw; + friend class AnimTests; enum BoneSet { FullBodyBoneSet = 0, @@ -42,12 +42,12 @@ public: virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; - protected: - void buildBoneSet(BoneSet boneSet); - void setBoneSetVar(const std::string& boneSetVar) { _boneSetVar = boneSetVar; } void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } + protected: + void buildBoneSet(BoneSet boneSet); + // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index 5dca9d3e16..f19ac04d56 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -5,7 +5,8 @@ "type": "overlay", "data": { "boneSet": "upperBody", - "alpha": 1.0 + "alpha": 1.0, + "alphaVar": "sine" }, "children": [ { From b7a9b54628690e4988434fca87133eaef8d796bf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 26 Aug 2015 16:42:08 -0700 Subject: [PATCH 029/117] Better AnimDebugDraw rendering --- interface/src/avatar/MyAvatar.cpp | 15 +- interface/src/avatar/MyAvatar.h | 1 + libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimSkeleton.cpp | 14 ++ libraries/animation/src/AnimSkeleton.h | 6 +- libraries/render-utils/src/AnimDebugDraw.cpp | 137 +++++++++++++++---- libraries/shared/src/GLMHelpers.cpp | 18 +++ libraries/shared/src/GLMHelpers.h | 8 ++ 8 files changed, 168 insertions(+), 33 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4c452dafc8..91eeb0c0d7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1021,6 +1021,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _billboardValid = false; _skeletonModel.setVisibleInScene(true, scene); _headBoneSet.clear(); + teardownNewAnimationSystem(); } void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) { @@ -1227,9 +1228,11 @@ void MyAvatar::setupNewAnimationSystem() { } _animSkeleton = make_shared(joints); - // add it to the debug renderer, so we can see it. - //AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); - //AnimDebugDraw::getInstance().addSkeleton("my-avatar", _animSkeleton, xform); + // add skeleton to the debug renderer, so we can see it. + /* + AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); + AnimDebugDraw::getInstance().addSkeleton("my-avatar", _animSkeleton, xform); + */ // load the anim graph // https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9 @@ -1246,6 +1249,12 @@ void MyAvatar::setupNewAnimationSystem() { }); } +void MyAvatar::teardownNewAnimationSystem() { + _animSkeleton = nullptr; + _animLoader = nullptr; + _animNode = nullptr; +} + void MyAvatar::preRender(RenderArgs* renderArgs) { render::ScenePointer scene = Application::getInstance()->getMain3DScene(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3946edbaba..305b1ee05a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -202,6 +202,7 @@ signals: private: void setupNewAnimationSystem(); + void teardownNewAnimationSystem(); glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 51e32143be..4dd89dd51a 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -145,7 +145,7 @@ void AnimClip::copyFromNetworkAnim() { int k = jointMap[j]; if (k >= 0 && k < skeletonJointCount) { // currently FBX animations only have rotation. - _anim[i][k].rot = _skeleton->getRelativeBindPose(j).rot * geom.animationFrames[i].rotations[j]; + _anim[i][k].rot = _skeleton->getRelativeBindPose(k).rot * geom.animationFrames[i].rotations[j]; } } } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 38c8bf8166..3d16f99473 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -27,6 +27,20 @@ glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const { return trans + (rot * (scale * rhs)); } +glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const { + return *this * rhs; +} + +// really slow +glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const { + glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); + glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); + glm::mat3 mat(xAxis, yAxis, zAxis); + glm::mat3 transInvMat = glm::inverse(glm::transpose(mat)); + return transInvMat * rhs; +} + AnimPose AnimPose::operator*(const AnimPose& rhs) const { return AnimPose(static_cast(*this) * static_cast(rhs)); } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 02ecd5ea7e..ef2d90fcea 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -20,8 +20,12 @@ struct AnimPose { AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} static const AnimPose identity; - glm::vec3 operator*(const glm::vec3& rhs) const; + glm::vec3 xformPoint(const glm::vec3& rhs) const; + glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow + + glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint AnimPose operator*(const AnimPose& rhs) const; + AnimPose inverse() const; operator glm::mat4() const; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 7bfe7be98d..4f62a8407f 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -10,10 +10,11 @@ #include "animdebugdraw_vert.h" #include "animdebugdraw_frag.h" #include - -#include "AnimDebugDraw.h" #include "AbstractViewStateInterface.h" #include "RenderUtilsLogging.h" +#include "GLMHelpers.h" + +#include "AnimDebugDraw.h" struct Vertex { glm::vec3 pos; @@ -165,45 +166,120 @@ static const uint32_t green = toRGBA(0, 255, 0, 255); static const uint32_t blue = toRGBA(0, 0, 255, 255); static const uint32_t cyan = toRGBA(0, 128, 128, 255); -static void addWireframeSphereWithAxes(const AnimPose& rootPose, const AnimPose& pose, float radius, Vertex*& v) { +const int NUM_CIRCLE_SLICES = 24; +static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius, Vertex*& v) { + + AnimPose finalPose = rootPose * pose; glm::vec3 base = rootPose * pose.trans; + glm::vec3 xRing[NUM_CIRCLE_SLICES + 1]; // one extra for last index. + glm::vec3 yRing[NUM_CIRCLE_SLICES + 1]; + glm::vec3 zRing[NUM_CIRCLE_SLICES + 1]; + const float dTheta = (2.0f * (float)M_PI) / NUM_CIRCLE_SLICES; + for (int i = 0; i < NUM_CIRCLE_SLICES + 1; i++) { + float rCosTheta = radius * cos(dTheta * i); + float rSinTheta = radius * sin(dTheta * i); + xRing[i] = finalPose * glm::vec3(0.0f, rCosTheta, rSinTheta); + yRing[i] = finalPose * glm::vec3(rCosTheta, 0.0f, rSinTheta); + zRing[i] = finalPose *glm::vec3(rCosTheta, rSinTheta, 0.0f); + } + // x-axis v->pos = base; v->rgba = red; v++; - v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(radius, 0.0f, 0.0f)); + v->pos = finalPose * glm::vec3(radius * 2.0f, 0.0f, 0.0f); v->rgba = red; v++; + // x-ring + for (int i = 0; i < NUM_CIRCLE_SLICES; i++) { + v->pos = xRing[i]; + v->rgba = red; + v++; + v->pos = xRing[i + 1]; + v->rgba = red; + v++; + } + // y-axis v->pos = base; v->rgba = green; v++; - v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, radius, 0.0f)); + v->pos = finalPose * glm::vec3(0.0f, radius * 2.0f, 0.0f); v->rgba = green; v++; + // y-ring + for (int i = 0; i < NUM_CIRCLE_SLICES; i++) { + v->pos = yRing[i]; + v->rgba = green; + v++; + v->pos = yRing[i + 1]; + v->rgba = green; + v++; + } + // z-axis v->pos = base; v->rgba = blue; v++; - v->pos = rootPose * (pose.trans + pose.rot * glm::vec3(0.0f, 0.0f, radius)); + v->pos = finalPose * glm::vec3(0.0f, 0.0f, radius * 2.0f); v->rgba = blue; v++; + + // z-ring + for (int i = 0; i < NUM_CIRCLE_SLICES; i++) { + v->pos = zRing[i]; + v->rgba = blue; + v++; + v->pos = zRing[i + 1]; + v->rgba = blue; + v++; + } } -static void addWireframeBoneAxis(const AnimPose& rootPose, const AnimPose& pose, - const AnimPose& parentPose, float radius, Vertex*& v) { - v->pos = rootPose * pose.trans; - v->rgba = cyan; - v++; - v->pos = rootPose * parentPose.trans; - v->rgba = cyan; - v++; -} +static void addLink(const AnimPose& rootPose, const AnimPose& pose, + const AnimPose& parentPose, float radius, Vertex*& v) { + AnimPose pose0 = rootPose * parentPose; + AnimPose pose1 = rootPose * pose; + + glm::vec3 boneAxisWorld = glm::normalize(pose1.trans - pose0.trans); + glm::vec3 boneAxis0 = glm::normalize(pose0.inverse().xformVector(boneAxisWorld)); + glm::vec3 boneAxis1 = glm::normalize(pose1.inverse().xformVector(boneAxisWorld)); + + glm::vec3 uAxis, vAxis, wAxis; + generateBasisVectors(boneAxis0, glm::vec3(1, 0, 0), uAxis, vAxis, wAxis); + + const int NUM_BASE_CORNERS = 4; + glm::vec3 boneBaseCorners[NUM_BASE_CORNERS]; + boneBaseCorners[0] = pose0 * ((uAxis * radius) + (vAxis * radius) + (wAxis * radius)); + boneBaseCorners[1] = pose0 * ((uAxis * radius) - (vAxis * radius) + (wAxis * radius)); + boneBaseCorners[2] = pose0 * ((uAxis * radius) - (vAxis * radius) - (wAxis * radius)); + boneBaseCorners[3] = pose0 * ((uAxis * radius) + (vAxis * radius) - (wAxis * radius)); + + glm::vec3 boneTip = pose1 * (boneAxis1 * -radius); + + for (int i = 0; i < NUM_BASE_CORNERS; i++) { + v->pos = boneBaseCorners[i]; + v->rgba = cyan; + v++; + v->pos = boneBaseCorners[(i + 1) % NUM_BASE_CORNERS]; + v->rgba = cyan; + v++; + } + + for (int i = 0; i < NUM_BASE_CORNERS; i++) { + v->pos = boneBaseCorners[i]; + v->rgba = cyan; + v++; + v->pos = boneTip; + v->rgba = cyan; + v++; + } +} void AnimDebugDraw::update() { @@ -215,15 +291,20 @@ void AnimDebugDraw::update() { render::PendingChanges pendingChanges; pendingChanges.updateItem(_itemID, [&](AnimDebugDrawData& data) { + const size_t VERTICES_PER_BONE = (6 + (NUM_CIRCLE_SLICES * 2) * 3); + const size_t VERTICES_PER_LINK = 8 * 2; + + const float BONE_RADIUS = 0.0075f; + // figure out how many verts we will need. int numVerts = 0; for (auto&& iter : _skeletons) { AnimSkeleton::Pointer& skeleton = iter.second.first; - numVerts += skeleton->getNumJoints() * 6; + numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; for (int i = 0; i < skeleton->getNumJoints(); i++) { auto parentIndex = skeleton->getParentIndex(i); if (parentIndex >= 0) { - numVerts += 2; + numVerts += VERTICES_PER_LINK; } } } @@ -231,12 +312,12 @@ void AnimDebugDraw::update() { for (auto&& iter : _animNodes) { AnimNode::Pointer& animNode = iter.second.first; auto poses = animNode->getPosesInternal(); - numVerts += poses.size() * 6; + numVerts += poses.size() * VERTICES_PER_BONE; auto skeleton = animNode->getSkeleton(); for (size_t i = 0; i < poses.size(); i++) { auto parentIndex = skeleton->getParentIndex(i); if (parentIndex >= 0) { - numVerts += 2; + numVerts += VERTICES_PER_LINK; } } } @@ -251,16 +332,17 @@ void AnimDebugDraw::update() { for (int i = 0; i < skeleton->getNumJoints(); i++) { AnimPose pose = skeleton->getAbsoluteBindPose(i); - // draw axes - const float radius = 0.1f; - addWireframeSphereWithAxes(rootPose, pose, radius, v); + const float radius = BONE_RADIUS / (pose.scale.x * rootPose.scale.x); - // line to parent. + // draw bone + addBone(rootPose, pose, radius, v); + + // draw link to parent auto parentIndex = skeleton->getParentIndex(i); if (parentIndex >= 0) { assert(parentIndex < skeleton->getNumJoints()); AnimPose parentPose = skeleton->getAbsoluteBindPose(parentIndex); - addWireframeBoneAxis(rootPose, pose, parentPose, radius, v); + addLink(rootPose, pose, parentPose, radius, v); } } } @@ -284,14 +366,13 @@ void AnimDebugDraw::update() { absAnimPose[i] = poses[i]; } - // draw axes - const float radius = 0.1f; - addWireframeSphereWithAxes(rootPose, absAnimPose[i], radius, v); + const float radius = BONE_RADIUS / (absAnimPose[i].scale.x * rootPose.scale.x); + addBone(rootPose, absAnimPose[i], radius, v); if (parentIndex >= 0) { assert((size_t)parentIndex < poses.size()); // draw line to parent - addWireframeBoneAxis(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, v); + addLink(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, v); } } } diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index afb3f1b38c..a81abc62ae 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -411,3 +411,21 @@ glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p) { return glm::vec3(temp.x / temp.w, temp.y / temp.w, temp.z / temp.w); } +glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& v) { + glm::mat3 rot(m); + return glm::inverse(glm::transpose(rot)) * v; +} + +void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& secondaryAxis, + glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut) { + + uAxisOut = glm::normalize(primaryAxis); + wAxisOut = glm::cross(uAxisOut, secondaryAxis); + if (glm::length(wAxisOut) > 0.0f) { + wAxisOut = glm::normalize(wAxisOut); + } else { + wAxisOut = glm::normalize(glm::cross(uAxisOut, glm::vec3(0, 1, 0))); + } + vAxisOut = glm::cross(wAxisOut, uAxisOut); +} + diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index d6e6b5b676..91f487d1fd 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -198,5 +198,13 @@ glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p); glm::quat cancelOutRollAndPitch(const glm::quat& q); glm::mat4 cancelOutRollAndPitch(const glm::mat4& m); glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p); +glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& v); + +// Calculate an orthogonal basis from a primary and secondary axis. +// The uAxis, vAxis & wAxis will form an orthognal basis. +// The primary axis will be the uAxis. +// The vAxis will be as close as possible to to the secondary axis. +void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& secondaryAxis, + glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut); #endif // hifi_GLMHelpers_h From 778521f66410436077b51efddb4670e8b982e171 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Aug 2015 10:21:23 -0700 Subject: [PATCH 030/117] Improvement to bone link rendering when bones are large. --- libraries/render-utils/src/AnimDebugDraw.cpp | 70 +++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 4f62a8407f..7bbf656f35 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -250,34 +250,54 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, glm::vec3 boneAxis0 = glm::normalize(pose0.inverse().xformVector(boneAxisWorld)); glm::vec3 boneAxis1 = glm::normalize(pose1.inverse().xformVector(boneAxisWorld)); - glm::vec3 uAxis, vAxis, wAxis; - generateBasisVectors(boneAxis0, glm::vec3(1, 0, 0), uAxis, vAxis, wAxis); - - const int NUM_BASE_CORNERS = 4; - glm::vec3 boneBaseCorners[NUM_BASE_CORNERS]; - boneBaseCorners[0] = pose0 * ((uAxis * radius) + (vAxis * radius) + (wAxis * radius)); - boneBaseCorners[1] = pose0 * ((uAxis * radius) - (vAxis * radius) + (wAxis * radius)); - boneBaseCorners[2] = pose0 * ((uAxis * radius) - (vAxis * radius) - (wAxis * radius)); - boneBaseCorners[3] = pose0 * ((uAxis * radius) + (vAxis * radius) - (wAxis * radius)); - + glm::vec3 boneBase = pose0 * (boneAxis0 * radius); glm::vec3 boneTip = pose1 * (boneAxis1 * -radius); - for (int i = 0; i < NUM_BASE_CORNERS; i++) { - v->pos = boneBaseCorners[i]; - v->rgba = cyan; - v++; - v->pos = boneBaseCorners[(i + 1) % NUM_BASE_CORNERS]; - v->rgba = cyan; - v++; - } + const int NUM_BASE_CORNERS = 4; - for (int i = 0; i < NUM_BASE_CORNERS; i++) { - v->pos = boneBaseCorners[i]; - v->rgba = cyan; - v++; - v->pos = boneTip; - v->rgba = cyan; - v++; + // make sure there's room between the two bones to draw a nice bone link. + if (glm::dot(boneTip - pose0.trans, boneAxisWorld) > glm::dot(boneBase - pose0.trans, boneAxisWorld)) { + + // there is room, so lets draw a nice bone + + glm::vec3 uAxis, vAxis, wAxis; + generateBasisVectors(boneAxis0, glm::vec3(1, 0, 0), uAxis, vAxis, wAxis); + + glm::vec3 boneBaseCorners[NUM_BASE_CORNERS]; + boneBaseCorners[0] = pose0 * ((uAxis * radius) + (vAxis * radius) + (wAxis * radius)); + boneBaseCorners[1] = pose0 * ((uAxis * radius) - (vAxis * radius) + (wAxis * radius)); + boneBaseCorners[2] = pose0 * ((uAxis * radius) - (vAxis * radius) - (wAxis * radius)); + boneBaseCorners[3] = pose0 * ((uAxis * radius) + (vAxis * radius) - (wAxis * radius)); + + for (int i = 0; i < NUM_BASE_CORNERS; i++) { + v->pos = boneBaseCorners[i]; + v->rgba = cyan; + v++; + v->pos = boneBaseCorners[(i + 1) % NUM_BASE_CORNERS]; + v->rgba = cyan; + v++; + } + + for (int i = 0; i < NUM_BASE_CORNERS; i++) { + v->pos = boneBaseCorners[i]; + v->rgba = cyan; + v++; + v->pos = boneTip; + v->rgba = cyan; + v++; + } + } else { + // There's no room between the bones to draw the link. + // just draw a line between the two bone centers. + // We add the same line multiple times, so the vertex count is correct. + for (int i = 0; i < NUM_BASE_CORNERS * 2; i++) { + v->pos = pose0.trans; + v->rgba = cyan; + v++; + v->pos = pose1.trans; + v->rgba = cyan; + v++; + } } } From 637e3b0a155a8f85491ca8bdd2f97aeff2f0a393 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Aug 2015 10:41:01 -0700 Subject: [PATCH 031/117] Added triggers to AnimVariantMap. --- libraries/animation/src/AnimVariant.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 36d189f85c..7c14bfd096 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -14,6 +14,7 @@ #include #include #include +#include class AnimVariant { public: @@ -69,8 +70,11 @@ class AnimVariantMap { public: bool lookup(const std::string& key, bool defaultValue) const { + // check triggers first, then map if (key.empty()) { return defaultValue; + } else if (_triggers.find(key) != _triggers.end()) { + return true; } else { auto iter = _map.find(key); return iter != _map.end() ? iter->second.getBool() : defaultValue; @@ -129,8 +133,12 @@ public: void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); } void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } + void setTrigger(const std::string& key) { _triggers.insert(key); } + void clearTirggers() { _triggers.clear(); } + protected: std::map _map; + std::set _triggers; }; #endif // hifi_AnimVariant_h From 3286a32afc543c9b2d1682f5ddf3e415054a45f9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Aug 2015 20:41:53 -0700 Subject: [PATCH 032/117] Initial version of AnimStateMachine. No interpolation support, but basic avatar.json is working with two states and two transitions between them. --- interface/src/avatar/MyAvatar.cpp | 11 +- libraries/animation/src/AnimBlendLinear.cpp | 2 +- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimNode.h | 18 +- libraries/animation/src/AnimNodeLoader.cpp | 200 ++++++++++++++++--- libraries/animation/src/AnimOverlay.cpp | 2 +- libraries/animation/src/AnimSkeleton.h | 6 +- libraries/animation/src/AnimStateMachine.cpp | 94 +++++++++ libraries/animation/src/AnimStateMachine.h | 137 +++++++++---- libraries/animation/src/AnimVariant.h | 80 +++++--- tests/animation/src/data/avatar.json | 24 ++- 11 files changed, 453 insertions(+), 123 deletions(-) create mode 100644 libraries/animation/src/AnimStateMachine.cpp diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 91eeb0c0d7..4e6aba85f9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -166,6 +166,15 @@ void MyAvatar::update(float deltaTime) { if (_animNode) { static float t = 0.0f; _animVars.set("sine", 0.5f * sin(t) + 0.5f); + + if (glm::length(getVelocity()) > 0.01) { + _animVars.set("isMoving", true); + _animVars.set("isNotMoving", false); + } else { + _animVars.set("isMoving", false); + _animVars.set("isNotMoving", true); + } + t += deltaTime; _animNode->evaluate(_animVars, deltaTime); } @@ -1236,7 +1245,7 @@ void MyAvatar::setupNewAnimationSystem() { // load the anim graph // https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9 - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/c4a9223e97b1d00b423b87542a2a57895ca72d21/avatar.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/c684000794675bc84ed63efefc21870e47c58d6a/avatar.json"); _animLoader.reset(new AnimNodeLoader(graphUrl)); connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { _animNode = nodeIn; diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 90dd85f313..c505984b87 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -13,7 +13,7 @@ #include "AnimUtil.h" AnimBlendLinear::AnimBlendLinear(const std::string& id, float alpha) : - AnimNode(AnimNode::BlendLinearType, id), + AnimNode(AnimNode::Type::BlendLinear, id), _alpha(alpha) { } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 4dd89dd51a..e4eb63f23a 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,7 +13,7 @@ #include "AnimUtil.h" AnimClip::AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : - AnimNode(AnimNode::ClipType, id), + AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), _endFrame(endFrame), _timeScale(timeScale), diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index b7c7826540..b6eedf9b2e 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -25,7 +25,7 @@ class QJsonObject; // Base class for all elements in the animation blend tree. // It provides the following categories of functions: // -// * id getter, id is a string name useful for debugging and searching. +// * id getter, id is used to identify a node, useful for debugging and node searching. // * type getter, helpful for determining the derived type of this node. // * hierarchy accessors, for adding, removing and iterating over child AnimNodes // * skeleton accessors, the skeleton is from the model whose bones we are going to manipulate @@ -33,15 +33,17 @@ class QJsonObject; class AnimNode { public: - friend class AnimDebugDraw; - - enum Type { - ClipType = 0, - BlendLinearType, - OverlayType, + enum class Type { + Clip = 0, + BlendLinear, + Overlay, + StateMachine, NumTypes }; - typedef std::shared_ptr Pointer; + using Pointer = std::shared_ptr; + + friend class AnimDebugDraw; + friend void buildChildMap(std::map& map, Pointer node); AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} virtual ~AnimNode() {} diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 74bafb8630..e41c3550ad 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -18,31 +18,52 @@ #include "AnimationLogging.h" #include "AnimOverlay.h" #include "AnimNodeLoader.h" +#include "AnimStateMachine.h" -struct TypeInfo { - AnimNode::Type type; - const char* str; -}; - -// This will result in a compile error if someone adds a new -// item to the AnimNode::Type enum. This is by design. -static TypeInfo typeInfoArray[AnimNode::NumTypes] = { - { AnimNode::ClipType, "clip" }, - { AnimNode::BlendLinearType, "blendLinear" }, - { AnimNode::OverlayType, "overlay" } -}; - -typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +// factory functions static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); -static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { - loadClipNode, - loadBlendLinearNode, - loadOverlayNode -}; +// called after children have been loaded +static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); + +static const char* animNodeTypeToString(AnimNode::Type type) { + switch (type) { + case AnimNode::Type::Clip: return "clip"; + case AnimNode::Type::BlendLinear: return "blendLinear"; + case AnimNode::Type::Overlay: return "overlay"; + case AnimNode::Type::StateMachine: return "stateMachine"; + }; + return nullptr; +} + +static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { + switch (type) { + case AnimNode::Type::Clip: return loadClipNode; + case AnimNode::Type::BlendLinear: return loadBlendLinearNode; + case AnimNode::Type::Overlay: return loadOverlayNode; + case AnimNode::Type::StateMachine: return loadStateMachineNode; + }; + return nullptr; +} + +static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { + switch (type) { + case AnimNode::Type::Clip: return processClipNode; + case AnimNode::Type::BlendLinear: return processBlendLinearNode; + case AnimNode::Type::Overlay: return processOverlayNode; + case AnimNode::Type::StateMachine: return processStateMachineNode; + }; + return nullptr; +} #define READ_STRING(NAME, JSON_OBJ, ID, URL) \ auto NAME##_VAL = JSON_OBJ.value(#NAME); \ @@ -82,12 +103,15 @@ static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { float NAME = (float)NAME##_VAL.toDouble() static AnimNode::Type stringToEnum(const QString& str) { - for (int i = 0; i < AnimNode::NumTypes; i++ ) { - if (str == typeInfoArray[i].str) { - return typeInfoArray[i].type; + // O(n), move to map when number of types becomes large. + const int NUM_TYPES = static_cast(AnimNode::Type::NumTypes); + for (int i = 0; i < NUM_TYPES; i++ ) { + AnimNode::Type type = static_cast(i); + if (str == animNodeTypeToString(type)) { + return type; } } - return AnimNode::NumTypes; + return AnimNode::Type::NumTypes; } static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) { @@ -105,7 +129,7 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr } QString typeStr = typeVal.toString(); AnimNode::Type type = stringToEnum(typeStr); - if (type == AnimNode::NumTypes) { + if (type == AnimNode::Type::NumTypes) { qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } @@ -117,23 +141,28 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr } auto dataObj = dataValue.toObject(); - assert((int)type >= 0 && type < AnimNode::NumTypes); - auto node = nodeLoaderFuncs[type](dataObj, id, jsonUrl); + assert((int)type >= 0 && type < AnimNode::Type::NumTypes); + auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl); auto childrenValue = jsonObj.value("children"); if (!childrenValue.isArray()) { qCCritical(animation) << "AnimNodeLoader, bad array \"children\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } - auto childrenAry = childrenValue.toArray(); - for (const auto& childValue : childrenAry) { + auto childrenArray = childrenValue.toArray(); + for (const auto& childValue : childrenArray) { if (!childValue.isObject()) { qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } node->addChild(loadNode(childValue.toObject(), jsonUrl)); } - return node; + + if ((animNodeTypeToProcessFunc(type))(node, dataObj, id, jsonUrl)) { + return node; + } else { + return nullptr; + } } static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { @@ -230,6 +259,119 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri return node; } +static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { + auto node = std::make_shared(id.toStdString()); + return node; +} + +static void buildChildMap(std::map& map, AnimNode::Pointer node) { + for ( auto child : node->_children ) { + map.insert(std::pair(child->_id, child)); + } +} + +static bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) { + auto smNode = std::static_pointer_cast(node); + assert(smNode); + + READ_STRING(currentState, jsonObj, nodeId, jsonUrl); + + auto statesValue = jsonObj.value("states"); + if (!statesValue.isArray()) { + qCCritical(animation) << "AnimNodeLoader, bad array \"states\" in stateMachine node, id =" << nodeId << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + // build a map for all children by name. + std::map childMap; + buildChildMap(childMap, node); + + // first pass parse all the states and build up the state and transition map. + using StringPair = std::pair; + using TransitionMap = std::multimap; + TransitionMap transitionMap; + + using StateMap = std::map; + StateMap stateMap; + + auto statesArray = statesValue.toArray(); + for (const auto& stateValue : statesArray) { + if (!stateValue.isObject()) { + qCCritical(animation) << "AnimNodeLoader, bad state object in \"states\", id =" << nodeId << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + auto stateObj = stateValue.toObject(); + + READ_STRING(id, stateObj, nodeId, jsonUrl); + READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl); + READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl); + + READ_OPTIONAL_STRING(interpTargetVar, stateObj); + READ_OPTIONAL_STRING(interpDurationVar, stateObj); + + auto stdId = id.toStdString(); + + auto iter = childMap.find(stdId); + if (iter == childMap.end()) { + qCCritical(animation) << "AnimNodeLoader, could not find stateMachine child (state) with nodeId =" << nodeId << "stateId =" << id << "url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + auto statePtr = std::make_shared(stdId, iter->second, interpTarget, interpDuration); + assert(statePtr); + + if (!interpTargetVar.isEmpty()) { + statePtr->setInterpTargetVar(interpTargetVar.toStdString()); + } + if (!interpDurationVar.isEmpty()) { + statePtr->setInterpDurationVar(interpDurationVar.toStdString()); + } + + smNode->addState(statePtr); + stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr)); + + auto transitionsValue = stateObj.value("transitions"); + if (!transitionsValue.isArray()) { + qCritical(animation) << "AnimNodeLoader, bad array \"transitions\" in stateMachine node, stateId =" << id << "nodeId =" << nodeId << "url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + auto transitionsArray = transitionsValue.toArray(); + for (const auto& transitionValue : transitionsArray) { + if (!transitionValue.isObject()) { + qCritical(animation) << "AnimNodeLoader, bad transition object in \"transtions\", stateId =" << id << "nodeId =" << nodeId << "url =" << jsonUrl.toDisplayString(); + return nullptr; + } + auto transitionObj = transitionValue.toObject(); + + READ_STRING(var, transitionObj, nodeId, jsonUrl); + READ_STRING(state, transitionObj, nodeId, jsonUrl); + + transitionMap.insert(TransitionMap::value_type(statePtr, StringPair(var.toStdString(), state.toStdString()))); + } + } + + // second pass: now iterate thru all transitions and add them to the appropriate states. + for (auto& transition : transitionMap) { + AnimStateMachine::State::Pointer srcState = transition.first; + auto iter = stateMap.find(transition.second.second); + if (iter != stateMap.end()) { + srcState->addTransition(AnimStateMachine::State::Transition(transition.second.first, iter->second)); + } else { + qCCritical(animation) << "AnimNodeLoader, bad state machine transtion from srcState =" << srcState->_id.c_str() << "dstState =" << transition.second.second.c_str() << "nodeId =" << nodeId << "url = " << jsonUrl.toDisplayString(); + return nullptr; + } + } + + auto iter = stateMap.find(currentState.toStdString()); + if (iter == stateMap.end()) { + qCCritical(animation) << "AnimNodeLoader, bad currentState =" << currentState << "could not find child node" << "id =" << nodeId << "url = " << jsonUrl.toDisplayString(); + } + smNode->setCurrentState(iter->second); + + return true; +} + AnimNodeLoader::AnimNodeLoader(const QUrl& url) : _url(url), _resource(nullptr) { diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 97db1711ca..21c893810a 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -12,7 +12,7 @@ #include AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet, float alpha) : - AnimNode(AnimNode::OverlayType, id), _boneSet(boneSet), _alpha(alpha) { + AnimNode(AnimNode::Type::Overlay, id), _boneSet(boneSet), _alpha(alpha) { } AnimOverlay::~AnimOverlay() { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index ef2d90fcea..3faade0dbd 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -39,12 +39,12 @@ inline QDebug operator<<(QDebug debug, const AnimPose& pose) { return debug; } -typedef std::vector AnimPoseVec; +using AnimPoseVec = std::vector; class AnimSkeleton { public: - typedef std::shared_ptr Pointer; - typedef std::shared_ptr ConstPointer; + using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp new file mode 100644 index 0000000000..2faea905de --- /dev/null +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -0,0 +1,94 @@ +// +// AnimStateMachine.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 "AnimStateMachine.h" +#include "AnimationLogging.h" + +AnimStateMachine::AnimStateMachine(const std::string& id) : + AnimNode(AnimNode::Type::StateMachine, id) { + +} + +AnimStateMachine::~AnimStateMachine() { + +} + + + +const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt) { + + std::string desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); + if (_currentState->getID() != desiredStateID) { + // switch states + bool foundState = false; + for (auto& state : _states) { + if (state->getID() == desiredStateID) { + switchState(state); + foundState = true; + break; + } + } + if (!foundState) { + qCCritical(animation) << "AnimStateMachine could not find state =" << desiredStateID.c_str() << ", referenced by _currentStateVar =" << _currentStateVar.c_str(); + } + } + + // evaluate currentState transitions + auto desiredState = evaluateTransitions(animVars); + if (desiredState != _currentState) { + switchState(desiredState); + } + + if (_duringInterp) { + // TODO: do interpolation + /* + const float FRAMES_PER_SECOND = 30.0f; + // blend betwen poses + blend(_poses.size(), _prevPose, _nextPose, _alpha, &_poses[0]); + */ + } else { + // eval current state + assert(_currentState); + auto currentStateNode = _currentState->getNode(); + assert(currentStateNode); + _poses = currentStateNode->evaluate(animVars, dt); + } + + qCDebug(animation) << "StateMachine::evalute"; + + return _poses; +} + +void AnimStateMachine::setCurrentState(State::Pointer state) { + _currentState = state; +} + +void AnimStateMachine::addState(State::Pointer state) { + _states.push_back(state); +} + +void AnimStateMachine::switchState(State::Pointer desiredState) { + qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID().c_str() << "->" << desiredState->getID().c_str(); + // TODO: interp. + _currentState = desiredState; +} + +AnimStateMachine::State::Pointer AnimStateMachine::evaluateTransitions(const AnimVariantMap& animVars) const { + assert(_currentState); + for (auto& transition : _currentState->_transitions) { + if (animVars.lookup(transition._var, false)) { + return transition._state; + } + } + return _currentState; +} + +const AnimPoseVec& AnimStateMachine::getPosesInternal() const { + return _poses; +} diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 65c1bafdca..148429e722 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -1,66 +1,117 @@ +// +// AnimStateMachine.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_AnimStateMachine_h #define hifi_AnimStateMachine_h -// AJT: post-pone state machine work. -#if 0 +#include +#include +#include "AnimNode.h" + class AnimStateMachine : public AnimNode { public: - friend class AnimDebugDraw; + friend class AnimNodeLoader; + friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl); - AnimStateMachine(const std::string& id, float alpha); +protected: + class State { + public: + friend AnimStateMachine; + friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl); + + using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; + + class Transition { + public: + friend AnimStateMachine; + Transition(const std::string& var, State::Pointer state) : _var(var), _state(state) {} + protected: + std::string _var; + State::Pointer _state; + }; + + State(const std::string& id, AnimNode::Pointer node, float interpTarget, float interpDuration) : + _id(id), + _node(node), + _interpTarget(interpTarget), + _interpDuration(interpDuration) {} + + void setInterpTargetVar(const std::string& interpTargetVar) { _interpTargetVar = interpTargetVar; } + void setInterpDurationVar(const std::string& interpDurationVar) { _interpDurationVar = interpDurationVar; } + + AnimNode::Pointer getNode() const { return _node; } + const std::string& getID() const { return _id; } + + protected: + + void setInterpTarget(float interpTarget) { _interpTarget = interpTarget; } + void setInterpDuration(float interpDuration) { _interpDuration = interpDuration; } + + void addTransition(const Transition& transition) { _transitions.push_back(transition); } + + std::string _id; + AnimNode::Pointer _node; + float _interpTarget; // frames + float _interpDuration; // frames + + std::string _interpTargetVar; + std::string _interpDurationVar; + + std::vector _transitions; + + private: + // no copies + State(const State&) = delete; + State& operator=(const State&) = delete; + }; + +public: + + AnimStateMachine(const std::string& id); virtual ~AnimStateMachine() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; - class AnimTransition; - - class AnimState { - public: - typedef std::shared_ptr Pointer; - AnimState(const std::string& name, AnimNode::Pointer node, float interpFrame, float interpDuration) : - _name(name), - _node(node), - _interpFrame(interpFrame), - _interpDuration(interpDuration) {} - protected: - std::string _name; - AnimNode::Pointer _node; - float _interpFrame; - float _interpDuration; - std::vector _transitions; - private: - // no copies - AnimState(const AnimState&) = delete; - AnimState& operator=(const AnimState&) = delete; - }; - - class AnimTransition { - AnimTransition(const std::string& trigger, AnimState::Pointer state) : _trigger(trigger), _state(state) {} - protected: - std::string _trigger; - AnimState::Pointer _state; - }; - - void addState(AnimState::Pointer state); - void removeState(AnimState::Pointer state); - void setCurrentState(AnimState::Pointer state); + void setCurrentStateVar(std::string& currentStateVar) { _currentStateVar = currentStateVar; } protected: + + void setCurrentState(State::Pointer state); + + void addState(State::Pointer state); + + void switchState(State::Pointer desiredState); + State::Pointer evaluateTransitions(const AnimVariantMap& animVars) const; + // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; AnimPoseVec _poses; - std::vector _states; - AnimState::Pointer _currentState; - AnimState::Pointer _defaultState; + // interpolation state + bool _duringInterp = false; + float _interpFrame; + float _interpDuration; + float _alpha; + AnimPoseVec _prevPoses; + AnimPoseVec _nextPoses; + + State::Pointer _currentState; + std::vector _states; + + std::string _currentStateVar; private: // no copies - AnimStateMachine(const AnimBlendLinear&) = delete; - AnimStateMachine& operator=(const AnimBlendLinear&) = delete; + AnimStateMachine(const AnimStateMachine&) = delete; + AnimStateMachine& operator=(const AnimStateMachine&) = delete; }; -#endif -#endif // hifi_AnimBlendLinear_h +#endif // hifi_AnimStateMachine_h diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 7c14bfd096..15ab7e94b1 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -18,47 +18,53 @@ class AnimVariant { public: - enum Type { - BoolType = 0, - IntType, - FloatType, - Vec3Type, - QuatType, - Mat4Type, + enum class Type { + Bool = 0, + Int, + Float, + Vec3, + Quat, + Mat4, + String, NumTypes }; - AnimVariant() : _type(BoolType) { memset(&_val, 0, sizeof(_val)); } - AnimVariant(bool value) : _type(BoolType) { _val.boolVal = value; } - AnimVariant(int value) : _type(IntType) { _val.intVal = value; } - AnimVariant(float value) : _type(FloatType) { _val.floats[0] = value; } - AnimVariant(const glm::vec3& value) : _type(Vec3Type) { *reinterpret_cast(&_val) = value; } - AnimVariant(const glm::quat& value) : _type(QuatType) { *reinterpret_cast(&_val) = value; } - AnimVariant(const glm::mat4& value) : _type(Mat4Type) { *reinterpret_cast(&_val) = value; } + AnimVariant() : _type(Type::Bool) { memset(&_val, 0, sizeof(_val)); } + AnimVariant(bool value) : _type(Type::Bool) { _val.boolVal = value; } + AnimVariant(int value) : _type(Type::Int) { _val.intVal = value; } + AnimVariant(float value) : _type(Type::Float) { _val.floats[0] = value; } + AnimVariant(const glm::vec3& value) : _type(Type::Vec3) { *reinterpret_cast(&_val) = value; } + AnimVariant(const glm::quat& value) : _type(Type::Quat) { *reinterpret_cast(&_val) = value; } + AnimVariant(const glm::mat4& value) : _type(Type::Mat4) { *reinterpret_cast(&_val) = value; } + AnimVariant(const std::string& value) : _type(Type::String) { _stringVal = value; } - bool isBool() const { return _type == BoolType; } - bool isInt() const { return _type == IntType; } - bool isFloat() const { return _type == FloatType; } - bool isVec3() const { return _type == Vec3Type; } - bool isQuat() const { return _type == QuatType; } - bool isMat4() const { return _type == Mat4Type; } + bool isBool() const { return _type == Type::Bool; } + bool isInt() const { return _type == Type::Int; } + bool isFloat() const { return _type == Type::Float; } + bool isVec3() const { return _type == Type::Vec3; } + bool isQuat() const { return _type == Type::Quat; } + bool isMat4() const { return _type == Type::Mat4; } + bool isString() const { return _type == Type::String; } - void setBool(bool value) { assert(_type == BoolType); _val.boolVal = value; } - void setInt(int value) { assert(_type == IntType); _val.intVal = value; } - void setFloat(float value) { assert(_type == FloatType); _val.floats[0] = value; } - void setVec3(const glm::vec3& value) { assert(_type == Vec3Type); *reinterpret_cast(&_val) = value; } - void setQuat(const glm::quat& value) { assert(_type == QuatType); *reinterpret_cast(&_val) = value; } - void setMat4(const glm::mat4& value) { assert(_type == Mat4Type); *reinterpret_cast(&_val) = value; } + void setBool(bool value) { assert(_type == Type::Bool); _val.boolVal = value; } + void setInt(int value) { assert(_type == Type::Int); _val.intVal = value; } + void setFloat(float value) { assert(_type == Type::Float); _val.floats[0] = value; } + void setVec3(const glm::vec3& value) { assert(_type == Type::Vec3); *reinterpret_cast(&_val) = value; } + void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast(&_val) = value; } + void setMat4(const glm::mat4& value) { assert(_type == Type::Mat4); *reinterpret_cast(&_val) = value; } + void setString(const std::string& value) { assert(_type == Type::String); _stringVal = value; } - bool getBool() const { assert(_type == BoolType); return _val.boolVal; } - int getInt() const { assert(_type == IntType); return _val.intVal; } - float getFloat() const { assert(_type == FloatType); return _val.floats[0]; } - const glm::vec3& getVec3() const { assert(_type == Vec3Type); return *reinterpret_cast(&_val); } - const glm::quat& getQuat() const { assert(_type == QuatType); return *reinterpret_cast(&_val); } - const glm::mat4& getMat4() const { assert(_type == Mat4Type); return *reinterpret_cast(&_val); } + bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } + int getInt() const { assert(_type == Type::Int); return _val.intVal; } + float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; } + const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast(&_val); } + const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast(&_val); } + const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast(&_val); } + const std::string& getString() const { assert(_type == Type::String); return _stringVal; } protected: Type _type; + std::string _stringVal; union { bool boolVal; int intVal; @@ -126,12 +132,22 @@ public: } } + const std::string& lookup(const std::string& key, const std::string& defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getString() : defaultValue; + } + } + void set(const std::string& key, bool value) { _map[key] = AnimVariant(value); } void set(const std::string& key, int value) { _map[key] = AnimVariant(value); } void set(const std::string& key, float value) { _map[key] = AnimVariant(value); } void set(const std::string& key, const glm::vec3& value) { _map[key] = AnimVariant(value); } void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); } void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } + void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); } void setTrigger(const std::string& key) { _triggers.insert(key); } void clearTirggers() { _triggers.clear(); } diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index f19ac04d56..7184e67a44 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -2,11 +2,27 @@ "version": "1.0", "root": { "id": "root", - "type": "overlay", + "type": "stateMachine", "data": { - "boneSet": "upperBody", - "alpha": 1.0, - "alphaVar": "sine" + "currentState": "idle", + "states": [ + { + "id": "idle", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isMoving", "state": "walk" } + ] + }, + { + "id": "walk", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isNotMoving", "state": "idle" } + ] + } + ] }, "children": [ { From 19e91bb392bb0e720a271c6bbbdec51e0f914b13 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Aug 2015 21:26:31 -0700 Subject: [PATCH 033/117] Added basic interpolation support to AnimStateMachine --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimBlendLinear.cpp | 2 +- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimNode.h | 1 + libraries/animation/src/AnimOverlay.cpp | 2 +- libraries/animation/src/AnimStateMachine.cpp | 54 ++++++++++++-------- libraries/animation/src/AnimStateMachine.h | 7 ++- 7 files changed, 42 insertions(+), 28 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4e6aba85f9..d1d62333a7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1245,7 +1245,7 @@ void MyAvatar::setupNewAnimationSystem() { // load the anim graph // https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9 - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/c684000794675bc84ed63efefc21870e47c58d6a/avatar.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/250ce1f207e23c74694351f04367063cf1269f94/avatar.json"); _animLoader.reset(new AnimNodeLoader(graphUrl)); connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { _animNode = nodeIn; diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index c505984b87..102d0e4df6 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -48,7 +48,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); - blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); } } } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index e4eb63f23a..4f3ea54e34 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -61,7 +61,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) const AnimPoseVec& nextFrame = _anim[nextIndex]; float alpha = glm::fract(_frame); - blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); + ::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); } return _poses; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index b6eedf9b2e..0b521211a2 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -44,6 +44,7 @@ public: friend class AnimDebugDraw; friend void buildChildMap(std::map& map, Pointer node); + friend class AnimStateMachine; AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} virtual ~AnimNode() {} diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 21c893810a..81df5811d7 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -57,7 +57,7 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float d for (size_t i = 0; i < _poses.size(); i++) { float alpha = _boneSetVec[i] * _alpha; - blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]); + ::blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]); } } } diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 2faea905de..43bb305797 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -8,6 +8,7 @@ // #include "AnimStateMachine.h" +#include "AnimUtil.h" #include "AnimationLogging.h" AnimStateMachine::AnimStateMachine(const std::string& id) : @@ -19,8 +20,6 @@ AnimStateMachine::~AnimStateMachine() { } - - const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt) { std::string desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); @@ -29,7 +28,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl bool foundState = false; for (auto& state : _states) { if (state->getID() == desiredStateID) { - switchState(state); + switchState(animVars, state); foundState = true; break; } @@ -42,26 +41,27 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl // evaluate currentState transitions auto desiredState = evaluateTransitions(animVars); if (desiredState != _currentState) { - switchState(desiredState); + switchState(animVars, desiredState); } + + assert(_currentState); + auto currentStateNode = _currentState->getNode(); + assert(currentStateNode); + if (_duringInterp) { - // TODO: do interpolation - /* - const float FRAMES_PER_SECOND = 30.0f; - // blend betwen poses - blend(_poses.size(), _prevPose, _nextPose, _alpha, &_poses[0]); - */ - } else { - // eval current state - assert(_currentState); - auto currentStateNode = _currentState->getNode(); - assert(currentStateNode); + _alpha += _alphaVel * dt; + if (_alpha < 1.0f) { + ::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]); + } else { + _duringInterp = false; + _prevPoses.clear(); + _nextPoses.clear(); + } + } + if (!_duringInterp) { _poses = currentStateNode->evaluate(animVars, dt); } - - qCDebug(animation) << "StateMachine::evalute"; - return _poses; } @@ -73,9 +73,23 @@ void AnimStateMachine::addState(State::Pointer state) { _states.push_back(state); } -void AnimStateMachine::switchState(State::Pointer desiredState) { +void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointer desiredState) { + qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID().c_str() << "->" << desiredState->getID().c_str(); - // TODO: interp. + + const float FRAMES_PER_SECOND = 30.0f; + + auto prevStateNode = _currentState->getNode(); + auto nextStateNode = desiredState->getNode(); + + _duringInterp = true; + _alpha = 0.0f; + float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration)); + _alphaVel = FRAMES_PER_SECOND / duration; + _prevPoses = _poses; + nextStateNode->setCurrentFrame(desiredState->_interpTarget); + _nextPoses = nextStateNode->evaluate(animVars, 0.0f); + _currentState = desiredState; } diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 148429e722..4e9a4b3214 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -87,7 +87,7 @@ protected: void addState(State::Pointer state); - void switchState(State::Pointer desiredState); + void switchState(const AnimVariantMap& animVars, State::Pointer desiredState); State::Pointer evaluateTransitions(const AnimVariantMap& animVars) const; // for AnimDebugDraw rendering @@ -97,9 +97,8 @@ protected: // interpolation state bool _duringInterp = false; - float _interpFrame; - float _interpDuration; - float _alpha; + float _alphaVel = 0.0f; + float _alpha = 0.0f; AnimPoseVec _prevPoses; AnimPoseVec _nextPoses; From 21c6ba9bdf0447b788bfea1d3e20f7e2cd35c0cb Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Aug 2015 21:27:37 -0700 Subject: [PATCH 034/117] updated avatar.json to match current gist. --- tests/animation/src/data/avatar.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index 7184e67a44..c8d507d03e 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -8,16 +8,16 @@ "states": [ { "id": "idle", - "interpTarget": 3, - "interpDuration": 3, + "interpTarget": 6, + "interpDuration": 6, "transitions": [ { "var": "isMoving", "state": "walk" } ] }, { "id": "walk", - "interpTarget": 3, - "interpDuration": 3, + "interpTarget": 6, + "interpDuration": 6, "transitions": [ { "var": "isNotMoving", "state": "idle" } ] From 9d983e0614099c42d86fc0e897377fec6e4b6e81 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 28 Aug 2015 17:16:32 -0700 Subject: [PATCH 035/117] Bug fix to AnimNode::setSkeletonModel and AnimNodeLoader. Also updated avatar.json to test nested graphs under a SM. --- interface/src/avatar/MyAvatar.cpp | 8 +- libraries/animation/src/AnimNode.h | 2 +- libraries/animation/src/AnimNodeLoader.cpp | 6 +- tests/animation/src/data/avatar.json | 94 ++++++++++++++++++++-- 4 files changed, 99 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d1d62333a7..9fe0d5f845 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -167,7 +167,7 @@ void MyAvatar::update(float deltaTime) { static float t = 0.0f; _animVars.set("sine", 0.5f * sin(t) + 0.5f); - if (glm::length(getVelocity()) > 0.01) { + if (glm::length(getVelocity()) > 0.07f) { _animVars.set("isMoving", true); _animVars.set("isNotMoving", false); } else { @@ -1245,12 +1245,14 @@ void MyAvatar::setupNewAnimationSystem() { // load the anim graph // https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9 - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/250ce1f207e23c74694351f04367063cf1269f94/avatar.json"); + // 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(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset() + glm::vec3(0, 0, 1)); + AnimPose xform(_skeletonModel.getScale() / 10.0f, glm::quat(), _skeletonModel.getOffset() + glm::vec3(0, 0, 1)); AnimDebugDraw::getInstance().addAnimNode("node", _animNode, xform); }); connect(_animLoader.get(), &AnimNodeLoader::error, [this, graphUrl](int error, QString str) { diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 0b521211a2..733428f188 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -70,7 +70,7 @@ public: void setSkeleton(const AnimSkeleton::Pointer skeleton) { setSkeletonInternal(skeleton); for (auto&& child : _children) { - child->setSkeletonInternal(skeleton); + child->setSkeleton(skeleton); } } diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index e41c3550ad..abb5b1b5f2 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -418,7 +418,11 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j void AnimNodeLoader::onRequestDone(QNetworkReply& request) { auto node = load(request.readAll(), _url); - emit success(node); + if (node) { + emit success(node); + } else { + emit error(0, "json parse error"); + } } void AnimNodeLoader::onRequestError(QNetworkReply::NetworkError netError) { diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index c8d507d03e..3856588c42 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -11,11 +11,11 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "isMoving", "state": "walk" } + { "var": "isMoving", "state": "walk_fwd" } ] }, { - "id": "walk", + "id": "walk_fwd", "interpTarget": 6, "interpDuration": 6, "transitions": [ @@ -27,27 +27,109 @@ "children": [ { "id": "idle", + "type": "blendLinear", + "data": { + "alpha": 0.5, + "alphaVar": "sine" + }, + "children": [ + { + "id": "normal_idle", + "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": "other_idle", + "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": [] + } + ] + }, + { + "id": "walk_fwd", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", "startFrame": 0.0, - "endFrame": 90.0, + "endFrame": 35.0, "timeScale": 1.0, "loopFlag": true }, "children": [] }, { - "id": "walk", + "id": "walk_bwd", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", + "startFrame": 0.0, + "endFrame": 37.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "turn_left", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx", "startFrame": 0.0, "endFrame": 28.0, "timeScale": 1.0, "loopFlag": true }, "children": [] + }, + { + "id": "turn_right", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafe_left", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_left.fbx", + "startFrame": 0.0, + "endFrame": 31.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafe_right", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_right.fbx", + "startFrame": 0.0, + "endFrame": 31.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] } ] } From 6f4fc24fb6082c06c86b528aea97deae3f0b9aab Mon Sep 17 00:00:00 2001 From: Bennett Goble Date: Sun, 30 Aug 2015 12:05:56 -0400 Subject: [PATCH 036/117] NodeConnectionData QDataStream #include --- domain-server/src/NodeConnectionData.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/domain-server/src/NodeConnectionData.cpp b/domain-server/src/NodeConnectionData.cpp index 3c75e8faad..80cb5950be 100644 --- a/domain-server/src/NodeConnectionData.cpp +++ b/domain-server/src/NodeConnectionData.cpp @@ -11,6 +11,8 @@ #include "NodeConnectionData.h" +#include + NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, const HifiSockAddr& senderSockAddr, bool isConnectRequest) { NodeConnectionData newHeader; From 64d0ce47f3ee23b54fc056a0fed3cc0ac1de5b41 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Aug 2015 09:59:33 -0700 Subject: [PATCH 037/117] attempting to stitch polyvox entites together --- .../display-plugins/OpenGLDisplayPlugin.cpp | 43 +++---- .../stereo/StereoDisplayPlugin.cpp | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 115 +++++++++++++++++- .../src/RenderablePolyVoxEntityItem.h | 8 ++ libraries/entities/src/EntityItemID.h | 18 --- .../entities/src/EntityItemProperties.cpp | 27 +++- libraries/entities/src/EntityItemProperties.h | 6 + .../entities/src/EntityItemPropertiesMacros.h | 8 ++ libraries/entities/src/EntityPropertyFlags.h | 12 +- .../entities/src/ParticleEffectEntityItem.cpp | 10 +- libraries/entities/src/PolyVoxEntityItem.cpp | 24 +++- libraries/entities/src/PolyVoxEntityItem.h | 15 +++ .../src/input-plugins/UserInputMapper.cpp | 4 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- .../src/AmbientOcclusionEffect.cpp | 2 +- libraries/shared/src/GLMHelpers.cpp | 18 +-- tests/entities/src/main.cpp | 4 +- tests/gpu-test/src/main.cpp | 4 +- 19 files changed, 249 insertions(+), 76 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index f99d1f2a3b..0ce63e87ac 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -80,32 +80,33 @@ void OpenGLDisplayPlugin::deactivate() { // Pass input events on to the application bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - case QEvent::Wheel: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + case QEvent::Wheel: - case QEvent::TouchBegin: - case QEvent::TouchEnd: - case QEvent::TouchUpdate: + case QEvent::TouchBegin: + case QEvent::TouchEnd: + case QEvent::TouchUpdate: - case QEvent::FocusIn: - case QEvent::FocusOut: + case QEvent::FocusIn: + case QEvent::FocusOut: - case QEvent::KeyPress: - case QEvent::KeyRelease: - case QEvent::ShortcutOverride: + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::ShortcutOverride: - case QEvent::DragEnter: - case QEvent::Drop: + case QEvent::DragEnter: + case QEvent::Drop: - case QEvent::Resize: - if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) { - return true; - } - default: - break; + case QEvent::Resize: + if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) { + return true; + } + break; + default: + break; } return false; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index de34451c83..ae3c1f29e2 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -78,7 +78,7 @@ void StereoDisplayPlugin::activate() { } void StereoDisplayPlugin::updateScreen() { - for (int i = 0; i < (int) _screenActions.size(); ++i) { + for (uint32_t i = 0; i < _screenActions.size(); ++i) { if (_screenActions[i]->isChecked()) { CONTAINER->setFullscreen(qApp->screens().at(i)); break; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 46c4986fa8..5ad23c91be 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -408,14 +408,17 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { + _xTexture.clear(); PolyVoxEntityItem::setXTextureURL(xTextureURL); } void RenderablePolyVoxEntityItem::setYTextureURL(QString yTextureURL) { + _yTexture.clear(); PolyVoxEntityItem::setYTextureURL(yTextureURL); } void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { + _zTexture.clear(); PolyVoxEntityItem::setZTextureURL(zTextureURL); } @@ -598,10 +601,27 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); } else { PolyVox::Vector3DInt32 lowCorner(0, 0, 0); - PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x - 1, // -1 because these corners are inclusive - _voxelVolumeSize.y - 1, - _voxelVolumeSize.z - 1); + PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x, // -1 because these corners are inclusive + _voxelVolumeSize.y, + _voxelVolumeSize.z); _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); + + + // // XXX + // { + // for (int x = 0; x <= _voxelVolumeSize.x; x++) { + // for (int y = 0; y <= _voxelVolumeSize.y; y++) { + // for (int z = 0; z <= _voxelVolumeSize.z; z++) { + // if (x == _voxelVolumeSize.x || + // y == _voxelVolumeSize.y || + // z == _voxelVolumeSize.z) { + // setVoxelInternal(x, y, z, 255); + // } + // } + // } + // } + // } + } // having the "outside of voxel-space" value be 255 has helped me notice some problems. @@ -851,9 +871,98 @@ void RenderablePolyVoxEntityItem::getMesh() { } +void RenderablePolyVoxEntityItem::clearOutOfDateNeighbors() { + if (_xNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentXNeighbor = _xNeighbor.lock(); + if (currentXNeighbor && currentXNeighbor->getID() != _xNeighborID) { + _xNeighbor.reset(); + } + } + if (_yNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentYNeighbor = _yNeighbor.lock(); + if (currentYNeighbor && currentYNeighbor->getID() != _yNeighborID) { + _yNeighbor.reset(); + } + } + if (_zNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentZNeighbor = _zNeighbor.lock(); + if (currentZNeighbor && currentZNeighbor->getID() != _zNeighborID) { + _zNeighbor.reset(); + } + } +} + +void RenderablePolyVoxEntityItem::cacheNeighbors() { + if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_CUBIC && + _voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { + return; + } + + clearOutOfDateNeighbors(); + EntityTreeElement* element = getElement(); + EntityTree* tree = element ? element->getTree() : nullptr; + if (!tree) { + return; + } + + if (_xNeighborID != UNKNOWN_ENTITY_ID && _xNeighbor.expired()) { + _xNeighbor = tree->findEntityByID(_xNeighborID); + } + if (_yNeighborID != UNKNOWN_ENTITY_ID && _yNeighbor.expired()) { + _yNeighbor = tree->findEntityByID(_yNeighborID); + } + if (_zNeighborID != UNKNOWN_ENTITY_ID && _zNeighbor.expired()) { + _zNeighbor = tree->findEntityByID(_zNeighborID); + } +} + +void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { + if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_CUBIC && + _voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { + return; + } + + EntityItemPointer currentXNeighbor = _xNeighbor.lock(); + EntityItemPointer currentYNeighbor = _yNeighbor.lock(); + EntityItemPointer currentZNeighbor = _zNeighbor.lock(); + + if (currentXNeighbor) { + auto polyVoxXNeighbor = std::dynamic_pointer_cast(currentXNeighbor); + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int z = 0; z < _voxelVolumeSize.z; y++) { + uint8_t neighborValue = polyVoxXNeighbor->getVoxel(0, y, z); + _volData->setVoxelAt(_voxelVolumeSize.x, y, z, neighborValue); + } + } + } + + if (currentYNeighbor) { + auto polyVoxYNeighbor = std::dynamic_pointer_cast(currentYNeighbor); + for (int x = 0; x < _voxelVolumeSize.x; x++) { + for (int z = 0; z < _voxelVolumeSize.z; z++) { + uint8_t neighborValue = polyVoxYNeighbor->getVoxel(x, 0, z); + _volData->setVoxelAt(x, _voxelVolumeSize.y, z, neighborValue); + } + } + } + + if (currentZNeighbor) { + auto polyVoxZNeighbor = std::dynamic_pointer_cast(currentZNeighbor); + for (int x = 0; x < _voxelVolumeSize.x; x++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + uint8_t neighborValue = polyVoxZNeighbor->getVoxel(x, y, 0); + _volData->setVoxelAt(x, y, _voxelVolumeSize.z, neighborValue); + } + } + } +} + void RenderablePolyVoxEntityItem::getMeshAsync() { model::MeshPointer mesh(new model::Mesh()); + cacheNeighbors(); + copyUpperEdgesFromNeighbors(); + // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 110e8f8ab4..fa203f5e4d 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -147,6 +147,14 @@ private: void computeShapeInfoWorkerAsync(); QSemaphore _threadRunning{1}; + + // these are cached lookups of _xNeighborID, _yNeighborID, _zNeighborID + EntityItemWeakPointer _xNeighbor; + EntityItemWeakPointer _yNeighbor; + EntityItemWeakPointer _zNeighbor; + void clearOutOfDateNeighbors(); + void cacheNeighbors(); + void copyUpperEdgesFromNeighbors(); }; diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 7f55005a17..765082ef0b 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -32,26 +32,8 @@ public: QScriptValue toScriptValue(QScriptEngine* engine) const; bool isInvalidID() const { return *this == UNKNOWN_ENTITY_ID; } - - // QUuid id; }; -// inline bool operator<(const EntityItemID& a, const EntityItemID& b) { -// return a.id < b.id; -// } - -// inline bool operator==(const EntityItemID& a, const EntityItemID& b) { -// return a.id == b.id; -// } - -// inline bool operator!=(const EntityItemID& a, const EntityItemID& b) { -// return !(a == b); -// } - -// inline uint qHash(const EntityItemID& a, uint seed) { -// return qHash(a.id, seed); -// } - inline QDebug operator<<(QDebug debug, const EntityItemID& id) { debug << "[entity-id:" << id.toString() << "]"; return debug; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c6c02f248c..199defbcb1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -109,6 +109,10 @@ CONSTRUCT_PROPERTY(strokeWidths, QVector()), CONSTRUCT_PROPERTY(xTextureURL, ""), CONSTRUCT_PROPERTY(yTextureURL, ""), CONSTRUCT_PROPERTY(zTextureURL, ""), +CONSTRUCT_PROPERTY(xNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(yNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(zNeighborID, UNKNOWN_ENTITY_ID), + _id(UNKNOWN_ENTITY_ID), _idSet(false), @@ -377,6 +381,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_X_TEXTURE_URL, xTextureURL); CHECK_PROPERTY_CHANGE(PROP_Y_TEXTURE_URL, yTextureURL); CHECK_PROPERTY_CHANGE(PROP_Z_TEXTURE_URL, zTextureURL); + CHECK_PROPERTY_CHANGE(PROP_X_NEIGHBOR_ID, xNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Y_NEIGHBOR_ID, yNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Z_NEIGHBOR_ID, zNeighborID); changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); @@ -521,6 +528,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(yTextureURL); COPY_PROPERTY_TO_QSCRIPTVALUE(zTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(xNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(yNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(zNeighborID); + return properties; } @@ -620,6 +631,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(zTextureURL, QString, setZTextureURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(xNeighborID, EntityItemID, setXNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(yNeighborID, EntityItemID, setYNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(zNeighborID, EntityItemID, setZNeighborID); + _lastEdited = usecTimestampNow(); } @@ -852,6 +867,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, Ent APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, properties.getXTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, properties.getYTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, properties.getZTextureURL()); + APPEND_ENTITY_PROPERTY(PROP_X_NEIGHBOR_ID, properties.getXNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_NEIGHBOR_ID, properties.getYNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_NEIGHBOR_ID, properties.getZNeighborID()); } if (properties.getType() == EntityTypes::Line) { @@ -1115,6 +1133,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_TEXTURE_URL, QString, setXTextureURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_TEXTURE_URL, QString, setYTextureURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_TEXTURE_URL, QString, setZTextureURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_NEIGHBOR_ID, EntityItemID, setXNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_NEIGHBOR_ID, EntityItemID, setYNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_NEIGHBOR_ID, EntityItemID, setZNeighborID); } if (properties.getType() == EntityTypes::Line) { @@ -1248,13 +1269,17 @@ void EntityItemProperties::markAllChanged() { _descriptionChanged = true; _faceCameraChanged = true; _actionDataChanged = true; - + _normalsChanged = true; _strokeWidthsChanged = true; _xTextureURLChanged = true; _yTextureURLChanged = true; _zTextureURLChanged = true; + + _xNeighborIDChanged = true; + _yNeighborIDChanged = true; + _zNeighborIDChanged = true; } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6d95faa9b1..0e0c36ad11 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -161,6 +161,9 @@ public: DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString); DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString); + DEFINE_PROPERTY_REF(PROP_X_NEIGHBOR_ID, XNeighborID, xNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Y_NEIGHBOR_ID, YNeighborID, yNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Z_NEIGHBOR_ID, ZNeighborID, zNeighborID, EntityItemID); static QString getBackgroundModeString(BackgroundMode mode); @@ -327,6 +330,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, XNeighborID, xNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, YNeighborID, yNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZNeighborID, zNeighborID, ""); properties.getStage().debugDump(); properties.getAtmosphere().debugDump(); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index e48be1da8c..a3e31024d1 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -14,6 +14,8 @@ #ifndef hifi_EntityItemPropertiesMacros_h #define hifi_EntityItemPropertiesMacros_h +#include "EntityItemID.h" + #define APPEND_ENTITY_PROPERTY(P,V) \ if (requestedProperties.getHasProperty(P)) { \ LevelDetails propertyLevel = packetData->startLevel(); \ @@ -106,6 +108,9 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { return QScriptValue(QString(b64)); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) { return QScriptValue(QUuid(v).toString()); } + + #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(G,g,P,p) \ if (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P()) { \ QScriptValue groupProperties = properties.property(#g); \ @@ -143,6 +148,9 @@ inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { re inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); } inline QString QString_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toString().trimmed(); } inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } +inline EntityItemID EntityItemID_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } + + inline QDateTime QDateTime_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index abb8241d8f..9ad34fa69d 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -124,21 +124,25 @@ enum EntityPropertyList { PROP_FACE_CAMERA, PROP_SCRIPT_TIMESTAMP, - + PROP_ACTION_DATA, - + PROP_X_TEXTURE_URL, // used by PolyVox PROP_Y_TEXTURE_URL, // used by PolyVox PROP_Z_TEXTURE_URL, // used by PolyVox - + // Used by PolyLine entity PROP_NORMALS, PROP_STROKE_WIDTHS, - + // used by particles PROP_VELOCITY_SPREAD, PROP_ACCELERATION_SPREAD, + PROP_X_NEIGHBOR_ID, // used by PolyVox + PROP_Y_NEIGHBOR_ID, // used by PolyVox + PROP_Z_NEIGHBOR_ID, // used by PolyVox + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 9e45efe88d..7dab825adc 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -131,20 +131,20 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { float maxVelocityX = fabsf(_velocity.x) + _velocitySpread.x; float maxAccelerationX = fabsf(_acceleration.x) + _accelerationSpread.x; - float maxXDistance = (maxVelocityX * time) + (0.5 * maxAccelerationX * time * time); + float maxXDistance = (maxVelocityX * time) + (0.5f * maxAccelerationX * time * time); - float maxVelocityY = fabs(_velocity.y) + _velocitySpread.y; + float maxVelocityY = fabsf(_velocity.y) + _velocitySpread.y; float maxAccelerationY = fabsf(_acceleration.y) + _accelerationSpread.y; - float maxYDistance = (maxVelocityY * time) + (0.5 * maxAccelerationY * time * time); + float maxYDistance = (maxVelocityY * time) + (0.5f * maxAccelerationY * time * time); float maxVelocityZ = fabsf(_velocity.z) + _velocitySpread.z; float maxAccelerationZ = fabsf(_acceleration.z) + _accelerationSpread.z; - float maxZDistance = (maxVelocityZ * time) + (0.5 * maxAccelerationZ * time * time); + float maxZDistance = (maxVelocityZ * time) + (0.5f * maxAccelerationZ * time * time); float maxDistance = std::max(maxXDistance, std::max(maxYDistance, maxZDistance)); //times 2 because dimensions are diameters not radii - glm::vec3 dims(2.0 * maxDistance); + glm::vec3 dims(2.0f * maxDistance); EntityItem::setDimensions(dims); } diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index e765afd430..dbf0f67288 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -56,7 +56,10 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent _voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE), _xTextureURL(PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL), _yTextureURL(PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL), - _zTextureURL(PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL) + _zTextureURL(PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL), + _xNeighborID(UNKNOWN_ENTITY_ID), + _yNeighborID(UNKNOWN_ENTITY_ID), + _zNeighborID(UNKNOWN_ENTITY_ID) { _type = EntityTypes::PolyVox; setProperties(properties); @@ -104,6 +107,9 @@ EntityItemProperties PolyVoxEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(xTextureURL, getXTextureURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(yTextureURL, getYTextureURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(zTextureURL, getZTextureURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(xNeighborID, getXNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(yNeighborID, getYNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(zNeighborID, getZNeighborID); return properties; } @@ -116,6 +122,9 @@ bool PolyVoxEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(xTextureURL, setXTextureURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(yTextureURL, setYTextureURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(zTextureURL, setZTextureURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(xNeighborID, setXNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(yNeighborID, setYNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(zNeighborID, setZNeighborID); if (somethingChanged) { bool wantDebug = false; @@ -143,6 +152,9 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat READ_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, QString, setXTextureURL); READ_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, QString, setYTextureURL); READ_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, QString, setZTextureURL); + READ_ENTITY_PROPERTY(PROP_X_NEIGHBOR_ID, EntityItemID, setXNeighborID); + READ_ENTITY_PROPERTY(PROP_Y_NEIGHBOR_ID, EntityItemID, setYNeighborID); + READ_ENTITY_PROPERTY(PROP_Z_NEIGHBOR_ID, EntityItemID, setZNeighborID); return bytesRead; } @@ -160,13 +172,13 @@ EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams return requestedProperties; } -void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, +void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { + int& propertyCount, + OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, getVoxelVolumeSize()); @@ -175,7 +187,9 @@ void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeB APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, getXTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, getYTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, getZTextureURL()); - + APPEND_ENTITY_PROPERTY(PROP_X_NEIGHBOR_ID, getXNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_NEIGHBOR_ID, getYNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_NEIGHBOR_ID, getZNeighborID()); } void PolyVoxEntityItem::debugDump() const { diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index c84dc9f4c1..1e22327df0 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -103,6 +103,17 @@ class PolyVoxEntityItem : public EntityItem { virtual void setZTextureURL(QString zTextureURL) { _zTextureURL = zTextureURL; } virtual const QString& getZTextureURL() const { return _zTextureURL; } + virtual void setXNeighborID(const EntityItemID& xNeighborID) { _xNeighborID = xNeighborID; } + virtual void setXNeighborID(const QString& xNeighborID) { _xNeighborID = QUuid(xNeighborID); } + virtual const EntityItemID& getXNeighborID() const { return _xNeighborID; } + virtual void setYNeighborID(const EntityItemID& yNeighborID) { _yNeighborID = yNeighborID; } + virtual void setYNeighborID(const QString& yNeighborID) { _yNeighborID = QUuid(yNeighborID); } + virtual const EntityItemID& getYNeighborID() const { return _yNeighborID; } + virtual void setZNeighborID(const EntityItemID& zNeighborID) { _zNeighborID = zNeighborID; } + virtual void setZNeighborID(const QString& zNeighborID) { _zNeighborID = QUuid(zNeighborID); } + virtual const EntityItemID& getZNeighborID() const { return _zNeighborID; } + + protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes @@ -116,6 +127,10 @@ class PolyVoxEntityItem : public EntityItem { QString _yTextureURL; QString _zTextureURL; + // for non-edged surface styles, these are used to compute the high-axis edges + EntityItemID _xNeighborID; + EntityItemID _yNeighborID; + EntityItemID _zNeighborID; }; #endif // hifi_PolyVoxEntityItem_h diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index fabb488ab4..1b9b87684c 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -230,7 +230,7 @@ void UserInputMapper::update(float deltaTime) { for (auto i = 0; i < NUM_ACTIONS; i++) { _actionStates[i] *= _actionScales[i]; // Emit only on change, and emit when moving back to 0 - if (fabs(_actionStates[i] - _lastActionStates[i]) > EPSILON) { + if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { _lastActionStates[i] = _actionStates[i]; emit actionEvent(i, _actionStates[i]); } @@ -319,4 +319,4 @@ void UserInputMapper::createActionNames() { _actionNames[SHIFT] = "SHIFT"; _actionNames[ACTION1] = "ACTION1"; _actionNames[ACTION2] = "ACTION2"; -} \ No newline at end of file +} diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 7e73b4c660..737e827c90 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityAdd: case EntityEdit: case EntityData: - return VERSION_ENTITIES_PARTICLE_MODIFICATIONS; + return VERSION_ENTITIES_POLYVOX_NEIGHBORS; case AvatarData: return 12; default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index fa6178b627..d22ee59cad 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -144,5 +144,6 @@ const PacketVersion VERSION_POLYVOX_TEXTURES = 36; const PacketVersion VERSION_ENTITIES_POLYLINE = 37; const PacketVersion VERSION_OCTREE_CENTERED_ORIGIN = 38; const PacketVersion VERSION_ENTITIES_PARTICLE_MODIFICATIONS = 39; +const PacketVersion VERSION_ENTITIES_POLYVOX_NEIGHBORS = 40; -#endif // hifi_PacketHeaders_h \ No newline at end of file +#endif // hifi_PacketHeaders_h diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 64f3bb6708..ebd2053442 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -243,7 +243,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT); batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight); - batch._glUniform2f(_renderTargetResInvLoc, 1.0/fbWidth, 1.0/fbHeight); + batch._glUniform2f(_renderTargetResInvLoc, 1.0f / fbWidth, 1.0f / fbHeight); glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); glm::vec2 bottomLeft(-1.0f, -1.0f); diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 86707d9dcd..4ca8ed330b 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -372,20 +372,20 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { // create matrix from orientation and position glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p) { glm::mat4 m = glm::mat4_cast(q); - m[3] = glm::vec4(p, 1); + m[3] = glm::vec4(p, 1.0f); return m; } // cancel out roll and pitch glm::quat cancelOutRollAndPitch(const glm::quat& q) { - glm::vec3 zAxis = q * glm::vec3(0, 0, 1); + glm::vec3 zAxis = q * glm::vec3(0.0f, 0.0f, 1.0f); // cancel out the roll and pitch - glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z)); - glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ); + glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z)); + glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ); glm::vec3 newY = glm::cross(newZ, newX); - glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), glm::vec4(0, 0, 0, 1)); + glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); return glm::quat_cast(temp); } @@ -394,15 +394,15 @@ glm::mat4 cancelOutRollAndPitch(const glm::mat4& m) { glm::vec3 zAxis = glm::vec3(m[2]); // cancel out the roll and pitch - glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z)); - glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ); + glm::vec3 newZ = (zAxis.x == 0.0f && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z)); + glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ); glm::vec3 newY = glm::cross(newZ, newX); - glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), m[3]); + glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), m[3]); return temp; } glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p) { - glm::vec4 temp = m * glm::vec4(p, 1); + glm::vec4 temp = m * glm::vec4(p, 1.0f); return glm::vec3(temp.x / temp.w, temp.y / temp.w, temp.z / temp.w); } diff --git a/tests/entities/src/main.cpp b/tests/entities/src/main.cpp index 740d401107..a94bda9a86 100644 --- a/tests/entities/src/main.cpp +++ b/tests/entities/src/main.cpp @@ -93,8 +93,8 @@ template void testByteCountCoded() { testByteCountCodedStable(0); testByteCountCodedStable(1); - testByteCountCodedStable(1 << 16); - testByteCountCodedStable(std::numeric_limits::max() >> 16); + testByteCountCodedStable(1 << 8*sizeof(T)); + testByteCountCodedStable(std::numeric_limits::max() >> 8*sizeof(T)); testByteCountCodedStable(std::numeric_limits::max() >> 8); testByteCountCodedStable(std::numeric_limits::max() >> 1); testByteCountCodedStable(std::numeric_limits::max()); diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 758d9b29bb..b27d10e312 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -342,8 +342,8 @@ public: glm::vec3 unitscale { 1.0f }; glm::vec3 up { 0.0f, 1.0f, 0.0f }; - glm::vec3 cam_pos { 1.5f * sin(t), 0.0f, 2.0f }; -// glm::vec3 camera_focus { 5.0f * cos(t * 0.1f), 0.0f, 0.0f }; + glm::vec3 cam_pos { 1.5f * sinf(t), 0.0f, 2.0f }; +// glm::vec3 camera_focus { 5.0f * cosf(t * 0.1f), 0.0f, 0.0f }; glm::vec3 camera_focus { 0.0f, 0.0f, 0.0f }; glm::quat cam_rotation; // glm::quat cam_rotation = glm::quat_cast(glm::lookAt(cam_pos, camera_focus, up)); From 41a2ff2fff61dc324daf9f67659a440dc3339641 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Aug 2015 18:42:15 -0700 Subject: [PATCH 038/117] working on stitching marching-cube polyvoxs together --- examples/voxels.js | 109 +++++++--- .../src/RenderablePolyVoxEntityItem.cpp | 186 +++++++++--------- libraries/entities/src/PolyVoxEntityItem.cpp | 9 +- libraries/entities/src/PolyVoxEntityItem.h | 6 +- 4 files changed, 183 insertions(+), 127 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index d9049e2b0c..1859ac8a8b 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -155,48 +155,111 @@ var toolBar = (function () { }()); + +function getTerrainAlignedLocation(pos) { + var posDiv16 = Vec3.multiply(pos, 1.0 / 16.0); + var posDiv16Floored = floorVector(posDiv16); + return Vec3.multiply(posDiv16Floored, 16.0); +} + + +function lookupTerrainForLocation(pos) { + var baseLocation = getTerrainAlignedLocation(pos); + entitiesAtLoc = Entities.findEntities(baseLocation, 1.0); + for (var i = 0; i < entitiesAtLoc.length; i++) { + var id = entitiesAtLoc[i]; + var properties = Entities.getEntityProperties(id); + if (properties.name == "terrain") { + return id; + } + } + + return false; +} + + function addTerrainBlock() { - - var myPosDiv16 = Vec3.multiply(Vec3.sum(MyAvatar.position, {x:8, x:8, z:8}), 1.0 / 16.0); - var myPosDiv16Floored = floorVector(myPosDiv16); - var baseLocation = Vec3.multiply(myPosDiv16Floored, 16.0); - - if (baseLocation.y + 8 > MyAvatar.position.y) { + var baseLocation = getTerrainAlignedLocation(Vec3.sum(MyAvatar.position, {x:8, y:8, z:8})); + if (baseLocation.y > MyAvatar.position.y) { baseLocation.y -= 16; } - print("myPosDiv16 is " + vectorToString(myPosDiv16)); - print("MyPosDiv16Floored is " + vectorToString(myPosDiv16Floored)); - print("baseLocation is " + vectorToString(baseLocation)); - - alreadyThere = Entities.findEntities(baseLocation, 1.0); - for (var i = 0; i < alreadyThere.length; i++) { - var id = alreadyThere[i]; - var properties = Entities.getEntityProperties(id); - if (properties.name == "terrain") { - print("already terrain there"); - return; - } + var alreadyThere = lookupTerrainForLocation(baseLocation); + if (alreadyThere) { + return; } var polyVoxId = Entities.addEntity({ type: "PolyVox", name: "terrain", position: baseLocation, - dimensions: { x: 16, y: 16, z: 16 }, - voxelVolumeSize: {x:16, y:16, z:16}, - voxelSurfaceStyle: 2 + dimensions: { x:16, y:16, z:16 }, + voxelVolumeSize: {x:16, y:32, z:16}, + voxelSurfaceStyle: 0, + xTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg", + yTextureURL: "http://headache.hungry.com/~seth/hifi/grass.png", + zTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg" }); Entities.setAllVoxels(polyVoxId, 255); - for (var y = 8; y < 16; y++) { + // XXX use setCuboid + for (var y = 16; y < 32; y++) { for (var x = 0; x < 16; x++) { for (var z = 0; z < 16; z++) { - Entities.setVoxel(polyVoxId, {x: x, y: y, z: z}, 0); + Entities.setVoxel(polyVoxId, {x:x, y:y, z:z}, 0); } } } + ////////// + // stitch together the terrain with x/y/z NeighorID properties + ////////// + + // link plots which are lower on the axes to this one + imXNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:-16, y:0, z:0})); + if (imXNeighborFor) { + var properties = Entities.getEntityProperties(imXNeighborFor); + properties.xNeighborID = polyVoxId; + Entities.editEntity(imXNeighborFor, properties); + } + + imYNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:-16, z:0})); + if (imYNeighborFor) { + var properties = Entities.getEntityProperties(imYNeighborFor); + properties.yNeighborID = polyVoxId; + Entities.editEntity(imYNeighborFor, properties); + } + + imZNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:-16})); + if (imZNeighborFor) { + var properties = Entities.getEntityProperties(imZNeighborFor); + properties.zNeighborID = polyVoxId; + Entities.editEntity(imZNeighborFor, properties); + } + + + // link this plot to plots which are higher on the axes + xNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:16, y:0, z:0})); + if (xNeighborFor) { + var properties = Entities.getEntityProperties(polyVoxId); + properties.xNeighborID = xNeighborFor; + Entities.editEntity(polyVoxId, properties); + } + + yNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:16, z:0})); + if (yNeighborFor) { + var properties = Entities.getEntityProperties(polyVoxId); + properties.yNeighborID = yNeighborFor; + Entities.editEntity(polyVoxId, properties); + } + + zNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); + if (zNeighborFor) { + var properties = Entities.getEntityProperties(polyVoxId); + properties.zNeighborID = zNeighborFor; + Entities.editEntity(polyVoxId, properties); + } + return true; } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 5ad23c91be..7ec9357dda 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -68,6 +68,19 @@ RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { } +bool isEdged(PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle) { + switch (surfaceStyle) { + case PolyVoxEntityItem::SURFACE_CUBIC: + return true; + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + return true; + } + return false; +} + + void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { _voxelDataLock.lockForWrite(); if (_voxelData == voxelData) { @@ -88,10 +101,8 @@ void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxel } // if we are switching to or from "edged" we need to force a resize of _volData. - bool wasEdged = (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); - bool willBeEdged = (voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); + bool wasEdged = isEdged(_voxelSurfaceStyle); + bool willBeEdged = isEdged(voxelSurfaceStyle); if (wasEdged != willBeEdged) { _volDataLock.lockForWrite(); @@ -113,15 +124,10 @@ void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxel glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const { glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_CUBIC: - return scale / 2.0f; - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - return scale / -2.0f; + if (isEdged(_voxelSurfaceStyle)) { + return scale / -2.0f; } - return glm::vec3(0.0f, 0.0f, 0.0f); + return scale / 2.0f; } @@ -429,9 +435,12 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { _volDataLock.lockForRead(); if (_volDataDirty) { + _volDataLock.unlock(); getMesh(); + } else { + _volDataLock.unlock(); } - _volDataLock.unlock(); + _meshLock.lockForRead(); model::MeshPointer mesh = _mesh; @@ -551,18 +560,10 @@ glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxel glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { glm::vec3 result = glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f)); - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_CUBIC: - result += glm::vec3(0.5f, 0.5f, 0.5f); - break; - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - result -= glm::vec3(0.5f, 0.5f, 0.5f); - break; + if (isEdged(_voxelSurfaceStyle)) { + return result - glm::vec3(0.5f, 0.5f, 0.5f); } - - return result; + return result + glm::vec3(0.5f, 0.5f, 0.5f); } glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const { @@ -588,8 +589,7 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) } _onCount = 0; - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + if (isEdged(_voxelSurfaceStyle)) { // with _EDGED_ we maintain an extra box of voxels around those that the user asked for. This // changes how the surface extractor acts -- mainly it becomes impossible to have holes in the // generated mesh. The non _EDGED_ modes will leave holes in the mesh at the edges of the @@ -601,27 +601,12 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); } else { PolyVox::Vector3DInt32 lowCorner(0, 0, 0); - PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x, // -1 because these corners are inclusive + // these should each have -1 after them, but if we leave layers on the upper-axis faces, + // they act more like I expect. + PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z); _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); - - - // // XXX - // { - // for (int x = 0; x <= _voxelVolumeSize.x; x++) { - // for (int y = 0; y <= _voxelVolumeSize.y; y++) { - // for (int z = 0; z <= _voxelVolumeSize.z; z++) { - // if (x == _voxelVolumeSize.x || - // y == _voxelVolumeSize.y || - // z == _voxelVolumeSize.z) { - // setVoxelInternal(x, y, z, 255); - // } - // } - // } - // } - // } - } // having the "outside of voxel-space" value be 255 has helped me notice some problems. @@ -635,25 +620,19 @@ bool RenderablePolyVoxEntityItem::inUserBounds(const PolyVox::SimpleVolume= vol->getWidth() || y >= vol->getHeight() || z >= vol->getDepth()) { - return false; - } - return true; - - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - if (x < 0 || y < 0 || z < 0 || - x >= vol->getWidth() - 2 || y >= vol->getHeight() - 2 || z >= vol->getDepth() - 2) { - return false; - } - return true; + if (isEdged(surfaceStyle)) { + if (x < 0 || y < 0 || z < 0 || + x >= vol->getWidth() - 2 || y >= vol->getHeight() - 2 || z >= vol->getDepth() - 2) { + return false; + } + return true; + } else { + if (x < 0 || y < 0 || z < 0 || + x >= vol->getWidth() || y >= vol->getHeight() || z >= vol->getDepth()) { + return false; + } + return true; } - - return false; } @@ -670,19 +649,13 @@ uint8_t RenderablePolyVoxEntityItem::getVoxelInternal(int x, int y, int z) { return 0; } - // if _voxelSurfaceStyle is SURFACE_EDGED_CUBIC, we maintain an extra layer of + // if _voxelSurfaceStyle is *_EDGED_*, we maintain an extra layer of // voxels all around the requested voxel space. Having the empty voxels around // the edges changes how the surface extractor behaves. - - uint8_t result; - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { - result = _volData->getVoxelAt(x + 1, y + 1, z + 1); - } else { - result = _volData->getVoxelAt(x, y, z); + if (isEdged(_voxelSurfaceStyle)) { + return _volData->getVoxelAt(x + 1, y + 1, z + 1); } - - return result; + return _volData->getVoxelAt(x, y, z); } @@ -695,9 +668,7 @@ bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t result = updateOnCount(x, y, z, toValue); - assert(_volData); - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + if (isEdged(_voxelSurfaceStyle)) { _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); } else { _volData->setVoxelAt(x, y, z, toValue); @@ -893,8 +864,7 @@ void RenderablePolyVoxEntityItem::clearOutOfDateNeighbors() { } void RenderablePolyVoxEntityItem::cacheNeighbors() { - if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_CUBIC && - _voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { + if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { return; } @@ -917,8 +887,7 @@ void RenderablePolyVoxEntityItem::cacheNeighbors() { } void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { - if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_CUBIC && - _voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { + if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { return; } @@ -927,36 +896,51 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { EntityItemPointer currentZNeighbor = _zNeighbor.lock(); if (currentXNeighbor) { - auto polyVoxXNeighbor = std::dynamic_pointer_cast(currentXNeighbor); - for (int y = 0; y < _voxelVolumeSize.y; y++) { - for (int z = 0; z < _voxelVolumeSize.z; y++) { - uint8_t neighborValue = polyVoxXNeighbor->getVoxel(0, y, z); - _volData->setVoxelAt(_voxelVolumeSize.x, y, z, neighborValue); + auto polyVoxXNeighbor = std::dynamic_pointer_cast(currentXNeighbor); + if (polyVoxXNeighbor->_volData->getEnclosingRegion() == _volData->getEnclosingRegion()) { + for (int y = 0; y < _volData->getHeight(); y++) { + for (int z = 0; z < _volData->getDepth(); z++) { + uint8_t neighborValue = polyVoxXNeighbor->_volData->getVoxelAt(1, y, z); + _volData->setVoxelAt(_volData->getWidth() - 1, y, z, neighborValue); + } } } } if (currentYNeighbor) { - auto polyVoxYNeighbor = std::dynamic_pointer_cast(currentYNeighbor); - for (int x = 0; x < _voxelVolumeSize.x; x++) { - for (int z = 0; z < _voxelVolumeSize.z; z++) { - uint8_t neighborValue = polyVoxYNeighbor->getVoxel(x, 0, z); - _volData->setVoxelAt(x, _voxelVolumeSize.y, z, neighborValue); + auto polyVoxYNeighbor = std::dynamic_pointer_cast(currentYNeighbor); + if (polyVoxYNeighbor->_volData->getEnclosingRegion() == _volData->getEnclosingRegion()) { + for (int x = 0; x < _volData->getWidth(); x++) { + for (int z = 0; z < _volData->getDepth(); z++) { + uint8_t neighborValue = polyVoxYNeighbor->_volData->getVoxelAt(x, 1, z); + _volData->setVoxelAt(x, _volData->getWidth() - 1, z, neighborValue); + } } } } if (currentZNeighbor) { - auto polyVoxZNeighbor = std::dynamic_pointer_cast(currentZNeighbor); - for (int x = 0; x < _voxelVolumeSize.x; x++) { - for (int y = 0; y < _voxelVolumeSize.y; y++) { - uint8_t neighborValue = polyVoxZNeighbor->getVoxel(x, y, 0); - _volData->setVoxelAt(x, y, _voxelVolumeSize.z, neighborValue); + auto polyVoxZNeighbor = std::dynamic_pointer_cast(currentZNeighbor); + if (polyVoxZNeighbor->_volData->getEnclosingRegion() == _volData->getEnclosingRegion()) { + for (int x = 0; x < _volData->getWidth(); x++) { + for (int y = 0; y < _volData->getHeight(); y++) { + uint8_t neighborValue = polyVoxZNeighbor->_volData->getVoxelAt(x, y, 1); + _volData->setVoxelAt(x, y, _volData->getDepth() - 1, neighborValue); + } } } } } + +// PolyVox::Region shrinkRegion(PolyVox::Region originalRegion) { +// PolyVox::Region smallerRegion = originalRegion; +// smallerRegion.shiftLowerCorner(PolyVox::Vector3DInt32(1, 1, 1)); +// smallerRegion.shiftUpperCorner(PolyVox::Vector3DInt32(-1, -1, -1)); +// return smallerRegion; +// } + + void RenderablePolyVoxEntityItem::getMeshAsync() { model::MeshPointer mesh(new model::Mesh()); @@ -968,14 +952,24 @@ void RenderablePolyVoxEntityItem::getMeshAsync() { _volDataLock.lockForRead(); switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: { + PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); surfaceExtractor.execute(); break; } - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: { + PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } case PolyVoxEntityItem::SURFACE_CUBIC: { PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index dbf0f67288..0ce71a37ad 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -56,11 +56,7 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent _voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE), _xTextureURL(PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL), _yTextureURL(PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL), - _zTextureURL(PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL), - _xNeighborID(UNKNOWN_ENTITY_ID), - _yNeighborID(UNKNOWN_ENTITY_ID), - _zNeighborID(UNKNOWN_ENTITY_ID) -{ + _zTextureURL(PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL) { _type = EntityTypes::PolyVox; setProperties(properties); } @@ -169,6 +165,9 @@ EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams requestedProperties += PROP_X_TEXTURE_URL; requestedProperties += PROP_Y_TEXTURE_URL; requestedProperties += PROP_Z_TEXTURE_URL; + requestedProperties += PROP_X_NEIGHBOR_ID; + requestedProperties += PROP_Y_NEIGHBOR_ID; + requestedProperties += PROP_Z_NEIGHBOR_ID; return requestedProperties; } diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 1e22327df0..886a9c37c7 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -128,9 +128,9 @@ class PolyVoxEntityItem : public EntityItem { QString _zTextureURL; // for non-edged surface styles, these are used to compute the high-axis edges - EntityItemID _xNeighborID; - EntityItemID _yNeighborID; - EntityItemID _zNeighborID; + EntityItemID _xNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _yNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _zNeighborID{UNKNOWN_ENTITY_ID}; }; #endif // hifi_PolyVoxEntityItem_h From 59a758b5ecdb9c71841b6de34585c89d2bee5dde Mon Sep 17 00:00:00 2001 From: Raffi Bedikian Date: Sun, 30 Aug 2015 20:38:05 -0700 Subject: [PATCH 039/117] Add AA state to RenderContext and Menu --- interface/src/Application.cpp | 1 + interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + libraries/render/src/render/Engine.h | 1 + 4 files changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c227e5e1c9..88ebf25da8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3542,6 +3542,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect(); renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion); + renderContext._fxaaStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing); renderArgs->_shouldRender = LODManager::shouldRender; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4696463181..a93f5e0425 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -317,6 +317,7 @@ Menu::Menu() { 0, // QML Qt::SHIFT | Qt::Key_A, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing); MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight); QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 94e49abcc7..12aee08994 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -134,6 +134,7 @@ namespace MenuOption { const QString AlignForearmsWithWrists = "Align Forearms with Wrists"; const QString AlternateIK = "Alternate IK"; const QString Animations = "Animations..."; + const QString Antialiasing = "Antialiasing"; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; const QString AudioNoiseReduction = "Audio Noise Reduction"; diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 8d096c5e15..5da7956b22 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -53,6 +53,7 @@ public: bool _drawHitEffect = false; bool _occlusionStatus = false; + bool _fxaaStatus = false; RenderContext() {} }; From 1001cf9000178e2117bf5372db47054c002f0f7b Mon Sep 17 00:00:00 2001 From: Raffi Bedikian Date: Sun, 30 Aug 2015 20:48:06 -0700 Subject: [PATCH 040/117] Add shader files for FXAA --- libraries/render-utils/src/fxaa.slf | 77 +++++++++++++++++++++++ libraries/render-utils/src/fxaa.slv | 26 ++++++++ libraries/render-utils/src/fxaa_blend.slf | 24 +++++++ 3 files changed, 127 insertions(+) create mode 100644 libraries/render-utils/src/fxaa.slf create mode 100644 libraries/render-utils/src/fxaa.slv create mode 100644 libraries/render-utils/src/fxaa_blend.slf diff --git a/libraries/render-utils/src/fxaa.slf b/libraries/render-utils/src/fxaa.slf new file mode 100644 index 0000000000..1f10756ce6 --- /dev/null +++ b/libraries/render-utils/src/fxaa.slf @@ -0,0 +1,77 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// fxaa.frag +// fragment shader +// +// Created by Raffi Bedikian on 8/30/15 +// 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 +// + +// FXAA shader, GLSL code adapted from: +// http://horde3d.org/wiki/index.php5?title=Shading_Technique_-_FXAA +// Whitepaper describing the technique: +// http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D colorTexture; +uniform vec2 texcoordOffset; + +in vec2 varTexcoord; +out vec4 outFragColor; + +void main() { + float FXAA_SPAN_MAX = 8.0; + float FXAA_REDUCE_MUL = 1.0/8.0; + float FXAA_REDUCE_MIN = (1.0/128.0); + + vec3 rgbNW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, -1.0) * texcoordOffset)).xyz; + vec3 rgbNE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, -1.0) * texcoordOffset)).xyz; + vec3 rgbSW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, +1.0) * texcoordOffset)).xyz; + vec3 rgbSE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, +1.0) * texcoordOffset)).xyz; + vec3 rgbM = texture2D(colorTexture, varTexcoord).xyz; + + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot( rgbM, luma); + + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + + float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texcoordOffset; + + vec3 rgbA = (1.0/2.0) * ( + texture2D(colorTexture, varTexcoord + dir * (1.0/3.0 - 0.5)).xyz + + texture2D(colorTexture, varTexcoord + dir * (2.0/3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * ( + texture2D(colorTexture, varTexcoord + dir * (0.0/3.0 - 0.5)).xyz + + texture2D(colorTexture, varTexcoord + dir * (3.0/3.0 - 0.5)).xyz); + float lumaB = dot(rgbB, luma); + + if (lumaB < lumaMin || lumaB > lumaMax) { + outFragColor.xyz=rgbA; + } else { + outFragColor.xyz=rgbB; + } + outFragColor.a = 1.0; +} diff --git a/libraries/render-utils/src/fxaa.slv b/libraries/render-utils/src/fxaa.slv new file mode 100644 index 0000000000..35a96ceb24 --- /dev/null +++ b/libraries/render-utils/src/fxaa.slv @@ -0,0 +1,26 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// fxaa.vert +// vertex shader +// +// Created by Raffi Bedikian on 8/30/15 +// 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 gpu/Inputs.slh@> + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +out vec2 varTexcoord; + +void main(void) { + varTexcoord = inTexCoord0.xy; + gl_Position = inPosition; +} diff --git a/libraries/render-utils/src/fxaa_blend.slf b/libraries/render-utils/src/fxaa_blend.slf new file mode 100644 index 0000000000..d5819cc9a6 --- /dev/null +++ b/libraries/render-utils/src/fxaa_blend.slf @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// fxaa_blend.frag +// fragment shader +// +// Created by Raffi Bedikian on 8/30/15 +// 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 DeferredBufferWrite.slh@> + +in vec2 varTexcoord; +out vec4 outFragColor; + +uniform sampler2D colorTexture; + +void main(void) { + outFragColor = texture(colorTexture, varTexcoord); +} From ff2a58b3eb622e3b6d84c4e1513b87e1e696dade Mon Sep 17 00:00:00 2001 From: Raffi Bedikian Date: Sun, 30 Aug 2015 20:49:03 -0700 Subject: [PATCH 041/117] Add FXAA effect class, modeled after AO effect --- .../render-utils/src/AntialiasingEffect.cpp | 151 ++++++++++++++++++ .../render-utils/src/AntialiasingEffect.h | 44 +++++ .../render-utils/src/RenderDeferredTask.cpp | 8 + .../render-utils/src/RenderDeferredTask.h | 5 + 4 files changed, 208 insertions(+) create mode 100644 libraries/render-utils/src/AntialiasingEffect.cpp create mode 100644 libraries/render-utils/src/AntialiasingEffect.h diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp new file mode 100644 index 0000000000..4e1b6f451c --- /dev/null +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -0,0 +1,151 @@ +// +// AntialiasingEffect.cpp +// libraries/render-utils/src/ +// +// Created by Raffi Bedikian on 8/30/15 +// 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 + +#include +#include +#include + +#include "gpu/StandardShaderLib.h" +#include "AntialiasingEffect.h" +#include "TextureCache.h" +#include "FramebufferCache.h" +#include "DependencyManager.h" +#include "ViewFrustum.h" +#include "GeometryCache.h" + +#include "fxaa_vert.h" +#include "fxaa_frag.h" +#include "fxaa_blend_frag.h" + + +Antialiasing::Antialiasing() { +} + +const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { + if (!_antialiasingPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0)); + + gpu::Shader::makeProgram(*program, slotBindings); + + _texcoordOffsetLoc = program->getUniforms().findLocation("texcoordOffset"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setDepthTest(false, false, gpu::LESS_EQUAL); + + // Link the antialiasing FBO to texture + _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, + DependencyManager::get()->getFrameBufferSize().width(), DependencyManager::get()->getFrameBufferSize().height())); + auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + auto width = _antialiasingBuffer->getWidth(); + auto height = _antialiasingBuffer->getHeight(); + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); + _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); + + // Good to go add the brand new pipeline + _antialiasingPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _antialiasingPipeline; +} + +const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { + if (!_blendPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_blend_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0)); + + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setDepthTest(false, false, gpu::LESS_EQUAL); + + // Good to go add the brand new pipeline + _blendPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _blendPipeline; +} + +void Antialiasing::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + gpu::Batch batch; + RenderArgs* args = renderContext->args; + + auto framebufferCache = DependencyManager::get(); + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + float fbWidth = framebufferSize.width(); + float fbHeight = framebufferSize.height(); + float sMin = args->_viewport.x / fbWidth; + float sWidth = args->_viewport.z / fbWidth; + float tMin = args->_viewport.y / fbHeight; + float tHeight = args->_viewport.w / fbHeight; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // FXAA step + getAntialiasingPipeline(); + batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); + _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); + batch.setFramebuffer(_antialiasingBuffer); + batch.setPipeline(getAntialiasingPipeline()); + + // initialize the view-space unpacking uniforms using frustum data + float left, right, bottom, top, nearVal, farVal; + glm::vec4 nearClipPlane, farClipPlane; + + args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + + float depthScale = (farVal - nearVal) / farVal; + float nearScale = -1.0f / nearVal; + float depthTexCoordScaleS = (right - left) * nearScale / sWidth; + float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight; + float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS; + float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT; + + batch._glUniform2f(_texcoordOffsetLoc, 1.0 / fbWidth, 1.0 / fbHeight); + + glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); + glm::vec2 bottomLeft(-1.0f, -1.0f); + glm::vec2 topRight(1.0f, 1.0f); + glm::vec2 texCoordTopLeft(0.0f, 0.0f); + glm::vec2 texCoordBottomRight(1.0f, 1.0f); + DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); + + // Blend step + getBlendPipeline(); + batch.setResourceTexture(0, _antialiasingTexture); + batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); + batch.setPipeline(getBlendPipeline()); + + DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); + + // Ready to render + args->_context->render((batch)); +} diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h new file mode 100644 index 0000000000..96cf3aaf3e --- /dev/null +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -0,0 +1,44 @@ +// +// AntialiasingEffect.h +// libraries/render-utils/src/ +// +// Created by Raffi Bedikian on 8/30/15 +// 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_AntialiasingEffect_h +#define hifi_AntialiasingEffect_h + +#include + +#include "render/DrawTask.h" + +class Antialiasing { +public: + + Antialiasing(); + + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + typedef render::Job::Model JobModel; + + const gpu::PipelinePointer& getAntialiasingPipeline(); + const gpu::PipelinePointer& getBlendPipeline(); + +private: + + // Uniforms for AA + gpu::int32 _texcoordOffsetLoc; + + gpu::FramebufferPointer _antialiasingBuffer; + + gpu::TexturePointer _antialiasingTexture; + + gpu::PipelinePointer _antialiasingPipeline; + gpu::PipelinePointer _blendPipeline; + +}; + +#endif // hifi_AntialiasingEffect_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index ca3f87f53f..137294c5b6 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -23,6 +23,7 @@ #include "render/DrawStatus.h" #include "AmbientOcclusionEffect.h" +#include "AntialiasingEffect.h" #include "overlay3D_vert.h" #include "overlay3D_frag.h" @@ -88,6 +89,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.back().setEnabled(false); _occlusionJobIndex = _jobs.size() - 1; + _jobs.push_back(Job(new Antialiasing::JobModel("Antialiasing"))); + + _jobs.back().setEnabled(false); + _antialiasingJobIndex = _jobs.size() - 1; + _jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent", FetchItems( ItemFilter::Builder::transparentShape().withoutLayered(), @@ -146,6 +152,8 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend // TODO: turn on/off AO through menu item setOcclusionStatus(renderContext->_occlusionStatus); + setAntialiasingStatus(renderContext->_fxaaStatus); + renderContext->args->_context->syncCache(); for (auto job : _jobs) { diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 0041f5d9aa..8366a2665d 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -91,6 +91,11 @@ public: void setOcclusionStatus(bool draw) { if (_occlusionJobIndex >= 0) { _jobs[_occlusionJobIndex].setEnabled(draw); } } bool doOcclusionStatus() const { if (_occlusionJobIndex >= 0) { return _jobs[_occlusionJobIndex].isEnabled(); } else { return false; } } + int _antialiasingJobIndex = -1; + + void setAntialiasingStatus(bool draw) { if (_antialiasingJobIndex >= 0) { _jobs[_antialiasingJobIndex].setEnabled(draw); } } + bool doAntialiasingStatus() const { if (_antialiasingJobIndex >= 0) { return _jobs[_antialiasingJobIndex].isEnabled(); } else { return false; } } + virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); From 77b857031b2a0e94f97a860061fa96c32fde84c6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 31 Aug 2015 11:01:15 -0700 Subject: [PATCH 042/117] Take timeScale into account during interps & setCurrentFrame() --- libraries/animation/src/AnimClip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 4f3ea54e34..c4294495e7 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -75,7 +75,7 @@ void AnimClip::loadURL(const std::string& url) { void AnimClip::setCurrentFrameInternal(float frame) { const float dt = 0.0f; - _frame = accumulateTime(frame, dt); + _frame = accumulateTime(frame * _timeScale, dt); } float AnimClip::accumulateTime(float frame, float dt) const { From a907c5757b63b9da6592f0ee642f727a2356d4ef Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 31 Aug 2015 11:09:28 -0700 Subject: [PATCH 043/117] Do not reset animation frame when restarting while we're fading out. Keeps it smooth if we're oscillating on some theshold between running and not.) --- libraries/animation/src/AnimationHandle.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index 1f4c886a06..ad09e0736d 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -10,7 +10,7 @@ // #include "AnimationHandle.h" - +#include "AnimationLogging.h" void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { @@ -51,8 +51,8 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) { } void AnimationHandle::setRunning(bool running, bool doRestoreJoints) { - if (running && isRunning()) { - // if we're already running, this is the same as a restart + if (running && isRunning() && (getFadePerSecond() >= 0.0f)) { + // if we're already running, this is the same as a restart -- unless we're fading out. setFrameIndex(getFirstFrame()); return; } From 2dbfa5ce9f1d0adeb97e0ce86bdc90ff23eb564c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 31 Aug 2015 11:10:05 -0700 Subject: [PATCH 044/117] Be more uniform in deciding animation movement, and incorporate HMD standing mode (which sets position, but not velocity). --- libraries/animation/src/Rig.cpp | 40 ++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 3e466b94d6..24a86aa81a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -418,9 +418,30 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } bool isMoving = false; glm::vec3 front = worldRotation * IDENTITY_FRONT; - float forwardSpeed = glm::dot(worldVelocity, front); - float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT); - float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + glm::vec3 right = worldRotation * IDENTITY_RIGHT; + const float PERCEPTIBLE_DELTA = 0.001f; + const float PERCEPTIBLE_SPEED = 0.1f; + // It can be more accurate/smooth to use velocity rather than position, + // but some modes (e.g., hmd standing) update position without updating velocity. + // It's very hard to debug hmd standing. (Look down at yourself, or have a second person observe. HMD third person is a bit undefined...) + // So, let's create our own workingVelocity from the worldPosition... + glm::vec3 positionDelta = worldPosition - _lastPosition; + glm::vec3 workingVelocity = positionDelta / deltaTime; + // But for smoothest (non-hmd standing) results, go ahead and use velocity: +#if !WANT_DEBUG + // Note: Separately, we've arranged for starting/stopping animations by role (as we've done here) to pick up where they've left off when fading, + // so that you wouldn't notice the start/stop if it happens fast enough (e.g., one frame). But the print below would still be noisy. + if (!positionDelta.x && !positionDelta.y && !positionDelta.z) { + workingVelocity = worldVelocity; + } +#endif + + float forwardSpeed = glm::dot(workingVelocity, front); + float rightLateralSpeed = glm::dot(workingVelocity, right); + float rightTurningDelta = glm::orientedAngle(front, _lastFront, IDENTITY_UP); + float rightTurningSpeed = rightTurningDelta / deltaTime; + bool isTurning = (std::abs(rightTurningDelta) > PERCEPTIBLE_DELTA) && (std::abs(rightTurningSpeed) > PERCEPTIBLE_SPEED); + bool isStrafing = std::abs(rightLateralSpeed) > PERCEPTIBLE_SPEED; auto updateRole = [&](const QString& role, bool isOn) { isMoving = isMoving || isOn; if (isOn) { @@ -435,14 +456,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } } }; - updateRole("walk", forwardSpeed > 0.01f); - updateRole("backup", forwardSpeed < -0.01f); - bool isTurning = std::abs(rightTurningSpeed) > 0.5f; - updateRole("rightTurn", isTurning && (rightTurningSpeed > 0)); - updateRole("leftTurn", isTurning && (rightTurningSpeed < 0)); - bool isStrafing = !isTurning && (std::abs(rightLateralSpeed) > 0.01f); + updateRole("walk", forwardSpeed > PERCEPTIBLE_SPEED); + updateRole("backup", forwardSpeed < -PERCEPTIBLE_SPEED); + updateRole("rightTurn", isTurning && (rightTurningSpeed > 0.0f)); + updateRole("leftTurn", isTurning && (rightTurningSpeed < 0.0f)); + isStrafing = isStrafing && !isMoving; updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f)); - updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); + updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. _lastFront = front; _lastPosition = worldPosition; From 376a6bbb210a8d0db3d56fab661d73582ef95cb9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 31 Aug 2015 11:48:08 -0700 Subject: [PATCH 045/117] don't reload textures if the url didn't change --- .../src/RenderablePolyVoxEntityItem.cpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 7ec9357dda..66054aad5a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -71,8 +71,8 @@ RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { bool isEdged(PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle) { switch (surfaceStyle) { case PolyVoxEntityItem::SURFACE_CUBIC: - return true; case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + return false; case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: return true; @@ -414,18 +414,24 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { - _xTexture.clear(); - PolyVoxEntityItem::setXTextureURL(xTextureURL); + if (xTextureURL != _xTextureURL) { + _xTexture.clear(); + PolyVoxEntityItem::setXTextureURL(xTextureURL); + } } void RenderablePolyVoxEntityItem::setYTextureURL(QString yTextureURL) { - _yTexture.clear(); - PolyVoxEntityItem::setYTextureURL(yTextureURL); + if (yTextureURL != _yTextureURL) { + _yTexture.clear(); + PolyVoxEntityItem::setYTextureURL(yTextureURL); + } } void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { - _zTexture.clear(); - PolyVoxEntityItem::setZTextureURL(zTextureURL); + if (zTextureURL != _zTextureURL) { + _zTexture.clear(); + PolyVoxEntityItem::setZTextureURL(zTextureURL); + } } void RenderablePolyVoxEntityItem::render(RenderArgs* args) { From 9786954585c63c55a2628c85b0f265ead6f44ddd Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 31 Aug 2015 12:13:05 -0700 Subject: [PATCH 046/117] Added support for onDone and onLoop triggers. --- interface/src/avatar/MyAvatar.cpp | 7 ++++++- libraries/animation/src/AnimBlendLinear.cpp | 10 +++++----- libraries/animation/src/AnimBlendLinear.h | 2 +- libraries/animation/src/AnimClip.cpp | 14 ++++++++------ libraries/animation/src/AnimClip.h | 4 ++-- libraries/animation/src/AnimNode.h | 8 +++++--- libraries/animation/src/AnimOverlay.cpp | 6 +++--- libraries/animation/src/AnimOverlay.h | 2 +- libraries/animation/src/AnimStateMachine.cpp | 10 +++++++--- libraries/animation/src/AnimStateMachine.h | 2 +- libraries/animation/src/AnimVariant.h | 2 +- tests/animation/src/data/avatar.json | 20 ++++++++++---------- 12 files changed, 50 insertions(+), 37 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fa5f4da702..c5435c590b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -176,7 +176,12 @@ void MyAvatar::update(float deltaTime) { } t += deltaTime; - _animNode->evaluate(_animVars, deltaTime); + AnimNode::Triggers triggers; + _animNode->evaluate(_animVars, deltaTime, triggers); + _animVars.clearTriggers(); + for (auto& trigger : triggers) { + _animVars.setTrigger(trigger); + } } if (_referential) { diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 102d0e4df6..499579ae67 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -22,7 +22,7 @@ AnimBlendLinear::~AnimBlendLinear() { } -const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt) { +const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); @@ -31,7 +31,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo pose = AnimPose::identity; } } else if (_children.size() == 1) { - _poses = _children[0]->evaluate(animVars, dt); + _poses = _children[0]->evaluate(animVars, dt, triggersOut); } else { float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); size_t prevPoseIndex = glm::floor(clampedAlpha); @@ -39,11 +39,11 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo float alpha = glm::fract(clampedAlpha); if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, dt); + _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 2df1965064..8016f7994f 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -29,7 +29,7 @@ public: AnimBlendLinear(const std::string& id, float alpha); virtual ~AnimBlendLinear() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index c4294495e7..12ba97f377 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -27,14 +27,14 @@ AnimClip::~AnimClip() { } -const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) { +const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { // lookup parameters from animVars, using current instance variables as defaults. _startFrame = animVars.lookup(_startFrameVar, _startFrame); _endFrame = animVars.lookup(_endFrameVar, _endFrame); _timeScale = animVars.lookup(_timeScaleVar, _timeScale); _loopFlag = animVars.lookup(_loopFlagVar, _loopFlag); - _frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt); + _frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt, triggersOut); // poll network anim to see if it's finished loading yet. if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { @@ -74,11 +74,13 @@ void AnimClip::loadURL(const std::string& url) { } void AnimClip::setCurrentFrameInternal(float frame) { + // because dt is 0, we should not encounter any triggers const float dt = 0.0f; - _frame = accumulateTime(frame * _timeScale, dt); + Triggers triggers; + _frame = accumulateTime(frame * _timeScale, dt, triggers); } -float AnimClip::accumulateTime(float frame, float dt) const { +float AnimClip::accumulateTime(float frame, float dt, Triggers& triggersOut) const { const float startFrame = std::min(_startFrame, _endFrame); if (startFrame == _endFrame) { // when startFrame >= endFrame @@ -92,12 +94,12 @@ float AnimClip::accumulateTime(float frame, float dt) const { if (framesRemaining >= framesTillEnd) { if (_loopFlag) { // anim loop - // TODO: trigger onLoop event + triggersOut.push_back(_id + "OnLoop"); framesRemaining -= framesTillEnd; frame = startFrame; } else { // anim end - // TODO: trigger onDone event + triggersOut.push_back(_id + "OnDone"); frame = _endFrame; framesRemaining = 0.0f; } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 45924c1eed..86e6cf7733 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -27,7 +27,7 @@ public: AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setStartFrameVar(const std::string& startFrameVar) { _startFrameVar = startFrameVar; } void setEndFrameVar(const std::string& endFrameVar) { _endFrameVar = endFrameVar; } @@ -40,7 +40,7 @@ protected: virtual void setCurrentFrameInternal(float frame) override; - float accumulateTime(float frame, float dt) const; + float accumulateTime(float frame, float dt, Triggers& triggersOut) const; void copyFromNetworkAnim(); // for AnimDebugDraw rendering diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 733428f188..06085a471a 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -30,6 +30,7 @@ class QJsonObject; // * hierarchy accessors, for adding, removing and iterating over child AnimNodes // * skeleton accessors, the skeleton is from the model whose bones we are going to manipulate // * evaluate method, perform actual joint manipulations here and return result by reference. +// Also, append any triggers that are detected during evaluation. class AnimNode { public: @@ -41,6 +42,7 @@ public: NumTypes }; using Pointer = std::shared_ptr; + using Triggers = std::vector; friend class AnimDebugDraw; friend void buildChildMap(std::map& map, Pointer node); @@ -76,9 +78,9 @@ public: AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; } - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) = 0; - virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, const AnimPoseVec& underPoses) { - return evaluate(animVars, dt); + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) = 0; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { + return evaluate(animVars, dt, triggersOut); } protected: diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 81df5811d7..dcdd9f5b4b 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -36,7 +36,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) { } } -const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt) { +const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { // lookup parameters from animVars, using current instance variables as defaults. // NOTE: switching bonesets can be an expensive operation, let's try to avoid it. @@ -48,8 +48,8 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float d _alpha = animVars.lookup(_alphaVar, _alpha); if (_children.size() >= 2) { - auto underPoses = _children[1]->evaluate(animVars, dt); - auto overPoses = _children[0]->overlay(animVars, dt, underPoses); + auto underPoses = _children[1]->evaluate(animVars, dt, triggersOut); + auto overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses); if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) { _poses.resize(underPoses.size()); diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 5940f0b2b3..eb11510f74 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -40,7 +40,7 @@ public: AnimOverlay(const std::string& id, BoneSet boneSet, float alpha); virtual ~AnimOverlay() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setBoneSetVar(const std::string& boneSetVar) { _boneSetVar = boneSetVar; } void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 43bb305797..ccb3dcd91f 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -20,7 +20,7 @@ AnimStateMachine::~AnimStateMachine() { } -const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt) { +const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { std::string desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); if (_currentState->getID() != desiredStateID) { @@ -60,7 +60,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl } } if (!_duringInterp) { - _poses = currentStateNode->evaluate(animVars, dt); + _poses = currentStateNode->evaluate(animVars, dt, triggersOut); } return _poses; } @@ -88,7 +88,11 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe _alphaVel = FRAMES_PER_SECOND / duration; _prevPoses = _poses; nextStateNode->setCurrentFrame(desiredState->_interpTarget); - _nextPoses = nextStateNode->evaluate(animVars, 0.0f); + + // because dt is 0, we should not encounter any triggers + const float dt = 0.0f; + Triggers triggers; + _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); _currentState = desiredState; } diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 4e9a4b3214..e48e08e96e 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -77,7 +77,7 @@ public: AnimStateMachine(const std::string& id); virtual ~AnimStateMachine() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setCurrentStateVar(std::string& currentStateVar) { _currentStateVar = currentStateVar; } diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 15ab7e94b1..849b6a436a 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -150,7 +150,7 @@ public: void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); } void setTrigger(const std::string& key) { _triggers.insert(key); } - void clearTirggers() { _triggers.clear(); } + void clearTriggers() { _triggers.clear(); } protected: std::map _map; diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index 3856588c42..5647f74a7f 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -11,11 +11,11 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "isMoving", "state": "walk_fwd" } + { "var": "isMoving", "state": "walkFwd" } ] }, { - "id": "walk_fwd", + "id": "walkFwd", "interpTarget": 6, "interpDuration": 6, "transitions": [ @@ -34,7 +34,7 @@ }, "children": [ { - "id": "normal_idle", + "id": "normalIdle", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", @@ -46,7 +46,7 @@ "children": [] }, { - "id": "other_idle", + "id": "otherIdle", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", @@ -60,7 +60,7 @@ ] }, { - "id": "walk_fwd", + "id": "walkFwd", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", @@ -72,7 +72,7 @@ "children": [] }, { - "id": "walk_bwd", + "id": "walkBwd", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", @@ -84,7 +84,7 @@ "children": [] }, { - "id": "turn_left", + "id": "turnLeft", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx", @@ -96,7 +96,7 @@ "children": [] }, { - "id": "turn_right", + "id": "turnRight", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx", @@ -108,7 +108,7 @@ "children": [] }, { - "id": "strafe_left", + "id": "strafeLeft", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_left.fbx", @@ -120,7 +120,7 @@ "children": [] }, { - "id": "strafe_right", + "id": "strafeRight", "type": "clip", "data": { "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_right.fbx", From 4d06890d90310ce2aff14d969436f4cef536e7a1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 31 Aug 2015 15:24:15 -0700 Subject: [PATCH 047/117] lock/unlock less --- .../src/RenderablePolyVoxEntityItem.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 66054aad5a..7d7607860d 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1081,10 +1081,16 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { } else { unsigned int i = 0; + _volDataLock.lockForRead(); + if (!_volData) { + _volDataLock.unlock(); + return; + } + for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { - if (getVoxel(x, y, z) > 0) { + if (getVoxelInternal(x, y, z) > 0) { if ((x > 0 && getVoxel(x - 1, y, z) > 0) && (y > 0 && getVoxel(x, y - 1, z) > 0) && @@ -1142,6 +1148,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { } } } + _volDataLock.unlock(); } if (points.isEmpty()) { From 54ddcf2841a48c1a5f76c1b27a7c08df7710f02e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 31 Aug 2015 15:24:30 -0700 Subject: [PATCH 048/117] fix normals in shader --- libraries/entities-renderer/src/polyvox.slf | 23 +++++++-------------- libraries/entities-renderer/src/polyvox.slv | 4 ++-- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index 5772a93f7d..c694630770 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -12,16 +12,12 @@ // <@include gpu/Inputs.slh@> - -layout(location = 0) out vec4 _fragColor0; -layout(location = 1) out vec4 _fragColor1; -layout(location = 2) out vec4 _fragColor2; - <@include model/Material.slh@> +<@include DeferredBufferWrite.slh@> in vec3 _normal; in vec4 _position; -in vec4 _inPosition; +in vec4 _worldPosition; uniform sampler2D xMap; uniform sampler2D yMap; @@ -29,12 +25,12 @@ uniform sampler2D zMap; uniform vec3 voxelVolumeSize; void main(void) { - vec3 worldNormal = cross(dFdy(_inPosition.xyz), dFdx(_inPosition.xyz)); + vec3 worldNormal = cross(dFdy(_worldPosition.xyz), dFdx(_worldPosition.xyz)); worldNormal = normalize(worldNormal); - float inPositionX = (_inPosition.x - 0.5) / voxelVolumeSize.x; - float inPositionY = (_inPosition.y - 0.5) / voxelVolumeSize.y; - float inPositionZ = (_inPosition.z - 0.5) / voxelVolumeSize.z; + float inPositionX = (_worldPosition.x - 0.5) / voxelVolumeSize.x; + float inPositionY = (_worldPosition.y - 0.5) / voxelVolumeSize.y; + float inPositionZ = (_worldPosition.z - 0.5) / voxelVolumeSize.z; vec4 xyDiffuse = texture(xMap, vec2(-inPositionX, -inPositionY)); vec4 xzDiffuse = texture(yMap, vec2(-inPositionX, inPositionZ)); @@ -43,12 +39,7 @@ void main(void) { vec3 xyDiffuseScaled = xyDiffuse.rgb * abs(worldNormal.z); vec3 xzDiffuseScaled = xzDiffuse.rgb * abs(worldNormal.y); vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x); - vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0); - Material mat = getMaterial(); - - _fragColor0 = vec4(diffuse.rgb, 0.0); - _fragColor1 = vec4(_normal, 1.0); - _fragColor2 = vec4(getMaterialSpecular(mat), getMaterialShininess(mat) / 128.0); + packDeferredFragment(_normal, 0.0, vec3(diffuse), vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/entities-renderer/src/polyvox.slv b/libraries/entities-renderer/src/polyvox.slv index 0074993c80..eb8d264a1b 100644 --- a/libraries/entities-renderer/src/polyvox.slv +++ b/libraries/entities-renderer/src/polyvox.slv @@ -17,7 +17,7 @@ <$declareStandardTransform()$> out vec4 _position; -out vec4 _inPosition; +out vec4 _worldPosition; out vec3 _normal; void main(void) { @@ -26,5 +26,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> <$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$> - _inPosition = inPosition; + _worldPosition = inPosition; } From d003ec9c584433a0ecddc5f7ee7890efbe66eec8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 31 Aug 2015 15:49:17 -0700 Subject: [PATCH 049/117] Delete collision sounds (and their threads) after use. This has been broken ever since I added avatar collision sounds around https://github.com/highfidelity/hifi/pull/5159 or so. --- libraries/audio/src/AudioInjector.cpp | 11 +++++++++-- libraries/audio/src/AudioInjector.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 8fd7cb9ce5..716ed5d43e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -313,7 +313,7 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol QByteArray samples = sound->getByteArray(); if (stretchFactor == 1.0f) { - return playSound(samples, options, NULL); + return playSoundAndDelete(samples, options, NULL); } soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I); @@ -333,9 +333,16 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol qCDebug(audio) << "Unable to resample" << soundUrl << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate; resampled = samples; } - return playSound(resampled, options, NULL); + return playSoundAndDelete(resampled, options, NULL); } +AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { + AudioInjector* sound = playSound(buffer, options, localInterface); + sound->triggerDeleteAfterFinish(); + return sound; +} + + AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { QThread* injectorThread = new QThread(); injectorThread->setObjectName("Audio Injector Thread"); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index d65925b865..0e98fe1682 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -46,6 +46,7 @@ public: void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; } + static AudioInjector* playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface); static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface); static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position); From 0d98ab336582cb78e37073008fe2363c5cefb588 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 31 Aug 2015 17:27:23 -0700 Subject: [PATCH 050/117] Normalize scale on AnimSkeleton bind pose. --- interface/src/avatar/MyAvatar.cpp | 21 ++++++----- libraries/animation/src/AnimSkeleton.cpp | 37 +++++++++++++++++++- libraries/animation/src/AnimSkeleton.h | 6 +++- libraries/animation/src/AnimStateMachine.cpp | 1 - 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c5435c590b..45c22dc35c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1240,25 +1240,26 @@ void MyAvatar::setupNewAnimationSystem() { for (auto& joint : geom.joints) { joints.push_back(joint); } - _animSkeleton = make_shared(joints); + AnimPose geometryOffset(_skeletonModel.getGeometry()->getFBXGeometry().offset); + _animSkeleton = make_shared(joints, geometryOffset); // add skeleton to the debug renderer, so we can see it. - /* - AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); - AnimDebugDraw::getInstance().addSkeleton("my-avatar", _animSkeleton, xform); - */ + 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"); + 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(_skeletonModel.getScale() / 10.0f, glm::quat(), _skeletonModel.getOffset() + glm::vec3(0, 0, 1)); - AnimDebugDraw::getInstance().addAnimNode("node", _animNode, xform); + 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; @@ -1266,6 +1267,8 @@ void MyAvatar::setupNewAnimationSystem() { } void MyAvatar::teardownNewAnimationSystem() { + AnimDebugDraw::getInstance().removeSkeleton("my-avatar-skeleton"); + AnimDebugDraw::getInstance().removeAnimNode("my-avatar-animation"); _animSkeleton = nullptr; _animLoader = nullptr; _animNode = nullptr; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 3d16f99473..d650c0b964 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -62,13 +62,14 @@ static bool matrixIsIdentity(const glm::mat4& m) { return m == IDENTITY; } -AnimSkeleton::AnimSkeleton(const std::vector& joints) { +AnimSkeleton::AnimSkeleton(const std::vector& joints, const AnimPose& geometryOffset) { _joints = joints; // build a cache of bind poses _absoluteBindPoses.reserve(joints.size()); _relativeBindPoses.reserve(joints.size()); + // iterate over FBXJoints and extract the bind pose information. for (size_t i = 0; i < joints.size(); i++) { if (_joints[i].bindTransformIsValid) { // Use the FBXJoint::bindTransform, which is absolute model coordinates @@ -97,6 +98,23 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints) { } } } + + // now we want to normalize scale from geometryOffset to all poses. + // This will ensure our bone translations will be in meters, even if the model was authored with some other unit of mesure. + for (auto& absPose : _absoluteBindPoses) { + absPose.trans = (geometryOffset * absPose).trans; + absPose.scale = vec3(1, 1, 1); + } + + // re-compute relative poses based on the modified absolute poses. + for (size_t i = 0; i < _relativeBindPoses.size(); i++) { + int parentIndex = getParentIndex(i); + if (parentIndex >= 0) { + _relativeBindPoses[i] = _absoluteBindPoses[parentIndex].inverse() * _absoluteBindPoses[i]; + } else { + _relativeBindPoses[i] = _absoluteBindPoses[i]; + } + } } int AnimSkeleton::nameToJointIndex(const QString& jointName) const { @@ -127,3 +145,20 @@ int AnimSkeleton::getParentIndex(int jointIndex) const { const QString& AnimSkeleton::getJointName(int jointIndex) const { return _joints[jointIndex].name; } + +#ifndef NDEBUG +void AnimSkeleton::dump() 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); + if (getParentIndex(i) >= 0) { + qCDebug(animation) << " parent =" << getJointName(getParentIndex(i)); + } + qCDebug(animation) << " },"; + } + qCDebug(animation) << "]"; +} +#endif diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 3faade0dbd..cc0d0b1c70 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -46,7 +46,7 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - AnimSkeleton(const std::vector& joints); + AnimSkeleton(const std::vector& joints, const AnimPose& geometryOffset); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; int getNumJoints() const; @@ -59,6 +59,10 @@ public: int getParentIndex(int jointIndex) const; +#ifndef NDEBUG + void dump() const; +#endif + protected: std::vector _joints; AnimPoseVec _absoluteBindPoses; diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index ccb3dcd91f..7815381ca3 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -44,7 +44,6 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl switchState(animVars, desiredState); } - assert(_currentState); auto currentStateNode = _currentState->getNode(); assert(currentStateNode); From 6096c20db0817f2087343fda337601b21f7c8726 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 31 Aug 2015 18:00:31 -0700 Subject: [PATCH 051/117] allow adding and deleting spheres --- examples/voxels.js | 134 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 31 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 1859ac8a8b..3fbdb4b6e6 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -12,9 +12,12 @@ var toolWidth = 50; var addingVoxels = false; var deletingVoxels = false; +var addingSpheres = false; +var deletingSpheres = false; -offAlpha = 0.5; -onAlpha = 0.9; +var offAlpha = 0.5; +var onAlpha = 0.9; +var editSphereRadius = 3.0; function floorVector(v) { return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)}; @@ -30,6 +33,8 @@ var toolBar = (function () { activeButton, addVoxelButton, deleteVoxelButton, + addSphereButton, + deleteSphereButton, addTerrainButton; function initialize() { @@ -66,6 +71,24 @@ var toolBar = (function () { visible: false }); + addSphereButton = toolBar.addTool({ + imageURL: toolIconUrl + "sphere-add.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: offAlpha, + visible: false + }); + + deleteSphereButton = toolBar.addTool({ + imageURL: toolIconUrl + "sphere-delete.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: offAlpha, + visible: false + }); + addTerrainButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-terrain.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, @@ -78,6 +101,22 @@ var toolBar = (function () { that.setActive(false); } + function disableAllButtons() { + addingVoxels = false; + deletingVoxels = false; + addingSpheres = false; + deletingSpheres = false; + + toolBar.setAlpha(offAlpha, addVoxelButton); + toolBar.setAlpha(offAlpha, deleteVoxelButton); + toolBar.setAlpha(offAlpha, addSphereButton); + toolBar.setAlpha(offAlpha, deleteSphereButton); + + toolBar.selectTool(addVoxelButton, false); + toolBar.selectTool(deleteVoxelButton, false); + toolBar.selectTool(addSphereButton, false); + toolBar.selectTool(deleteSphereButton, false); + } that.setActive = function(active) { if (active != isActive) { @@ -91,6 +130,8 @@ var toolBar = (function () { that.showTools = function(doShow) { toolBar.showTool(addVoxelButton, doShow); toolBar.showTool(deleteVoxelButton, doShow); + toolBar.showTool(addSphereButton, doShow); + toolBar.showTool(deleteSphereButton, doShow); toolBar.showTool(addTerrainButton, doShow); }; @@ -103,37 +144,46 @@ var toolBar = (function () { } if (addVoxelButton === toolBar.clicked(clickedOverlay)) { - if (addingVoxels) { - addingVoxels = false; - deletingVoxels = false; - toolBar.setAlpha(offAlpha, addVoxelButton); - toolBar.setAlpha(offAlpha, deleteVoxelButton); - toolBar.selectTool(addVoxelButton, false); - toolBar.selectTool(deleteVoxelButton, false); - } else { + var wasAddingVoxels = addingVoxels; + disableAllButtons() + if (!wasAddingVoxels) { addingVoxels = true; - deletingVoxels = false; toolBar.setAlpha(onAlpha, addVoxelButton); - toolBar.setAlpha(offAlpha, deleteVoxelButton); } return true; } if (deleteVoxelButton === toolBar.clicked(clickedOverlay)) { - if (deletingVoxels) { - deletingVoxels = false; - addingVoxels = false; - toolBar.setAlpha(offAlpha, addVoxelButton); - toolBar.setAlpha(offAlpha, deleteVoxelButton); - } else { + var wasDeletingVoxels = deletingVoxels; + disableAllButtons() + if (!wasDeletingVoxels) { deletingVoxels = true; - addingVoxels = false; - toolBar.setAlpha(offAlpha, addVoxelButton); toolBar.setAlpha(onAlpha, deleteVoxelButton); } return true; } + if (addSphereButton === toolBar.clicked(clickedOverlay)) { + var wasAddingSpheres = addingSpheres + disableAllButtons() + if (!wasAddingSpheres) { + addingSpheres = true; + toolBar.setAlpha(onAlpha, addSphereButton); + } + return true; + } + + if (deleteSphereButton === toolBar.clicked(clickedOverlay)) { + var wasDeletingSpheres = deletingSpheres; + disableAllButtons() + if (!wasDeletingSpheres) { + deletingSpheres = true; + toolBar.setAlpha(onAlpha, deleteSphereButton); + } + return true; + } + + if (addTerrainButton === toolBar.clicked(clickedOverlay)) { addTerrainBlock(); return true; @@ -202,14 +252,15 @@ function addTerrainBlock() { }); Entities.setAllVoxels(polyVoxId, 255); - // XXX use setCuboid - for (var y = 16; y < 32; y++) { - for (var x = 0; x < 16; x++) { - for (var z = 0; z < 16; z++) { - Entities.setVoxel(polyVoxId, {x:x, y:y, z:z}, 0); - } - } - } + // for (var y = 16; y < 32; y++) { + // for (var x = 0; x < 16; x++) { + // for (var z = 0; z < 16; z++) { + // Entities.setVoxel(polyVoxId, {x:x, y:y, z:z}, 0); + // } + // } + // } + Entities.setVoxelsInCuboid(polyVoxId, {x:0, y:16, z:0}, {x:16, y:16, z:16}, 0); + ////////// // stitch together the terrain with x/y/z NeighorID properties @@ -271,7 +322,7 @@ function attemptVoxelChange(pickRayDir, intersection) { return false; } - if (addingVoxels == false && deletingVoxels == false) { + if (addingVoxels == false && deletingVoxels == false && addingSpheres == false && deletingSpheres == false) { return false; } @@ -281,10 +332,23 @@ function attemptVoxelChange(pickRayDir, intersection) { var doAdd = addingVoxels; var doDelete = deletingVoxels; + var doAddSphere = addingSpheres; + var doDeleteSphere = deletingSpheres; if (controlHeld) { - doAdd = deletingVoxels; - doDelete = addingVoxels; + if (doAdd) { + doAdd = false; + doDelete = true; + } else if (doDelete) { + doDelete = false; + doAdd = true; + } else if (doAddSphere) { + doAddSphere = false; + doDeleteSphere = true; + } else if (doDeleteSphere) { + doDeleteSphere = false; + doAddSphere = true; + } } if (doDelete) { @@ -295,6 +359,14 @@ function attemptVoxelChange(pickRayDir, intersection) { var toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); return Entities.setVoxel(intersection.entityID, floorVector(toDrawPosition), 255); } + if (doDeleteSphere) { + var toErasePosition = intersection.intersection; + return Entities.setVoxelSphere(intersection.entityID, floorVector(toErasePosition), editSphereRadius, 0); + } + if (doAddSphere) { + var toDrawPosition = intersection.intersection; + return Entities.setVoxelSphere(intersection.entityID, floorVector(toDrawPosition), editSphereRadius, 255); + } } function mousePressEvent(event) { From 7369ca19e9874498da5e27776547c1d34530e5e3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 31 Aug 2015 18:01:15 -0700 Subject: [PATCH 052/117] allow setting cuboid voxel spaces. clean up some locking --- .../src/RenderablePolyVoxEntityItem.cpp | 59 +++++++++++++++---- .../src/RenderablePolyVoxEntityItem.h | 1 + .../entities/src/EntityScriptingInterface.cpp | 7 +++ .../entities/src/EntityScriptingInterface.h | 2 + libraries/entities/src/PolyVoxEntityItem.cpp | 18 ++++-- libraries/entities/src/PolyVoxEntityItem.h | 3 +- 6 files changed, 70 insertions(+), 20 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 7d7607860d..2a7b3fe299 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -201,6 +201,37 @@ bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { } +bool RenderablePolyVoxEntityItem::setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue) { + bool result = false; + if (_locked) { + return result; + } + + int xLow = std::max(std::min((int)roundf(lowPosition.x), (int)roundf(_voxelVolumeSize.x) - 1), 0); + int yLow = std::max(std::min((int)roundf(lowPosition.y), (int)roundf(_voxelVolumeSize.y) - 1), 0); + int zLow = std::max(std::min((int)roundf(lowPosition.z), (int)roundf(_voxelVolumeSize.z) - 1), 0); + int xHigh = std::max(std::min(xLow + (int)roundf(cuboidSize.x), (int)roundf(_voxelVolumeSize.x)), xLow); + int yHigh = std::max(std::min(yLow + (int)roundf(cuboidSize.y), (int)roundf(_voxelVolumeSize.y)), yLow); + int zHigh = std::max(std::min(zLow + (int)roundf(cuboidSize.z), (int)roundf(_voxelVolumeSize.z)), zLow); + + _volDataLock.lockForWrite(); + _volDataDirty = true; + + for (int x = xLow; x < xHigh; x++) { + for (int y = yLow; y < yHigh; y++) { + for (int z = zLow; z < zHigh; z++) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + _volDataLock.unlock(); + if (result) { + compressVolumeDataAndSendEditPacket(); + } + return result; +} + + bool RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { if (_locked) { @@ -408,9 +439,8 @@ bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { } void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { - _shapeInfoLock.lockForRead(); + QReadLocker(&this->_shapeInfoLock); info = _shapeInfo; - _shapeInfoLock.unlock(); } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { @@ -643,10 +673,8 @@ bool RenderablePolyVoxEntityItem::inUserBounds(const PolyVox::SimpleVolume_volDataLock); + return getVoxelInternal(x, y, z); } @@ -903,10 +931,10 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { if (currentXNeighbor) { auto polyVoxXNeighbor = std::dynamic_pointer_cast(currentXNeighbor); - if (polyVoxXNeighbor->_volData->getEnclosingRegion() == _volData->getEnclosingRegion()) { + if (polyVoxXNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { for (int y = 0; y < _volData->getHeight(); y++) { for (int z = 0; z < _volData->getDepth(); z++) { - uint8_t neighborValue = polyVoxXNeighbor->_volData->getVoxelAt(1, y, z); + uint8_t neighborValue = polyVoxXNeighbor->getVoxel(0, y, z); _volData->setVoxelAt(_volData->getWidth() - 1, y, z, neighborValue); } } @@ -915,10 +943,10 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { if (currentYNeighbor) { auto polyVoxYNeighbor = std::dynamic_pointer_cast(currentYNeighbor); - if (polyVoxYNeighbor->_volData->getEnclosingRegion() == _volData->getEnclosingRegion()) { + if (polyVoxYNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { for (int x = 0; x < _volData->getWidth(); x++) { for (int z = 0; z < _volData->getDepth(); z++) { - uint8_t neighborValue = polyVoxYNeighbor->_volData->getVoxelAt(x, 1, z); + uint8_t neighborValue = polyVoxYNeighbor->getVoxel(x, 0, z); _volData->setVoxelAt(x, _volData->getWidth() - 1, z, neighborValue); } } @@ -927,10 +955,10 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { if (currentZNeighbor) { auto polyVoxZNeighbor = std::dynamic_pointer_cast(currentZNeighbor); - if (polyVoxZNeighbor->_volData->getEnclosingRegion() == _volData->getEnclosingRegion()) { + if (polyVoxZNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { for (int x = 0; x < _volData->getWidth(); x++) { for (int y = 0; y < _volData->getHeight(); y++) { - uint8_t neighborValue = polyVoxZNeighbor->_volData->getVoxelAt(x, y, 1); + uint8_t neighborValue = polyVoxZNeighbor->getVoxel(x, y, 0); _volData->setVoxelAt(x, y, _volData->getDepth() - 1, neighborValue); } } @@ -951,12 +979,17 @@ void RenderablePolyVoxEntityItem::getMeshAsync() { model::MeshPointer mesh(new model::Mesh()); cacheNeighbors(); - copyUpperEdgesFromNeighbors(); // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; _volDataLock.lockForRead(); + if (!_volData) { + _volDataLock.unlock(); + return; + } + copyUpperEdgesFromNeighbors(); + switch (_voxelSurfaceStyle) { case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: { PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index fa203f5e4d..98d2b0665d 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -93,6 +93,7 @@ public: // coords are in world-space virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue); virtual bool setAll(uint8_t toValue); + virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue); virtual void setXTextureURL(QString xTextureURL); virtual void setYTextureURL(QString yTextureURL); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index fee2055bd0..b337c05776 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -493,6 +493,13 @@ bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) { }); } +bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, + const glm::vec3& cuboidSize, int value) { + return setVoxels(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) { + return polyVoxEntity.setCuboid(lowPosition, cuboidSize, value); + }); +} + bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector& points) { EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); if (!entity) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index a51ebfb61c..ff693e4585 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -122,6 +122,8 @@ public slots: Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); + Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, + const glm::vec3& cuboidSize, int value); Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 0ce71a37ad..0da66e962b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -62,6 +63,8 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent } void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + QWriteLocker(&this->_voxelDataLock); + assert((int)_voxelVolumeSize.x == _voxelVolumeSize.x); assert((int)_voxelVolumeSize.y == _voxelVolumeSize.y); assert((int)_voxelVolumeSize.z == _voxelVolumeSize.z); @@ -95,6 +98,12 @@ void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { } } +const glm::vec3& PolyVoxEntityItem::getVoxelVolumeSize() const { + QWriteLocker locker(&this->_voxelDataLock); + return _voxelVolumeSize; +} + + EntityItemProperties PolyVoxEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelVolumeSize, getVoxelVolumeSize); @@ -200,15 +209,12 @@ void PolyVoxEntityItem::debugDump() const { } void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) { - _voxelDataLock.lockForWrite(); + QWriteLocker(&this->_voxelDataLock); _voxelData = voxelData; _voxelDataDirty = true; - _voxelDataLock.unlock(); } const QByteArray PolyVoxEntityItem::getVoxelData() const { - _voxelDataLock.lockForRead(); - auto result = _voxelData; - _voxelDataLock.unlock(); - return result; + QReadLocker(&this->_voxelDataLock); + return _voxelData; } diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 886a9c37c7..55118f9017 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -50,7 +50,7 @@ class PolyVoxEntityItem : public EntityItem { virtual void debugDump() const; virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); - virtual const glm::vec3& getVoxelVolumeSize() const { return _voxelVolumeSize; } + virtual const glm::vec3& getVoxelVolumeSize() const; virtual void setVoxelData(QByteArray voxelData); virtual const QByteArray getVoxelData() const; @@ -85,6 +85,7 @@ class PolyVoxEntityItem : public EntityItem { // coords are in world-space virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) { return false; } virtual bool setAll(uint8_t toValue) { return false; } + virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value) { return false; } virtual uint8_t getVoxel(int x, int y, int z) { return 0; } virtual bool setVoxel(int x, int y, int z, uint8_t toValue) { return false; } From 4cbfe96b7081fabcc9ba4532cd1b58db101e5db7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 31 Aug 2015 18:16:32 -0700 Subject: [PATCH 053/117] allow a sphere set/clear to affect more than one polyvox at a time --- examples/voxels.js | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 3fbdb4b6e6..114e9bc5dd 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -17,7 +17,7 @@ var deletingSpheres = false; var offAlpha = 0.5; var onAlpha = 0.9; -var editSphereRadius = 3.0; +var editSphereRadius = 2.5; function floorVector(v) { return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)}; @@ -250,15 +250,8 @@ function addTerrainBlock() { yTextureURL: "http://headache.hungry.com/~seth/hifi/grass.png", zTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg" }); - Entities.setAllVoxels(polyVoxId, 255); - // for (var y = 16; y < 32; y++) { - // for (var x = 0; x < 16; x++) { - // for (var z = 0; z < 16; z++) { - // Entities.setVoxel(polyVoxId, {x:x, y:y, z:z}, 0); - // } - // } - // } + Entities.setAllVoxels(polyVoxId, 255); Entities.setVoxelsInCuboid(polyVoxId, {x:0, y:16, z:0}, {x:16, y:16, z:16}, 0); @@ -315,9 +308,9 @@ function addTerrainBlock() { } -function attemptVoxelChange(pickRayDir, intersection) { +function attemptVoxelChangeForEntity(entityID, pickRayDir, intersectionLocation) { - var properties = Entities.getEntityProperties(intersection.entityID); + var properties = Entities.getEntityProperties(entityID); if (properties.type != "PolyVox") { return false; } @@ -326,8 +319,8 @@ function attemptVoxelChange(pickRayDir, intersection) { return false; } - var voxelPosition = Entities.worldCoordsToVoxelCoords(intersection.entityID, intersection.intersection); - var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(intersection.entityID, pickRayDir); + var voxelPosition = Entities.worldCoordsToVoxelCoords(entityID, intersectionLocation); + var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(entityID, pickRayDir); pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace); var doAdd = addingVoxels; @@ -353,22 +346,34 @@ function attemptVoxelChange(pickRayDir, intersection) { if (doDelete) { var toErasePosition = Vec3.sum(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); - return Entities.setVoxel(intersection.entityID, floorVector(toErasePosition), 0); + return Entities.setVoxel(entityID, floorVector(toErasePosition), 0); } if (doAdd) { var toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); - return Entities.setVoxel(intersection.entityID, floorVector(toDrawPosition), 255); + return Entities.setVoxel(entityID, floorVector(toDrawPosition), 255); } if (doDeleteSphere) { - var toErasePosition = intersection.intersection; - return Entities.setVoxelSphere(intersection.entityID, floorVector(toErasePosition), editSphereRadius, 0); + var toErasePosition = intersectionLocation; + return Entities.setVoxelSphere(entityID, floorVector(toErasePosition), editSphereRadius, 0); } if (doAddSphere) { - var toDrawPosition = intersection.intersection; - return Entities.setVoxelSphere(intersection.entityID, floorVector(toDrawPosition), editSphereRadius, 255); + var toDrawPosition = intersectionLocation; + return Entities.setVoxelSphere(entityID, floorVector(toDrawPosition), editSphereRadius, 255); } } + +function attemptVoxelChange(pickRayDir, intersection) { + var ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); + var success = false; + for (var i = 0; i < ids.length; i++) { + var entityID = ids[i]; + success |= attemptVoxelChangeForEntity(entityID, pickRayDir, intersection.intersection) + } + return success; +} + + function mousePressEvent(event) { if (!event.isLeftButton) { return; From 2b17eafef4cefb4fc5324b5b5e9b0129b1c96416 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 1 Sep 2015 10:24:50 -0700 Subject: [PATCH 054/117] Only render display names in front of camera --- interface/src/avatar/Avatar.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e7423336b1..3d3dee3f44 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -784,8 +784,10 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::ivec4& viewport) const { bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats() && !isMyAvatar(); - // If we have nothing to draw, or it's tottaly transparent, return - if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) { + // If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return + const float CLIP_DISTANCE = 0.2f; + if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f + || (glm::dot(frustum.getDirection(), getDisplayNamePosition() - frustum.getPosition()) <= CLIP_DISTANCE)) { return; } auto renderer = textRenderer(DISPLAYNAME); From ca1602e443755c5ec467d2c54b4083101ba69f31 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 11:09:46 -0700 Subject: [PATCH 055/117] if avatar is already standing on a terrain-polyvox, try creating a new one where they are facing --- examples/voxels.js | 75 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 114e9bc5dd..9537d821fb 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -17,14 +17,14 @@ var deletingSpheres = false; var offAlpha = 0.5; var onAlpha = 0.9; -var editSphereRadius = 2.5; +var editSphereRadius = 4; function floorVector(v) { return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)}; } function vectorToString(v){ - return "{" + v.x + ", " + v.x + ", " + v.x + "}"; + return "{" + v.x + ", " + v.y + ", " + v.z + "}"; } var toolBar = (function () { @@ -228,6 +228,19 @@ function lookupTerrainForLocation(pos) { } +function grabLowestJointY() { + var jointNames = MyAvatar.getJointNames(); + var floorY = MyAvatar.position.y; + for (var jointName in jointNames) { + if (MyAvatar.getJointPosition(jointNames[jointName]).y < floorY) { + floorY = MyAvatar.getJointPosition(jointNames[jointName]).y; + } + } + return floorY; +} + + + function addTerrainBlock() { var baseLocation = getTerrainAlignedLocation(Vec3.sum(MyAvatar.position, {x:8, y:8, z:8})); if (baseLocation.y > MyAvatar.position.y) { @@ -236,23 +249,41 @@ function addTerrainBlock() { var alreadyThere = lookupTerrainForLocation(baseLocation); if (alreadyThere) { - return; + // there is already a terrain block under MyAvatar. + // try in front of the avatar. + print("alreadyThere = " + alreadyThere); + print("MyAvatar.position = " + vectorToString(MyAvatar.position)); + print("baseLocation = " + vectorToString(baseLocation)); + facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getFront(Camera.getOrientation()))); + facingPosition = Vec3.sum(facingPosition, {x:8, y:8, z:8}); + print("facingPosition = " + vectorToString(facingPosition)); + baseLocation = getTerrainAlignedLocation(facingPosition); + print("baseLocation = " + vectorToString(baseLocation)); + alreadyThere = lookupTerrainForLocation(baseLocation); + print("alreadyThere = " + alreadyThere); + if (alreadyThere) { + return; + } } - var polyVoxId = Entities.addEntity({ + var polyVoxID = Entities.addEntity({ type: "PolyVox", name: "terrain", position: baseLocation, dimensions: { x:16, y:16, z:16 }, - voxelVolumeSize: {x:16, y:32, z:16}, + voxelVolumeSize: {x:16, y:64, z:16}, voxelSurfaceStyle: 0, xTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg", yTextureURL: "http://headache.hungry.com/~seth/hifi/grass.png", zTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg" }); - Entities.setAllVoxels(polyVoxId, 255); - Entities.setVoxelsInCuboid(polyVoxId, {x:0, y:16, z:0}, {x:16, y:16, z:16}, 0); + var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position); + // TODO -- how to find the avatar's feet? + var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4; + // Entities.setAllVoxels(polyVoxID, 255); + // Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:topY, z:0}, {x:16, y:64 - topY, z:16}, 0); + Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:0, z:0}, {x:16, y:topY, z:16}, 255); ////////// @@ -263,21 +294,21 @@ function addTerrainBlock() { imXNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:-16, y:0, z:0})); if (imXNeighborFor) { var properties = Entities.getEntityProperties(imXNeighborFor); - properties.xNeighborID = polyVoxId; + properties.xNeighborID = polyVoxID; Entities.editEntity(imXNeighborFor, properties); } imYNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:-16, z:0})); if (imYNeighborFor) { var properties = Entities.getEntityProperties(imYNeighborFor); - properties.yNeighborID = polyVoxId; + properties.yNeighborID = polyVoxID; Entities.editEntity(imYNeighborFor, properties); } imZNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:-16})); if (imZNeighborFor) { var properties = Entities.getEntityProperties(imZNeighborFor); - properties.zNeighborID = polyVoxId; + properties.zNeighborID = polyVoxID; Entities.editEntity(imZNeighborFor, properties); } @@ -285,23 +316,23 @@ function addTerrainBlock() { // link this plot to plots which are higher on the axes xNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:16, y:0, z:0})); if (xNeighborFor) { - var properties = Entities.getEntityProperties(polyVoxId); + var properties = Entities.getEntityProperties(polyVoxID); properties.xNeighborID = xNeighborFor; - Entities.editEntity(polyVoxId, properties); + Entities.editEntity(polyVoxID, properties); } yNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:16, z:0})); if (yNeighborFor) { - var properties = Entities.getEntityProperties(polyVoxId); + var properties = Entities.getEntityProperties(polyVoxID); properties.yNeighborID = yNeighborFor; - Entities.editEntity(polyVoxId, properties); + Entities.editEntity(polyVoxID, properties); } zNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); if (zNeighborFor) { - var properties = Entities.getEntityProperties(polyVoxId); + var properties = Entities.getEntityProperties(polyVoxID); properties.zNeighborID = zNeighborFor; - Entities.editEntity(polyVoxId, properties); + Entities.editEntity(polyVoxID, properties); } return true; @@ -364,7 +395,17 @@ function attemptVoxelChangeForEntity(entityID, pickRayDir, intersectionLocation) function attemptVoxelChange(pickRayDir, intersection) { - var ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); + + var ids; + + if (addingSpheres || deletingSpheres) { + ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); + } else { + ids = [intersection.entityID]; + } + + print("ids = " + ids); + var success = false; for (var i = 0; i < ids.length; i++) { var entityID = ids[i]; From 930c5cefc909e954b44c8604f8c919dff3a14fba Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 11:10:21 -0700 Subject: [PATCH 056/117] fix setSphere to create a world-space sphere rather than a voxel-space one --- .../src/RenderablePolyVoxEntityItem.cpp | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 2a7b3fe299..5d3af51ddc 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -250,7 +250,6 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi // This three-level for loop iterates over every voxel in the volume _volDataLock.lockForWrite(); - _volDataDirty = true; for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { @@ -265,20 +264,59 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi } } } - _volDataLock.unlock(); + if (result) { + _volDataDirty = true; + _volDataLock.unlock(); compressVolumeDataAndSendEditPacket(); + } else { + _volDataLock.unlock(); } return result; } bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { - // glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords); - glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f); - glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - float scaleY = scale.y; - float radiusVoxelCoords = radiusWorldCoords / scaleY; - return setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); + // // glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords); + // glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f); + // glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units + // float scaleY = scale.y; + // float radiusVoxelCoords = radiusWorldCoords / scaleY; + // return setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); + + bool result = false; + if (_locked) { + return result; + } + + glm::mat4 vtwMatrix = voxelToWorldMatrix(); + + // This three-level for loop iterates over every voxel in the volume + _volDataLock.lockForWrite(); + for (int z = 0; z < _voxelVolumeSize.z; z++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int x = 0; x < _voxelVolumeSize.x; x++) { + // Store our current position as a vector... + glm::vec4 pos(x + 0.5f, y + 0.5f, z + 0.5f, 1.0); // consider voxels cenetered on their coordinates + // convert to world coordinates + glm::vec3 worldPos = glm::vec3(vtwMatrix * pos); + // compute how far the current position is from the center of the volume + float fDistToCenter = glm::distance(worldPos, centerWorldCoords); + // If the current voxel is less than 'radius' units from the center then we set its value + if (fDistToCenter <= radiusWorldCoords) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + } + + if (result) { + _volDataDirty = true; + _volDataLock.unlock(); + compressVolumeDataAndSendEditPacket(); + } else { + _volDataLock.unlock(); + } + return result; } class RaycastFunctor From 4a7a384c650f811cb5066470846da3b8a1da6026 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Sep 2015 14:12:14 -0700 Subject: [PATCH 057/117] avoid bad joints when computing bounding capsule --- interface/src/avatar/SkeletonModel.cpp | 109 +++++++++++++++++++------ interface/src/avatar/SkeletonModel.h | 3 +- libraries/animation/src/Rig.cpp | 23 +++--- libraries/fbx/src/FBXReader.cpp | 16 +++- 4 files changed, 110 insertions(+), 41 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index a2e5908477..90c30b41ee 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -86,7 +86,7 @@ void SkeletonModel::initJointStates(QVector states) { _rig->updateJointState(i, rootTransform); } - buildShapes(); + computeBoundingShape(); Extents meshExtents = getMeshExtents(); _headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z); @@ -248,6 +248,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY); } + void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; @@ -346,9 +347,9 @@ void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointInde } OrientationLineIDs& jointLineIDs = _jointOrientationLines[jointIndex]; - glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size; - glm::vec3 pUp = position + orientation * IDENTITY_UP * size; - glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size; + glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size; + glm::vec3 pUp = position + orientation * IDENTITY_UP * size; + glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size; glm::vec3 red(1.0f, 0.0f, 0.0f); geometryCache->renderLine(batch, position, pRight, red, jointLineIDs._right); @@ -466,7 +467,7 @@ float MIN_JOINT_MASS = 1.0f; float VERY_BIG_MASS = 1.0e6f; // virtual -void SkeletonModel::buildShapes() { +void SkeletonModel::computeBoundingShape() { if (_geometry == NULL || _rig->jointStatesEmpty()) { return; } @@ -476,36 +477,87 @@ void SkeletonModel::buildShapes() { // rootJointIndex == -1 if the avatar model has no skeleton return; } - computeBoundingShape(geometry); -} -void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { - // compute default joint transforms - int numStates = _rig->getJointStateCount(); - QVector transforms; - transforms.fill(glm::mat4(), numStates); + // BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the + // hands and feet into positions that are more correct than the default pose. + + // Measure limb lengths so we can specify IK targets that will pull hands and feet tight to body + QVector endEffectors; + endEffectors.push_back("RightHand"); + endEffectors.push_back("LeftHand"); + endEffectors.push_back("RightFoot"); + endEffectors.push_back("LeftFoot"); + + QVector baseJoints; + baseJoints.push_back("RightArm"); + baseJoints.push_back("LeftArm"); + baseJoints.push_back("RightUpLeg"); + baseJoints.push_back("LeftUpLeg"); + + for (int i = 0; i < endEffectors.size(); ++i) { + QString tipName = endEffectors[i]; + QString baseName = baseJoints[i]; + float limbLength = 0.0f; + int tipIndex = _rig->indexOfJoint(tipName); + if (tipIndex == -1) { + continue; + } + // save tip's relative rotation for later + glm::quat tipRotation = _rig->getJointState(tipIndex).getRotationInConstrainedFrame(); + + // IK on each endpoint + int jointIndex = tipIndex; + QVector freeLineage; + float priority = 1.0f; + while (jointIndex > -1) { + JointState limbJoint = _rig->getJointState(jointIndex); + freeLineage.push_back(jointIndex); + if (limbJoint.getName() == baseName) { + glm::vec3 targetPosition = limbJoint.getPosition() - glm::vec3(0.0f, 1.5f * limbLength, 0.0f); + // do IK a few times to make sure the endpoint gets close to its target + for (int j = 0; j < 5; ++j) { + _rig->inverseKinematics(tipIndex, + targetPosition, + glm::quat(), + priority, + freeLineage, + glm::mat4()); + } + const JointState& movedState = _rig->getJointState(tipIndex); + break; + } + limbLength += limbJoint.getDistanceToParent(); + jointIndex = limbJoint.getParentIndex(); + } + + // since this IK target is totally bogus we restore the tip's relative rotation + _rig->setJointRotationInConstrainedFrame(tipIndex, tipRotation, priority); + } + + // recompute all joint model-frame transforms + glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + for (int i = 0; i < _rig->getJointStateCount(); i++) { + _rig->updateJointState(i, rootTransform); + } + // END BOUNDING SHAPE HACK // compute bounding box that encloses all shapes Extents totalExtents; totalExtents.reset(); totalExtents.addPoint(glm::vec3(0.0f)); + int numStates = _rig->getJointStateCount(); for (int i = 0; i < numStates; i++) { // compute the default transform of this joint const JointState& state = _rig->getJointState(i); - int parentIndex = state.getParentIndex(); - if (parentIndex == -1) { - transforms[i] = _rig->getJointTransform(i); - } else { - glm::quat modifiedRotation = state.getPreRotation() * state.getDefaultRotation() * state.getPostRotation(); - transforms[i] = transforms[parentIndex] * glm::translate(state.getTranslation()) - * state.getPreTransform() * glm::mat4_cast(modifiedRotation) * state.getPostTransform(); - } - // Each joint contributes a sphere at its position - glm::vec3 axis(state.getBoneRadius()); - glm::vec3 jointPosition = extractTranslation(transforms[i]); - totalExtents.addPoint(jointPosition + axis); - totalExtents.addPoint(jointPosition - axis); + // HACK WORKAROUND: ignore joints that may have bad translation (e.g. have been flagged as such with zero radius) + if (state.getBoneRadius() > 0.0f) { + // Each joint contributes a sphere at its position + glm::vec3 axis(state.getBoneRadius()); + glm::vec3 jointPosition = state.getPosition(); + totalExtents.addPoint(jointPosition + axis); + totalExtents.addPoint(jointPosition - axis); + } } // compute bounding shape parameters @@ -517,6 +569,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition(); _boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; + + // RECOVER FROM BOUNINDG SHAPE HACK: now that we're all done, restore the default pose + for (int i = 0; i < numStates; i++) { + _rig->restoreJointRotation(i, 1.0f, 1.0f); + } } void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { @@ -535,7 +592,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha glm::vec4(0.6f, 0.6f, 0.8f, alpha)); // draw a yellow sphere at the capsule bottom point - glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f); + glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f); glm::vec3 axis = topPoint - bottomPoint; transform.setTranslation(bottomPoint); batch.setModelTransform(transform); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 4ae615eadd..75ad728d46 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -94,7 +94,6 @@ public: /// \return whether or not the head was found. glm::vec3 getDefaultEyeModelPosition() const; - void computeBoundingShape(const FBXGeometry& geometry); void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha); float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; } float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; } @@ -112,7 +111,7 @@ signals: protected: - void buildShapes(); + void computeBoundingShape(); /// \param jointIndex index of joint in model /// \param position position of joint in model-frame diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index d536bcb608..556953e6e7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -9,30 +9,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Rig.h" + #include #include #include "AnimationHandle.h" #include "AnimationLogging.h" -#include "Rig.h" void Rig::HeadParameters::dump() const { qCDebug(animation, "HeadParameters ="); - qCDebug(animation, " leanSideways = %0.5f", leanSideways); - qCDebug(animation, " leanForward = %0.5f", leanForward); - qCDebug(animation, " torsoTwist = %0.5f", torsoTwist); + qCDebug(animation, " leanSideways = %0.5f", (double)leanSideways); + qCDebug(animation, " leanForward = %0.5f", (double)leanForward); + qCDebug(animation, " torsoTwist = %0.5f", (double)torsoTwist); glm::vec3 axis = glm::axis(localHeadOrientation); float theta = glm::angle(localHeadOrientation); - qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta); + qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); axis = glm::axis(worldHeadOrientation); theta = glm::angle(worldHeadOrientation); - qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta); + qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); axis = glm::axis(modelRotation); theta = glm::angle(modelRotation); - qCDebug(animation, " modelRotation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta); - qCDebug(animation, " modelTranslation = (%.5f, %.5f, %.5f)", modelTranslation.x, modelTranslation.y, modelTranslation.z); - qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", eyeLookAt.x, eyeLookAt.y, eyeLookAt.z); - qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", eyeSaccade.x, eyeSaccade.y, eyeSaccade.z); + qCDebug(animation, " modelRotation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); + qCDebug(animation, " modelTranslation = (%.5f, %.5f, %.5f)", (double)modelTranslation.x, (double)modelTranslation.y, (double)modelTranslation.z); + qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", (double)eyeLookAt.x, (double)eyeLookAt.y, (double)eyeLookAt.z); + qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", (double)eyeSaccade.x, (double)eyeSaccade.y, (double)eyeSaccade.z); qCDebug(animation, " leanJointIndex = %.d", leanJointIndex); qCDebug(animation, " neckJointIndex = %.d", neckJointIndex); qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex); @@ -103,7 +104,7 @@ AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QStrin const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/"; if (role == "walk") { standard = base + "walk_fwd.fbx"; - } else if (role == "backup") { + } else if (role == "backup") { standard = base + "walk_bwd.fbx"; } else if (role == "leftTurn") { standard = base + "turn_left.fbx"; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 8bbe8dafd8..6d83a87a1c 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2677,8 +2677,21 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping foreach (const glm::vec3& vertex, extracted.mesh.vertices) { averageRadius += glm::distance(vertex, averageVertex); } - jointShapeInfo.averageRadius = averageRadius * radiusScale; + jointShapeInfo.averageRadius = averageRadius * radiusScale / (float)jointShapeInfo.numVertices; } + + // BUG: the boneBegin and/or boneEnd are incorrect for meshes that are "connected + // under the bone" without weights. Unfortunately we haven't been able to find it yet. + // Although the the mesh vertices are correct in the model-frame, the joint's transform + // in the same frame is just BAD. + // + // HACK WORKAROUND: prevent these shapes from contributing to the collision capsule by setting + // some key members of jointShapeInfo to zero: + jointShapeInfo.numVertices = 0; + jointShapeInfo.sumVertexWeights = 0.0f; + jointShapeInfo.numVertexWeights = 0; + jointShapeInfo.boneBegin = glm::vec3(0.0f); + jointShapeInfo.averageRadius = 0.0f; } extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); @@ -2728,7 +2741,6 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping // the average radius to the average point. if (jointShapeInfo.numVertexWeights == 0 && jointShapeInfo.numVertices > 0) { - jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices; joint.boneRadius = jointShapeInfo.averageRadius; } } From a7d57d7c60d0d865df2ae60ac055a32c11c9785c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Sep 2015 14:31:47 -0700 Subject: [PATCH 058/117] cleanup whitespace --- interface/src/avatar/SkeletonModel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 90c30b41ee..567f9b30ac 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -347,9 +347,9 @@ void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointInde } OrientationLineIDs& jointLineIDs = _jointOrientationLines[jointIndex]; - glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size; - glm::vec3 pUp = position + orientation * IDENTITY_UP * size; - glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size; + glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size; + glm::vec3 pU = position + orientation * IDENTITY_UP * size; + glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size; glm::vec3 red(1.0f, 0.0f, 0.0f); geometryCache->renderLine(batch, position, pRight, red, jointLineIDs._right); @@ -478,7 +478,7 @@ void SkeletonModel::computeBoundingShape() { return; } - // BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the + // BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the // hands and feet into positions that are more correct than the default pose. // Measure limb lengths so we can specify IK targets that will pull hands and feet tight to body From 2ce225d76c43262a47f0e09140a95892eaebe47c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 1 Sep 2015 15:18:59 -0600 Subject: [PATCH 059/117] fix for sixense not present on OS X --- libraries/input-plugins/src/input-plugins/SixenseManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index c6cc8a404b..052a05b9a9 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -50,8 +50,10 @@ const float CONTROLLER_THRESHOLD = 0.35f; #ifdef __APPLE__ typedef int (*SixenseBaseFunction)(); typedef int (*SixenseTakeIntFunction)(int); +#ifdef HAVE_SIXENSE typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*); #endif +#endif const QString SixenseManager::NAME = "Sixense"; From 9a4d666b0ceea55ed402f1c779fdfe492ef7e39a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 1 Sep 2015 15:19:50 -0600 Subject: [PATCH 060/117] no consts if HAVE_SIXENSE not set --- libraries/input-plugins/src/input-plugins/SixenseManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 052a05b9a9..579fddcd63 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -34,6 +34,8 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") const unsigned int LEFT_MASK = 0; const unsigned int RIGHT_MASK = 1U << 1; +#ifdef HAVE_SIXENSE + const int CALIBRATION_STATE_IDLE = 0; const int CALIBRATION_STATE_X = 1; const int CALIBRATION_STATE_Y = 2; @@ -47,6 +49,8 @@ const float NECK_Z = 0.3f; // meters const float CONTROLLER_THRESHOLD = 0.35f; +#endif + #ifdef __APPLE__ typedef int (*SixenseBaseFunction)(); typedef int (*SixenseTakeIntFunction)(int); From 264d39aa9ba7e19dc37d89e0796a511b61ba502c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 14:49:17 -0700 Subject: [PATCH 061/117] have knitted polyvoxes keep track of low-axis neighbors as well as high --- .../src/RenderablePolyVoxEntityItem.cpp | 97 ++++++++++++------- .../src/RenderablePolyVoxEntityItem.h | 11 ++- .../entities/src/EntityItemProperties.cpp | 66 +++++++++---- libraries/entities/src/EntityItemProperties.h | 18 ++-- libraries/entities/src/EntityPropertyFlags.h | 9 +- libraries/entities/src/PolyVoxEntityItem.cpp | 45 ++++++--- libraries/entities/src/PolyVoxEntityItem.h | 38 +++++--- 7 files changed, 190 insertions(+), 94 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 5d3af51ddc..1944398cf6 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -915,24 +915,44 @@ void RenderablePolyVoxEntityItem::getMesh() { void RenderablePolyVoxEntityItem::clearOutOfDateNeighbors() { - if (_xNeighborID != UNKNOWN_ENTITY_ID) { - EntityItemPointer currentXNeighbor = _xNeighbor.lock(); - if (currentXNeighbor && currentXNeighbor->getID() != _xNeighborID) { - _xNeighbor.reset(); + if (_xNNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); + if (currentXNNeighbor && currentXNNeighbor->getID() != _xNNeighborID) { + _xNNeighbor.reset(); } } - if (_yNeighborID != UNKNOWN_ENTITY_ID) { - EntityItemPointer currentYNeighbor = _yNeighbor.lock(); - if (currentYNeighbor && currentYNeighbor->getID() != _yNeighborID) { - _yNeighbor.reset(); + if (_yNNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); + if (currentYNNeighbor && currentYNNeighbor->getID() != _yNNeighborID) { + _yNNeighbor.reset(); } } - if (_zNeighborID != UNKNOWN_ENTITY_ID) { - EntityItemPointer currentZNeighbor = _zNeighbor.lock(); - if (currentZNeighbor && currentZNeighbor->getID() != _zNeighborID) { - _zNeighbor.reset(); + if (_zNNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentZNNeighbor = _zNNeighbor.lock(); + if (currentZNNeighbor && currentZNNeighbor->getID() != _zNNeighborID) { + _zNNeighbor.reset(); } } + + if (_xPNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentXPNeighbor = _xPNeighbor.lock(); + if (currentXPNeighbor && currentXPNeighbor->getID() != _xPNeighborID) { + _xPNeighbor.reset(); + } + } + if (_yPNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentYPNeighbor = _yPNeighbor.lock(); + if (currentYPNeighbor && currentYPNeighbor->getID() != _yPNeighborID) { + _yPNeighbor.reset(); + } + } + if (_zPNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentZPNeighbor = _zPNeighbor.lock(); + if (currentZPNeighbor && currentZPNeighbor->getID() != _zPNeighborID) { + _zPNeighbor.reset(); + } + } + } void RenderablePolyVoxEntityItem::cacheNeighbors() { @@ -947,15 +967,26 @@ void RenderablePolyVoxEntityItem::cacheNeighbors() { return; } - if (_xNeighborID != UNKNOWN_ENTITY_ID && _xNeighbor.expired()) { - _xNeighbor = tree->findEntityByID(_xNeighborID); + if (_xPNeighborID != UNKNOWN_ENTITY_ID && _xPNeighbor.expired()) { + _xPNeighbor = tree->findEntityByID(_xPNeighborID); } - if (_yNeighborID != UNKNOWN_ENTITY_ID && _yNeighbor.expired()) { - _yNeighbor = tree->findEntityByID(_yNeighborID); + if (_yPNeighborID != UNKNOWN_ENTITY_ID && _yPNeighbor.expired()) { + _yPNeighbor = tree->findEntityByID(_yPNeighborID); } - if (_zNeighborID != UNKNOWN_ENTITY_ID && _zNeighbor.expired()) { - _zNeighbor = tree->findEntityByID(_zNeighborID); + if (_zPNeighborID != UNKNOWN_ENTITY_ID && _zPNeighbor.expired()) { + _zPNeighbor = tree->findEntityByID(_zPNeighborID); } + + if (_xPNeighborID != UNKNOWN_ENTITY_ID && _xPNeighbor.expired()) { + _xPNeighbor = tree->findEntityByID(_xPNeighborID); + } + if (_yPNeighborID != UNKNOWN_ENTITY_ID && _yPNeighbor.expired()) { + _yPNeighbor = tree->findEntityByID(_yPNeighborID); + } + if (_zPNeighborID != UNKNOWN_ENTITY_ID && _zPNeighbor.expired()) { + _zPNeighbor = tree->findEntityByID(_zPNeighborID); + } + } void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { @@ -963,40 +994,40 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { return; } - EntityItemPointer currentXNeighbor = _xNeighbor.lock(); - EntityItemPointer currentYNeighbor = _yNeighbor.lock(); - EntityItemPointer currentZNeighbor = _zNeighbor.lock(); + EntityItemPointer currentXPNeighbor = _xPNeighbor.lock(); + EntityItemPointer currentYPNeighbor = _yPNeighbor.lock(); + EntityItemPointer currentZPNeighbor = _zPNeighbor.lock(); - if (currentXNeighbor) { - auto polyVoxXNeighbor = std::dynamic_pointer_cast(currentXNeighbor); - if (polyVoxXNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + if (currentXPNeighbor) { + auto polyVoxXPNeighbor = std::dynamic_pointer_cast(currentXPNeighbor); + if (polyVoxXPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { for (int y = 0; y < _volData->getHeight(); y++) { for (int z = 0; z < _volData->getDepth(); z++) { - uint8_t neighborValue = polyVoxXNeighbor->getVoxel(0, y, z); + uint8_t neighborValue = polyVoxXPNeighbor->getVoxel(0, y, z); _volData->setVoxelAt(_volData->getWidth() - 1, y, z, neighborValue); } } } } - if (currentYNeighbor) { - auto polyVoxYNeighbor = std::dynamic_pointer_cast(currentYNeighbor); - if (polyVoxYNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + if (currentYPNeighbor) { + auto polyVoxYPNeighbor = std::dynamic_pointer_cast(currentYPNeighbor); + if (polyVoxYPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { for (int x = 0; x < _volData->getWidth(); x++) { for (int z = 0; z < _volData->getDepth(); z++) { - uint8_t neighborValue = polyVoxYNeighbor->getVoxel(x, 0, z); + uint8_t neighborValue = polyVoxYPNeighbor->getVoxel(x, 0, z); _volData->setVoxelAt(x, _volData->getWidth() - 1, z, neighborValue); } } } } - if (currentZNeighbor) { - auto polyVoxZNeighbor = std::dynamic_pointer_cast(currentZNeighbor); - if (polyVoxZNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + if (currentZPNeighbor) { + auto polyVoxZPNeighbor = std::dynamic_pointer_cast(currentZPNeighbor); + if (polyVoxZPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { for (int x = 0; x < _volData->getWidth(); x++) { for (int y = 0; y < _volData->getHeight(); y++) { - uint8_t neighborValue = polyVoxZNeighbor->getVoxel(x, y, 0); + uint8_t neighborValue = polyVoxZPNeighbor->getVoxel(x, y, 0); _volData->setVoxelAt(x, y, _volData->getDepth() - 1, neighborValue); } } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 98d2b0665d..8e42884051 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -149,10 +149,13 @@ private: QSemaphore _threadRunning{1}; - // these are cached lookups of _xNeighborID, _yNeighborID, _zNeighborID - EntityItemWeakPointer _xNeighbor; - EntityItemWeakPointer _yNeighbor; - EntityItemWeakPointer _zNeighbor; + // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID + EntityItemWeakPointer _xNNeighbor; // neighor found by going along negative X axis + EntityItemWeakPointer _yNNeighbor; + EntityItemWeakPointer _zNNeighbor; + EntityItemWeakPointer _xPNeighbor; // neighor found by going along positive X axis + EntityItemWeakPointer _yPNeighbor; + EntityItemWeakPointer _zPNeighbor; void clearOutOfDateNeighbors(); void cacheNeighbors(); void copyUpperEdgesFromNeighbors(); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 199defbcb1..89284b2123 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -109,9 +109,12 @@ CONSTRUCT_PROPERTY(strokeWidths, QVector()), CONSTRUCT_PROPERTY(xTextureURL, ""), CONSTRUCT_PROPERTY(yTextureURL, ""), CONSTRUCT_PROPERTY(zTextureURL, ""), -CONSTRUCT_PROPERTY(xNeighborID, UNKNOWN_ENTITY_ID), -CONSTRUCT_PROPERTY(yNeighborID, UNKNOWN_ENTITY_ID), -CONSTRUCT_PROPERTY(zNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(xNNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(yNNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(zNNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(xPNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(yPNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(zPNeighborID, UNKNOWN_ENTITY_ID), _id(UNKNOWN_ENTITY_ID), @@ -381,9 +384,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_X_TEXTURE_URL, xTextureURL); CHECK_PROPERTY_CHANGE(PROP_Y_TEXTURE_URL, yTextureURL); CHECK_PROPERTY_CHANGE(PROP_Z_TEXTURE_URL, zTextureURL); - CHECK_PROPERTY_CHANGE(PROP_X_NEIGHBOR_ID, xNeighborID); - CHECK_PROPERTY_CHANGE(PROP_Y_NEIGHBOR_ID, yNeighborID); - CHECK_PROPERTY_CHANGE(PROP_Z_NEIGHBOR_ID, zNeighborID); + CHECK_PROPERTY_CHANGE(PROP_X_N_NEIGHBOR_ID, xNNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Y_N_NEIGHBOR_ID, yNNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Z_N_NEIGHBOR_ID, zNNeighborID); + CHECK_PROPERTY_CHANGE(PROP_X_P_NEIGHBOR_ID, xPNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Y_P_NEIGHBOR_ID, yPNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); @@ -528,9 +534,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(yTextureURL); COPY_PROPERTY_TO_QSCRIPTVALUE(zTextureURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(xNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(yNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(zNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(xNNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(yNNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(zNNeighborID); + + COPY_PROPERTY_TO_QSCRIPTVALUE(xPNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(yPNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(zPNeighborID); return properties; } @@ -631,9 +641,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(zTextureURL, QString, setZTextureURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(xNeighborID, EntityItemID, setXNeighborID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(yNeighborID, EntityItemID, setYNeighborID); - COPY_PROPERTY_FROM_QSCRIPTVALUE(zNeighborID, EntityItemID, setZNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(xNNeighborID, EntityItemID, setXNNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(yNNeighborID, EntityItemID, setYNNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(zNNeighborID, EntityItemID, setZNNeighborID); + + COPY_PROPERTY_FROM_QSCRIPTVALUE(xPNeighborID, EntityItemID, setXPNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(yPNeighborID, EntityItemID, setYPNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(zPNeighborID, EntityItemID, setZPNeighborID); _lastEdited = usecTimestampNow(); } @@ -867,9 +881,12 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, Ent APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, properties.getXTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, properties.getYTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, properties.getZTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_X_NEIGHBOR_ID, properties.getXNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Y_NEIGHBOR_ID, properties.getYNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Z_NEIGHBOR_ID, properties.getZNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, properties.getXNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, properties.getYNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, properties.getZNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, properties.getXPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, properties.getYPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, properties.getZPNeighborID()); } if (properties.getType() == EntityTypes::Line) { @@ -1133,9 +1150,12 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_TEXTURE_URL, QString, setXTextureURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_TEXTURE_URL, QString, setYTextureURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_TEXTURE_URL, QString, setZTextureURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_NEIGHBOR_ID, EntityItemID, setXNeighborID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_NEIGHBOR_ID, EntityItemID, setYNeighborID); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_NEIGHBOR_ID, EntityItemID, setZNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_N_NEIGHBOR_ID, EntityItemID, setXNNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_N_NEIGHBOR_ID, EntityItemID, setYNNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_N_NEIGHBOR_ID, EntityItemID, setZNNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_P_NEIGHBOR_ID, EntityItemID, setXPNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_P_NEIGHBOR_ID, EntityItemID, setYPNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_P_NEIGHBOR_ID, EntityItemID, setZPNeighborID); } if (properties.getType() == EntityTypes::Line) { @@ -1277,9 +1297,13 @@ void EntityItemProperties::markAllChanged() { _yTextureURLChanged = true; _zTextureURLChanged = true; - _xNeighborIDChanged = true; - _yNeighborIDChanged = true; - _zNeighborIDChanged = true; + _xNNeighborIDChanged = true; + _yNNeighborIDChanged = true; + _zNNeighborIDChanged = true; + + _xPNeighborIDChanged = true; + _yPNeighborIDChanged = true; + _zPNeighborIDChanged = true; } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 0e0c36ad11..942dc3edb1 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -161,9 +161,12 @@ public: DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString); DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString); - DEFINE_PROPERTY_REF(PROP_X_NEIGHBOR_ID, XNeighborID, xNeighborID, EntityItemID); - DEFINE_PROPERTY_REF(PROP_Y_NEIGHBOR_ID, YNeighborID, yNeighborID, EntityItemID); - DEFINE_PROPERTY_REF(PROP_Z_NEIGHBOR_ID, ZNeighborID, zNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_X_N_NEIGHBOR_ID, XNNeighborID, xNNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Y_N_NEIGHBOR_ID, YNNeighborID, yNNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Z_N_NEIGHBOR_ID, ZNNeighborID, zNNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_X_P_NEIGHBOR_ID, XPNeighborID, xPNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID); static QString getBackgroundModeString(BackgroundMode mode); @@ -330,9 +333,12 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, XNeighborID, xNeighborID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, YNeighborID, yNeighborID, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZNeighborID, zNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, XNNeighborID, xNNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, YNNeighborID, yNNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZNNeighborID, zNNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, XPNeighborID, xPNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, YPNeighborID, yPNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZPNeighborID, zPNeighborID, ""); properties.getStage().debugDump(); properties.getAtmosphere().debugDump(); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 9ad34fa69d..d929bdbef5 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -139,9 +139,12 @@ enum EntityPropertyList { PROP_VELOCITY_SPREAD, PROP_ACCELERATION_SPREAD, - PROP_X_NEIGHBOR_ID, // used by PolyVox - PROP_Y_NEIGHBOR_ID, // used by PolyVox - PROP_Z_NEIGHBOR_ID, // used by PolyVox + PROP_X_N_NEIGHBOR_ID, // used by PolyVox + PROP_Y_N_NEIGHBOR_ID, // used by PolyVox + PROP_Z_N_NEIGHBOR_ID, // used by PolyVox + PROP_X_P_NEIGHBOR_ID, // used by PolyVox + PROP_Y_P_NEIGHBOR_ID, // used by PolyVox + PROP_Z_P_NEIGHBOR_ID, // used by PolyVox //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 0da66e962b..6c53dbfa16 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -112,9 +112,12 @@ EntityItemProperties PolyVoxEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(xTextureURL, getXTextureURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(yTextureURL, getYTextureURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(zTextureURL, getZTextureURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(xNeighborID, getXNeighborID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(yNeighborID, getYNeighborID); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(zNeighborID, getZNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(xNNeighborID, getXNNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(yNNeighborID, getYNNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(zNNeighborID, getZNNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(xPNeighborID, getXPNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(yPNeighborID, getYPNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(zPNeighborID, getZPNeighborID); return properties; } @@ -127,9 +130,12 @@ bool PolyVoxEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(xTextureURL, setXTextureURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(yTextureURL, setYTextureURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(zTextureURL, setZTextureURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(xNeighborID, setXNeighborID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(yNeighborID, setYNeighborID); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(zNeighborID, setZNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(xNNeighborID, setXNNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(yNNeighborID, setYNNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(zNNeighborID, setZNNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(xPNeighborID, setXPNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(yPNeighborID, setYPNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(zPNeighborID, setZPNeighborID); if (somethingChanged) { bool wantDebug = false; @@ -157,9 +163,12 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat READ_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, QString, setXTextureURL); READ_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, QString, setYTextureURL); READ_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, QString, setZTextureURL); - READ_ENTITY_PROPERTY(PROP_X_NEIGHBOR_ID, EntityItemID, setXNeighborID); - READ_ENTITY_PROPERTY(PROP_Y_NEIGHBOR_ID, EntityItemID, setYNeighborID); - READ_ENTITY_PROPERTY(PROP_Z_NEIGHBOR_ID, EntityItemID, setZNeighborID); + READ_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, EntityItemID, setXNNeighborID); + READ_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, EntityItemID, setYNNeighborID); + READ_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, EntityItemID, setZNNeighborID); + READ_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, EntityItemID, setXPNeighborID); + READ_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, EntityItemID, setYPNeighborID); + READ_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, EntityItemID, setZPNeighborID); return bytesRead; } @@ -174,9 +183,12 @@ EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams requestedProperties += PROP_X_TEXTURE_URL; requestedProperties += PROP_Y_TEXTURE_URL; requestedProperties += PROP_Z_TEXTURE_URL; - requestedProperties += PROP_X_NEIGHBOR_ID; - requestedProperties += PROP_Y_NEIGHBOR_ID; - requestedProperties += PROP_Z_NEIGHBOR_ID; + requestedProperties += PROP_X_N_NEIGHBOR_ID; + requestedProperties += PROP_Y_N_NEIGHBOR_ID; + requestedProperties += PROP_Z_N_NEIGHBOR_ID; + requestedProperties += PROP_X_P_NEIGHBOR_ID; + requestedProperties += PROP_Y_P_NEIGHBOR_ID; + requestedProperties += PROP_Z_P_NEIGHBOR_ID; return requestedProperties; } @@ -195,9 +207,12 @@ void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeB APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, getXTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, getYTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, getZTextureURL()); - APPEND_ENTITY_PROPERTY(PROP_X_NEIGHBOR_ID, getXNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Y_NEIGHBOR_ID, getYNeighborID()); - APPEND_ENTITY_PROPERTY(PROP_Z_NEIGHBOR_ID, getZNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, getXNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, getYNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, getZNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, getXPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, getYPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, getZPNeighborID()); } void PolyVoxEntityItem::debugDump() const { diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 55118f9017..69bfb9323b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -104,15 +104,25 @@ class PolyVoxEntityItem : public EntityItem { virtual void setZTextureURL(QString zTextureURL) { _zTextureURL = zTextureURL; } virtual const QString& getZTextureURL() const { return _zTextureURL; } - virtual void setXNeighborID(const EntityItemID& xNeighborID) { _xNeighborID = xNeighborID; } - virtual void setXNeighborID(const QString& xNeighborID) { _xNeighborID = QUuid(xNeighborID); } - virtual const EntityItemID& getXNeighborID() const { return _xNeighborID; } - virtual void setYNeighborID(const EntityItemID& yNeighborID) { _yNeighborID = yNeighborID; } - virtual void setYNeighborID(const QString& yNeighborID) { _yNeighborID = QUuid(yNeighborID); } - virtual const EntityItemID& getYNeighborID() const { return _yNeighborID; } - virtual void setZNeighborID(const EntityItemID& zNeighborID) { _zNeighborID = zNeighborID; } - virtual void setZNeighborID(const QString& zNeighborID) { _zNeighborID = QUuid(zNeighborID); } - virtual const EntityItemID& getZNeighborID() const { return _zNeighborID; } + virtual void setXNNeighborID(const EntityItemID& xNNeighborID) { _xNNeighborID = xNNeighborID; } + virtual void setXNNeighborID(const QString& xNNeighborID) { _xNNeighborID = QUuid(xNNeighborID); } + virtual const EntityItemID& getXNNeighborID() const { return _xNNeighborID; } + virtual void setYNNeighborID(const EntityItemID& yNNeighborID) { _yNNeighborID = yNNeighborID; } + virtual void setYNNeighborID(const QString& yNNeighborID) { _yNNeighborID = QUuid(yNNeighborID); } + virtual const EntityItemID& getYNNeighborID() const { return _yNNeighborID; } + virtual void setZNNeighborID(const EntityItemID& zNNeighborID) { _zNNeighborID = zNNeighborID; } + virtual void setZNNeighborID(const QString& zNNeighborID) { _zNNeighborID = QUuid(zNNeighborID); } + virtual const EntityItemID& getZNNeighborID() const { return _zNNeighborID; } + + virtual void setXPNeighborID(const EntityItemID& xPNeighborID) { _xPNeighborID = xPNeighborID; } + virtual void setXPNeighborID(const QString& xPNeighborID) { _xPNeighborID = QUuid(xPNeighborID); } + virtual const EntityItemID& getXPNeighborID() const { return _xPNeighborID; } + virtual void setYPNeighborID(const EntityItemID& yPNeighborID) { _yPNeighborID = yPNeighborID; } + virtual void setYPNeighborID(const QString& yPNeighborID) { _yPNeighborID = QUuid(yPNeighborID); } + virtual const EntityItemID& getYPNeighborID() const { return _yPNeighborID; } + virtual void setZPNeighborID(const EntityItemID& zPNeighborID) { _zPNeighborID = zPNeighborID; } + virtual void setZPNeighborID(const QString& zPNeighborID) { _zPNeighborID = QUuid(zPNeighborID); } + virtual const EntityItemID& getZPNeighborID() const { return _zPNeighborID; } protected: @@ -129,9 +139,13 @@ class PolyVoxEntityItem : public EntityItem { QString _zTextureURL; // for non-edged surface styles, these are used to compute the high-axis edges - EntityItemID _xNeighborID{UNKNOWN_ENTITY_ID}; - EntityItemID _yNeighborID{UNKNOWN_ENTITY_ID}; - EntityItemID _zNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _xNNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _yNNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _zNNeighborID{UNKNOWN_ENTITY_ID}; + + EntityItemID _xPNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _yPNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _zPNeighborID{UNKNOWN_ENTITY_ID}; }; #endif // hifi_PolyVoxEntityItem_h From 4ab8ac29b90206a1b9cb6ffaa5568213044d00a7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 15:23:47 -0700 Subject: [PATCH 062/117] make terrain a doubly linked list --- examples/voxels.js | 82 +++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 9537d821fb..809b8f95a6 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -290,50 +290,56 @@ function addTerrainBlock() { // stitch together the terrain with x/y/z NeighorID properties ////////// - // link plots which are lower on the axes to this one - imXNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:-16, y:0, z:0})); - if (imXNeighborFor) { - var properties = Entities.getEntityProperties(imXNeighborFor); - properties.xNeighborID = polyVoxID; - Entities.editEntity(imXNeighborFor, properties); + // link neighbors to this plot + imXNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:16, y:0, z:0})); + imYNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:16, z:0})); + imZNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); + imXPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:-16, y:0, z:0})); + imYPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:-16, z:0})); + imZPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:-16})); + + if (imXNNeighborFor) { + var properties = Entities.getEntityProperties(imXNNeighborFor); + properties.xNNeighborID = polyVoxID; + Entities.editEntity(imXNNeighborFor, properties); + } + if (imYNNeighborFor) { + var properties = Entities.getEntityProperties(imYNNeighborFor); + properties.yNNeighborID = polyVoxID; + Entities.editEntity(imYNNeighborFor, properties); + } + if (imZNNeighborFor) { + var properties = Entities.getEntityProperties(imZNNeighborFor); + properties.zNNeighborID = polyVoxID; + Entities.editEntity(imZNNeighborFor, properties); } - imYNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:-16, z:0})); - if (imYNeighborFor) { - var properties = Entities.getEntityProperties(imYNeighborFor); - properties.yNeighborID = polyVoxID; - Entities.editEntity(imYNeighborFor, properties); + if (imXPNeighborFor) { + var properties = Entities.getEntityProperties(imXPNeighborFor); + properties.xPNeighborID = polyVoxID; + Entities.editEntity(imXPNeighborFor, properties); } - - imZNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:-16})); - if (imZNeighborFor) { - var properties = Entities.getEntityProperties(imZNeighborFor); - properties.zNeighborID = polyVoxID; - Entities.editEntity(imZNeighborFor, properties); + if (imYPNeighborFor) { + var properties = Entities.getEntityProperties(imYPNeighborFor); + properties.yPNeighborID = polyVoxID; + Entities.editEntity(imYPNeighborFor, properties); + } + if (imZPNeighborFor) { + var properties = Entities.getEntityProperties(imZPNeighborFor); + properties.zPNeighborID = polyVoxID; + Entities.editEntity(imZPNeighborFor, properties); } - // link this plot to plots which are higher on the axes - xNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:16, y:0, z:0})); - if (xNeighborFor) { - var properties = Entities.getEntityProperties(polyVoxID); - properties.xNeighborID = xNeighborFor; - Entities.editEntity(polyVoxID, properties); - } - - yNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:16, z:0})); - if (yNeighborFor) { - var properties = Entities.getEntityProperties(polyVoxID); - properties.yNeighborID = yNeighborFor; - Entities.editEntity(polyVoxID, properties); - } - - zNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); - if (zNeighborFor) { - var properties = Entities.getEntityProperties(polyVoxID); - properties.zNeighborID = zNeighborFor; - Entities.editEntity(polyVoxID, properties); - } + // link this plot to its neighbors + var properties = Entities.getEntityProperties(polyVoxID); + properties.xNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:-16, y:0, z:0})); + properties.yNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:-16, z:0})); + properties.zNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:-16})); + properties.xPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:16, y:0, z:0})); + properties.yPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:16, z:0})); + properties.zPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); + Entities.editEntity(polyVoxID, properties); return true; } From 9256917f150b85aa241eab5109de33dcef72340f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 16:21:12 -0700 Subject: [PATCH 063/117] rebake mesh when a neighbor changes --- .../src/RenderablePolyVoxEntityItem.cpp | 60 +++++++++++++++++++ .../src/RenderablePolyVoxEntityItem.h | 10 ++++ libraries/entities/src/PolyVoxEntityItem.h | 13 ++-- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1944398cf6..cd150dc44c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -746,6 +746,25 @@ bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t _volData->setVoxelAt(x, y, z, toValue); } + EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); + EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); + EntityItemPointer currentZNNeighbor = _zNNeighbor.lock(); + + if (result) { + if (x == 0 && currentXNNeighbor && currentXNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxXNNeighbor = std::dynamic_pointer_cast(currentXNNeighbor); + polyVoxXNNeighbor->rebakeMesh(); + } + if (y == 0 && currentYNNeighbor && currentYNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxYNNeighbor = std::dynamic_pointer_cast(currentYNNeighbor); + polyVoxYNNeighbor->rebakeMesh(); + } + if (z == 0 && currentZNNeighbor && currentZNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxZNNeighbor = std::dynamic_pointer_cast(currentZNNeighbor); + polyVoxZNNeighbor->rebakeMesh(); + } + } + return result; } @@ -1274,3 +1293,44 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { _threadRunning.release(); return; } + + +void RenderablePolyVoxEntityItem::setXNNeighborID(const EntityItemID& xNNeighborID) { + PolyVoxEntityItem::setXNNeighborID(xNNeighborID); +} + +void RenderablePolyVoxEntityItem::setYNNeighborID(const EntityItemID& yNNeighborID) { + PolyVoxEntityItem::setYNNeighborID(yNNeighborID); +} + +void RenderablePolyVoxEntityItem::setZNNeighborID(const EntityItemID& zNNeighborID) { + PolyVoxEntityItem::setZNNeighborID(zNNeighborID); +} + + +void RenderablePolyVoxEntityItem::setXPNeighborID(const EntityItemID& xPNeighborID) { + if (xPNeighborID != _xPNeighborID) { + PolyVoxEntityItem::setXPNeighborID(xPNeighborID); + rebakeMesh(); + } +} + +void RenderablePolyVoxEntityItem::setYPNeighborID(const EntityItemID& yPNeighborID) { + if (yPNeighborID != _yPNeighborID) { + PolyVoxEntityItem::setYPNeighborID(yPNeighborID); + rebakeMesh(); + } +} + +void RenderablePolyVoxEntityItem::setZPNeighborID(const EntityItemID& zPNeighborID) { + if (zPNeighborID != _zPNeighborID) { + PolyVoxEntityItem::setZPNeighborID(zPNeighborID); + rebakeMesh(); + } +} + + +void RenderablePolyVoxEntityItem::rebakeMesh() { + QReadLocker(&this->_volDataLock); + _volDataDirty = true; +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 8e42884051..ee94310e18 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -106,6 +106,16 @@ public: std::shared_ptr scene, render::PendingChanges& pendingChanges); + virtual void setXNNeighborID(const EntityItemID& xNNeighborID); + virtual void setYNNeighborID(const EntityItemID& yNNeighborID); + virtual void setZNNeighborID(const EntityItemID& zNNeighborID); + + virtual void setXPNeighborID(const EntityItemID& xPNeighborID); + virtual void setYPNeighborID(const EntityItemID& yPNeighborID); + virtual void setZPNeighborID(const EntityItemID& zPNeighborID); + + virtual void rebakeMesh(); + private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 69bfb9323b..20a0646c9b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -105,25 +105,26 @@ class PolyVoxEntityItem : public EntityItem { virtual const QString& getZTextureURL() const { return _zTextureURL; } virtual void setXNNeighborID(const EntityItemID& xNNeighborID) { _xNNeighborID = xNNeighborID; } - virtual void setXNNeighborID(const QString& xNNeighborID) { _xNNeighborID = QUuid(xNNeighborID); } + void setXNNeighborID(const QString& xNNeighborID) { setXNNeighborID(QUuid(xNNeighborID)); } virtual const EntityItemID& getXNNeighborID() const { return _xNNeighborID; } virtual void setYNNeighborID(const EntityItemID& yNNeighborID) { _yNNeighborID = yNNeighborID; } - virtual void setYNNeighborID(const QString& yNNeighborID) { _yNNeighborID = QUuid(yNNeighborID); } + void setYNNeighborID(const QString& yNNeighborID) { setYNNeighborID(QUuid(yNNeighborID)); } virtual const EntityItemID& getYNNeighborID() const { return _yNNeighborID; } virtual void setZNNeighborID(const EntityItemID& zNNeighborID) { _zNNeighborID = zNNeighborID; } - virtual void setZNNeighborID(const QString& zNNeighborID) { _zNNeighborID = QUuid(zNNeighborID); } + void setZNNeighborID(const QString& zNNeighborID) { setZNNeighborID(QUuid(zNNeighborID)); } virtual const EntityItemID& getZNNeighborID() const { return _zNNeighborID; } virtual void setXPNeighborID(const EntityItemID& xPNeighborID) { _xPNeighborID = xPNeighborID; } - virtual void setXPNeighborID(const QString& xPNeighborID) { _xPNeighborID = QUuid(xPNeighborID); } + void setXPNeighborID(const QString& xPNeighborID) { setXPNeighborID(QUuid(xPNeighborID)); } virtual const EntityItemID& getXPNeighborID() const { return _xPNeighborID; } virtual void setYPNeighborID(const EntityItemID& yPNeighborID) { _yPNeighborID = yPNeighborID; } - virtual void setYPNeighborID(const QString& yPNeighborID) { _yPNeighborID = QUuid(yPNeighborID); } + void setYPNeighborID(const QString& yPNeighborID) { setYPNeighborID(QUuid(yPNeighborID)); } virtual const EntityItemID& getYPNeighborID() const { return _yPNeighborID; } virtual void setZPNeighborID(const EntityItemID& zPNeighborID) { _zPNeighborID = zPNeighborID; } - virtual void setZPNeighborID(const QString& zPNeighborID) { _zPNeighborID = QUuid(zPNeighborID); } + void setZPNeighborID(const QString& zPNeighborID) { setZPNeighborID(QUuid(zPNeighborID)); } virtual const EntityItemID& getZPNeighborID() const { return _zPNeighborID; } + virtual void rebakeMesh() {}; protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes From 8608e4f2b0cadcf9e441b21444465026cb38c967 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 16:51:55 -0700 Subject: [PATCH 064/117] small bug-fixes --- examples/voxels.js | 9 ++------- .../src/RenderablePolyVoxEntityItem.cpp | 18 ++++++++---------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 809b8f95a6..00d49ae711 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -404,13 +404,8 @@ function attemptVoxelChange(pickRayDir, intersection) { var ids; - if (addingSpheres || deletingSpheres) { - ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); - } else { - ids = [intersection.entityID]; - } - - print("ids = " + ids); + ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); + // print("ids = " + ids); var success = false; for (var i = 0; i < ids.length; i++) { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index cd150dc44c..9d6648ee04 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -164,6 +164,8 @@ bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) return false; } + cacheNeighbors(); + _volDataLock.lockForWrite(); bool result = setVoxelInternal(x, y, z, toValue); if (result) { @@ -975,10 +977,6 @@ void RenderablePolyVoxEntityItem::clearOutOfDateNeighbors() { } void RenderablePolyVoxEntityItem::cacheNeighbors() { - if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { - return; - } - clearOutOfDateNeighbors(); EntityTreeElement* element = getElement(); EntityTree* tree = element ? element->getTree() : nullptr; @@ -986,14 +984,14 @@ void RenderablePolyVoxEntityItem::cacheNeighbors() { return; } - if (_xPNeighborID != UNKNOWN_ENTITY_ID && _xPNeighbor.expired()) { - _xPNeighbor = tree->findEntityByID(_xPNeighborID); + if (_xNNeighborID != UNKNOWN_ENTITY_ID && _xNNeighbor.expired()) { + _xNNeighbor = tree->findEntityByID(_xNNeighborID); } - if (_yPNeighborID != UNKNOWN_ENTITY_ID && _yPNeighbor.expired()) { - _yPNeighbor = tree->findEntityByID(_yPNeighborID); + if (_yNNeighborID != UNKNOWN_ENTITY_ID && _yNNeighbor.expired()) { + _yNNeighbor = tree->findEntityByID(_yNNeighborID); } - if (_zPNeighborID != UNKNOWN_ENTITY_ID && _zPNeighbor.expired()) { - _zPNeighbor = tree->findEntityByID(_zPNeighborID); + if (_zNNeighborID != UNKNOWN_ENTITY_ID && _zNNeighbor.expired()) { + _zNNeighbor = tree->findEntityByID(_zNNeighborID); } if (_xPNeighborID != UNKNOWN_ENTITY_ID && _xPNeighbor.expired()) { From 7b4cb8655c7909220f597dac0cd65d5b2ed9e5d9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 1 Sep 2015 17:57:01 -0700 Subject: [PATCH 065/117] First pass integration of new anim system into rig. --- interface/src/Menu.cpp | 2 + interface/src/Menu.h | 1 + interface/src/avatar/MyAvatar.cpp | 84 ++-------- interface/src/avatar/MyAvatar.h | 11 +- interface/src/avatar/SkeletonModel.cpp | 4 + interface/src/avatar/SkeletonModel.h | 2 + libraries/animation/src/AnimSkeleton.cpp | 16 ++ libraries/animation/src/AnimSkeleton.h | 1 + libraries/animation/src/Rig.cpp | 187 +++++++++++++++++------ libraries/animation/src/Rig.h | 12 ++ tests/animation/src/data/avatar.json | 36 +---- 11 files changed, 198 insertions(+), 158 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4696463181..854f6fefe9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -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, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 94e49abcc7..c050e0b7a3 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -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"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 45c22dc35c..b72dd5c813 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -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 joints; - for (auto& joint : geom.joints) { - joints.push_back(joint); - } - AnimPose geometryOffset(_skeletonModel.getGeometry()->getFBXGeometry().offset); - _animSkeleton = make_shared(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) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b48964d02c..0283310e71 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -16,9 +16,6 @@ #include #include -#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 _headBoneSet; RigPointer _rig; bool _prevShouldDrawHead; - - std::shared_ptr _animNode; - std::shared_ptr _animSkeleton; - std::unique_ptr _animLoader; - AnimVariantMap _animVars; }; #endif // hifi_MyAvatar_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index acd2f038f4..0e29986bd1 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -571,3 +571,7 @@ bool SkeletonModel::hasSkeleton() { void SkeletonModel::onInvalidate() { } + +void SkeletonModel::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { + _rig->initAnimGraph(url, fbxGeometry); +} diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 4ae615eadd..6b04d36de0 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -106,6 +106,8 @@ public: virtual void onInvalidate() override; + void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry); + signals: void skeletonLoaded(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index d650c0b964..d8efe55f5d 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -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 diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index cc0d0b1c70..f266fe04b3 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -61,6 +61,7 @@ public: #ifndef NDEBUG void dump() const; + void dump(const AnimPoseVec& poses) const; #endif protected: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 8adb53024c..356afe6620 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -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 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 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(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; + }); +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index a574807eb4..1b7bf72e88 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -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 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 _runningAnimations; bool _enableRig; + bool _enableAnimGraph; glm::vec3 _lastFront; glm::vec3 _lastPosition; + + std::shared_ptr _animNode; + std::shared_ptr _animSkeleton; + std::unique_ptr _animLoader; + AnimVariantMap _animVars; }; #endif /* defined(__hifi__Rig__) */ diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index 5647f74a7f..0988e40ca8 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -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", From 9318661bc38ac18cf6c8d76d411a2c25cf302d5c Mon Sep 17 00:00:00 2001 From: Raffi Bedikian Date: Tue, 1 Sep 2015 23:54:48 -0700 Subject: [PATCH 066/117] Add algorithm comments to FXAA shader --- libraries/render-utils/src/fxaa.slf | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/fxaa.slf b/libraries/render-utils/src/fxaa.slf index 1f10756ce6..69596e13ce 100644 --- a/libraries/render-utils/src/fxaa.slf +++ b/libraries/render-utils/src/fxaa.slf @@ -29,16 +29,28 @@ in vec2 varTexcoord; out vec4 outFragColor; void main() { + // filter width limit for dependent "two-tap" texture samples float FXAA_SPAN_MAX = 8.0; - float FXAA_REDUCE_MUL = 1.0/8.0; - float FXAA_REDUCE_MIN = (1.0/128.0); + // local contrast multiplier for performing AA + // higher = sharper, but setting this value too high will cause near-vertical and near-horizontal edges to fail + // see "fxaaQualityEdgeThreshold" + float FXAA_REDUCE_MUL = 1.0/8.0; + + // luminance threshold for processing dark colors + // see "fxaaQualityEdgeThresholdMin" + float FXAA_REDUCE_MIN = (1.0/128.0); + + // fetch raw RGB values for nearby locations + // sampling pattern is "five on a die" (each diagonal direction and the center) + // computing the coordinates for these texture reads could be moved to the vertex shader for speed if needed vec3 rgbNW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, -1.0) * texcoordOffset)).xyz; vec3 rgbNE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, -1.0) * texcoordOffset)).xyz; vec3 rgbSW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, +1.0) * texcoordOffset)).xyz; vec3 rgbSE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, +1.0) * texcoordOffset)).xyz; vec3 rgbM = texture2D(colorTexture, varTexcoord).xyz; + // convert RGB values to luminance vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); float lumaNE = dot(rgbNE, luma); @@ -46,20 +58,23 @@ void main() { float lumaSE = dot(rgbSE, luma); float lumaM = dot( rgbM, luma); + // luma range of local neighborhood float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + // direction perpendicular to local luma gradient vec2 dir; dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - + + // compute clamped direction offset for additional "two-tap" samples + // longer vector = blurry, shorter vector = sharp float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce); - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texcoordOffset; + // perform additional texture sampling perpendicular to gradient vec3 rgbA = (1.0/2.0) * ( texture2D(colorTexture, varTexcoord + dir * (1.0/3.0 - 0.5)).xyz + texture2D(colorTexture, varTexcoord + dir * (2.0/3.0 - 0.5)).xyz); @@ -68,6 +83,8 @@ void main() { texture2D(colorTexture, varTexcoord + dir * (3.0/3.0 - 0.5)).xyz); float lumaB = dot(rgbB, luma); + // compare luma of new samples to the luma range of the original neighborhood + // if the new samples exceed this range, just use the first two samples instead of all four if (lumaB < lumaMin || lumaB > lumaMax) { outFragColor.xyz=rgbA; } else { From f54d924f4ab79d1d494db0371ea032c71b863407 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 2 Sep 2015 09:29:51 -0700 Subject: [PATCH 067/117] make sure intersected entity is in the list of possible polyvoxes to adjust --- examples/voxels.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/voxels.js b/examples/voxels.js index 00d49ae711..c5564fe391 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -405,6 +405,10 @@ function attemptVoxelChange(pickRayDir, intersection) { var ids; ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); + if (ids.indexOf(intersection.entityID) < 0) { + ids.push(intersection.entityID); + } + // print("ids = " + ids); var success = false; From 16ee5199e88f3db7679123f51c69cad8fee99c75 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 2 Sep 2015 09:30:32 -0700 Subject: [PATCH 068/117] change how polyvox neighbors are notified of updates --- .../src/RenderablePolyVoxEntityItem.cpp | 82 +++++++++++++------ .../src/RenderablePolyVoxEntityItem.h | 1 + 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 9d6648ee04..6fb5648fe0 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -164,8 +164,6 @@ bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) return false; } - cacheNeighbors(); - _volDataLock.lockForWrite(); bool result = setVoxelInternal(x, y, z, toValue); if (result) { @@ -748,25 +746,6 @@ bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t _volData->setVoxelAt(x, y, z, toValue); } - EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); - EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); - EntityItemPointer currentZNNeighbor = _zNNeighbor.lock(); - - if (result) { - if (x == 0 && currentXNNeighbor && currentXNNeighbor->getType() == EntityTypes::PolyVox) { - auto polyVoxXNNeighbor = std::dynamic_pointer_cast(currentXNNeighbor); - polyVoxXNNeighbor->rebakeMesh(); - } - if (y == 0 && currentYNNeighbor && currentYNNeighbor->getType() == EntityTypes::PolyVox) { - auto polyVoxYNNeighbor = std::dynamic_pointer_cast(currentYNNeighbor); - polyVoxYNNeighbor->rebakeMesh(); - } - if (z == 0 && currentZNNeighbor && currentZNNeighbor->getType() == EntityTypes::PolyVox) { - auto polyVoxZNNeighbor = std::dynamic_pointer_cast(currentZNNeighbor); - polyVoxZNNeighbor->rebakeMesh(); - } - } - return result; } @@ -801,6 +780,10 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { } + + + + // take compressed data and expand it into _volData. void RenderablePolyVoxEntityItem::decompressVolumeDataAsync() { _voxelDataLock.lockForRead(); @@ -1136,7 +1119,7 @@ void RenderablePolyVoxEntityItem::getMeshAsync() { _meshLock.unlock(); _volDataDirty = false; _volDataLock.unlock(); - + bonkNeighbors(); _threadRunning.release(); } @@ -1294,15 +1277,42 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { void RenderablePolyVoxEntityItem::setXNNeighborID(const EntityItemID& xNNeighborID) { - PolyVoxEntityItem::setXNNeighborID(xNNeighborID); + if (xNNeighborID != _xNNeighborID) { + PolyVoxEntityItem::setXNNeighborID(xNNeighborID); + cacheNeighbors(); + EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); + if (currentXNNeighbor && currentXNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxXNNeighbor = std::dynamic_pointer_cast(currentXNNeighbor); + polyVoxXNNeighbor->setXPNeighborID(_id); + polyVoxXNNeighbor->rebakeMesh(); + } + } } void RenderablePolyVoxEntityItem::setYNNeighborID(const EntityItemID& yNNeighborID) { - PolyVoxEntityItem::setYNNeighborID(yNNeighborID); + if (yNNeighborID != _yNNeighborID) { + PolyVoxEntityItem::setYNNeighborID(yNNeighborID); + cacheNeighbors(); + EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); + if (currentYNNeighbor && currentYNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxYNNeighbor = std::dynamic_pointer_cast(currentYNNeighbor); + polyVoxYNNeighbor->setYPNeighborID(_id); + polyVoxYNNeighbor->rebakeMesh(); + } + } } void RenderablePolyVoxEntityItem::setZNNeighborID(const EntityItemID& zNNeighborID) { - PolyVoxEntityItem::setZNNeighborID(zNNeighborID); + if (zNNeighborID != _zNNeighborID) { + PolyVoxEntityItem::setZNNeighborID(zNNeighborID); + cacheNeighbors(); + EntityItemPointer currentZNNeighbor = _yNNeighbor.lock(); + if (currentZNNeighbor && currentZNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxZNNeighbor = std::dynamic_pointer_cast(currentZNNeighbor); + polyVoxZNNeighbor->setZPNeighborID(_id); + polyVoxZNNeighbor->rebakeMesh(); + } + } } @@ -1332,3 +1342,25 @@ void RenderablePolyVoxEntityItem::rebakeMesh() { QReadLocker(&this->_volDataLock); _volDataDirty = true; } + +void RenderablePolyVoxEntityItem::bonkNeighbors() { + clearOutOfDateNeighbors(); + cacheNeighbors(); + + EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); + EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); + EntityItemPointer currentZNNeighbor = _zNNeighbor.lock(); + + if (currentXNNeighbor && currentXNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxXNNeighbor = std::dynamic_pointer_cast(currentXNNeighbor); + polyVoxXNNeighbor->rebakeMesh(); + } + if (currentYNNeighbor && currentYNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxYNNeighbor = std::dynamic_pointer_cast(currentYNNeighbor); + polyVoxYNNeighbor->rebakeMesh(); + } + if (currentZNNeighbor && currentZNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxZNNeighbor = std::dynamic_pointer_cast(currentZNNeighbor); + polyVoxZNNeighbor->rebakeMesh(); + } +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index ee94310e18..3b10a12bd2 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -169,6 +169,7 @@ private: void clearOutOfDateNeighbors(); void cacheNeighbors(); void copyUpperEdgesFromNeighbors(); + void bonkNeighbors(); }; From d13a188dde74c805e406b0d36560cea74dd58fb9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 10:31:45 -0700 Subject: [PATCH 069/117] Compile fixes and added test case to verify onDone and onLoop triggers --- tests/animation/src/AnimTests.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 91bb1ce5bb..806560d96f 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -40,7 +40,7 @@ void AnimTests::testClipInternalState() { AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); QVERIFY(clip.getID() == id); - QVERIFY(clip.getType() == AnimNode::ClipType); + QVERIFY(clip.getType() == AnimNode::Type::Clip); QVERIFY(clip._url == url); QVERIFY(clip._startFrame == startFrame); @@ -55,7 +55,7 @@ static float framesToSec(float secs) { } void AnimTests::testClipEvaulate() { - std::string id = "my clip node"; + std::string id = "myClipNode"; std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 22.0f; @@ -67,21 +67,30 @@ void AnimTests::testClipEvaulate() { AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag); - clip.evaluate(vars, framesToSec(10.0f)); + AnimNode::Triggers triggers; + clip.evaluate(vars, framesToSec(10.0f), triggers); QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, EPSILON); // does it loop? - clip.evaluate(vars, framesToSec(11.0f)); + triggers.clear(); + clip.evaluate(vars, framesToSec(11.0f), triggers); QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); + // did we receive a loop trigger? + QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnLoop") != triggers.end()); + // does it pause at end? + triggers.clear(); clip.setLoopFlagVar("FalseVar"); - clip.evaluate(vars, framesToSec(20.0f)); + clip.evaluate(vars, framesToSec(20.0f), triggers); QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, EPSILON); + + // did we receive a done trigger? + QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnDone") != triggers.end()); } void AnimTests::testClipEvaulateWithVars() { - std::string id = "my clip node"; + std::string id = "myClipNode"; std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 22.0f; @@ -105,7 +114,8 @@ void AnimTests::testClipEvaulateWithVars() { clip.setTimeScaleVar("timeScale2"); clip.setLoopFlagVar("loopFlag2"); - clip.evaluate(vars, framesToSec(0.1f)); + AnimNode::Triggers triggers; + clip.evaluate(vars, framesToSec(0.1f), triggers); // verify that the values from the AnimVariantMap made it into the clipNode's // internal state @@ -137,11 +147,11 @@ void AnimTests::testLoader() { QVERIFY((bool)node); QVERIFY(node->getID() == "blend"); - QVERIFY(node->getType() == AnimNode::BlendLinearType); + QVERIFY(node->getType() == AnimNode::Type::BlendLinear); QVERIFY((bool)node); QVERIFY(node->getID() == "blend"); - QVERIFY(node->getType() == AnimNode::BlendLinearType); + QVERIFY(node->getType() == AnimNode::Type::BlendLinear); auto blend = std::static_pointer_cast(node); QVERIFY(blend->_alpha == 0.5f); From 4a749a040379711f26061de5905e88d4ed9850fe Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 10:41:04 -0700 Subject: [PATCH 070/117] Updated MyAvatar to use animGraph from gist instead of local host. --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7d9d68714a..8e07aa3442 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1224,8 +1224,8 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { // 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"); + //auto graphUrl = QUrl("http://localhost:8000/avatar.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/a6e3754beef1524f95bae178066bae3b2f839952/avatar.json"); _skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); } From 1ae22268ac1e091811440ebad709f5cb230c1424 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 11:04:52 -0700 Subject: [PATCH 071/117] Added comment to AnimStateMachine header. --- libraries/animation/src/AnimNodeLoader.cpp | 2 +- libraries/animation/src/AnimStateMachine.h | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index abb5b1b5f2..5863380d49 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -1,5 +1,5 @@ // -// AnimNodeLoader.h +// AnimNodeLoader.cpp // // Copyright 2015 High Fidelity, Inc. // diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index e48e08e96e..066dfabbbb 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -14,6 +14,23 @@ #include #include "AnimNode.h" +// State Machine for transitioning between children AnimNodes +// +// This is mechinisim for playing animations and smoothly interpolating/fading +// between them. A StateMachine has a set of States, which typically reference +// child AnimNodes. Each State has a list of Transitions, which are evaluated +// to determine when we should switch to a new State. Parameters for the smooth +// interpolation/fading are read from the State that you are transitioning to. +// +// The currentState can be set directly via the setCurrentStateVar() and will override +// any State transitions. +// +// Each State has two parameters that can be changed via AnimVars, +// * interpTarget - (frames) The destination frame of the interpolation. i.e. the first frame of the animation that will +// visible after interpolation is complete. +// * interpDuration - (frames) The total length of time it will take to interp between the current pose and the +// interpTarget frame. + class AnimStateMachine : public AnimNode { public: friend class AnimNodeLoader; From 1e6198cf597fef194fc657ce2a992c01cd705409 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 2 Sep 2015 11:29:37 -0700 Subject: [PATCH 072/117] update dhydra grab to grab near and far objects, and removed grabbing logic from toybox.js --- examples/controllers/hydra/hydraGrab.js | 524 ++++++++++++------------ examples/controllers/toybox.js | 233 +---------- 2 files changed, 267 insertions(+), 490 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 34484eb9e8..dfb0fdcadf 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -1,307 +1,315 @@ -// // hydraGrab.js // examples // -// Created by Clément Brisset on 4/24/14. -// Updated by Eric Levin on 5/14/15. -// Copyright 2014 High Fidelity, Inc. +// Created by Eric Levin on 9/2/15 +// Copyright 2015 High Fidelity, Inc. // -// This script allows you to grab and move/rotate physical objects with the hydra -// -// Using the hydras : -// grab physical entities with the right trigger +// Grab's physically moveable entities with the hydra- works for either near or far objects. User can also grab a far away object and drag it towards them by pressing the "4" button on either the left or ride controller. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); +var rightTriggerAction = RIGHT_HAND_CLICK; +var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK"); +var leftTriggerAction = LEFT_HAND_CLICK; -var entityProps, currentPosition, currentVelocity, currentRotation, distanceToTarget, velocityTowardTarget, desiredVelocity; -var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance; -var LEFT = 0; -var RIGHT = 1; -var LASER_WIDTH = 3; -var LASER_COLOR = { - red: 50, - green: 150, - blue: 200 -}; -var LASER_HOVER_COLOR = { - red: 200, - green: 50, - blue: 50 -}; - -var DROP_DISTANCE = 5.0; -var DROP_COLOR = { - red: 200, - green: 200, - blue: 200 -}; - -var FULL_STRENGTH = 0.05; -var LASER_LENGTH_FACTOR = 500; -var CLOSE_ENOUGH = 0.001; -var SPRING_RATE = 1.5; -var DAMPING_RATE = 0.8; -var SCREEN_TO_METERS = 0.001; -var DISTANCE_SCALE_FACTOR = 1000 - -var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); -var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); - -function getRayIntersection(pickRay) { - var intersection = Entities.findRayIntersection(pickRay, true); - return intersection; -} - - -function controller(side) { - this.triggerHeld = false; - this.triggerThreshold = 0.9; - this.side = side; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.trigger = side; - this.originalGravity = { +var ZERO_VEC = { x: 0, y: 0, z: 0 - }; +} +var LINE_LENGTH = 500; +var THICK_LINE_WIDTH = 7; +var THIN_LINE_WIDTH = 2; - this.laser = Overlays.addOverlay("line3d", { - start: { - x: 0, - y: 0, - z: 0 - }, - end: { - x: 0, - y: 0, - z: 0 - }, - color: LASER_COLOR, - alpha: 1, - lineWidth: LASER_WIDTH, - anchor: "MyAvatar" - }); +var NO_INTERSECT_COLOR = { + red: 10, + green: 10, + blue: 255 +}; +var INTERSECT_COLOR = { + red: 250, + green: 10, + blue: 10 +}; - this.dropLine = Overlays.addOverlay("line3d", { - color: DROP_COLOR, - alpha: 1, - visible: false, - lineWidth: 2 - }); +var GRAB_RADIUS = 2; +var GRAB_COLOR = { + red: 250, + green: 10, + blue: 250 +}; +var SHOW_LINE_THRESHOLD = 0.2; +var DISTANCE_HOLD_THRESHOLD = 0.8; - this.update = function(deltaTime) { - this.updateControllerState(); - this.moveLaser(); - this.checkTrigger(); - this.checkEntityIntersection(); - if (this.grabbing) { - this.updateEntity(deltaTime); - } +var right4Action = 18; +var left4Action = 17; - this.oldPalmPosition = this.palmPosition; - this.oldTipPosition = this.tipPosition; - } +var TRACTOR_BEAM_VELOCITY_THRESHOLD = 0.5; - this.updateEntity = function(deltaTime) { - this.dControllerPosition = Vec3.subtract(this.palmPosition, this.oldPalmPosition); - this.cameraEntityDistance = Vec3.distance(Camera.getPosition(), this.currentPosition); - this.targetPosition = Vec3.sum(this.targetPosition, Vec3.multiply(this.dControllerPosition, this.cameraEntityDistance * SCREEN_TO_METERS * DISTANCE_SCALE_FACTOR)); +var RIGHT = 1; +var LEFT = 0; +var rightController = new controller(RIGHT, rightTriggerAction, right4Action, "right") +var leftController = new controller(LEFT, leftTriggerAction, left4Action, "left") - this.entityProps = Entities.getEntityProperties(this.grabbedEntity); - this.currentPosition = this.entityProps.position; - this.currentVelocity = this.entityProps.velocity; - - var dPosition = Vec3.subtract(this.targetPosition, this.currentPosition); - this.distanceToTarget = Vec3.length(dPosition); - if (this.distanceToTarget > CLOSE_ENOUGH) { - // compute current velocity in the direction we want to move - this.velocityTowardTarget = Vec3.dot(this.currentVelocity, Vec3.normalize(dPosition)); - this.velocityTowardTarget = Vec3.multiply(Vec3.normalize(dPosition), this.velocityTowardTarget); - // compute the speed we would like to be going toward the target position - - this.desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE); - // compute how much we want to add to the existing velocity - this.addedVelocity = Vec3.subtract(this.desiredVelocity, this.velocityTowardTarget); - //If target is to far, roll off force as inverse square of distance - if(this.distanceToTarget/ this.cameraEntityDistance > FULL_STRENGTH) { - this.addedVelocity = Vec3.multiply(this.addedVelocity, Math.pow(FULL_STRENGTH/ this.distanceToTarget, 2.0)); - } - this.newVelocity = Vec3.sum(this.currentVelocity, this.addedVelocity); - this.newVelocity = Vec3.subtract(this.newVelocity, Vec3.multiply(this.newVelocity, DAMPING_RATE)); +function controller(side, triggerAction, pullAction, hand) { + this.hand = hand; + if (hand === "right") { + this.getHandPosition = MyAvatar.getRightPalmPosition; + this.getHandRotation = MyAvatar.getRightPalmRotation; } else { - this.newVelocity = { - x: 0, - y: 0, - z: 0 - }; - } - this.transformedAngularVelocity = Controller.getSpatialControlRawAngularVelocity(this.tip); - this.transformedAngularVelocity = Vec3.multiplyQbyV(Camera.getOrientation(), this.transformedAngularVelocity); - Entities.editEntity(this.grabbedEntity, { - velocity: this.newVelocity, - angularVelocity: this.transformedAngularVelocity + this.getHandPosition = MyAvatar.getLeftPalmPosition; + this.getHandRotation = MyAvatar.getLeftPalmRotation; + } + this.triggerAction = triggerAction; + this.pullAction = pullAction; + this.actionID = null; + this.tractorBeamActive = false; + this.distanceHolding = false; + this.triggerValue = 0; + this.prevTriggerValue = 0; + this.palm = 2 * side; + this.tip = 2 * side + 1; + this.pointer = Entities.addEntity({ + type: "Line", + name: "pointer", + color: NO_INTERSECT_COLOR, + dimensions: { + x: 1000, + y: 1000, + z: 1000 + }, + visible: false, + }); +} + + +controller.prototype.updateLine = function() { + var handPosition = Controller.getSpatialControlPosition(this.palm); + var direction = Controller.getSpatialControlNormal(this.tip); + + Entities.editEntity(this.pointer, { + position: handPosition, + linePoints: [ + ZERO_VEC, + Vec3.multiply(direction, LINE_LENGTH) + ] }); - this.updateDropLine(this.targetPosition); - - } - - - this.updateControllerState = function() { - this.palmPosition = Controller.getSpatialControlPosition(this.palm); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); - this.triggerValue = Controller.getTriggerValue(this.trigger); - } - - this.checkTrigger = function() { - if (this.triggerValue > this.triggerThreshold && !this.triggerHeld) { - this.triggerHeld = true; - } else if (this.triggerValue < this.triggerThreshold && this.triggerHeld) { - this.triggerHeld = false; - if (this.grabbing) { - this.release(); - } + //only check if we havent already grabbed an object + if (this.distanceHolding) { + return; } - } + + //move origin a bit away from hand so nothing gets in way + var origin = Vec3.sum(handPosition, direction); + if (this.checkForIntersections(origin, direction)) { + Entities.editEntity(this.pointer, { + color: INTERSECT_COLOR, + }); + } else { + Entities.editEntity(this.pointer, { + color: NO_INTERSECT_COLOR, + }); + } +} - this.updateDropLine = function(position) { - - Overlays.editOverlay(this.dropLine, { - visible: true, - start: { - x: position.x, - y: position.y + DROP_DISTANCE, - z: position.z - }, - end: { - x: position.x, - y: position.y - DROP_DISTANCE, - z: position.z - } - }); - - } - - this.checkEntityIntersection = function() { +controller.prototype.checkForIntersections = function(origin, direction) { var pickRay = { - origin: this.palmPosition, - direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) + origin: origin, + direction: direction }; - var intersection = getRayIntersection(pickRay, true); - if (intersection.intersects && intersection.properties.collisionsWillMove) { - this.laserWasHovered = true; - if (this.triggerHeld && !this.grabbing) { - this.grab(intersection.entityID); - } - Overlays.editOverlay(this.laser, { - color: LASER_HOVER_COLOR - }); - } else if (this.laserWasHovered) { - this.laserWasHovered = false; - Overlays.editOverlay(this.laser, { - color: LASER_COLOR - }); + + var intersection = Entities.findRayIntersection(pickRay, true); + + if (intersection.intersects) { + this.distanceToEntity = Vec3.distance(origin, intersection.properties.position); + Entities.editEntity(this.pointer, { + linePoints: [ + ZERO_VEC, + Vec3.multiply(direction, this.distanceToEntity) + ] + }); + this.grabbedEntity = intersection.entityID; + return true; } - } + return false; +} - this.grab = function(entityId) { - this.grabbing = true; - this.grabbedEntity = entityId; - this.entityProps = Entities.getEntityProperties(this.grabbedEntity); - this.targetPosition = this.entityProps.position; - this.currentPosition = this.targetPosition; - this.oldPalmPosition = this.palmPosition; - this.originalGravity = this.entityProps.gravity; - Entities.editEntity(this.grabbedEntity, { - gravity: { - x: 0, - y: 0, - z: 0 - } - }); - Overlays.editOverlay(this.laser, { - visible: false - }); - Audio.playSound(grabSound, { - position: this.entityProps.position, - volume: 0.25 - }); - } +controller.prototype.attemptMove = function() { + if (this.tractorBeamActive) { + return; + } + if (this.grabbedEntity || this.distanceHolding) { + var handPosition = Controller.getSpatialControlPosition(this.palm); + var direction = Controller.getSpatialControlNormal(this.tip); - this.release = function() { - this.grabbing = false; + var newPosition = Vec3.sum(handPosition, Vec3.multiply(direction, this.distanceToEntity)) + this.distanceHolding = true; + //TO DO : USE SPRING ACTION UPDATE FOR MOVING + if (this.actionID === null) { + this.actionID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: newPosition, + linearTimeScale: 0.1 + }); + } else { + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: newPosition + }); + } + } + +} + +controller.prototype.showPointer = function() { + Entities.editEntity(this.pointer, { + visible: true + }); + +} + +controller.prototype.hidePointer = function() { + Entities.editEntity(this.pointer, { + visible: false + }); +} + + +controller.prototype.letGo = function() { + Entities.deleteAction(this.grabbedEntity, this.actionID); this.grabbedEntity = null; - Overlays.editOverlay(this.laser, { - visible: true - }); - Overlays.editOverlay(this.dropLine, { - visible: false - }); + this.actionID = null; + this.distanceHolding = false; + this.tractorBeamActive = false; + this.checkForEntityArrival = false; +} - Audio.playSound(releaseSound, { - position: this.entityProps.position, - volume: 0.25 - }); - - // only restore the original gravity if it's not zero. This is to avoid... - // 1. interface A grabs an entity and locally saves off its gravity - // 2. interface A sets the entity's gravity to zero - // 3. interface B grabs the entity and saves off its gravity (which is zero) - // 4. interface A releases the entity and puts the original gravity back - // 5. interface B releases the entity and puts the original gravity back (to zero) - if(vectorIsZero(this.originalGravity)) { - Entities.editEntity(this.grabbedEntity, { - gravity: this.originalGravity - }); +controller.prototype.update = function() { + if (this.tractorBeamActive && this.checkForEntityArrival) { + var entityVelocity = Entities.getEntityProperties(this.grabbedEntity).velocity + if (Vec3.length(entityVelocity) < TRACTOR_BEAM_VELOCITY_THRESHOLD) { + this.letGo(); + } + return; + } + this.triggerValue = Controller.getActionValue(this.triggerAction); + if (this.triggerValue > SHOW_LINE_THRESHOLD && this.prevTriggerValue < SHOW_LINE_THRESHOLD) { + //First check if an object is within close range and then run the close grabbing logic + if (this.checkForInRangeObject()) { + this.grabEntity(); + } else { + this.showPointer(); + this.shouldDisplayLine = true; + } + } else if (this.triggerValue < SHOW_LINE_THRESHOLD && this.prevTriggerValue > SHOW_LINE_THRESHOLD) { + this.hidePointer(); + this.letGo(); + this.shouldDisplayLine = false; } - } - this.moveLaser = function() { - var inverseRotation = Quat.inverse(MyAvatar.orientation); - var startPosition = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.palmPosition, MyAvatar.position)); - // startPosition = Vec3.multiply(startPosition, 1 / MyAvatar.scale); - var direction = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.tipPosition, this.palmPosition)); - direction = Vec3.multiply(direction, LASER_LENGTH_FACTOR / (Vec3.length(direction) * MyAvatar.scale)); - var endPosition = Vec3.sum(startPosition, direction); + if (this.shouldDisplayLine) { + this.updateLine(); + } + if (this.triggerValue > DISTANCE_HOLD_THRESHOLD) { + this.attemptMove(); + } - Overlays.editOverlay(this.laser, { - start: startPosition, - end: endPosition + + this.prevTriggerValue = this.triggerValue; +} + +controller.prototype.grabEntity = function() { + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var objectRotation = Entities.getEntityProperties(this.grabbedEntity).rotation; + var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var objectPosition = Entities.getEntityProperties(this.grabbedEntity).position; + var offset = Vec3.subtract(objectPosition, handPosition); + var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + relativePosition: offsetPosition, + relativeRotation: offsetRotation, + hand: this.hand, + timeScale: 0.05 }); - - } - - this.cleanup = function() { - Overlays.deleteOverlay(this.laser); - Overlays.deleteOverlay(this.dropLine); - } } -function update(deltaTime) { - rightController.update(deltaTime); - leftController.update(deltaTime); + +controller.prototype.checkForInRangeObject = function() { + var handPosition = Controller.getSpatialControlPosition(this.palm); + var entities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = GRAB_RADIUS; + var grabbedEntity = null; + //Get nearby entities and assign nearest + for (var i = 0; i < entities.length; i++) { + var props = Entities.getEntityProperties(entities[i]); + var distance = Vec3.distance(props.position, handPosition); + if (distance < minDistance && props.name !== "pointer") { + grabbedEntity = entities[i]; + minDistance = distance; + } + } + if (grabbedEntity === null) { + return false; + } else { + this.grabbedEntity = grabbedEntity; + return true; + } } -function scriptEnding() { - rightController.cleanup(); - leftController.cleanup(); + +controller.prototype.onActionEvent = function(action, state) { + if (this.pullAction === action && state === 1) { + if (this.actionID !== null) { + var self = this; + this.tractorBeamActive = true; + //We need to wait a bit before checking for entity arrival at target destination (meaning checking for velocity being close to some + //low threshold) because otherwise we'll think the entity has arrived before its even really gotten moving! + Script.setTimeout(function() { + self.checkForEntityArrival = true; + }, 500); + var handPosition = Controller.getSpatialControlPosition(this.palm); + var direction = Controller.getSpatialControlNormal(this.tip); + //move final destination along line a bit, so it doesnt hit avatar hand + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: Vec3.sum(handPosition, Vec3.multiply(2, direction)) + }); + } + } + } -function vectorIsZero(v) { - return v.x === 0 && v.y === 0 && v.z === 0; +controller.prototype.cleanup = function() { + Entities.deleteEntity(this.pointer); + Entities.deleteAction(this.grabbedEntity, this.actionID); } -var rightController = new controller(RIGHT); -var leftController = new controller(LEFT); +function update() { + rightController.update(); + leftController.update(); +} + +function onActionEvent(action, state) { + rightController.onActionEvent(action, state); + leftController.onActionEvent(action, state); + +} -Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +function cleanup() { + rightController.cleanup(); + leftController.cleanup(); +} + + +Script.scriptEnding.connect(cleanup); +Script.update.connect(update) +Controller.actionEvent.connect(onActionEvent); \ No newline at end of file diff --git a/examples/controllers/toybox.js b/examples/controllers/toybox.js index 4c0925f3b6..bf03974fda 100644 --- a/examples/controllers/toybox.js +++ b/examples/controllers/toybox.js @@ -13,32 +13,7 @@ Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.j HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -var nullActionID = "00000000-0000-0000-0000-000000000000"; -var controllerID; -var controllerActive; -var leftHandObjectID = null; -var rightHandObjectID = null; -var leftHandActionID = nullActionID; -var rightHandActionID = nullActionID; -var TRIGGER_THRESHOLD = 0.2; -var GRAB_RADIUS = 0.15; - -var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK"); -var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); -var ACTION1 = Controller.findAction("ACTION1"); -var ACTION2 = Controller.findAction("ACTION2"); - -var rightHandGrabAction = RIGHT_HAND_CLICK; -var leftHandGrabAction = LEFT_HAND_CLICK; - -var rightHandGrabValue = 0; -var leftHandGrabValue = 0; -var prevRightHandGrabValue = 0 -var prevLeftHandGrabValue = 0; - -var grabColor = { red: 0, green: 255, blue: 0}; -var releaseColor = { red: 0, green: 0, blue: 255}; var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.toybox.toolbar", function() { return { @@ -63,25 +38,6 @@ var cleanupButton = toolBar.addOverlay("image", { alpha: 1 }); -var overlays = false; -var leftHandOverlay; -var rightHandOverlay; -if (overlays) { - leftHandOverlay = Overlays.addOverlay("sphere", { - position: MyAvatar.getLeftPalmPosition(), - size: GRAB_RADIUS, - color: releaseColor, - alpha: 0.5, - solid: false - }); - rightHandOverlay = Overlays.addOverlay("sphere", { - position: MyAvatar.getRightPalmPosition(), - size: GRAB_RADIUS, - color: releaseColor, - alpha: 0.5, - solid: false - }); -} var OBJECT_HEIGHT_OFFSET = 0.5; var MIN_OBJECT_SIZE = 0.05; @@ -98,8 +54,6 @@ var GRAVITY = { z: 0.0 } -var LEFT = 0; -var RIGHT = 1; var tableCreated = false; @@ -108,7 +62,6 @@ var tableEntities = Array(NUM_OBJECTS + 1); // Also includes table var VELOCITY_MAG = 0.3; -var entitiesToResize = []; var MODELS = Array( { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx" }, @@ -136,196 +89,15 @@ var COLLISION_SOUNDS = Array( var RESIZE_TIMER = 0.0; var RESIZE_WAIT = 0.05; // 50 milliseconds -var leftFist = Entities.addEntity( { - type: "Sphere", - shapeType: 'sphere', - position: MyAvatar.getLeftPalmPosition(), - dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS }, - rotation: MyAvatar.getLeftPalmRotation(), - visible: false, - collisionsWillMove: false, - ignoreForCollisions: true - }); -var rightFist = Entities.addEntity( { - type: "Sphere", - shapeType: 'sphere', - position: MyAvatar.getRightPalmPosition(), - dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS }, - rotation: MyAvatar.getRightPalmRotation(), - visible: false, - collisionsWillMove: false, - ignoreForCollisions: true - }); -function letGo(hand) { - var actionIDToRemove = (hand == LEFT) ? leftHandActionID : rightHandActionID; - var entityIDToEdit = (hand == LEFT) ? leftHandObjectID : rightHandObjectID; - var handVelocity = (hand == LEFT) ? MyAvatar.getLeftPalmVelocity() : MyAvatar.getRightPalmVelocity(); - var handAngularVelocity = (hand == LEFT) ? MyAvatar.getLeftPalmAngularVelocity() : - MyAvatar.getRightPalmAngularVelocity(); - if (actionIDToRemove != nullActionID && entityIDToEdit != null) { - Entities.deleteAction(entityIDToEdit, actionIDToRemove); - // TODO: upon successful letGo, restore collision groups - if (hand == LEFT) { - leftHandObjectID = null; - leftHandActionID = nullActionID; - } else { - rightHandObjectID = null; - rightHandActionID = nullActionID; - } - } -} -function setGrabbedObject(hand) { - var handPosition = (hand == LEFT) ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); - var entities = Entities.findEntities(handPosition, GRAB_RADIUS); - var objectID = null; - var minDistance = GRAB_RADIUS; - for (var i = 0; i < entities.length; i++) { - // Don't grab the object in your other hands, your fists, or the table - if ((hand == LEFT && entities[i] == rightHandObjectID) || - (hand == RIGHT && entities[i] == leftHandObjectID) || - entities[i] == leftFist || entities[i] == rightFist || - (tableCreated && entities[i] == tableEntities[0])) { - continue; - } else { - var distance = Vec3.distance(Entities.getEntityProperties(entities[i]).position, handPosition); - if (distance <= minDistance) { - objectID = entities[i]; - minDistance = distance; - } - } - } - if (objectID == null) { - return false; - } - if (hand == LEFT) { - leftHandObjectID = objectID; - } else { - rightHandObjectID = objectID; - } - return true; -} - -function grab(hand) { - if (!setGrabbedObject(hand)) { - // If you don't grab an object, make a fist - Entities.editEntity((hand == LEFT) ? leftFist : rightFist, { ignoreForCollisions: false } ); - return; - } - var objectID = (hand == LEFT) ? leftHandObjectID : rightHandObjectID; - var handRotation = (hand == LEFT) ? MyAvatar.getLeftPalmRotation() : MyAvatar.getRightPalmRotation(); - var handPosition = (hand == LEFT) ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); - - var objectRotation = Entities.getEntityProperties(objectID).rotation; - var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var objectPosition = Entities.getEntityProperties(objectID).position; - var offset = Vec3.subtract(objectPosition, handPosition); - var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); - // print(JSON.stringify(offsetPosition)); - var actionID = Entities.addAction("hold", objectID, { - relativePosition: { x: 0, y: 0, z: 0 }, - relativeRotation: offsetRotation, - hand: (hand == LEFT) ? "left" : "right", - timeScale: 0.05 - }); - if (actionID == nullActionID) { - if (hand == LEFT) { - leftHandObjectID = null; - } else { - rightHandObjectID = null; - } - } else { - // TODO: upon successful grab, add to collision group so object doesn't collide with immovable entities - if (hand == LEFT) { - leftHandActionID = actionID; - } else { - rightHandActionID = actionID; - } - } -} - -function resizeModels() { - var newEntitiesToResize = []; - for (var i = 0; i < entitiesToResize.length; i++) { - var naturalDimensions = Entities.getEntityProperties(entitiesToResize[i]).naturalDimensions; - if (naturalDimensions.x != 1.0 || naturalDimensions.y != 1.0 || naturalDimensions.z != 1.0) { - // bigger range of sizes for models - var dimensions = Vec3.multiply(randFloat(MIN_OBJECT_SIZE, 3.0*MAX_OBJECT_SIZE), Vec3.normalize(naturalDimensions)); - Entities.editEntity(entitiesToResize[i], { - dimensions: dimensions, - shapeType: "box" - }); - } else { - newEntitiesToResize.push(entitiesToResize[i]); - } - - } - entitiesToResize = newEntitiesToResize; -} - -function update(deltaTime) { - if (overlays) { - Overlays.editOverlay(leftHandOverlay, { position: MyAvatar.getLeftPalmPosition() }); - Overlays.editOverlay(rightHandOverlay, { position: MyAvatar.getRightPalmPosition() }); - } - - // if (tableCreated && RESIZE_TIMER < RESIZE_WAIT) { - // RESIZE_TIMER += deltaTime; - // } else if (tableCreated) { - // resizeModels(); - // } - - rightHandGrabValue = Controller.getActionValue(rightHandGrabAction); - leftHandGrabValue = Controller.getActionValue(leftHandGrabAction); - - Entities.editEntity(leftFist, { position: MyAvatar.getLeftPalmPosition() }); - Entities.editEntity(rightFist, { position: MyAvatar.getRightPalmPosition() }); - - if (rightHandGrabValue > TRIGGER_THRESHOLD && - prevRightHandGrabValue < TRIGGER_THRESHOLD) { - if (overlays) { - Overlays.editOverlay(rightHandOverlay, { color: grabColor }); - } - grab(RIGHT); - } else if (rightHandGrabValue < TRIGGER_THRESHOLD && - prevRightHandGrabValue > TRIGGER_THRESHOLD) { - Entities.editEntity(rightFist, { ignoreForCollisions: true } ); - if (overlays) { - Overlays.editOverlay(rightHandOverlay, { color: releaseColor }); - } - letGo(RIGHT); - } - - if (leftHandGrabValue > TRIGGER_THRESHOLD && - prevLeftHandGrabValue < TRIGGER_THRESHOLD) { - if (overlays) { - Overlays.editOverlay(leftHandOverlay, { color: grabColor }); - } - grab(LEFT); - } else if (leftHandGrabValue < TRIGGER_THRESHOLD && - prevLeftHandGrabValue > TRIGGER_THRESHOLD) { - Entities.editEntity(leftFist, { ignoreForCollisions: true } ); - if (overlays) { - Overlays.editOverlay(leftHandOverlay, { color: releaseColor }); - } - letGo(LEFT); - } - - prevRightHandGrabValue = rightHandGrabValue; - prevLeftHandGrabValue = leftHandGrabValue; -} function cleanUp() { - letGo(RIGHT); - letGo(LEFT); + print("CLEANUP!!!") if (overlays) { Overlays.deleteOverlay(leftHandOverlay); Overlays.deleteOverlay(rightHandOverlay); } - Entities.deleteEntity(leftFist); - Entities.deleteEntity(rightFist); removeTable(); toolBar.cleanup(); } @@ -405,7 +177,6 @@ function createTable() { density: 0.5, collisionsWillMove: true, color: { red: randInt(0, 255), green: randInt(0, 255), blue: randInt(0, 255) }, - // collisionSoundURL: COLLISION_SOUNDS[randInt(0, COLLISION_SOUNDS.length)] }); if (type == "Model") { var randModel = randInt(0, MODELS.length); @@ -413,7 +184,6 @@ function createTable() { shapeType: "box", modelURL: MODELS[randModel].modelURL }); - entitiesToResize.push(tableEntities[i]); } } } @@ -426,5 +196,4 @@ function removeTable() { } Script.scriptEnding.connect(cleanUp); -Script.update.connect(update); Controller.mousePressEvent.connect(onClick); \ No newline at end of file From eb78b39bea5de68b060188283d0e2b9391c8110e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Sep 2015 11:31:05 -0700 Subject: [PATCH 073/117] fix typo that broke build and remove warning --- interface/src/avatar/SkeletonModel.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 567f9b30ac..c7fa674d80 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -348,7 +348,7 @@ void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointInde OrientationLineIDs& jointLineIDs = _jointOrientationLines[jointIndex]; glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size; - glm::vec3 pU = position + orientation * IDENTITY_UP * size; + glm::vec3 pUp = position + orientation * IDENTITY_UP * size; glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size; glm::vec3 red(1.0f, 0.0f, 0.0f); @@ -523,7 +523,6 @@ void SkeletonModel::computeBoundingShape() { freeLineage, glm::mat4()); } - const JointState& movedState = _rig->getJointState(tipIndex); break; } limbLength += limbJoint.getDistanceToParent(); From 992bd5c9d20e578df833fe7ec406e00e7da0e22b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Sep 2015 11:16:36 -0700 Subject: [PATCH 074/117] fix sixense and vive rotation readings for palms --- interface/src/Application.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 4 +--- libraries/avatars/src/HandData.h | 6 +++++- .../src/input-plugins/SixenseManager.cpp | 7 +++++-- .../src/input-plugins/ViveControllerManager.cpp | 13 ++++++------- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a8781ee308..e0726311c9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4919,7 +4919,7 @@ void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float // Store the one fingertip in the palm structure so we can track velocity const float FINGER_LENGTH = 0.3f; // meters - const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH); + const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; glm::vec3 oldTipPosition = palm->getTipRawPosition(); if (deltaTime > 0.0f) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index a2e5908477..6a36a41dc4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -261,9 +261,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { // the palm's position must be transformed into the model-frame glm::quat inverseRotation = glm::inverse(_rotation); glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation); - - // the palm's "raw" rotation is already in the model-frame - glm::quat palmRotation = palm.getRawRotation(); + glm::quat palmRotation = inverseRotation * palm.getRotation(); inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 6388c882c7..c87c840399 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -64,12 +64,13 @@ public: bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, const PalmData*& collidingPalm) const; + glm::quat getBaseOrientation() const; + friend class AvatarData; protected: AvatarData* _owningAvatarData; std::vector _palms; - glm::quat getBaseOrientation() const; glm::vec3 getBasePosition() const; float getBaseScale() const; @@ -95,6 +96,7 @@ public: void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; }; glm::quat getRawRotation() const { return _rawRotation; } + glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; } void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; } const glm::vec3& getRawVelocity() const { return _rawVelocity; } @@ -147,6 +149,7 @@ public: glm::vec3 getNormal() const; private: + // unless marked otherwise, these are all in the model-frame glm::quat _rawRotation; glm::vec3 _rawPosition; glm::vec3 _rawVelocity; @@ -156,6 +159,7 @@ private: glm::vec3 _tipPosition; glm::vec3 _tipVelocity; glm::vec3 _totalPenetration; // accumulator for per-frame penetrations + unsigned int _controllerButtons; unsigned int _lastControllerButtons; float _trigger; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 579fddcd63..f090db7959 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -481,6 +481,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int // Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis) // const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f); + const glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f); const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f); const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis); @@ -491,13 +492,15 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis); // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation - // (fingers forward, palm down) aligned properly in the avatar's model-frame. - const glm::quat postOffset = glm::angleAxis(PI / 2.0f, xAxis); + // (fingers forward, palm down) aligned properly in the avatar's model-frame, + // and then a flip about the yAxis to get into model-frame. + const glm::quat postOffset = glm::angleAxis(PI, yAxis) * glm::angleAxis(PI / 2.0f, xAxis); // The total rotation of the hand uses the formula: // // rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh // + // TODO: find a shortcut with fewer rotations. rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 082c37a837..5410db11a4 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -362,18 +362,17 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { // // Qoffset = glm::inverse(deltaRotation when hand is posed fingers forward, palm down) // - // An approximate offset for the Vive can be obtained by inpection: + // An approximate offset for the Vive can be obtained by inspection: // // Qoffset = glm::inverse(glm::angleAxis(sign * PI/4.0f, zAxis) * glm::angleAxis(PI/2.0f, xAxis)) // - - // Finally there is another flip around the yAxis to re-align from model to Vive space, so the full equation is: + // So the full equation is: // - // Q = yFlip * combinedMeasurement * viveToHand + // Q = combinedMeasurement * viveToHand // - // Q = yFlip * (deltaQ * QOffset) * (yFlip * quarterTurnAboutX) + // Q = (deltaQ * QOffset) * (yFlip * quarterTurnAboutX) // - // Q = yFlip * (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX) + // Q = (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX) const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); @@ -381,7 +380,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f)); const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f)); const glm::quat offset = glm::inverse(signedQuaterZ * eighthX); - rotation = yFlip * rotation * offset * yFlip * quarterX; + rotation = rotation * offset * yFlip * quarterX; position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET); From fea030b9a0474374936b453631dc35a342ba0a81 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 12:18:17 -0700 Subject: [PATCH 075/117] Compile and warning fixes for MacOSX. --- libraries/animation/src/AnimNodeLoader.cpp | 9 ++++++--- libraries/animation/src/AnimSkeleton.cpp | 5 ----- libraries/animation/src/Rig.cpp | 3 +-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 5863380d49..e96ce37ed1 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -33,7 +33,7 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } -static bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static const char* animNodeTypeToString(AnimNode::Type type) { switch (type) { @@ -41,6 +41,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) { case AnimNode::Type::BlendLinear: return "blendLinear"; case AnimNode::Type::Overlay: return "overlay"; case AnimNode::Type::StateMachine: return "stateMachine"; + case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; } @@ -51,6 +52,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { case AnimNode::Type::BlendLinear: return loadBlendLinearNode; case AnimNode::Type::Overlay: return loadOverlayNode; case AnimNode::Type::StateMachine: return loadStateMachineNode; + case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; } @@ -61,6 +63,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { case AnimNode::Type::BlendLinear: return processBlendLinearNode; case AnimNode::Type::Overlay: return processOverlayNode; case AnimNode::Type::StateMachine: return processStateMachineNode; + case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; } @@ -264,13 +267,13 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const return node; } -static void buildChildMap(std::map& map, AnimNode::Pointer node) { +void buildChildMap(std::map& map, AnimNode::Pointer node) { for ( auto child : node->_children ) { map.insert(std::pair(child->_id, child)); } } -static bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) { +bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) { auto smNode = std::static_pointer_cast(node); assert(smNode); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index d8efe55f5d..07ab485c45 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -57,11 +57,6 @@ AnimPose::operator glm::mat4() const { glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); } -static const mat4 IDENTITY = mat4(); -static bool matrixIsIdentity(const glm::mat4& m) { - return m == IDENTITY; -} - AnimSkeleton::AnimSkeleton(const std::vector& joints, const AnimPose& geometryOffset) { _joints = joints; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4f0257b4dc..89ab062c02 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -418,7 +418,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos if (_enableAnimGraph) { static float t = 0.0f; - _animVars.set("sine", 0.5f * sin(t) + 0.5f); + _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); if (glm::length(worldVelocity) > 0.07f) { _animVars.set("isMoving", true); @@ -483,7 +483,6 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { // 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); } From df26f182227ef8492b96932edb73b9c1eb4990a0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 13:29:29 -0700 Subject: [PATCH 076/117] Fix for compilation errors on linux --- libraries/animation/src/AnimNodeLoader.cpp | 8 ++++---- libraries/animation/src/AnimNodeLoader.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index e96ce37ed1..3ac3f3909a 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -7,10 +7,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include -#include +#include +#include +#include +#include #include "AnimNode.h" #include "AnimClip.h" diff --git a/libraries/animation/src/AnimNodeLoader.h b/libraries/animation/src/AnimNodeLoader.h index 095c05cf7e..8082a1e84b 100644 --- a/libraries/animation/src/AnimNodeLoader.h +++ b/libraries/animation/src/AnimNodeLoader.h @@ -12,9 +12,9 @@ #include -#include -#include -#include +#include +#include +#include #include "AnimNode.h" From 91fbbf7d4eae82bfd7ac8c7e21558ec9ae123a6a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 13:35:26 -0700 Subject: [PATCH 077/117] Updated copyright boiler plate. --- libraries/animation/src/AnimBlendLinear.cpp | 3 ++- libraries/animation/src/AnimBlendLinear.h | 3 ++- libraries/animation/src/AnimClip.cpp | 3 ++- libraries/animation/src/AnimClip.h | 3 ++- libraries/animation/src/AnimNode.h | 3 ++- libraries/animation/src/AnimNodeLoader.cpp | 3 ++- libraries/animation/src/AnimNodeLoader.h | 3 ++- libraries/animation/src/AnimOverlay.cpp | 3 ++- libraries/animation/src/AnimOverlay.h | 3 ++- libraries/animation/src/AnimSkeleton.cpp | 3 ++- libraries/animation/src/AnimSkeleton.h | 3 ++- libraries/animation/src/AnimStateMachine.cpp | 3 ++- libraries/animation/src/AnimStateMachine.h | 3 ++- libraries/animation/src/AnimUtil.cpp | 3 ++- libraries/animation/src/AnimUtil.h | 3 ++- libraries/animation/src/AnimVariant.h | 3 ++- 16 files changed, 32 insertions(+), 16 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 499579ae67..63c66a2b9d 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -1,7 +1,8 @@ // // AnimBlendLinear.cpp // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 8016f7994f..3a09245575 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -1,7 +1,8 @@ // // AnimBlendLinear.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 12ba97f377..fdc5fc504a 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -1,7 +1,8 @@ // // AnimClip.cpp // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 86e6cf7733..1b9649cc3e 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -1,7 +1,8 @@ // // AnimClip.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 06085a471a..3a54710b39 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -1,7 +1,8 @@ // // AnimNode.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 3ac3f3909a..5f0260db6d 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -1,7 +1,8 @@ // // AnimNodeLoader.cpp // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimNodeLoader.h b/libraries/animation/src/AnimNodeLoader.h index 8082a1e84b..71b5552879 100644 --- a/libraries/animation/src/AnimNodeLoader.h +++ b/libraries/animation/src/AnimNodeLoader.h @@ -1,7 +1,8 @@ // // AnimNodeLoader.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index dcdd9f5b4b..52026f7711 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -1,7 +1,8 @@ // // AnimOverlay.cpp // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index eb11510f74..de563cc403 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -1,7 +1,8 @@ // // AnimOverlay.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 07ab485c45..9e600c4e15 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -1,7 +1,8 @@ // // AnimSkeleton.cpp // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index f266fe04b3..c0c5036cc7 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -1,7 +1,8 @@ // // AnimSkeleton.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 7815381ca3..eccfcfa6e6 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -1,7 +1,8 @@ // // AnimStateMachine.cpp // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 066dfabbbb..f2d941a568 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -1,7 +1,8 @@ // // AnimStateMachine.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index 4423b5c933..81b294e66c 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -1,7 +1,8 @@ // // AnimUtil.cpp // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index 556eba8989..23c02b6183 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -1,7 +1,8 @@ // // AnimUtil.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 849b6a436a..1d720ba565 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -1,7 +1,8 @@ // // AnimVariant.h // -// Copyright 2015 High Fidelity, Inc. +// Created by Anthony J. Thibault on 9/2/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html From 99586f259c394eaec1a6790f2689965fdf9f8eee Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 13:44:34 -0700 Subject: [PATCH 078/117] Renamed bindTransformIsValid to bindTransformFoundInCluster --- libraries/animation/src/AnimSkeleton.cpp | 2 +- libraries/fbx/src/FBXReader.cpp | 4 ++-- libraries/fbx/src/FBXReader.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 9e600c4e15..3f11607f26 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -67,7 +67,7 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints, const AnimPose& // iterate over FBXJoints and extract the bind pose information. for (size_t i = 0; i < joints.size(); i++) { - if (_joints[i].bindTransformIsValid) { + if (_joints[i].bindTransformFoundInCluster) { // Use the FBXJoint::bindTransform, which is absolute model coordinates // i.e. not relative to it's parent. AnimPose absoluteBindPose(_joints[i].bindTransform); diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 7c80b97117..c885f56b55 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2309,7 +2309,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping } } - joint.bindTransformIsValid = false; + joint.bindTransformFoundInCluster = false; geometry.joints.append(joint); geometry.jointIndices.insert(model.name, geometry.joints.size()); @@ -2538,7 +2538,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping FBXJoint& joint = geometry.joints[fbxCluster.jointIndex]; joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); joint.bindTransform = cluster.transformLink; - joint.bindTransformIsValid = true; + joint.bindTransformFoundInCluster = true; // update the bind pose extents glm::vec3 bindTranslation = extractTranslation(geometry.offset * joint.bindTransform); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index d8e1ae59e6..158b5581c6 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -84,7 +84,7 @@ public: glm::mat4 bindTransform; QString name; bool isSkeletonJoint; - bool bindTransformIsValid; + bool bindTransformFoundInCluster; }; From 2d95d1c236f5d2dc00a2811ccc2b052ba6b67e76 Mon Sep 17 00:00:00 2001 From: Raffi Bedikian Date: Wed, 2 Sep 2015 13:49:21 -0700 Subject: [PATCH 079/117] Fix various whitespace issues --- .../render-utils/src/AntialiasingEffect.cpp | 170 +++++++++--------- .../render-utils/src/AntialiasingEffect.h | 22 +-- libraries/render-utils/src/fxaa.slf | 18 +- 3 files changed, 105 insertions(+), 105 deletions(-) diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 4e1b6f451c..283561fc57 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -33,119 +33,119 @@ Antialiasing::Antialiasing() { } const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { - if (!_antialiasingPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert))); - auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_frag))); - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + if (!_antialiasingPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0)); + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0)); - gpu::Shader::makeProgram(*program, slotBindings); + gpu::Shader::makeProgram(*program, slotBindings); - _texcoordOffsetLoc = program->getUniforms().findLocation("texcoordOffset"); + _texcoordOffsetLoc = program->getUniforms().findLocation("texcoordOffset"); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(false, false, gpu::LESS_EQUAL); + state->setDepthTest(false, false, gpu::LESS_EQUAL); - // Link the antialiasing FBO to texture - _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, - DependencyManager::get()->getFrameBufferSize().width(), DependencyManager::get()->getFrameBufferSize().height())); - auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - auto width = _antialiasingBuffer->getWidth(); - auto height = _antialiasingBuffer->getHeight(); - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); + // Link the antialiasing FBO to texture + _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, + DependencyManager::get()->getFrameBufferSize().width(), DependencyManager::get()->getFrameBufferSize().height())); + auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + auto width = _antialiasingBuffer->getWidth(); + auto height = _antialiasingBuffer->getHeight(); + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); + _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); - // Good to go add the brand new pipeline - _antialiasingPipeline.reset(gpu::Pipeline::create(program, state)); - } - return _antialiasingPipeline; + // Good to go add the brand new pipeline + _antialiasingPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _antialiasingPipeline; } const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { - if (!_blendPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert))); - auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_blend_frag))); - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + if (!_blendPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(fxaa_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fxaa_blend_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0)); + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), 0)); - gpu::Shader::makeProgram(*program, slotBindings); + gpu::Shader::makeProgram(*program, slotBindings); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(false, false, gpu::LESS_EQUAL); + state->setDepthTest(false, false, gpu::LESS_EQUAL); - // Good to go add the brand new pipeline - _blendPipeline.reset(gpu::Pipeline::create(program, state)); - } - return _blendPipeline; + // Good to go add the brand new pipeline + _blendPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _blendPipeline; } void Antialiasing::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); - gpu::Batch batch; - RenderArgs* args = renderContext->args; + gpu::Batch batch; + RenderArgs* args = renderContext->args; - auto framebufferCache = DependencyManager::get(); - QSize framebufferSize = framebufferCache->getFrameBufferSize(); - float fbWidth = framebufferSize.width(); - float fbHeight = framebufferSize.height(); - float sMin = args->_viewport.x / fbWidth; - float sWidth = args->_viewport.z / fbWidth; - float tMin = args->_viewport.y / fbHeight; - float tHeight = args->_viewport.w / fbHeight; + auto framebufferCache = DependencyManager::get(); + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + float fbWidth = framebufferSize.width(); + float fbHeight = framebufferSize.height(); + float sMin = args->_viewport.x / fbWidth; + float sWidth = args->_viewport.z / fbWidth; + float tMin = args->_viewport.y / fbHeight; + float tHeight = args->_viewport.w / fbHeight; - glm::mat4 projMat; - Transform viewMat; - args->_viewFrustum->evalProjectionMatrix(projMat); - args->_viewFrustum->evalViewTransform(viewMat); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - batch.setModelTransform(Transform()); + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); - // FXAA step - getAntialiasingPipeline(); - batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); - _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); - batch.setFramebuffer(_antialiasingBuffer); - batch.setPipeline(getAntialiasingPipeline()); + // FXAA step + getAntialiasingPipeline(); + batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); + _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); + batch.setFramebuffer(_antialiasingBuffer); + batch.setPipeline(getAntialiasingPipeline()); - // initialize the view-space unpacking uniforms using frustum data - float left, right, bottom, top, nearVal, farVal; - glm::vec4 nearClipPlane, farClipPlane; + // initialize the view-space unpacking uniforms using frustum data + float left, right, bottom, top, nearVal, farVal; + glm::vec4 nearClipPlane, farClipPlane; - args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); - float depthScale = (farVal - nearVal) / farVal; - float nearScale = -1.0f / nearVal; - float depthTexCoordScaleS = (right - left) * nearScale / sWidth; - float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight; - float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS; - float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT; + float depthScale = (farVal - nearVal) / farVal; + float nearScale = -1.0f / nearVal; + float depthTexCoordScaleS = (right - left) * nearScale / sWidth; + float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight; + float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS; + float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT; - batch._glUniform2f(_texcoordOffsetLoc, 1.0 / fbWidth, 1.0 / fbHeight); + batch._glUniform2f(_texcoordOffsetLoc, 1.0 / fbWidth, 1.0 / fbHeight); - glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); - glm::vec2 bottomLeft(-1.0f, -1.0f); - glm::vec2 topRight(1.0f, 1.0f); - glm::vec2 texCoordTopLeft(0.0f, 0.0f); - glm::vec2 texCoordBottomRight(1.0f, 1.0f); - DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); + glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); + glm::vec2 bottomLeft(-1.0f, -1.0f); + glm::vec2 topRight(1.0f, 1.0f); + glm::vec2 texCoordTopLeft(0.0f, 0.0f); + glm::vec2 texCoordBottomRight(1.0f, 1.0f); + DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); - // Blend step - getBlendPipeline(); - batch.setResourceTexture(0, _antialiasingTexture); - batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); - batch.setPipeline(getBlendPipeline()); + // Blend step + getBlendPipeline(); + batch.setResourceTexture(0, _antialiasingTexture); + batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); + batch.setPipeline(getBlendPipeline()); - DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); + DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); - // Ready to render - args->_context->render((batch)); + // Ready to render + args->_context->render((batch)); } diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index 96cf3aaf3e..c7cce4cb15 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -19,25 +19,25 @@ class Antialiasing { public: - Antialiasing(); + Antialiasing(); - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + typedef render::Job::Model JobModel; - const gpu::PipelinePointer& getAntialiasingPipeline(); - const gpu::PipelinePointer& getBlendPipeline(); + const gpu::PipelinePointer& getAntialiasingPipeline(); + const gpu::PipelinePointer& getBlendPipeline(); private: - // Uniforms for AA - gpu::int32 _texcoordOffsetLoc; + // Uniforms for AA + gpu::int32 _texcoordOffsetLoc; - gpu::FramebufferPointer _antialiasingBuffer; + gpu::FramebufferPointer _antialiasingBuffer; - gpu::TexturePointer _antialiasingTexture; + gpu::TexturePointer _antialiasingTexture; - gpu::PipelinePointer _antialiasingPipeline; - gpu::PipelinePointer _blendPipeline; + gpu::PipelinePointer _antialiasingPipeline; + gpu::PipelinePointer _blendPipeline; }; diff --git a/libraries/render-utils/src/fxaa.slf b/libraries/render-utils/src/fxaa.slf index 69596e13ce..d1c50b2c58 100644 --- a/libraries/render-utils/src/fxaa.slf +++ b/libraries/render-utils/src/fxaa.slf @@ -35,11 +35,11 @@ void main() { // local contrast multiplier for performing AA // higher = sharper, but setting this value too high will cause near-vertical and near-horizontal edges to fail // see "fxaaQualityEdgeThreshold" - float FXAA_REDUCE_MUL = 1.0/8.0; + float FXAA_REDUCE_MUL = 1.0 / 8.0; // luminance threshold for processing dark colors // see "fxaaQualityEdgeThresholdMin" - float FXAA_REDUCE_MIN = (1.0/128.0); + float FXAA_REDUCE_MIN = 1.0 / 128.0; // fetch raw RGB values for nearby locations // sampling pattern is "five on a die" (each diagonal direction and the center) @@ -70,17 +70,17 @@ void main() { // compute clamped direction offset for additional "two-tap" samples // longer vector = blurry, shorter vector = sharp float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce); + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texcoordOffset; // perform additional texture sampling perpendicular to gradient - vec3 rgbA = (1.0/2.0) * ( - texture2D(colorTexture, varTexcoord + dir * (1.0/3.0 - 0.5)).xyz + - texture2D(colorTexture, varTexcoord + dir * (2.0/3.0 - 0.5)).xyz); - vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * ( - texture2D(colorTexture, varTexcoord + dir * (0.0/3.0 - 0.5)).xyz + - texture2D(colorTexture, varTexcoord + dir * (3.0/3.0 - 0.5)).xyz); + vec3 rgbA = (1.0 / 2.0) * ( + texture2D(colorTexture, varTexcoord + dir * (1.0 / 3.0 - 0.5)).xyz + + texture2D(colorTexture, varTexcoord + dir * (2.0 / 3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * ( + texture2D(colorTexture, varTexcoord + dir * (0.0 / 3.0 - 0.5)).xyz + + texture2D(colorTexture, varTexcoord + dir * (3.0 / 3.0 - 0.5)).xyz); float lumaB = dot(rgbB, luma); // compare luma of new samples to the luma range of the original neighborhood From c61bc190dee217ad5cf3078c74c532bd3dbc962e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Sep 2015 14:16:02 -0700 Subject: [PATCH 080/117] fix finger and palm-normal directions for JS --- libraries/avatars/src/HandData.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 9a9b51c1c8..1a9b6775d3 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -114,12 +114,14 @@ float HandData::getBaseScale() const { } glm::vec3 PalmData::getFingerDirection() const { - const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 0.0f, 1.0f); + // finger points along yAxis in hand-frame + const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 1.0f, 0.0f); return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION)); } glm::vec3 PalmData::getNormal() const { - const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, -1.0f, 0.0f); + // palm normal points along zAxis in hand-frame + const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, 0.0f, 1.0f); return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION)); } From 54b7a063e2d23ab326d03fefa2172f07d81f44cb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 31 Aug 2015 20:31:33 -0700 Subject: [PATCH 081/117] Support HTML colors in overlays --- interface/src/ui/overlays/Overlay.cpp | 11 +---------- libraries/shared/src/RegisteredMetaTypes.cpp | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 7824c0c498..0c909a1bfb 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -66,16 +66,7 @@ Overlay::~Overlay() { void Overlay::setProperties(const QScriptValue& properties) { QScriptValue color = properties.property("color"); - if (color.isValid()) { - QScriptValue red = color.property("red"); - QScriptValue green = color.property("green"); - QScriptValue blue = color.property("blue"); - if (red.isValid() && green.isValid() && blue.isValid()) { - _color.red = red.toVariant().toInt(); - _color.green = green.toVariant().toInt(); - _color.blue = blue.toVariant().toInt(); - } - } + xColorFromScriptValue(properties.property("color"), _color); if (properties.property("alpha").isValid()) { setAlpha(properties.property("alpha").toVariant().toFloat()); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index c4e05a68fb..2c4b213fcb 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -197,9 +197,23 @@ QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { } void xColorFromScriptValue(const QScriptValue &object, xColor& color) { - color.red = object.property("red").toVariant().toInt(); - color.green = object.property("green").toVariant().toInt(); - color.blue = object.property("blue").toVariant().toInt(); + if (!object.isValid()) { + return; + } + if (object.isNumber()) { + color.red = color.green = color.blue = (uint8_t)object.toUInt32(); + } else if (object.isString()) { + QColor qcolor(object.toString()); + if (qcolor.isValid()) { + color.red = (uint8_t)qcolor.red(); + color.blue = (uint8_t)qcolor.blue(); + color.green = (uint8_t)qcolor.green(); + } + } else { + color.red = object.property("red").toVariant().toInt(); + color.green = object.property("green").toVariant().toInt(); + color.blue = object.property("blue").toVariant().toInt(); + } } QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) { From 849249d7fec44004ea95d6e6a4c746588bd84872 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 31 Aug 2015 20:39:08 -0700 Subject: [PATCH 082/117] Fixing colors Conflicts: examples/toys/magBalls/handController.js --- examples/libraries/htmlColors.js | 145 +++++++++++++++++++++++ examples/toys/magBalls/handController.js | 1 + examples/toys/magBalls/magBallsMain.js | 1 + 3 files changed, 147 insertions(+) create mode 100644 examples/libraries/htmlColors.js diff --git a/examples/libraries/htmlColors.js b/examples/libraries/htmlColors.js new file mode 100644 index 0000000000..e9ebbba841 --- /dev/null +++ b/examples/libraries/htmlColors.js @@ -0,0 +1,145 @@ + +HTML_COLORS = { + AliceBlue: "#F0F8FF", + AntiqueWhite: "#FAEBD7", + Aqua: "#00FFFF", + Aquamarine: "#7FFFD4", + Azure: "#F0FFFF", + Beige: "#F5F5DC", + Bisque: "#FFE4C4", + Black: "#000000", + BlanchedAlmond: "#FFEBCD", + Blue: "#0000FF", + BlueViolet: "#8A2BE2", + Brown: "#A52A2A", + BurlyWood: "#DEB887", + CadetBlue: "#5F9EA0", + Chartreuse: "#7FFF00", + Chocolate: "#D2691E", + Coral: "#FF7F50", + CornflowerBlue: "#6495ED", + Cornsilk: "#FFF8DC", + Crimson: "#DC143C", + Cyan: "#00FFFF", + DarkBlue: "#00008B", + DarkCyan: "#008B8B", + DarkGoldenRod: "#B8860B", + DarkGray: "#A9A9A9", + DarkGreen: "#006400", + DarkKhaki: "#BDB76B", + DarkMagenta: "#8B008B", + DarkOliveGreen: "#556B2F", + DarkOrange: "#FF8C00", + DarkOrchid: "#9932CC", + DarkRed: "#8B0000", + DarkSalmon: "#E9967A", + DarkSeaGreen: "#8FBC8F", + DarkSlateBlue: "#483D8B", + DarkSlateGray: "#2F4F4F", + DarkTurquoise: "#00CED1", + DarkViolet: "#9400D3", + DeepPink: "#FF1493", + DeepSkyBlue: "#00BFFF", + DimGray: "#696969", + DodgerBlue: "#1E90FF", + FireBrick: "#B22222", + FloralWhite: "#FFFAF0", + ForestGreen: "#228B22", + Fuchsia: "#FF00FF", + Gainsboro: "#DCDCDC", + GhostWhite: "#F8F8FF", + Gold: "#FFD700", + GoldenRod: "#DAA520", + Gray: "#808080", + Green: "#008000", + GreenYellow: "#ADFF2F", + HoneyDew: "#F0FFF0", + HotPink: "#FF69B4", + IndianRed: "#CD5C5C", + Indigo: "#4B0082", + Ivory: "#FFFFF0", + Khaki: "#F0E68C", + Lavender: "#E6E6FA", + LavenderBlush: "#FFF0F5", + LawnGreen: "#7CFC00", + LemonChiffon: "#FFFACD", + LightBlue: "#ADD8E6", + LightCoral: "#F08080", + LightCyan: "#E0FFFF", + LightGoldenRodYellow: "#FAFAD2", + LightGray: "#D3D3D3", + LightGreen: "#90EE90", + LightPink: "#FFB6C1", + LightSalmon: "#FFA07A", + LightSeaGreen: "#20B2AA", + LightSkyBlue: "#87CEFA", + LightSlateGray: "#778899", + LightSteelBlue: "#B0C4DE", + LightYellow: "#FFFFE0", + Lime: "#00FF00", + LimeGreen: "#32CD32", + Linen: "#FAF0E6", + Magenta: "#FF00FF", + Maroon: "#800000", + MediumAquaMarine: "#66CDAA", + MediumBlue: "#0000CD", + MediumOrchid: "#BA55D3", + MediumPurple: "#9370DB", + MediumSeaGreen: "#3CB371", + MediumSlateBlue: "#7B68EE", + MediumSpringGreen: "#00FA9A", + MediumTurquoise: "#48D1CC", + MediumVioletRed: "#C71585", + MidnightBlue: "#191970", + MintCream: "#F5FFFA", + MistyRose: "#FFE4E1", + Moccasin: "#FFE4B5", + NavajoWhite: "#FFDEAD", + Navy: "#000080", + OldLace: "#FDF5E6", + Olive: "#808000", + OliveDrab: "#6B8E23", + Orange: "#FFA500", + OrangeRed: "#FF4500", + Orchid: "#DA70D6", + PaleGoldenRod: "#EEE8AA", + PaleGreen: "#98FB98", + PaleTurquoise: "#AFEEEE", + PaleVioletRed: "#DB7093", + PapayaWhip: "#FFEFD5", + PeachPuff: "#FFDAB9", + Peru: "#CD853F", + Pink: "#FFC0CB", + Plum: "#DDA0DD", + PowderBlue: "#B0E0E6", + Purple: "#800080", + RebeccaPurple: "#663399", + Red: "#FF0000", + RosyBrown: "#BC8F8F", + RoyalBlue: "#4169E1", + SaddleBrown: "#8B4513", + Salmon: "#FA8072", + SandyBrown: "#F4A460", + SeaGreen: "#2E8B57", + SeaShell: "#FFF5EE", + Sienna: "#A0522D", + Silver: "#C0C0C0", + SkyBlue: "#87CEEB", + SlateBlue: "#6A5ACD", + SlateGray: "#708090", + Snow: "#FFFAFA", + SpringGreen: "#00FF7F", + SteelBlue: "#4682B4", + Tan: "#D2B48C", + Teal: "#008080", + Thistle: "#D8BFD8", + Tomato: "#FF6347", + Turquoise: "#40E0D0", + Violet: "#EE82EE", + Wheat: "#F5DEB3", + White: "#FFFFFF", + WhiteSmoke: "#F5F5F5", + Yellow: "#FFFF00", + YellowGreen: "#9ACD32", +} + diff --git a/examples/toys/magBalls/handController.js b/examples/toys/magBalls/handController.js index 998d22c6f8..4aba43d412 100644 --- a/examples/toys/magBalls/handController.js +++ b/examples/toys/magBalls/handController.js @@ -10,6 +10,7 @@ RIGHT_CONTROLLER = 1; // FIXME add a customizable wand model and a mechanism to switch between wands HandController = function(side) { + this.side = side; this.palm = 2 * side; this.tip = 2 * side + 1; diff --git a/examples/toys/magBalls/magBallsMain.js b/examples/toys/magBalls/magBallsMain.js index e54b818e4a..4eb98fab26 100644 --- a/examples/toys/magBalls/magBallsMain.js +++ b/examples/toys/magBalls/magBallsMain.js @@ -6,6 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.include("../../libraries/htmlColors.js"); Script.include("constants.js"); Script.include("utils.js"); Script.include("magBalls.js"); From 2e880e9aab1e430ad4729ce596d69a7a6b4a1a80 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 2 Sep 2015 16:29:15 -0700 Subject: [PATCH 083/117] change how pick-ray direction is calculated --- examples/voxels.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index c5564fe391..547585198c 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -356,8 +356,9 @@ function attemptVoxelChangeForEntity(entityID, pickRayDir, intersectionLocation) return false; } + var voxelOrigin = Entities.worldCoordsToVoxelCoords(entityID, Vec3.subtract(intersectionLocation, pickRayDir)); var voxelPosition = Entities.worldCoordsToVoxelCoords(entityID, intersectionLocation); - var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(entityID, pickRayDir); + var pickRayDirInVoxelSpace = Vec3.subtract(voxelPosition, voxelOrigin); pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace); var doAdd = addingVoxels; @@ -409,8 +410,6 @@ function attemptVoxelChange(pickRayDir, intersection) { ids.push(intersection.entityID); } - // print("ids = " + ids); - var success = false; for (var i = 0; i < ids.length; i++) { var entityID = ids[i]; From 2fe6e9c3179a1fc75923f9b3d4a974162fc3434d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 2 Sep 2015 16:30:11 -0700 Subject: [PATCH 084/117] use AABox's ray intersection code rather than the goofed-up version that was here --- .../src/RenderablePolyVoxEntityItem.cpp | 99 +++++-------------- 1 file changed, 23 insertions(+), 76 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 6fb5648fe0..f393391780 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -276,13 +276,6 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi } bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { - // // glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords); - // glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f); - // glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - // float scaleY = scale.y; - // float radiusVoxelCoords = radiusWorldCoords / scaleY; - // return setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); - bool result = false; if (_locked) { return result; @@ -371,7 +364,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o glm::mat4 wtvMatrix = worldToVoxelMatrix(); glm::mat4 vtwMatrix = voxelToWorldMatrix(); - glm::mat4 vtlMatrix = voxelToLocalMatrix(); glm::vec3 normDirection = glm::normalize(direction); // the PolyVox ray intersection code requires a near and far point. @@ -379,67 +371,33 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o float distanceToEntity = glm::distance(origin, getPosition()); float largestDimension = glm::max(getDimensions().x, getDimensions().y, getDimensions().z) * 2.0f; glm::vec3 farPoint = origin + normDirection * (distanceToEntity + largestDimension); + glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); - glm::vec4 result; + glm::vec4 directionInVoxel = glm::normalize(farInVoxel - originInVoxel); + + glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); PolyVox::RaycastResult raycastResult = doRayCast(originInVoxel, farInVoxel, result); if (raycastResult == PolyVox::RaycastResults::Completed) { // the ray completed its path -- nothing was hit. return false; } - // set up ray tests against each face of the voxel. - glm::vec3 minXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.0f, 0.5f, 0.5f, 0.0f))); - glm::vec3 maxXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(1.0f, 0.5f, 0.5f, 0.0f))); - glm::vec3 minYPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.0f, 0.5f, 0.0f))); - glm::vec3 maxYPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 1.0f, 0.5f, 0.0f))); - glm::vec3 minZPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.5f, 0.0f, 0.0f))); - glm::vec3 maxZPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.5f, 1.0f, 0.0f))); + glm::vec3 result3 = vec3(result); - glm::vec4 baseDimensions = glm::vec4(1.0, 1.0, 1.0, 0.0); - glm::vec3 worldDimensions = glm::vec3(vtlMatrix * baseDimensions); - glm::vec2 xDimensions = glm::vec2(worldDimensions.z, worldDimensions.y); - glm::vec2 yDimensions = glm::vec2(worldDimensions.x, worldDimensions.z); - glm::vec2 zDimensions = glm::vec2(worldDimensions.x, worldDimensions.y); + AABox voxelBox; + voxelBox += result3 + glm::vec3(-0.5f, -0.5f, -0.5f); + voxelBox += result3 + glm::vec3(0.5f, 0.5f, 0.5f); - glm::quat vtwRotation = extractRotation(vtwMatrix); - glm::quat minXRotation = vtwRotation * glm::quat(glm::vec3(0.0f, PI_OVER_TWO, 0.0f)); - glm::quat maxXRotation = vtwRotation * glm::quat(glm::vec3(0.0f, PI_OVER_TWO, 0.0f)); - glm::quat minYRotation = vtwRotation * glm::quat(glm::vec3(PI_OVER_TWO, 0.0f, 0.0f)); - glm::quat maxYRotation = vtwRotation * glm::quat(glm::vec3(PI_OVER_TWO, 0.0f, 0.0f)); - glm::quat minZRotation = vtwRotation * glm::quat(glm::vec3(0.0f, 0.0f, 0.0f)); - glm::quat maxZRotation = vtwRotation * glm::quat(glm::vec3(0.0f, 0.0f, 0.0f)); + float voxelDistance; - float bestDx = FLT_MAX; - bool hit[ 6 ]; - float dx[ 6 ] = {FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX}; + bool hit = voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), voxelDistance, face); - hit[0] = findRayRectangleIntersection(origin, direction, minXRotation, minXPosition, xDimensions, dx[0]); - hit[1] = findRayRectangleIntersection(origin, direction, maxXRotation, maxXPosition, xDimensions, dx[1]); - hit[2] = findRayRectangleIntersection(origin, direction, minYRotation, minYPosition, yDimensions, dx[2]); - hit[3] = findRayRectangleIntersection(origin, direction, maxYRotation, maxYPosition, yDimensions, dx[3]); - hit[4] = findRayRectangleIntersection(origin, direction, minZRotation, minZPosition, zDimensions, dx[4]); - hit[5] = findRayRectangleIntersection(origin, direction, maxZRotation, maxZPosition, zDimensions, dx[5]); - - bool ok = false; - for (int i = 0; i < 6; i ++) { - if (hit[ i ] && dx[ i ] < bestDx) { - face = (BoxFace)i; - distance = dx[ i ]; - ok = true; - bestDx = dx[ i ]; - } - } - - if (!ok) { - // if the attempt to put the ray against one of the voxel-faces fails, just return the center - glm::vec4 intersectedWorldPosition = vtwMatrix * (result + vec4(0.5f, 0.5f, 0.5f, 0.0f)); - distance = glm::distance(glm::vec3(intersectedWorldPosition), origin); - face = BoxFace::MIN_X_FACE; - } - - return true; + glm::vec4 voxelIntersectionPoint = glm::vec4(glm::vec3(originInVoxel) + glm::vec3(directionInVoxel) * voxelDistance, 1.0); + glm::vec4 intersectionPoint = vtwMatrix * voxelIntersectionPoint; + distance = glm::distance(origin, glm::vec3(intersectionPoint)); + return hit; } @@ -455,7 +413,7 @@ PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originIn _volDataLock.unlock(); // result is in voxel-space coordinates. - result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); + result = callback._result; return raycastResult; } @@ -629,7 +587,13 @@ namespace render { glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const { - return glm::vec3(voxelToWorldMatrix() * glm::vec4(voxelCoords, 1.0f)); + glm::vec3 adjustedCoords; + if (isEdged(_voxelSurfaceStyle)) { + adjustedCoords = voxelCoords + glm::vec3(0.5f, 0.5f, 0.5f); + } else { + adjustedCoords = voxelCoords - glm::vec3(0.5f, 0.5f, 0.5f); + } + return glm::vec3(voxelToWorldMatrix() * glm::vec4(adjustedCoords, 1.0f)); } glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { @@ -773,17 +737,11 @@ bool RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toV return false; } - void RenderablePolyVoxEntityItem::decompressVolumeData() { _threadRunning.acquire(); QtConcurrent::run(this, &RenderablePolyVoxEntityItem::decompressVolumeDataAsync); } - - - - - // take compressed data and expand it into _volData. void RenderablePolyVoxEntityItem::decompressVolumeDataAsync() { _voxelDataLock.lockForRead(); @@ -844,7 +802,6 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { QtConcurrent::run(this, &RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacketAsync); } - // compress the data in _volData and save the results. The compressed form is used during // saves to disk and for transmission over the wire void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacketAsync() { @@ -917,7 +874,6 @@ void RenderablePolyVoxEntityItem::getMesh() { QtConcurrent::run(this, &RenderablePolyVoxEntityItem::getMeshAsync); } - void RenderablePolyVoxEntityItem::clearOutOfDateNeighbors() { if (_xNNeighborID != UNKNOWN_ENTITY_ID) { EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); @@ -1035,15 +991,6 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { } } - -// PolyVox::Region shrinkRegion(PolyVox::Region originalRegion) { -// PolyVox::Region smallerRegion = originalRegion; -// smallerRegion.shiftLowerCorner(PolyVox::Vector3DInt32(1, 1, 1)); -// smallerRegion.shiftUpperCorner(PolyVox::Vector3DInt32(-1, -1, -1)); -// return smallerRegion; -// } - - void RenderablePolyVoxEntityItem::getMeshAsync() { model::MeshPointer mesh(new model::Mesh()); @@ -1136,7 +1083,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_MARCHING_CUBES || _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { - /* pull each triangle in the mesh into a polyhedron which can be collided with */ + // pull each triangle in the mesh into a polyhedron which can be collided with unsigned int i = 0; _meshLock.lockForRead(); From d303c7e119021c3fd438c940e6c56e3197d8003c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 2 Sep 2015 16:45:50 -0700 Subject: [PATCH 085/117] only add actions to physical objects --- examples/controllers/hydra/hydraGrab.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index dfb0fdcadf..71e4d2a07e 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -9,6 +9,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + + var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); var rightTriggerAction = RIGHT_HAND_CLICK; @@ -70,6 +72,7 @@ function controller(side, triggerAction, pullAction, hand) { this.actionID = null; this.tractorBeamActive = false; this.distanceHolding = false; + this.closeGrabbing = false; this.triggerValue = 0; this.prevTriggerValue = 0; this.palm = 2 * side; @@ -127,8 +130,7 @@ controller.prototype.checkForIntersections = function(origin, direction) { }; var intersection = Entities.findRayIntersection(pickRay, true); - - if (intersection.intersects) { + if (intersection.intersects && intersection.properties.collisionsWillMove === 1) { this.distanceToEntity = Vec3.distance(origin, intersection.properties.position); Entities.editEntity(this.pointer, { linePoints: [ @@ -156,7 +158,7 @@ controller.prototype.attemptMove = function() { if (this.actionID === null) { this.actionID = Entities.addAction("spring", this.grabbedEntity, { targetPosition: newPosition, - linearTimeScale: 0.1 + linearTimeScale: .1 }); } else { Entities.updateAction(this.grabbedEntity, this.actionID, { @@ -188,6 +190,7 @@ controller.prototype.letGo = function() { this.distanceHolding = false; this.tractorBeamActive = false; this.checkForEntityArrival = false; + this.closeGrabbing = false; } controller.prototype.update = function() { @@ -216,7 +219,7 @@ controller.prototype.update = function() { if (this.shouldDisplayLine) { this.updateLine(); } - if (this.triggerValue > DISTANCE_HOLD_THRESHOLD) { + if (this.triggerValue > DISTANCE_HOLD_THRESHOLD && !this.closeGrabbing) { this.attemptMove(); } @@ -234,6 +237,7 @@ controller.prototype.grabEntity = function() { var objectPosition = Entities.getEntityProperties(this.grabbedEntity).position; var offset = Vec3.subtract(objectPosition, handPosition); var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); + this.closeGrabbing = true; this.actionID = Entities.addAction("hold", this.grabbedEntity, { relativePosition: offsetPosition, relativeRotation: offsetRotation, @@ -252,7 +256,7 @@ controller.prototype.checkForInRangeObject = function() { for (var i = 0; i < entities.length; i++) { var props = Entities.getEntityProperties(entities[i]); var distance = Vec3.distance(props.position, handPosition); - if (distance < minDistance && props.name !== "pointer") { + if (distance < minDistance && props.name !== "pointer" && props.collisionsWillMove === 1) { grabbedEntity = entities[i]; minDistance = distance; } From 46b3a7fd2313a811d1d8618ce42fe8a1ba364ec3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 17:28:06 -0700 Subject: [PATCH 086/117] Improved default avatar.json state machine. Now triggers 7 states. Idle, WalkFwd, WalkBwd, StrafeLeft, StrafeRight, TurnLeft & TurnRight. As well as variable speed walking to match current velocity. --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimStateMachine.cpp | 4 +- libraries/animation/src/Rig.cpp | 144 ++++++++++++------- tests/animation/src/data/avatar.json | 85 ++++++++++- 4 files changed, 181 insertions(+), 54 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8e07aa3442..c89a583f32 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1225,7 +1225,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { // 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/a6e3754beef1524f95bae178066bae3b2f839952/avatar.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/e2cb37aee601b6fba31d60eac3f6ae3ef72d4a66/avatar.json"); _skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); } diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index eccfcfa6e6..5de379dd33 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -52,7 +52,9 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl if (_duringInterp) { _alpha += _alphaVel * dt; if (_alpha < 1.0f) { - ::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]); + if (_poses.size() > 0) { + ::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]); + } } else { _duringInterp = false; _prevPoses.clear(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 89ab062c02..c96a588e5c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -416,52 +416,107 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { + glm::vec3 front = worldRotation * IDENTITY_FRONT; + + // at the moment worldVelocity comes from the Avatar physics body, which is not always correct when + // moving in the HMD, so let's compute our own veloicty. + glm::vec3 worldVel = (worldPosition - _lastPosition) / deltaTime; + glm::vec3 localVel = glm::inverse(worldRotation) * worldVel; + float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); + float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); + float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + if (_enableAnimGraph) { + + // sine wave LFO var for testing. static float t = 0.0f; _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); - if (glm::length(worldVelocity) > 0.07f) { - _animVars.set("isMoving", true); - _animVars.set("isNotMoving", false); + // default anim vars to notMoving and notTurning + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + + const float ANIM_WALK_SPEED = 1.4f; // m/s + _animVars.set("walkTimeScale", glm::clamp(0.1f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); + + const float MOVE_SPEED_THRESHOLD = 0.01f; // m/sec + const float TURN_SPEED_THRESHOLD = 0.5f; // rad/sec + if (glm::length(localVel) > MOVE_SPEED_THRESHOLD) { + if (fabs(forwardSpeed) > fabs(lateralSpeed)) { + if (forwardSpeed > 0.0f) { + // forward + _animVars.set("isMovingForward", true); + _animVars.set("isNotMoving", false); + + } else { + // backward + _animVars.set("isMovingBackward", true); + _animVars.set("isNotMoving", false); + } + } else { + if (lateralSpeed > 0.0f) { + // right + _animVars.set("isMovingRight", true); + _animVars.set("isNotMoving", false); + } else { + // left + _animVars.set("isMovingLeft", true); + _animVars.set("isNotMoving", false); + } + } } else { - _animVars.set("isMoving", false); - _animVars.set("isNotMoving", true); + if (fabs(turningSpeed) > TURN_SPEED_THRESHOLD) { + if (turningSpeed > 0.0f) { + // turning right + _animVars.set("isTurningRight", true); + _animVars.set("isNotTurning", false); + } else { + // turning left + _animVars.set("isTurningLeft", true); + _animVars.set("isNotTurning", false); + } + } else { + // idle + } } t += deltaTime; } - if (!_enableRig) { - return; + if (_enableRig) { + bool isMoving = false; + auto updateRole = [&](const QString& role, bool isOn) { + isMoving = isMoving || isOn; + if (isOn) { + if (!isRunningRole(role)) { + qCDebug(animation) << "Rig STARTING" << role; + startAnimationByRole(role); + } + } else { + if (isRunningRole(role)) { + qCDebug(animation) << "Rig stopping" << role; + stopAnimationByRole(role); + } + } + }; + + updateRole("walk", forwardSpeed > 0.01f); + updateRole("backup", forwardSpeed < -0.01f); + bool isTurning = std::abs(turningSpeed) > 0.5f; + updateRole("rightTurn", isTurning && (turningSpeed > 0)); + updateRole("leftTurn", isTurning && (turningSpeed < 0)); + bool isStrafing = !isTurning && (std::abs(lateralSpeed) > 0.01f); + updateRole("rightStrafe", isStrafing && (lateralSpeed > 0.0f)); + updateRole("leftStrafe", isStrafing && (lateralSpeed < 0.0f)); + updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. } - bool isMoving = false; - glm::vec3 front = worldRotation * IDENTITY_FRONT; - float forwardSpeed = glm::dot(worldVelocity, front); - float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT); - float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; - auto updateRole = [&](const QString& role, bool isOn) { - isMoving = isMoving || isOn; - if (isOn) { - if (!isRunningRole(role)) { - qCDebug(animation) << "Rig STARTING" << role; - startAnimationByRole(role); - } - } else { - if (isRunningRole(role)) { - qCDebug(animation) << "Rig stopping" << role; - stopAnimationByRole(role); - } - } - }; - updateRole("walk", forwardSpeed > 0.01f); - updateRole("backup", forwardSpeed < -0.01f); - bool isTurning = std::abs(rightTurningSpeed) > 0.5f; - updateRole("rightTurn", isTurning && (rightTurningSpeed > 0)); - updateRole("leftTurn", isTurning && (rightTurningSpeed < 0)); - bool isStrafing = !isTurning && (std::abs(rightLateralSpeed) > 0.01f); - updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f)); - updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); - updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. + _lastFront = front; _lastPosition = worldPosition; } @@ -487,13 +542,6 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false); } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, rootTransform); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } - } else { // First normalize the fades so that they sum to 1.0. @@ -538,13 +586,13 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { handle->setMix(mix); handle->simulate(deltaTime); } + } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, rootTransform); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i, rootTransform); + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); } } diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index 0988e40ca8..24967979ea 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -11,7 +11,12 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "isMoving", "state": "walkFwd" } + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } ] }, { @@ -19,7 +24,77 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ - { "var": "isNotMoving", "state": "idle" } + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "walkBwd", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "strafeRight", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "strafeLeft", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "turnRight", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotTurning", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningLeft", "state": "turnLeft" } + ] + }, + { + "id": "turnLeft", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotTurning", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" } ] } ] @@ -45,7 +120,8 @@ "startFrame": 0.0, "endFrame": 35.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": true, + "timeScaleVar": "walkTimeScale" }, "children": [] }, @@ -57,7 +133,8 @@ "startFrame": 0.0, "endFrame": 37.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": true, + "timeScaleVar": "walkTimeScale" }, "children": [] }, From ea415071b48d1c3060c6b455536e54e951aa90fe Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 31 Aug 2015 22:54:23 -0700 Subject: [PATCH 087/117] OmniTool first pass --- .../{toys/magBalls => libraries}/constants.js | 0 .../magBalls => libraries}/highlighter.js | 6 + examples/libraries/omniTool.js | 300 ++++++++++++++++++ .../libraries/omniTool/models/modelBase.js | 19 ++ examples/libraries/omniTool/models/wand.js | 120 +++++++ examples/libraries/omniTool/modules/test.js | 9 + .../{toys/magBalls => libraries}/utils.js | 8 +- examples/toys/magBalls/magBallsMain.js | 26 -- 8 files changed, 461 insertions(+), 27 deletions(-) rename examples/{toys/magBalls => libraries}/constants.js (100%) rename examples/{toys/magBalls => libraries}/highlighter.js (92%) create mode 100644 examples/libraries/omniTool.js create mode 100644 examples/libraries/omniTool/models/modelBase.js create mode 100644 examples/libraries/omniTool/models/wand.js create mode 100644 examples/libraries/omniTool/modules/test.js rename examples/{toys/magBalls => libraries}/utils.js (93%) delete mode 100644 examples/toys/magBalls/magBallsMain.js diff --git a/examples/toys/magBalls/constants.js b/examples/libraries/constants.js similarity index 100% rename from examples/toys/magBalls/constants.js rename to examples/libraries/constants.js diff --git a/examples/toys/magBalls/highlighter.js b/examples/libraries/highlighter.js similarity index 92% rename from examples/toys/magBalls/highlighter.js rename to examples/libraries/highlighter.js index 149d9ec5b7..b3550b6c8a 100644 --- a/examples/toys/magBalls/highlighter.js +++ b/examples/libraries/highlighter.js @@ -53,6 +53,12 @@ Highlighter.prototype.setSize = function(newSize) { }); } +Highlighter.prototype.setRotation = function(newRotation) { + Overlays.editOverlay(this.highlightCube, { + rotation: newRotation + }); +} + Highlighter.prototype.updateHighlight = function() { if (this.hightlighted) { var properties = Entities.getEntityProperties(this.hightlighted); diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js new file mode 100644 index 0000000000..643b789a0e --- /dev/null +++ b/examples/libraries/omniTool.js @@ -0,0 +1,300 @@ +// +// Created by Bradley Austin Davis on 2015/09/01 +// 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 +// + +Script.include("constants.js"); +Script.include("utils.js"); +Script.include("highlighter.js"); +Script.include("omniTool/models/modelBase.js"); +Script.include("omniTool/models/wand.js"); + +OmniToolModules = {}; +OmniToolModuleType = null; + +OmniTool = function(side) { + this.OMNI_KEY = "OmniTool"; + this.MAX_FRAMERATE = 30; + this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE + this.SIDE = side; + this.PALM = 2 * side; + this.ACTION = findAction(side ? "ACTION2" : "ACTION1"); + this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2"); + + this.highlighter = new Highlighter(); + this.ignoreEntities = {}; + this.nearestOmniEntity = { + id: null, + inside: false, + position: null, + distance: Infinity, + radius: 0, + omniProperties: {}, + boundingBox: null, + }; + + this.activeOmniEntityId = null; + this.lastUpdateInterval = 0; + this.tipLength = 0.4; + this.active = false; + this.module = null; + this.moduleEntityId = null; + this.lastScanPosition = ZERO_VECTOR; + this.model = new Wand(); + this.model.setLength(this.tipLength); + + // Connect to desired events + var _this = this; + Controller.actionEvent.connect(function(action, state) { + _this.onActionEvent(action, state); + }); + + Script.update.connect(function(deltaTime) { + _this.lastUpdateInterval += deltaTime; + if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) { + _this.onUpdate(_this.lastUpdateInterval); + _this.lastUpdateInterval = 0; + } + }); + + Script.scriptEnding.connect(function() { + _this.onCleanup(); + }); +} + +OmniTool.prototype.onActionEvent = function(action, state) { + // FIXME figure out the issues when only one spatial controller is active + // logDebug("Action: " + action + " " + state); + + if (this.module && this.module.onActionEvent) { + this.module.onActionEvent(action, state); + } + + if (action == this.ACTION) { + if (state) { + this.onClick(); + } else { + this.onRelease(); + } + } + + // FIXME Does not work + //// with only one controller active (listed as 2 here because 'tip' + 'palm') + //// then treat the alt action button as the action button +} + +OmniTool.prototype.getOmniToolData = function(entityId) { + return getEntityCustomData(this.OMNI_KEY, entityId, null); +} + +OmniTool.prototype.setOmniToolData = function(entityId, data) { + setEntityCustomData(this.OMNI_KEY, entityId, data); +} + +OmniTool.prototype.updateOmniToolData = function(entityId, data) { + var currentData = this.getOmniToolData(entityId) || {}; + for (var key in data) { + currentData[key] = data[key]; + } + setEntityCustomData(this.OMNI_KEY, entityId, currentData); +} + +OmniTool.prototype.setActive = function(active) { + if (active === this.active) { + return; + } + logDebug("omnitool changing active state: " + active); + this.active = active; + this.model.setVisible(this.active); + + if (this.module && this.module.onActiveChanged) { + this.module.onActiveChanged(this.side); + } +} + + +OmniTool.prototype.onUpdate = function(deltaTime) { + // FIXME this returns data if either the left or right controller is not on the base + this.position = Controller.getSpatialControlPosition(this.PALM); + // When on the base, hydras report a position of 0 + this.setActive(Vec3.length(this.position) > 0.001); + + var rawRotation = Controller.getSpatialControlRawRotation(this.PALM); + this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation); + + this.model.setTransform({ + rotation: this.rotation, + position: this.position, + }); + + this.scan(); + + if (this.module && this.module.onUpdate) { + this.module.onUpdate(deltaTime); + } +} + +OmniTool.prototype.onClick = function() { + // First check to see if the user is switching to a new omni module + if (this.nearestOmniEntity.inside && this.nearestOmniEntity.omniProperties.script) { + this.activateNewOmniModule(); + return; + } + + // Next check if there is an active module and if so propagate the click + // FIXME how to I switch to a new module? + if (this.module && this.module.onClick) { + this.module.onClick(); + return; + } +} + +OmniTool.prototype.onRelease = function() { + // FIXME how to I switch to a new module? + if (this.module && this.module.onRelease) { + this.module.onRelease(); + return; + } + logDebug("Base omnitool does nothing on release"); +} + +// FIXME resturn a structure of all nearby entities to distances +OmniTool.prototype.findNearestOmniEntity = function(maxDistance, selector) { + if (!maxDistance) { + maxDistance = 2.0; + } + var resultDistance = Infinity; + var resultId = null; + var resultProperties = null; + var resultOmniData = null; + var ids = Entities.findEntities(this.model.tipPosition, maxDistance); + for (var i in ids) { + var entityId = ids[i]; + if (this.ignoreEntities[entityId]) { + continue; + } + var omniData = this.getOmniToolData(entityId); + if (!omniData) { + // FIXME find a place to flush this information + this.ignoreEntities[entityId] = true; + continue; + } + + // Let searchers query specifically + if (selector && !selector(entityId, omniData)) { + continue; + } + + var properties = Entities.getEntityProperties(entityId); + var distance = Vec3.distance(this.model.tipPosition, properties.position); + if (distance < resultDistance) { + resultDistance = distance; + resultId = entityId; + } + } + + return resultId; +} + +OmniTool.prototype.onEnterNearestOmniEntity = function() { + this.nearestOmniEntity.inside = true; + this.highlighter.highlight(this.nearestOmniEntity.id); + logDebug("On enter omniEntity " + this.nearestOmniEntity.id); +} + +OmniTool.prototype.onLeaveNearestOmniEntity = function() { + this.nearestOmniEntity.inside = false; + this.highlighter.highlight(null); + logDebug("On leave omniEntity " + this.nearestOmniEntity.id); +} + +OmniTool.prototype.setNearestOmniEntity = function(entityId) { + if (entityId && entityId !== this.nearestOmniEntity.id) { + if (this.nearestOmniEntity.id && this.nearestOmniEntity.inside) { + this.onLeaveNearestOmniEntity(); + } + this.nearestOmniEntity.id = entityId; + this.nearestOmniEntity.omniProperties = this.getOmniToolData(entityId); + var properties = Entities.getEntityProperties(entityId); + this.nearestOmniEntity.position = properties.position; + // FIXME use a real bounding box, not a sphere + var bbox = properties.boundingBox; + this.nearestOmniEntity.radius = Vec3.length(Vec3.subtract(bbox.center, bbox.brn)); + this.highlighter.setRotation(properties.rotation); + this.highlighter.setSize(Vec3.multiply(1.05, bbox.dimensions)); + } +} + +OmniTool.prototype.scan = function() { + var scanDistance = Vec3.distance(this.model.tipPosition, this.lastScanPosition); + + if (scanDistance < 0.005) { + return; + } + + this.lastScanPosition = this.model.tipPosition; + + this.setNearestOmniEntity(this.findNearestOmniEntity()); + if (this.nearestOmniEntity.id) { + var distance = Vec3.distance(this.model.tipPosition, this.nearestOmniEntity.position); + // track distance on a half centimeter basis + if (Math.abs(this.nearestOmniEntity.distance - distance) > 0.005) { + this.nearestOmniEntity.distance = distance; + if (!this.nearestOmniEntity.inside && distance < this.nearestOmniEntity.radius) { + this.onEnterNearestOmniEntity(); + } + + if (this.nearestOmniEntity.inside && distance > this.nearestOmniEntity.radius + 0.01) { + this.onLeaveNearestOmniEntity(); + } + } + } +} + +OmniTool.prototype.unloadModule = function() { + if (this.module && this.module.onUnload) { + this.module.onUnload(); + } + this.module = null; + this.moduleEntityId = null; +} + +OmniTool.prototype.activateNewOmniModule = function() { + // Support the ability for scripts to just run without replacing the current module + var script = this.nearestOmniEntity.omniProperties.script; + if (script.indexOf("/") < 0) { + script = "omniTool/modules/" + script; + } + + // Reset the tool type + OmniToolModuleType = null; + logDebug("Including script path: " + script); + try { + Script.include(script); + } catch(err) { + logWarn("Failed to include script: " + script + "\n" + err); + return; + } + + // If we're building a new module, unload the old one + if (OmniToolModuleType) { + logDebug("New OmniToolModule: " + OmniToolModuleType); + this.unloadModule(); + + try { + this.module = new OmniToolModules[OmniToolModuleType](this, this.nearestOmniEntity.id); + this.moduleEntityId = this.nearestOmniEntity.id; + if (this.module.onLoad) { + this.module.onLoad(); + } + } catch(err) { + logWarn("Failed to instantiate new module: " + err); + } + } +} + +// FIXME find a good way to sync the two omni tools +OMNI_TOOLS = [ new OmniTool(0), new OmniTool(1) ]; diff --git a/examples/libraries/omniTool/models/modelBase.js b/examples/libraries/omniTool/models/modelBase.js new file mode 100644 index 0000000000..7697856d3f --- /dev/null +++ b/examples/libraries/omniTool/models/modelBase.js @@ -0,0 +1,19 @@ + +ModelBase = function() { + this.length = 0.2; +} + +ModelBase.prototype.setVisible = function(visible) { + this.visible = visible; +} + +ModelBase.prototype.setLength = function(length) { + this.length = length; +} + +ModelBase.prototype.setTransform = function(transform) { + this.rotation = transform.rotation; + this.position = transform.position; + this.tipVector = Vec3.multiplyQbyV(this.rotation, { x: 0, y: this.length, z: 0 }); + this.tipPosition = Vec3.sum(this.position, this.tipVector); +} diff --git a/examples/libraries/omniTool/models/wand.js b/examples/libraries/omniTool/models/wand.js new file mode 100644 index 0000000000..8f0fe92b53 --- /dev/null +++ b/examples/libraries/omniTool/models/wand.js @@ -0,0 +1,120 @@ + +Wand = function() { + // Max updates fps + this.MAX_FRAMERATE = 30 + this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE + this.DEFAULT_TIP_COLORS = [ { + red: 128, + green: 128, + blue: 128, + }, { + red: 64, + green: 64, + blue: 64, + }]; + this.POINTER_ROTATION = Quat.fromPitchYawRollDegrees(45, 0, 45); + + // FIXME does this need to be a member of this? + this.lastUpdateInterval = 0; + + this.pointers = [ + Overlays.addOverlay("cube", { + position: ZERO_VECTOR, + color: this.DEFAULT_TIP_COLORS[0], + alpha: 1.0, + solid: true, + visible: false, + }), + Overlays.addOverlay("cube", { + position: ZERO_VECTOR, + color: this.DEFAULT_TIP_COLORS[1], + alpha: 1.0, + solid: true, + visible: false, + }) + ]; + + this.wand = Overlays.addOverlay("cube", { + position: ZERO_VECTOR, + color: COLORS.WHITE, + dimensions: { x: 0.01, y: 0.01, z: 0.2 }, + alpha: 1.0, + solid: true, + visible: false + }); + + var _this = this; + Script.scriptEnding.connect(function() { + Overlays.deleteOverlay(_this.pointers[0]); + Overlays.deleteOverlay(_this.pointers[1]); + Overlays.deleteOverlay(_this.wand); + }); + + Script.update.connect(function(deltaTime) { + _this.lastUpdateInterval += deltaTime; + if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) { + _this.onUpdate(_this.lastUpdateInterval); + _this.lastUpdateInterval = 0; + } + }); +} + +Wand.prototype = Object.create( ModelBase.prototype ); + +Wand.prototype.setVisible = function(visible) { + ModelBase.prototype.setVisible.call(this, visible); + Overlays.editOverlay(this.pointers[0], { + visible: this.visible + }); + Overlays.editOverlay(this.pointers[1], { + visible: this.visible + }); + Overlays.editOverlay(this.wand, { + visible: this.visible + }); +} + +Wand.prototype.setTransform = function(transform) { + ModelBase.prototype.setTransform.call(this, transform); + + var wandPosition = Vec3.sum(this.position, Vec3.multiply(0.5, this.tipVector)); + Overlays.editOverlay(this.pointers[0], { + position: this.tipPosition, + rotation: this.rotation, + visible: true, + }); + Overlays.editOverlay(this.pointers[1], { + position: this.tipPosition, + rotation: Quat.multiply(this.POINTER_ROTATION, this.rotation), + visible: true, + }); + Overlays.editOverlay(this.wand, { + dimensions: { x: 0.01, y: this.length * 0.9, z: 0.01 }, + position: wandPosition, + rotation: this.rotation, + visible: true, + }); +} + +Wand.prototype.setTipColors = function(color1, color2) { + Overlays.editOverlay(this.pointers[0], { + color: color1 || this.DEFAULT_TIP_COLORS[0], + }); + Overlays.editOverlay(this.pointers[1], { + color: color2 || this.DEFAULT_TIP_COLORS[1], + }); +} + +Wand.prototype.onUpdate = function(deltaTime) { + if (this.visible) { + var time = new Date().getTime() / 250; + var scale1 = Math.abs(Math.sin(time)); + var scale2 = Math.abs(Math.cos(time)); + Overlays.editOverlay(this.pointers[0], { + scale: scale1 * 0.01, + }); + Overlays.editOverlay(this.pointers[1], { + scale: scale2 * 0.01, + }); + } +} diff --git a/examples/libraries/omniTool/modules/test.js b/examples/libraries/omniTool/modules/test.js new file mode 100644 index 0000000000..1ca806affa --- /dev/null +++ b/examples/libraries/omniTool/modules/test.js @@ -0,0 +1,9 @@ + +OmniToolModules.Test = function() { +} + +OmniToolModules.Test.prototype.onClick = function() { + logDebug("Test module onClick"); +} + +OmniToolModuleType = "Test" \ No newline at end of file diff --git a/examples/toys/magBalls/utils.js b/examples/libraries/utils.js similarity index 93% rename from examples/toys/magBalls/utils.js rename to examples/libraries/utils.js index ea1446f858..b15ea6c313 100644 --- a/examples/toys/magBalls/utils.js +++ b/examples/libraries/utils.js @@ -53,11 +53,17 @@ getEntityUserData = function(id) { var results = null; var properties = Entities.getEntityProperties(id); if (properties.userData) { - results = JSON.parse(properties.userData); + try { + results = JSON.parse(properties.userData); + } catch(err) { + logDebug(err); + logDebug(properties.userData); + } } return results ? results : {}; } + // Non-destructively modify the user data of an entity. setEntityCustomData = function(customKey, id, data) { var userData = getEntityUserData(id); diff --git a/examples/toys/magBalls/magBallsMain.js b/examples/toys/magBalls/magBallsMain.js deleted file mode 100644 index 4eb98fab26..0000000000 --- a/examples/toys/magBalls/magBallsMain.js +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/08/25 -// 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 -// - -Script.include("../../libraries/htmlColors.js"); -Script.include("constants.js"); -Script.include("utils.js"); -Script.include("magBalls.js"); - -Script.include("ballController.js"); - -var magBalls = new MagBalls(); - -// Clear any previous balls -// magBalls.clear(); - -MenuController = function(side) { - HandController.call(this, side); -} - -// FIXME resolve some of the issues with dual controllers before allowing both controllers active -var handControllers = [new BallController(LEFT_CONTROLLER, magBalls)]; //, new HandController(RIGHT) ]; From 5717b7783748ff543403d877e3770f594270f319 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 2 Sep 2015 18:01:47 -0700 Subject: [PATCH 088/117] Updating magballs to omnitool --- examples/libraries/constants.js | 77 -------------- examples/libraries/omniTool.js | 4 + examples/libraries/utils.js | 8 -- examples/toys/magBalls.js | 113 ++++++++++++++++++++ examples/toys/magBalls/ballController.js | 103 ------------------ examples/toys/magBalls/constants.js | 77 ++++++++++++++ examples/toys/magBalls/graph.js | 4 + examples/toys/magBalls/handController.js | 128 ----------------------- examples/toys/magBalls/magBalls.js | 115 +++++++++++++++----- examples/toys/magBalls/menuController.js | 66 ------------ 10 files changed, 289 insertions(+), 406 deletions(-) create mode 100644 examples/toys/magBalls.js delete mode 100644 examples/toys/magBalls/ballController.js create mode 100644 examples/toys/magBalls/constants.js delete mode 100644 examples/toys/magBalls/handController.js delete mode 100644 examples/toys/magBalls/menuController.js diff --git a/examples/libraries/constants.js b/examples/libraries/constants.js index d154910f91..3ed7c02633 100644 --- a/examples/libraries/constants.js +++ b/examples/libraries/constants.js @@ -9,17 +9,6 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; STICK_URL = HIFI_PUBLIC_BUCKET + "models/props/geo_stick.fbx"; -// FIXME make this editable through some script UI, so the user can customize the size of the structure built -SCALE = 0.5; -BALL_SIZE = 0.08 * SCALE; -STICK_LENGTH = 0.24 * SCALE; - -DEBUG_MAGSTICKS = true; - -CUSTOM_DATA_NAME = "magBalls"; -BALL_NAME = "MagBall"; -EDGE_NAME = "MagStick"; - ZERO_VECTOR = { x: 0, y: 0, z: 0 }; COLORS = { @@ -70,71 +59,5 @@ COLORS = { } } -BALL_RADIUS = BALL_SIZE / 2.0; - -BALL_SELECTION_RADIUS = BALL_RADIUS * 1.5; - -BALL_DIMENSIONS = { - x: BALL_SIZE, - y: BALL_SIZE, - z: BALL_SIZE -}; - -BALL_COLOR = { - red: 128, - green: 128, - blue: 128 -}; - -STICK_DIMENSIONS = { - x: STICK_LENGTH / 6, - y: STICK_LENGTH / 6, - z: STICK_LENGTH -}; - -BALL_DISTANCE = STICK_LENGTH + BALL_SIZE; - -BALL_PROTOTYPE = { - type: "Sphere", - name: BALL_NAME, - dimensions: BALL_DIMENSIONS, - color: BALL_COLOR, - ignoreCollisions: true, - collisionsWillMove: false -}; - -// 2 millimeters -BALL_EPSILON = (.002) / BALL_DISTANCE; - -LINE_DIMENSIONS = { - x: 5, - y: 5, - z: 5 -} - -LINE_PROTOTYPE = { - type: "Line", - name: EDGE_NAME, - color: COLORS.CYAN, - dimensions: LINE_DIMENSIONS, - lineWidth: 5, - visible: true, - ignoreCollisions: true, - collisionsWillMove: false, -} - -EDGE_PROTOTYPE = LINE_PROTOTYPE; - -// var EDGE_PROTOTYPE = { -// type: "Sphere", -// name: EDGE_NAME, -// color: { red: 0, green: 255, blue: 255 }, -// //dimensions: STICK_DIMENSIONS, -// dimensions: { x: 0.02, y: 0.02, z: 0.02 }, -// rotation: rotation, -// visible: true, -// ignoreCollisions: true, -// collisionsWillMove: false -// } diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index 643b789a0e..c9f041d672 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -199,6 +199,10 @@ OmniTool.prototype.findNearestOmniEntity = function(maxDistance, selector) { return resultId; } +OmniTool.prototype.getPosition = function() { + return this.model.tipPosition; +} + OmniTool.prototype.onEnterNearestOmniEntity = function() { this.nearestOmniEntity.inside = true; this.highlighter.highlight(this.nearestOmniEntity.id); diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index b15ea6c313..6e6012cfe3 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -76,14 +76,6 @@ getEntityCustomData = function(customKey, id, defaultValue) { return userData[customKey] ? userData[customKey] : defaultValue; } -getMagBallsData = function(id) { - return getEntityCustomData(CUSTOM_DATA_NAME, id, {}); -} - -setMagBallsData = function(id, value) { - setEntityCustomData(CUSTOM_DATA_NAME, id, value); -} - mergeObjects = function(proto, custom) { var result = {}; for (var attrname in proto) { diff --git a/examples/toys/magBalls.js b/examples/toys/magBalls.js new file mode 100644 index 0000000000..8e441901a2 --- /dev/null +++ b/examples/toys/magBalls.js @@ -0,0 +1,113 @@ +// +// Created by Bradley Austin Davis on 2015/08/25 +// 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 +// + +// FIXME Script paths have to be relative to the caller, in this case libraries/OmniTool.js +Script.include("../toys/magBalls/constants.js"); +Script.include("../toys/magBalls/graph.js"); +Script.include("../toys/magBalls/edgeSpring.js"); +Script.include("../toys/magBalls/magBalls.js"); + +OmniToolModuleType = "MagBallsController" + + +OmniToolModules.MagBallsController = function(omniTool, entityId) { + this.omniTool = omniTool; + this.entityId = entityId; + this.highlighter = new Highlighter(); + this.magBalls = new MagBalls(); + this.highlighter.setSize(BALL_SIZE); + this.ghostEdges = {}; +} + +var MAG_BALLS_DATA_NAME = "magBalls"; + +getMagBallsData = function(id) { + return getEntityCustomData(MAG_BALLS_DATA_NAME, id, {}); +} + +setMagBallsData = function(id, value) { + setEntityCustomData(MAG_BALLS_DATA_NAME, id, value); +} + +//var magBalls = new MagBalls(); +// DEBUGGING ONLY - Clear any previous balls +// magBalls.clear(); + +OmniToolModules.MagBallsController.prototype.onClick = function() { + logDebug("MagBallsController onClick: " + vec3toStr(this.tipPosition)); + + this.selected = this.highlighter.hightlighted; + logDebug("This selected: " + this.selected); + if (!this.selected) { + this.selected = this.magBalls.createBall(this.tipPosition); + } + this.magBalls.selectBall(this.selected); + this.highlighter.highlight(null); + logDebug("Selected " + this.selected); +} + +OmniToolModules.MagBallsController.prototype.onRelease = function() { + logDebug("MagBallsController onRelease: " + vec3toStr(this.tipPosition)); + this.clearGhostEdges(); + if (this.selected) { + this.magBalls.releaseBall(this.selected); + this.selected = null; + } +} + +OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) { + this.tipPosition = this.omniTool.getPosition(); + if (!this.selected) { + // Find the highlight target and set it. + var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS); + this.highlighter.highlight(target); + if (!target) { + this.magBalls.onUpdate(deltaTime); + } + return; + } + + this.highlighter.highlight(null); + Entities.editEntity(this.selected, { position: this.tipPosition }); + var targetBalls = this.magBalls.findPotentialEdges(this.selected); + for (var ballId in targetBalls) { + if (!this.ghostEdges[ballId]) { + // create the ovleray + this.ghostEdges[ballId] = Overlays.addOverlay("line3d", { + start: this.magBalls.getNodePosition(ballId), + end: this.tipPosition, + color: COLORS.RED, + alpha: 1, + lineWidth: 5, + visible: true, + }); + } else { + Overlays.editOverlay(this.ghostEdges[ballId], { + end: this.tipPosition, + }); + } + } + for (var ballId in this.ghostEdges) { + if (!targetBalls[ballId]) { + Overlays.deleteOverlay(this.ghostEdges[ballId]); + delete this.ghostEdges[ballId]; + } + } +} + +OmniToolModules.MagBallsController.prototype.clearGhostEdges = function() { + for(var ballId in this.ghostEdges) { + Overlays.deleteOverlay(this.ghostEdges[ballId]); + delete this.ghostEdges[ballId]; + } +} + + +BallController.prototype.onUnload = function() { + this.clearGhostEdges(); +} diff --git a/examples/toys/magBalls/ballController.js b/examples/toys/magBalls/ballController.js deleted file mode 100644 index 0f178b2804..0000000000 --- a/examples/toys/magBalls/ballController.js +++ /dev/null @@ -1,103 +0,0 @@ -Script.include("handController.js"); -Script.include("highlighter.js"); - -BallController = function(side, magBalls) { - HandController.call(this, side); - this.magBalls = magBalls; - this.highlighter = new Highlighter(); - this.highlighter.setSize(BALL_SIZE); - this.ghostEdges = {}; -} - -BallController.prototype = Object.create( HandController.prototype ); - -BallController.prototype.onUpdate = function(deltaTime) { - HandController.prototype.onUpdate.call(this, deltaTime); - - if (!this.selected) { - // Find the highlight target and set it. - var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS); - this.highlighter.highlight(target); - return; - } - this.highlighter.highlight(null); - Entities.editEntity(this.selected, { position: this.tipPosition }); - var targetBalls = this.magBalls.findPotentialEdges(this.selected); - for (var ballId in targetBalls) { - if (!this.ghostEdges[ballId]) { - // create the ovleray - this.ghostEdges[ballId] = Overlays.addOverlay("line3d", { - start: this.magBalls.getNodePosition(ballId), - end: this.tipPosition, - color: COLORS.RED, - alpha: 1, - lineWidth: 5, - visible: true, - }); - } else { - Overlays.editOverlay(this.ghostEdges[ballId], { - end: this.tipPosition, - }); - } - } - for (var ballId in this.ghostEdges) { - if (!targetBalls[ballId]) { - Overlays.deleteOverlay(this.ghostEdges[ballId]); - delete this.ghostEdges[ballId]; - } - } -} - -BallController.prototype.onClick = function() { - this.selected = this.magBalls.grabBall(this.tipPosition, BALL_SELECTION_RADIUS); - this.highlighter.highlight(null); -} - -BallController.prototype.onRelease = function() { - this.clearGhostEdges(); - this.magBalls.releaseBall(this.selected); - this.selected = null; -} - -BallController.prototype.clearGhostEdges = function() { - for(var ballId in this.ghostEdges) { - Overlays.deleteOverlay(this.ghostEdges[ballId]); - delete this.ghostEdges[ballId]; - } -} - -BallController.prototype.onCleanup = function() { - HandController.prototype.onCleanup.call(this); - this.clearGhostEdges(); -} - -BallController.prototype.onAltClick = function() { - return; - var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS); - if (!target) { - logDebug(target); - return; - } - - // FIXME move to delete shape - var toDelete = {}; - var deleteQueue = [ target ]; - while (deleteQueue.length) { - var curNode = deleteQueue.shift(); - if (toDelete[curNode]) { - continue; - } - toDelete[curNode] = true; - for (var nodeId in this.magBalls.getConnectedNodes(curNode)) { - deleteQueue.push(nodeId); - } - } - for (var nodeId in toDelete) { - this.magBalls.destroyNode(nodeId); - } -} - - - -BallController.prototype.onAltRelease = function() { -} diff --git a/examples/toys/magBalls/constants.js b/examples/toys/magBalls/constants.js new file mode 100644 index 0000000000..b692f0908c --- /dev/null +++ b/examples/toys/magBalls/constants.js @@ -0,0 +1,77 @@ + +// FIXME make this editable through some script UI, so the user can customize the size of the structure built +SCALE = 0.5; +BALL_SIZE = 0.08 * SCALE; +STICK_LENGTH = 0.24 * SCALE; + +DEBUG_MAGSTICKS = true; + +BALL_NAME = "MagBall"; +EDGE_NAME = "MagStick"; + +BALL_RADIUS = BALL_SIZE / 2.0; + +BALL_SELECTION_RADIUS = BALL_RADIUS * 1.5; + +BALL_DIMENSIONS = { + x: BALL_SIZE, + y: BALL_SIZE, + z: BALL_SIZE +}; + +BALL_COLOR = { + red: 128, + green: 128, + blue: 128 +}; + +STICK_DIMENSIONS = { + x: STICK_LENGTH / 6, + y: STICK_LENGTH / 6, + z: STICK_LENGTH +}; + +BALL_DISTANCE = STICK_LENGTH + BALL_SIZE; + +BALL_PROTOTYPE = { + type: "Sphere", + name: BALL_NAME, + dimensions: BALL_DIMENSIONS, + color: BALL_COLOR, + ignoreCollisions: true, + collisionsWillMove: false +}; + +// 2 millimeters +BALL_EPSILON = (.002) / BALL_DISTANCE; + +LINE_DIMENSIONS = { + x: 5, + y: 5, + z: 5 +} + +LINE_PROTOTYPE = { + type: "Line", + name: EDGE_NAME, + color: COLORS.CYAN, + dimensions: LINE_DIMENSIONS, + lineWidth: 5, + visible: true, + ignoreCollisions: true, + collisionsWillMove: false, +} + +EDGE_PROTOTYPE = LINE_PROTOTYPE; + +// var EDGE_PROTOTYPE = { +// type: "Sphere", +// name: EDGE_NAME, +// color: { red: 0, green: 255, blue: 255 }, +// //dimensions: STICK_DIMENSIONS, +// dimensions: { x: 0.02, y: 0.02, z: 0.02 }, +// rotation: rotation, +// visible: true, +// ignoreCollisions: true, +// collisionsWillMove: false +// } diff --git a/examples/toys/magBalls/graph.js b/examples/toys/magBalls/graph.js index df02ee3628..198c1b0c16 100644 --- a/examples/toys/magBalls/graph.js +++ b/examples/toys/magBalls/graph.js @@ -101,6 +101,10 @@ Graph.prototype.getConnectedNodes = function(nodeId) { return result; } +Graph.prototype.getNodesForEdge = function(edgeId) { + return Object.keys(this.edges[edgeId]); +} + Graph.prototype.getEdgeLength = function(edgeId) { var nodesInEdge = Object.keys(this.edges[edgeId]); return this.getNodeDistance(nodesInEdge[0], nodesInEdge[1]); diff --git a/examples/toys/magBalls/handController.js b/examples/toys/magBalls/handController.js deleted file mode 100644 index 4aba43d412..0000000000 --- a/examples/toys/magBalls/handController.js +++ /dev/null @@ -1,128 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/08/29 -// 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 -// -LEFT_CONTROLLER = 0; -RIGHT_CONTROLLER = 1; - -// FIXME add a customizable wand model and a mechanism to switch between wands -HandController = function(side) { - - this.side = side; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.action = findAction(side ? "ACTION2" : "ACTION1"); - this.altAction = findAction(side ? "ACTION1" : "ACTION2"); - this.active = false; - this.tipScale = 1.4; - this.pointer = Overlays.addOverlay("sphere", { - position: ZERO_VECTOR, - size: 0.01, - color: COLORS.YELLOW, - alpha: 1.0, - solid: true, - visible: false, - }); - - // Connect to desired events - var _this = this; - Controller.actionEvent.connect(function(action, state) { - _this.onActionEvent(action, state); - }); - - Script.update.connect(function(deltaTime) { - _this.onUpdate(deltaTime); - }); - - Script.scriptEnding.connect(function() { - _this.onCleanup(); - }); -} - -HandController.prototype.onActionEvent = function(action, state) { - var spatialControlCount = Controller.getNumberOfSpatialControls(); - // If only 2 spacial controls, then we only have one controller active, so use either button - // otherwise, only use the specified action - - if (action == this.action) { - if (state) { - this.onClick(); - } else { - this.onRelease(); - } - } - - if (action == this.altAction) { - if (state) { - this.onAltClick(); - } else { - this.onAltRelease(); - } - } -} - -HandController.prototype.setActive = function(active) { - if (active == this.active) { - return; - } - logDebug("Hand controller changing active state: " + active); - this.active = active; - Overlays.editOverlay(this.pointer, { - visible: this.active - }); - Entities.editEntity(this.wand, { - visible: this.active - }); -} - -HandController.prototype.updateControllerState = function() { - // FIXME this returns data if either the left or right controller is not on the base - this.palmPos = Controller.getSpatialControlPosition(this.palm); - var tipPos = Controller.getSpatialControlPosition(this.tip); - this.tipPosition = scaleLine(this.palmPos, tipPos, this.tipScale); - // When on the base, hydras report a position of 0 - this.setActive(Vec3.length(this.palmPos) > 0.001); - - //logDebug(Controller.getTriggerValue(0) + " " + Controller.getTriggerValue(1)); - - //if (this.active) { - // logDebug("#ctrls " + Controller.getNumberOfSpatialControls() + " Side: " + this.side + " Palm: " + this.palm + " " + vec3toStr(this.palmPos)) - //} -} - -HandController.prototype.onCleanup = function() { - Overlays.deleteOverlay(this.pointer); -} - -HandController.prototype.onUpdate = function(deltaTime) { - this.updateControllerState(); - if (this.active) { - Overlays.editOverlay(this.pointer, { - position: this.tipPosition - }); - Entities.editEntity(this.wand, { - position: this.tipPosition - }); - } -} - -HandController.prototype.onClick = function() { - logDebug("Base hand controller does nothing on click"); -} - -HandController.prototype.onRelease = function() { - logDebug("Base hand controller does nothing on release"); -} - -HandController.prototype.onAltClick = function() { - logDebug("Base hand controller does nothing on alt click"); -} - -HandController.prototype.onAltRelease = function() { - logDebug("Base hand controller does nothing on alt click"); -} - - diff --git a/examples/toys/magBalls/magBalls.js b/examples/toys/magBalls/magBalls.js index 187c550073..307be9f5e1 100644 --- a/examples/toys/magBalls/magBalls.js +++ b/examples/toys/magBalls/magBalls.js @@ -8,30 +8,34 @@ var UPDATE_INTERVAL = 0.1; -Script.include("graph.js"); -Script.include("edgeSpring.js"); // A collection of balls and edges connecting them. MagBalls = function() { Graph.call(this); - this.MAX_ADJUST_ITERATIONS = 100; + this.REFRESH_WAIT_TICKS = 10; + this.MAX_VARIANCE = 0.25; this.lastUpdateAge = 0; - this.stable = false; + this.stable = true; this.adjustIterations = 0; this.selectedNodes = {}; this.edgeObjects = {}; + this.unstableEdges = {}; this.refresh(); var _this = this; - Script.update.connect(function(deltaTime) { - _this.onUpdate(deltaTime); - }); + //Script.update.connect(function(deltaTime) { + // _this.onUpdate(deltaTime); + //}); Script.scriptEnding.connect(function() { _this.onCleanup(); }); + + Entities.addingEntity.connect(function(entityId) { + _this.onEntityAdded(entityId); + }); } MagBalls.prototype = Object.create( Graph.prototype ); @@ -40,14 +44,23 @@ MagBalls.prototype.onUpdate = function(deltaTime) { this.lastUpdateAge += deltaTime; if (this.lastUpdateAge > UPDATE_INTERVAL) { this.lastUpdateAge = 0; - if (!this.stable) { + if (this.refreshNeeded) { + if (++this.refreshNeeded > this.REFRESH_WAIT_TICKS) { + logDebug("Refreshing"); + this.refresh(); + this.refreshNeeded = 0; + } + } + if (!this.stable && !Object.keys(this.selectedNodes).length) { this.adjustIterations += 1; - // logDebug("Update"); var adjusted = false; var nodeAdjustResults = {}; var fixupEdges = {}; for(var edgeId in this.edges) { + if (!this.unstableEdges[edgeId]) { + continue; + } adjusted |= this.edgeObjects[edgeId].adjust(nodeAdjustResults); } for (var nodeId in nodeAdjustResults) { @@ -72,8 +85,12 @@ MagBalls.prototype.onUpdate = function(deltaTime) { }, ((UPDATE_INTERVAL * 1000) / 2)); if (!adjusted || this.adjustIterations > this.MAX_ADJUST_ITERATIONS) { + if (adjusted) { + logDebug("Could not stabilized after " + this.MAX_ADJUST_ITERATIONS + " abandoning"); + } this.adjustIterations = 0; this.stable = true; + this.unstableEdges = {}; } } } @@ -118,7 +135,7 @@ MagBalls.prototype.findPotentialEdges = function(nodeId) { // Check distance to attempt var distance = this.getNodeDistance(nodeId, otherNodeId); var variance = this.getVariance(distance); - if (Math.abs(variance) > 0.25) { + if (Math.abs(variance) > this.MAX_VARIANCE) { continue; } @@ -127,26 +144,38 @@ MagBalls.prototype.findPotentialEdges = function(nodeId) { return variances; } -MagBalls.prototype.grabBall = function(position, maxDist) { - var selected = this.findNearestNode(position, maxDist); - if (!selected) { - selected = this.createNode({ position: position }); - } - if (selected) { - this.stable = true; - this.breakEdges(selected); - this.selectedNodes[selected] = true; - } - return selected; +MagBalls.prototype.breakEdges = function(nodeId) { + //var unstableNodes = this.findShape(Object.keys.target); + //for (var node in unstableNodes) { + // this.unstableNodes[node] = true; + //} + Graph.prototype.breakEdges.call(this, nodeId); } +MagBalls.prototype.createBall = function(position) { + var created = this.createNode({ position: position }); + this.selectBall(created); + return created; +} + +MagBalls.prototype.selectBall = function(selected) { + if (!selected) { + return; + } + // stop updating shapes while manipulating + this.stable = true; + this.selectedNodes[selected] = true; + this.breakEdges(selected); +} + + MagBalls.prototype.releaseBall = function(releasedBall) { delete this.selectedNodes[releasedBall]; logDebug("Released ball: " + releasedBall); + var releasePosition = this.getNodePosition(releasedBall); this.stable = false; - var releasePosition = this.getNodePosition(releasedBall); // iterate through the other balls and ensure we don't intersect with // any of them. If we do, just delete this ball and return. @@ -169,10 +198,34 @@ MagBalls.prototype.releaseBall = function(releasedBall) { for (var otherBallId in targets) { this.createEdge(otherBallId, releasedBall); } + + var unstableNodes = this.findShape(releasedBall); + for (var nodeId in unstableNodes) { + for (var edgeId in this.nodes[nodeId]) { + this.unstableEdges[edgeId] = true; + } + } this.validate(); } +MagBalls.prototype.findShape = function(nodeId) { + var result = {}; + var queue = [ nodeId ]; + while (queue.length) { + var curNode = queue.shift(); + if (result[curNode]) { + continue; + } + result[curNode] = true; + for (var otherNodeId in this.getConnectedNodes(curNode)) { + queue.push(otherNodeId); + } + } + return result; +} + + MagBalls.prototype.getVariance = function(distance) { // FIXME different balls or edges might have different ideas of variance... // let something else handle this @@ -263,8 +316,11 @@ MagBalls.prototype.refresh = function() { Script.setTimeout(function() { for (var i in deleteEdges) { var edgeId = deleteEdges[i]; - logDebug("deleting invalid edge " + edgeId); - Entities.deleteEntity(edgeId); + //logDebug("deleting invalid edge " + edgeId); + //Entities.deleteEntity(edgeId); + Entities.editEntity(edgeId, { + color: COLORS.RED + }) } }, 1000); } @@ -291,3 +347,14 @@ MagBalls.prototype.fixupEdge = function(edgeId) { Entities.editEntity(edgeId, this.findEdgeParams(ballsInEdge[0], ballsInEdge[1])); } +MagBalls.prototype.onEntityAdded = function(entityId) { + // We already have it + if (this.nodes[entityId] || this.edges[entityId]) { + return; + } + + var properties = Entities.getEntityProperties(entityId); + if (properties.name == BALL_NAME || properties.name == EDGE_NAME) { + this.refreshNeeded = 1; + } +} \ No newline at end of file diff --git a/examples/toys/magBalls/menuController.js b/examples/toys/magBalls/menuController.js deleted file mode 100644 index 0a076d1ff8..0000000000 --- a/examples/toys/magBalls/menuController.js +++ /dev/null @@ -1,66 +0,0 @@ -Script.include("handController.js"); - -MenuController = function(side, magBalls) { - HandController.call(this, side); -} - -MenuController.prototype = Object.create( HandController.prototype ); - -MenuController.prototype.onUpdate = function(deltaTime) { - HandController.prototype.onUpdate.call(this, deltaTime); - if (!this.selected) { - // Find the highlight target and set it. - var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS); - this.highlighter.highlight(target); - return; - } - this.highlighter.highlight(null); - Entities.editEntity(this.selected, { position: this.tipPosition }); - var targetBalls = this.magBalls.findPotentialEdges(this.selected); - for (var ballId in targetBalls) { - if (!this.ghostEdges[ballId]) { - // create the ovleray - this.ghostEdges[ballId] = Overlays.addOverlay("line3d", { - start: this.magBalls.getNodePosition(ballId), - end: this.tipPosition, - color: COLORS.RED, - alpha: 1, - lineWidth: 5, - visible: true, - }); - } else { - Overlays.editOverlay(this.ghostEdges[ballId], { - end: this.tipPosition, - }); - } - } - for (var ballId in this.ghostEdges) { - if (!targetBalls[ballId]) { - Overlays.deleteOverlay(this.ghostEdges[ballId]); - delete this.ghostEdges[ballId]; - } - } -} - -MenuController.prototype.onClick = function() { - this.selected = this.magBalls.grabBall(this.tipPosition, BALL_SELECTION_RADIUS); - this.highlighter.highlight(null); -} - -MenuController.prototype.onRelease = function() { - this.clearGhostEdges(); - this.magBalls.releaseBall(this.selected); - this.selected = null; -} - -MenuController.prototype.clearGhostEdges = function() { - for(var ballId in this.ghostEdges) { - Overlays.deleteOverlay(this.ghostEdges[ballId]); - delete this.ghostEdges[ballId]; - } -} - -MenuController.prototype.onCleanup = function() { - HandController.prototype.onCleanup.call(this); - this.clearGhostEdges(); -} From b6ef2e314ff04f41621bee27d247e5ad26f408e9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 31 Aug 2015 22:48:30 -0700 Subject: [PATCH 089/117] Adding 'web3d' overlay type --- interface/src/ui/overlays/Overlays.cpp | 3 + interface/src/ui/overlays/Web3DOverlay.cpp | 163 +++++++++++++++++++++ interface/src/ui/overlays/Web3DOverlay.h | 50 +++++++ 3 files changed, 216 insertions(+) create mode 100644 interface/src/ui/overlays/Web3DOverlay.cpp create mode 100644 interface/src/ui/overlays/Web3DOverlay.h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 39b8892f13..563d90f6b9 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -30,6 +30,7 @@ #include "Grid3DOverlay.h" #include "TextOverlay.h" #include "Text3DOverlay.h" +#include "Web3DOverlay.h" Overlays::Overlays() : _nextOverlayID(1) { @@ -170,6 +171,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = std::make_shared(Application::getInstance()->getEntityClipboardRenderer()); } else if (type == ModelOverlay::TYPE) { thisOverlay = std::make_shared(); + } else if (type == Web3DOverlay::TYPE) { + thisOverlay = std::make_shared(); } if (thisOverlay) { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp new file mode 100644 index 0000000000..c173c5927f --- /dev/null +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -0,0 +1,163 @@ +// +// Web3DOverlay.cpp +// +// +// Created by Clement on 7/1/14. +// Modified and renamed by Zander Otavka on 8/4/15 +// 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 "Web3DOverlay.h" + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// #include "Application.h" +// #include "GeometryUtil.h" + +static const float DPI = 30.47f; +static const float METERS_TO_INCHES = 39.3701f; +static const float INCHES_TO_METERS = 1.0f / 39.3701f; + +QString const Web3DOverlay::TYPE = "web3d"; + +Web3DOverlay::Web3DOverlay() : _dpi(DPI) { } + +Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : + Billboard3DOverlay(Web3DOverlay), + _url(Web3DOverlay->_url), + _dpi(Web3DOverlay->_dpi), + _resolution(Web3DOverlay->_resolution) +{ +} + +Web3DOverlay::~Web3DOverlay() { + if (_webSurface) { + _webSurface->pause(); + _webSurface->disconnect(_connection); + // The lifetime of the QML surface MUST be managed by the main thread + // Additionally, we MUST use local variables copied by value, rather than + // member variables, since they would implicitly refer to a this that + // is no longer valid + auto webSurface = _webSurface; + AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { + webSurface->deleteLater(); + }); + } +} + +void Web3DOverlay::update(float deltatime) { + applyTransformTo(_transform); +} + +void Web3DOverlay::render(RenderArgs* args) { + if (!_visible || !getParentVisible()) { + return; + } + + QOpenGLContext * currentContext = QOpenGLContext::currentContext(); + QSurface * currentSurface = currentContext->surface(); + if (!_webSurface) { + _webSurface = new OffscreenQmlSurface(); + _webSurface->create(currentContext); + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); + _webSurface->load("WebEntity.qml"); + _webSurface->resume(); + _webSurface->getRootItem()->setProperty("url", _url); + _webSurface->resize(QSize(_resolution.x, _resolution.y)); + _connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) { + _texture = textureId; + }); + currentContext->makeCurrent(currentSurface); + } + + vec2 size = _resolution / _dpi * INCHES_TO_METERS; + vec2 halfSize = size / 2.0f; + vec4 color(toGlm(getColor()), getAlpha()); + + applyTransformTo(_transform, true); + Transform transform = _transform; + if (glm::length2(getDimensions()) != 1.0f) { + transform.postScale(vec3(getDimensions(), 1.0f)); + } + + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + if (_texture) { + batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture); + } else { + batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); + } + + batch.setModelTransform(transform); + DependencyManager::get()->bindSimpleProgram(batch, true, false, false, true); + DependencyManager::get()->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color); + batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me +} + +void Web3DOverlay::setProperties(const QScriptValue &properties) { + Billboard3DOverlay::setProperties(properties); + + QScriptValue urlValue = properties.property("url"); + if (urlValue.isValid()) { + QString newURL = urlValue.toVariant().toString(); + if (newURL != _url) { + setURL(newURL); + } + } + + QScriptValue resolution = properties.property("resolution"); + if (resolution.isValid()) { + vec2FromScriptValue(resolution, _resolution); + } + + + QScriptValue dpi = properties.property("dpi"); + if (dpi.isValid()) { + _dpi = dpi.toVariant().toFloat(); + } +} + +QScriptValue Web3DOverlay::getProperty(const QString& property) { + if (property == "url") { + return _url; + } + if (property == "dpi") { + return _dpi; + } + return Billboard3DOverlay::getProperty(property); +} + +void Web3DOverlay::setURL(const QString& url) { + _url = url; + _isLoaded = false; +} + +bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) { + //// Make sure position and rotation is updated. + applyTransformTo(_transform, true); + vec2 size = _resolution / _dpi * INCHES_TO_METERS * vec2(getDimensions()); + // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. + return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), size, distance); +} + +Web3DOverlay* Web3DOverlay::createClone() const { + return new Web3DOverlay(this); +} diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h new file mode 100644 index 0000000000..59c8fae0fe --- /dev/null +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -0,0 +1,50 @@ +// +// Created by Bradley Austin Davis on 2015/08/31 +// 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_Web3DOverlay_h +#define hifi_Web3DOverlay_h + +#include "Billboard3DOverlay.h" + +class OffscreenQmlSurface; + +class Web3DOverlay : public Billboard3DOverlay { + Q_OBJECT + +public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + + Web3DOverlay(); + Web3DOverlay(const Web3DOverlay* Web3DOverlay); + virtual ~Web3DOverlay(); + + virtual void render(RenderArgs* args); + + virtual void update(float deltatime); + + // setters + void setURL(const QString& url); + + virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); + + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); + + virtual Web3DOverlay* createClone() const; + +private: + OffscreenQmlSurface* _webSurface{ nullptr }; + QMetaObject::Connection _connection; + uint32_t _texture{ 0 }; + QString _url; + float _dpi; + vec2 _resolution{ 640, 480 }; +}; + +#endif // hifi_Web3DOverlay_h From 1f83d04423d476b25310783ecb1c6a2b5ff2c2e3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 1 Sep 2015 14:55:09 -0700 Subject: [PATCH 090/117] Fix oculus crash on switching display plugin to something else --- interface/src/Application.cpp | 133 +++++++++++------- interface/src/Application.h | 1 + interface/src/PluginContainerProxy.cpp | 12 -- .../src/display-plugins/DisplayPlugin.h | 6 + .../src/display-plugins/NullDisplayPlugin.cpp | 1 + .../src/display-plugins/NullDisplayPlugin.h | 1 + .../display-plugins/OpenGLDisplayPlugin.cpp | 11 +- .../src/display-plugins/OpenGLDisplayPlugin.h | 3 +- .../oculus/OculusDisplayPlugin.cpp | 6 - libraries/shared/src/Finally.h | 27 ++++ 10 files changed, 133 insertions(+), 68 deletions(-) create mode 100644 libraries/shared/src/Finally.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0726311c9..391ba748d0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -64,6 +64,7 @@ #include #include +#include #include #include #include @@ -1009,6 +1010,16 @@ void Application::paintGL() { if (nullptr == _displayPlugin) { return; } + + // Some plugins process message events, potentially leading to + // re-entering a paint event. don't allow further processing if this + // happens + if (_inPaint) { + return; + } + _inPaint = true; + Finally clearFlagLambda([this] { _inPaint = false; }); + auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); _offscreenContext->makeCurrent(); @@ -1203,7 +1214,7 @@ void Application::paintGL() { // Ensure all operations from the previous context are complete before we try to read the fbo glWaitSync(sync, 0, GL_TIMEOUT_IGNORED); glDeleteSync(sync); - + { PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); displayPlugin->display(finalTexture, toGlm(size)); @@ -1219,7 +1230,6 @@ void Application::paintGL() { _frameCount++; Stats::getInstance()->setRenderDetails(renderArgs._details); - // Reset the gpu::Context Stages // Back to the default framebuffer; gpu::Batch batch; @@ -4703,53 +4713,68 @@ void Application::updateDisplayMode() { auto offscreenUi = DependencyManager::get(); DisplayPluginPointer oldDisplayPlugin = _displayPlugin; - if (oldDisplayPlugin != newDisplayPlugin) { - if (!_currentDisplayPluginActions.isEmpty()) { - auto menu = Menu::getInstance(); - foreach(auto itemInfo, _currentDisplayPluginActions) { - menu->removeMenuItem(itemInfo.first, itemInfo.second); - } - _currentDisplayPluginActions.clear(); - } - - if (newDisplayPlugin) { - _offscreenContext->makeCurrent(); - _activatingDisplayPlugin = true; - newDisplayPlugin->activate(); - _activatingDisplayPlugin = false; - _offscreenContext->makeCurrent(); - offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); - _offscreenContext->makeCurrent(); - } - - oldDisplayPlugin = _displayPlugin; - _displayPlugin = newDisplayPlugin; - - // If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed - // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1 - bool newPluginWantsHMDTools = newDisplayPlugin ? - (newDisplayPlugin->isHmd() && (newDisplayPlugin->getHmdScreen() >= 0)) : false; - bool oldPluginWantedHMDTools = oldDisplayPlugin ? - (oldDisplayPlugin->isHmd() && (oldDisplayPlugin->getHmdScreen() >= 0)) : false; - - // Only show the hmd tools after the correct plugin has - // been activated so that it's UI is setup correctly - if (newPluginWantsHMDTools) { - _pluginContainer->showDisplayPluginsTools(); - } - - if (oldDisplayPlugin) { - oldDisplayPlugin->deactivate(); - _offscreenContext->makeCurrent(); - - // if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools - if (oldPluginWantedHMDTools && !newPluginWantsHMDTools) { - DependencyManager::get()->hmdTools(false); - } - } - emit activeDisplayPluginChanged(); - resetSensors(); + if (newDisplayPlugin == oldDisplayPlugin) { + return; } + + // Some plugins *cough* Oculus *cough* process message events from inside their + // display function, and we don't want to change the display plugin underneath + // the paintGL call, so we need to guard against that + if (_inPaint) { + qDebug() << "Deferring plugin switch until out of painting"; + // Have the old plugin stop requesting renders + oldDisplayPlugin->stop(); + QCoreApplication::postEvent(this, new LambdaEvent([this] { + updateDisplayMode(); + })); + return; + } + + if (!_currentDisplayPluginActions.isEmpty()) { + auto menu = Menu::getInstance(); + foreach(auto itemInfo, _currentDisplayPluginActions) { + menu->removeMenuItem(itemInfo.first, itemInfo.second); + } + _currentDisplayPluginActions.clear(); + } + + if (newDisplayPlugin) { + _offscreenContext->makeCurrent(); + _activatingDisplayPlugin = true; + newDisplayPlugin->activate(); + _activatingDisplayPlugin = false; + _offscreenContext->makeCurrent(); + offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); + _offscreenContext->makeCurrent(); + } + + oldDisplayPlugin = _displayPlugin; + _displayPlugin = newDisplayPlugin; + + // If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed + // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1 + bool newPluginWantsHMDTools = newDisplayPlugin ? + (newDisplayPlugin->isHmd() && (newDisplayPlugin->getHmdScreen() >= 0)) : false; + bool oldPluginWantedHMDTools = oldDisplayPlugin ? + (oldDisplayPlugin->isHmd() && (oldDisplayPlugin->getHmdScreen() >= 0)) : false; + + // Only show the hmd tools after the correct plugin has + // been activated so that it's UI is setup correctly + if (newPluginWantsHMDTools) { + _pluginContainer->showDisplayPluginsTools(); + } + + if (oldDisplayPlugin) { + oldDisplayPlugin->deactivate(); + _offscreenContext->makeCurrent(); + + // if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools + if (oldPluginWantedHMDTools && !newPluginWantsHMDTools) { + DependencyManager::get()->hmdTools(false); + } + } + emit activeDisplayPluginChanged(); + resetSensors(); Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); } @@ -5052,3 +5077,15 @@ void Application::crashApplication() { qCDebug(interfaceapp) << "Intentionally crashed Interface"; } + +void Application::setActiveDisplayPlugin(const QString& pluginName) { + auto menu = Menu::getInstance(); + foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { + QString name = displayPlugin->getName(); + QAction* action = menu->getActionForOption(name); + if (pluginName == name) { + action->setChecked(true); + } + } + updateDisplayMode(); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 9b819eae53..6c3b68cf6f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -673,6 +673,7 @@ private: int _simsPerSecondReport = 0; quint64 _lastSimsPerSecondUpdate = 0; bool _isForeground = true; // starts out assumed to be in foreground + bool _inPaint = false; friend class PluginContainerProxy; }; diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp index e172dbbd9e..4ef755bd1b 100644 --- a/interface/src/PluginContainerProxy.cpp +++ b/interface/src/PluginContainerProxy.cpp @@ -147,15 +147,3 @@ void PluginContainerProxy::showDisplayPluginsTools() { QGLWidget* PluginContainerProxy::getPrimarySurface() { return qApp->_glWidget; } - -void Application::setActiveDisplayPlugin(const QString& pluginName) { - auto menu = Menu::getInstance(); - foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { - QString name = displayPlugin->getName(); - QAction* action = menu->getActionForOption(name); - if (pluginName == name) { - action->setChecked(true); - } - } - updateDisplayMode(); -} diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index a9220d68f6..86cfabe724 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -57,6 +57,11 @@ public: // Rendering support + // Stop requesting renders, but don't do full deactivation + // needed to work around the issues caused by Oculus + // processing messages in the middle of submitFrame + virtual void stop() = 0; + /** * Called by the application before the frame rendering. Can be used for * render timing related calls (for instance, the Oculus begin frame timing @@ -120,6 +125,7 @@ public: virtual void resetSensors() {} virtual float devicePixelRatio() { return 1.0; } + static const QString& MENU_PATH(); signals: void recommendedFramebufferSizeChanged(const QSize & size); diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index e5a96d167e..1f8242f081 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -30,3 +30,4 @@ void NullDisplayPlugin::finishFrame() {} void NullDisplayPlugin::activate() {} void NullDisplayPlugin::deactivate() {} +void NullDisplayPlugin::stop() {} diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 90e717b5ee..bb1ab2d97f 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -17,6 +17,7 @@ public: void activate() override; void deactivate() override; + void stop() override; virtual glm::uvec2 getRecommendedRenderSize() const override; virtual bool hasFocus() const override; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 4101bebfa8..cfe101e1ab 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -16,7 +16,9 @@ OpenGLDisplayPlugin::OpenGLDisplayPlugin() { connect(&_timer, &QTimer::timeout, this, [&] { - emit requestRender(); + if (_active) { + emit requestRender(); + } }); } @@ -56,10 +58,17 @@ void OpenGLDisplayPlugin::customizeContext() { } void OpenGLDisplayPlugin::activate() { + _active = true; _timer.start(1); } +void OpenGLDisplayPlugin::stop() { + _active = false; + _timer.stop(); +} + void OpenGLDisplayPlugin::deactivate() { + _active = false; _timer.stop(); makeCurrent(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 0dc94b72f5..52715ebde7 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -25,7 +25,7 @@ public: virtual void activate() override; virtual void deactivate() override; - + virtual void stop() override; virtual bool eventFilter(QObject* receiver, QEvent* event) override; virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override; @@ -44,6 +44,7 @@ protected: mutable QTimer _timer; ProgramPtr _program; ShapeWrapperPtr _plane; + bool _active{ false }; bool _vsyncSupported{ false }; }; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index ff218987ec..c214b7e627 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -350,11 +350,6 @@ void OculusDisplayPlugin::deactivate() { } void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { - static bool inDisplay = false; - if (inDisplay) { - return; - } - inDisplay = true; #if (OVR_MAJOR_VERSION >= 6) using namespace oglplus; // Need to make sure only the display plugin is responsible for @@ -420,7 +415,6 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi ++_frameIndex; #endif - inDisplay = false; } // Pass input events on to the application diff --git a/libraries/shared/src/Finally.h b/libraries/shared/src/Finally.h new file mode 100644 index 0000000000..59e8be0228 --- /dev/null +++ b/libraries/shared/src/Finally.h @@ -0,0 +1,27 @@ +// +// Created by Bradley Austin Davis on 2015/09/01 +// Copyright 2013-2105 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 +// + +// Simulates a java finally block by executing a lambda when an instance leaves +// scope + +#include + +#pragma once +#ifndef hifi_Finally_h +#define hifi_Finally_h + +class Finally { +public: + template + Finally(F f) : _f(f) {} + ~Finally() { _f(); } +private: + std::function _f; +}; + +#endif From 2901155a7bc1550e9172d38513280cbddeede492 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 1 Sep 2015 15:34:25 -0700 Subject: [PATCH 091/117] Fix Oculus mirroring to window --- .../oculus/OculusDisplayPlugin.cpp | 34 ++++++++----------- .../oculus/OculusDisplayPlugin.h | 2 +- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index c214b7e627..2ed5e69fe7 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -325,19 +325,18 @@ void OculusDisplayPlugin::customizeContext() { //_texture = DependencyManager::get()-> // getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png"); uvec2 mirrorSize = toGlm(_window->geometry().size()); - _mirrorFbo = MirrorFboPtr(new MirrorFramebufferWrapper(_hmd)); - _mirrorFbo->Init(mirrorSize); _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd)); _sceneFbo->Init(getRecommendedRenderSize()); #endif + enableVsync(false); + isVsyncEnabled(); } void OculusDisplayPlugin::deactivate() { #if (OVR_MAJOR_VERSION >= 6) makeCurrent(); _sceneFbo.reset(); - _mirrorFbo.reset(); doneCurrent(); PerformanceTimer::setActive(false); @@ -378,12 +377,14 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi the UI visible in the output window (unlikely). This should be done before _sceneFbo->Increment or we're be using the wrong texture */ - _sceneFbo->Bound(Framebuffer::Target::Read, [&] { - glBlitFramebuffer( - 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, - 0, 0, windowSize.x, windowSize.y, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - }); + if (_enableMirror) { + _sceneFbo->Bound(Framebuffer::Target::Read, [&] { + glBlitFramebuffer( + 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, + 0, 0, windowSize.x, windowSize.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + }); + } { PerformanceTimer("OculusSubmit"); @@ -404,6 +405,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi The other alternative for mirroring is to use the Oculus mirror texture support, which will contain the post-distorted and fully composited scene regardless of how many layers we send. + Currently generates an error. */ //auto mirrorSize = _mirrorFbo->size; //_mirrorFbo->Bound(Framebuffer::Target::Read, [&] { @@ -419,16 +421,6 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi // Pass input events on to the application bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { -#if (OVR_MAJOR_VERSION >= 6) - if (event->type() == QEvent::Resize) { - QResizeEvent* resizeEvent = static_cast(event); - qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height(); - auto newSize = toGlm(resizeEvent->size()); - makeCurrent(); - _mirrorFbo->Resize(newSize); - doneCurrent(); - } -#endif return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); } @@ -438,7 +430,9 @@ bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { otherwise the swapbuffer delay will interefere with the framerate of the headset */ void OculusDisplayPlugin::finishFrame() { - //swapBuffers(); + if (_enableMirror) { + swapBuffers(); + } doneCurrent(); }; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h index 42f8d5763f..d30356daa0 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h @@ -58,6 +58,7 @@ private: mat4 _compositeEyeProjections[2]; uvec2 _desiredFramebufferSize; ovrTrackingState _trackingState; + bool _enableMirror{ false }; #if (OVR_MAJOR_VERSION >= 6) ovrHmd _hmd; @@ -70,7 +71,6 @@ private: ovrLayerEyeFov& getSceneLayer(); ovrHmdDesc _hmdDesc; SwapFboPtr _sceneFbo; - MirrorFboPtr _mirrorFbo; ovrLayerEyeFov _sceneLayer; #endif #if (OVR_MAJOR_VERSION == 7) From 471400e5955df489f8fe59c3f0819f6e478106f4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 19:59:05 -0700 Subject: [PATCH 092/117] Fix for jerky behavior when positionDelta is zero. This can occur with vsync disabled. Possibly due to two avatar updates occurring within a single physics time-step. --- libraries/animation/src/Rig.cpp | 38 ++++++++++++--------------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index cac5ad3a70..b4652d24ef 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -419,12 +419,21 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos glm::vec3 front = worldRotation * IDENTITY_FRONT; + // It can be more accurate/smooth to use velocity rather than position, + // but some modes (e.g., hmd standing) update position without updating velocity. + // It's very hard to debug hmd standing. (Look down at yourself, or have a second person observe. HMD third person is a bit undefined...) + // So, let's create our own workingVelocity from the worldPosition... + glm::vec3 positionDelta = worldPosition - _lastPosition; + glm::vec3 workingVelocity = positionDelta / deltaTime; + + // But for smoothest (non-hmd standing) results, go ahead and use velocity: + if (!positionDelta.x && !positionDelta.y && !positionDelta.z) { + workingVelocity = worldVelocity; + } + if (_enableAnimGraph) { - // at the moment worldVelocity comes from the Avatar physics body, which is not always correct when - // moving in the HMD, so let's compute our own veloicty. - glm::vec3 worldVel = (worldPosition - _lastPosition) / deltaTime; - glm::vec3 localVel = glm::inverse(worldRotation) * worldVel; + glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; @@ -496,20 +505,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos glm::vec3 right = worldRotation * IDENTITY_RIGHT; const float PERCEPTIBLE_DELTA = 0.001f; const float PERCEPTIBLE_SPEED = 0.1f; - // It can be more accurate/smooth to use velocity rather than position, - // but some modes (e.g., hmd standing) update position without updating velocity. - // It's very hard to debug hmd standing. (Look down at yourself, or have a second person observe. HMD third person is a bit undefined...) - // So, let's create our own workingVelocity from the worldPosition... - glm::vec3 positionDelta = worldPosition - _lastPosition; - glm::vec3 workingVelocity = positionDelta / deltaTime; - // But for smoothest (non-hmd standing) results, go ahead and use velocity: -#if !WANT_DEBUG + // Note: Separately, we've arranged for starting/stopping animations by role (as we've done here) to pick up where they've left off when fading, // so that you wouldn't notice the start/stop if it happens fast enough (e.g., one frame). But the print below would still be noisy. - if (!positionDelta.x && !positionDelta.y && !positionDelta.z) { - workingVelocity = worldVelocity; - } -#endif float forwardSpeed = glm::dot(workingVelocity, front); float rightLateralSpeed = glm::dot(workingVelocity, right); @@ -1001,19 +999,11 @@ void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { AnimPose geometryOffset(fbxGeometry.offset); _animSkeleton = std::make_shared(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; From 30264e9c3dd89d5f6fe36abaf93390b229ee1f34 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 2 Sep 2015 21:04:29 -0700 Subject: [PATCH 093/117] Added animation debug draw items to menu. * Debug Draw Bind Pose - used to display the current avatar's bind pose * Debug Draw Animation - used to display the current avatar's AnimGraph animation. Currently does not work with old animation so it's only valid when Enable Anim Graph is true. * Draw Mesh - used to hide or display the avatar mesh. --- interface/src/Menu.cpp | 6 ++ interface/src/Menu.h | 3 + interface/src/avatar/MyAvatar.cpp | 43 +++++++++++++ interface/src/avatar/MyAvatar.h | 7 +++ libraries/animation/src/AnimNode.h | 1 + libraries/animation/src/Rig.h | 4 ++ libraries/render-utils/src/AnimDebugDraw.cpp | 65 +++++++++++++------- libraries/render-utils/src/AnimDebugDraw.h | 10 +-- 8 files changed, 113 insertions(+), 26 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8f6ce50eab..216fa70121 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -435,6 +435,12 @@ Menu::Menu() { avatar, SLOT(setEnableRigAnimations(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, false, avatar, SLOT(setEnableAnimGraph(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBindPose, 0, false, + avatar, SLOT(setEnableDebugDrawBindPose(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false, + avatar, SLOT(setEnableDebugDrawAnimPose(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true, + avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4a3059259b..3b87de2c43 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -132,6 +132,8 @@ namespace MenuOption { const QString AddRemoveFriends = "Add/Remove Friends..."; const QString AddressBar = "Show Address Bar"; const QString Animations = "Animations..."; + const QString AnimDebugDrawAnimPose = "Debug Draw Animation"; + const QString AnimDebugDrawBindPose = "Debug Draw Bind Pose"; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; const QString AudioNoiseReduction = "Audio Noise Reduction"; @@ -215,6 +217,7 @@ namespace MenuOption { const QString Log = "Log"; const QString LogExtraTimings = "Log Extra Timing Details"; const QString LowVelocityFilter = "Low Velocity Filter"; + const QString MeshVisible = "Draw Mesh"; const QString Mirror = "Mirror"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c89a583f32..f6ec1242f2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "devices/Faceshift.h" @@ -718,6 +719,27 @@ void MyAvatar::setEnableAnimGraph(bool isEnabled) { } } +void MyAvatar::setEnableDebugDrawBindPose(bool isEnabled) { + _enableDebugDrawBindPose = isEnabled; + + if (!isEnabled) { + AnimDebugDraw::getInstance().removeSkeleton("myAvatar"); + } +} + +void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) { + _enableDebugDrawAnimPose = isEnabled; + + if (!isEnabled) { + AnimDebugDraw::getInstance().removeAnimNode("myAvatar"); + } +} + +void MyAvatar::setEnableMeshVisible(bool isEnabled) { + render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + _skeletonModel.setVisibleInScene(isEnabled, scene); +} + void MyAvatar::loadData() { Settings settings; settings.beginGroup("Avatar"); @@ -1230,6 +1252,27 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { _skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); } + if (_enableDebugDrawBindPose || _enableDebugDrawAnimPose) { + + AnimSkeleton::ConstPointer animSkeleton = _rig->getAnimSkeleton(); + AnimNode::ConstPointer animNode = _rig->getAnimNode(); + + // bones space is rotated + glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f)); + AnimPose xform(glm::vec3(1), rotY180 * getOrientation(), getPosition()); + + if (animSkeleton && _enableDebugDrawBindPose) { + glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); + AnimDebugDraw::getInstance().addSkeleton("myAvatar", animSkeleton, xform, gray); + } + + // This only works for when new anim system is enabled, at the moment. + if (animNode && animSkeleton && _enableDebugDrawAnimPose && _rig->getEnableAnimGraph()) { + glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); + AnimDebugDraw::getInstance().addAnimNode("myAvatar", animNode, xform, cyan); + } + } + if (shouldDrawHead != _prevShouldDrawHead) { _skeletonModel.setCauterizeBones(!shouldDrawHead); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 25e25e0960..1774eec71f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -190,8 +190,12 @@ public slots: void loadLastRecording(); virtual void rebuildSkeletonBody(); + void setEnableRigAnimations(bool isEnabled); void setEnableAnimGraph(bool isEnabled); + void setEnableDebugDrawBindPose(bool isEnabled); + void setEnableDebugDrawAnimPose(bool isEnabled); + void setEnableMeshVisible(bool isEnabled); signals: void transformChanged(); @@ -314,6 +318,9 @@ private: std::unordered_set _headBoneSet; RigPointer _rig; bool _prevShouldDrawHead; + + bool _enableDebugDrawBindPose = false; + bool _enableDebugDrawAnimPose = false; }; #endif // hifi_MyAvatar_h diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 3a54710b39..4675ae358f 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -43,6 +43,7 @@ public: NumTypes }; using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; using Triggers = std::vector; friend class AnimDebugDraw; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 845878f8a2..109605587e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -159,6 +159,7 @@ public: void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } void setEnableAnimGraph(bool isEnabled) { _enableAnimGraph = isEnabled; } + bool getEnableAnimGraph() const { return _enableAnimGraph; } void updateFromHeadParameters(const HeadParameters& params); void updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation, @@ -169,6 +170,9 @@ public: void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry); + AnimNode::ConstPointer getAnimNode() const { return _animNode; } + AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } + protected: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 7bbf656f35..1c0f7e0054 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -80,6 +80,13 @@ static uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24); } +static uint32_t toRGBA(const glm::vec4& v) { + return toRGBA(static_cast(v.r * 255.0f), + static_cast(v.g * 255.0f), + static_cast(v.b * 255.0f), + static_cast(v.a * 255.0f)); +} + gpu::PipelinePointer AnimDebugDraw::_pipeline; AnimDebugDraw::AnimDebugDraw() : @@ -145,16 +152,16 @@ AnimDebugDraw::~AnimDebugDraw() { } } -void AnimDebugDraw::addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const AnimPose& rootPose) { - _skeletons[key] = SkeletonInfo(skeleton, rootPose); +void AnimDebugDraw::addSkeleton(std::string key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color) { + _skeletons[key] = SkeletonInfo(skeleton, rootPose, color); } void AnimDebugDraw::removeSkeleton(std::string key) { _skeletons.erase(key); } -void AnimDebugDraw::addAnimNode(std::string key, AnimNode::Pointer animNode, const AnimPose& rootPose) { - _animNodes[key] = AnimNodeInfo(animNode, rootPose); +void AnimDebugDraw::addAnimNode(std::string key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color) { + _animNodes[key] = AnimNodeInfo(animNode, rootPose, color); } void AnimDebugDraw::removeAnimNode(std::string key) { @@ -164,7 +171,6 @@ void AnimDebugDraw::removeAnimNode(std::string key) { static const uint32_t red = toRGBA(255, 0, 0, 255); static const uint32_t green = toRGBA(0, 255, 0, 255); static const uint32_t blue = toRGBA(0, 0, 255, 255); -static const uint32_t cyan = toRGBA(0, 128, 128, 255); const int NUM_CIRCLE_SLICES = 24; @@ -182,7 +188,7 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius float rSinTheta = radius * sin(dTheta * i); xRing[i] = finalPose * glm::vec3(0.0f, rCosTheta, rSinTheta); yRing[i] = finalPose * glm::vec3(rCosTheta, 0.0f, rSinTheta); - zRing[i] = finalPose *glm::vec3(rCosTheta, rSinTheta, 0.0f); + zRing[i] = finalPose * glm::vec3(rCosTheta, rSinTheta, 0.0f); } // x-axis @@ -240,8 +246,10 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius } } -static void addLink(const AnimPose& rootPose, const AnimPose& pose, - const AnimPose& parentPose, float radius, Vertex*& v) { +static void addLink(const AnimPose& rootPose, const AnimPose& pose, const AnimPose& parentPose, + float radius, const glm::vec4& colorVec, Vertex*& v) { + + uint32_t color = toRGBA(colorVec); AnimPose pose0 = rootPose * parentPose; AnimPose pose1 = rootPose * pose; @@ -271,19 +279,19 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, for (int i = 0; i < NUM_BASE_CORNERS; i++) { v->pos = boneBaseCorners[i]; - v->rgba = cyan; + v->rgba = color; v++; v->pos = boneBaseCorners[(i + 1) % NUM_BASE_CORNERS]; - v->rgba = cyan; + v->rgba = color; v++; } for (int i = 0; i < NUM_BASE_CORNERS; i++) { v->pos = boneBaseCorners[i]; - v->rgba = cyan; + v->rgba = color; v++; v->pos = boneTip; - v->rgba = cyan; + v->rgba = color; v++; } } else { @@ -292,10 +300,10 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, // We add the same line multiple times, so the vertex count is correct. for (int i = 0; i < NUM_BASE_CORNERS * 2; i++) { v->pos = pose0.trans; - v->rgba = cyan; + v->rgba = color; v++; v->pos = pose1.trans; - v->rgba = cyan; + v->rgba = color; v++; } } @@ -319,7 +327,7 @@ void AnimDebugDraw::update() { // figure out how many verts we will need. int numVerts = 0; for (auto&& iter : _skeletons) { - AnimSkeleton::Pointer& skeleton = iter.second.first; + AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; for (int i = 0; i < skeleton->getNumJoints(); i++) { auto parentIndex = skeleton->getParentIndex(i); @@ -330,7 +338,7 @@ void AnimDebugDraw::update() { } for (auto&& iter : _animNodes) { - AnimNode::Pointer& animNode = iter.second.first; + AnimNode::ConstPointer& animNode = std::get<0>(iter.second); auto poses = animNode->getPosesInternal(); numVerts += poses.size() * VERTICES_PER_BONE; auto skeleton = animNode->getSkeleton(); @@ -346,8 +354,13 @@ void AnimDebugDraw::update() { Vertex* verts = (Vertex*)data._vertexBuffer->editData(); Vertex* v = verts; for (auto&& iter : _skeletons) { - AnimSkeleton::Pointer& skeleton = iter.second.first; - AnimPose& rootPose = iter.second.second; + AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); + AnimPose rootPose = std::get<1>(iter.second); + int hipsIndex = skeleton->nameToJointIndex("Hips"); + if (hipsIndex >= 0) { + rootPose.trans -= skeleton->getRelativeBindPose(hipsIndex).trans; + } + glm::vec4 color = std::get<2>(iter.second); for (int i = 0; i < skeleton->getNumJoints(); i++) { AnimPose pose = skeleton->getAbsoluteBindPose(i); @@ -362,14 +375,22 @@ void AnimDebugDraw::update() { if (parentIndex >= 0) { assert(parentIndex < skeleton->getNumJoints()); AnimPose parentPose = skeleton->getAbsoluteBindPose(parentIndex); - addLink(rootPose, pose, parentPose, radius, v); + addLink(rootPose, pose, parentPose, radius, color, v); } } } for (auto&& iter : _animNodes) { - AnimNode::Pointer& animNode = iter.second.first; - AnimPose& rootPose = iter.second.second; + AnimNode::ConstPointer& animNode = std::get<0>(iter.second); + AnimPose rootPose = std::get<1>(iter.second); + if (animNode->_skeleton) { + int hipsIndex = animNode->_skeleton->nameToJointIndex("Hips"); + if (hipsIndex >= 0) { + rootPose.trans -= animNode->_skeleton->getRelativeBindPose(hipsIndex).trans; + } + } + glm::vec4 color = std::get<2>(iter.second); + auto poses = animNode->getPosesInternal(); auto skeleton = animNode->getSkeleton(); @@ -392,7 +413,7 @@ void AnimDebugDraw::update() { if (parentIndex >= 0) { assert((size_t)parentIndex < poses.size()); // draw line to parent - addLink(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, v); + addLink(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, color, v); } } } diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index 34ee9c50c0..489213b80b 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -10,6 +10,8 @@ #ifndef hifi_AnimDebugDraw_h #define hifi_AnimDebugDraw_h +#include + #include "render/Scene.h" #include "gpu/Pipeline.h" #include "AnimNode.h" @@ -25,10 +27,10 @@ public: AnimDebugDraw(); ~AnimDebugDraw(); - void addSkeleton(std::string key, AnimSkeleton::Pointer skeleton, const AnimPose& rootPose); + void addSkeleton(std::string key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color); void removeSkeleton(std::string key); - void addAnimNode(std::string key, AnimNode::Pointer skeleton, const AnimPose& rootPose); + void addAnimNode(std::string key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color); void removeAnimNode(std::string key); void update(); @@ -40,8 +42,8 @@ protected: static gpu::PipelinePointer _pipeline; - typedef std::pair SkeletonInfo; - typedef std::pair AnimNodeInfo; + typedef std::tuple SkeletonInfo; + typedef std::tuple AnimNodeInfo; std::unordered_map _skeletons; std::unordered_map _animNodes; From 70ffe10057d5a410b74e8c37eb5f148596bc443e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 31 Aug 2015 14:44:12 -0700 Subject: [PATCH 094/117] Use a constant log file and rollover accumulated logs to a new file based on startup, time or size --- interface/src/FileLogger.cpp | 62 ++++++++++++++++++++++++++++-------- interface/src/FileLogger.h | 2 +- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 00da80814b..0b2128cf17 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -10,16 +10,39 @@ // #include "FileLogger.h" -#include "HifiSockAddr.h" -#include -#include -#include -#include -#include -const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; -const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; -const QString LOGS_DIRECTORY = "Logs"; +#include +#include +#include +#include + +#include +#include +#include + +#include "HifiSockAddr.h" + +static const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; +static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; +static const QString LOGS_DIRECTORY = "Logs"; +// Max log size is 1 MB +static const uint64_t MAX_LOG_SIZE = 1024 * 1024; +// Max log age is 1 hour +static const uint64_t MAX_LOG_AGE_USECS = USECS_PER_SECOND * 3600; + +QString getLogRollerFilename() { + QString result = FileUtils::standardPath(LOGS_DIRECTORY); + QHostAddress clientAddress = getLocalAddress(); + QDateTime now = QDateTime::currentDateTime(); + result.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT))); + return result; +} + +const QString& getLogFilename() { + static QString fileName = FileUtils::standardPath(LOGS_DIRECTORY) + "hifi-log.txt"; + return fileName; +} + class FilePersistThread : public GenericQueueThread < QString > { public: @@ -28,8 +51,22 @@ public: } protected: + void rollFileIfNecessary(QFile& file) { + uint64_t now = usecTimestampNow(); + if ((file.size() > MAX_LOG_SIZE) || (now - _lastRollTime) > MAX_LOG_AGE_USECS) { + QString newFileName = getLogRollerFilename(); + if (file.copy(newFileName)) { + _lastRollTime = now; + file.open(QIODevice::WriteOnly | QIODevice::Truncate); + file.close(); + qDebug() << "Rolled log file: " << newFileName; + } + } + } + virtual bool processQueueItems(const Queue& messages) { QFile file(_logger._fileName); + rollFileIfNecessary(file); if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { QTextStream out(&file); foreach(const QString& message, messages) { @@ -40,20 +77,17 @@ protected: } private: const FileLogger& _logger; + uint64_t _lastRollTime = 0; }; static FilePersistThread* _persistThreadInstance; FileLogger::FileLogger(QObject* parent) : - AbstractLoggerInterface(parent) + AbstractLoggerInterface(parent), _fileName(getLogFilename()) { _persistThreadInstance = new FilePersistThread(*this); _persistThreadInstance->initialize(true, QThread::LowestPriority); - _fileName = FileUtils::standardPath(LOGS_DIRECTORY); - QHostAddress clientAddress = getLocalAddress(); - QDateTime now = QDateTime::currentDateTime(); - _fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT))); } FileLogger::~FileLogger() { diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h index 549654ca5c..122da20ae7 100644 --- a/interface/src/FileLogger.h +++ b/interface/src/FileLogger.h @@ -27,7 +27,7 @@ public: virtual void locateLog() override; private: - QString _fileName; + const QString _fileName; friend class FilePersistThread; }; From 07b241c778cceec658add5aaeb0e10846463ca27 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 11:05:44 -0700 Subject: [PATCH 095/117] code review --- examples/voxels.js | 13 +------------ .../src/RenderablePolyVoxEntityItem.cpp | 16 ++++++++-------- .../src/RenderablePolyVoxEntityItem.h | 2 +- libraries/shared/src/GLMHelpers.cpp | 1 + libraries/shared/src/GLMHelpers.h | 1 + 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 547585198c..0776bce627 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -3,6 +3,7 @@ var shiftHeld = false; Script.include([ "libraries/toolBars.js", + "libraries/utils.js", ]); var isActive = false; @@ -23,10 +24,6 @@ function floorVector(v) { return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)}; } -function vectorToString(v){ - return "{" + v.x + ", " + v.y + ", " + v.z + "}"; -} - var toolBar = (function () { var that = {}, toolBar, @@ -251,16 +248,10 @@ function addTerrainBlock() { if (alreadyThere) { // there is already a terrain block under MyAvatar. // try in front of the avatar. - print("alreadyThere = " + alreadyThere); - print("MyAvatar.position = " + vectorToString(MyAvatar.position)); - print("baseLocation = " + vectorToString(baseLocation)); facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getFront(Camera.getOrientation()))); facingPosition = Vec3.sum(facingPosition, {x:8, y:8, z:8}); - print("facingPosition = " + vectorToString(facingPosition)); baseLocation = getTerrainAlignedLocation(facingPosition); - print("baseLocation = " + vectorToString(baseLocation)); alreadyThere = lookupTerrainForLocation(baseLocation); - print("alreadyThere = " + alreadyThere); if (alreadyThere) { return; } @@ -281,8 +272,6 @@ function addTerrainBlock() { var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position); // TODO -- how to find the avatar's feet? var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4; - // Entities.setAllVoxels(polyVoxID, 255); - // Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:topY, z:0}, {x:16, y:64 - topY, z:16}, 0); Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:0, z:0}, {x:16, y:topY, z:16}, 255); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index f393391780..43f97c23ee 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -136,7 +136,7 @@ glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const { glm::vec3 center = getCenterPosition(); glm::vec3 position = getPosition(); glm::vec3 positionToCenter = center - position; - positionToCenter -= getDimensions() * glm::vec3(0.5f, 0.5f, 0.5f) - getSurfacePositionAdjustment(); + positionToCenter -= getDimensions() * Vectors::HALF - getSurfacePositionAdjustment(); glm::mat4 centerToCorner = glm::translate(glm::mat4(), positionToCenter); glm::mat4 scaled = glm::scale(centerToCorner, scale); return scaled; @@ -387,8 +387,8 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o glm::vec3 result3 = vec3(result); AABox voxelBox; - voxelBox += result3 + glm::vec3(-0.5f, -0.5f, -0.5f); - voxelBox += result3 + glm::vec3(0.5f, 0.5f, 0.5f); + voxelBox += result3 - Vectors::HALF; + voxelBox += result3 + Vectors::HALF; float voxelDistance; @@ -589,9 +589,9 @@ namespace render { glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const { glm::vec3 adjustedCoords; if (isEdged(_voxelSurfaceStyle)) { - adjustedCoords = voxelCoords + glm::vec3(0.5f, 0.5f, 0.5f); + adjustedCoords = voxelCoords + Vectors::HALF; } else { - adjustedCoords = voxelCoords - glm::vec3(0.5f, 0.5f, 0.5f); + adjustedCoords = voxelCoords - Vectors::HALF; } return glm::vec3(voxelToWorldMatrix() * glm::vec4(adjustedCoords, 1.0f)); } @@ -599,9 +599,9 @@ glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxel glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { glm::vec3 result = glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f)); if (isEdged(_voxelSurfaceStyle)) { - return result - glm::vec3(0.5f, 0.5f, 0.5f); + return result - Vectors::HALF; } - return result + glm::vec3(0.5f, 0.5f, 0.5f); + return result + Vectors::HALF; } glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const { @@ -656,7 +656,7 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) bool RenderablePolyVoxEntityItem::inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, - int x, int y, int z) { + int x, int y, int z) const { // x, y, z are in user voxel-coords, not adjusted-for-edge voxel-coords. if (isEdged(surfaceStyle)) { if (x < 0 || y < 0 || z < 0 || diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 3b10a12bd2..01578b5e58 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -141,7 +141,7 @@ private: int _onCount; // how many non-zero voxels are in _volData bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, - int x, int y, int z); + int x, int y, int z) const; uint8_t getVoxelInternal(int x, int y, int z); bool setVoxelInternal(int x, int y, int z, uint8_t toValue); bool updateOnCount(int x, int y, int z, uint8_t toValue); diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 4ca8ed330b..ead08f5654 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -27,6 +27,7 @@ const vec3 Vectors::MAX{ FLT_MAX }; const vec3 Vectors::MIN{ -FLT_MAX }; const vec3 Vectors::ZERO{ 0.0f }; const vec3 Vectors::ONE{ 1.0f }; +const vec3 Vectors::HALF{ 0.5f }; const vec3& Vectors::RIGHT = Vectors::UNIT_X; const vec3& Vectors::UP = Vectors::UNIT_Y; const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 4b03ed2525..e05e075e5d 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -70,6 +70,7 @@ public: static const vec3 MIN; static const vec3 ZERO; static const vec3 ONE; + static const vec3 HALF; static const vec3& RIGHT; static const vec3& UP; static const vec3& FRONT; From fa423344305801f7bb1339f5ba02faf2cebcd2d9 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 3 Sep 2015 11:30:44 -0700 Subject: [PATCH 096/117] added userData manipulations to grab script --- examples/controllers/hydra/hydraGrab.js | 35 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 71e4d2a07e..1c0a46f661 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -11,9 +11,13 @@ // +Script.include("https://hifi-public.s3.amazonaws.com/scripts/libraries/utils.js"); + var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); var rightTriggerAction = RIGHT_HAND_CLICK; +var GRAB_USER_DATA_KEY = "grabKey"; + var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK"); var leftTriggerAction = LEFT_HAND_CLICK; @@ -37,7 +41,7 @@ var INTERSECT_COLOR = { blue: 10 }; -var GRAB_RADIUS = 2; +var GRAB_RADIUS = 0.5; var GRAB_COLOR = { red: 250, @@ -154,7 +158,6 @@ controller.prototype.attemptMove = function() { var newPosition = Vec3.sum(handPosition, Vec3.multiply(direction, this.distanceToEntity)) this.distanceHolding = true; - //TO DO : USE SPRING ACTION UPDATE FOR MOVING if (this.actionID === null) { this.actionID = Entities.addAction("spring", this.grabbedEntity, { targetPosition: newPosition, @@ -184,7 +187,10 @@ controller.prototype.hidePointer = function() { controller.prototype.letGo = function() { - Entities.deleteAction(this.grabbedEntity, this.actionID); + if (this.grabbedEntity && this.actionID) { + this.deactivateEntity(this.grabbedEntity); + Entities.deleteAction(this.grabbedEntity, this.actionID); + } this.grabbedEntity = null; this.actionID = null; this.distanceHolding = false; @@ -264,11 +270,29 @@ controller.prototype.checkForInRangeObject = function() { if (grabbedEntity === null) { return false; } else { + //We are grabbing an entity, so let it know we've grabbed it this.grabbedEntity = grabbedEntity; + this.activateEntity(this.grabbedEntity); + return true; } } +controller.prototype.activateEntity = function(entity) { + var data = { + activated: true, + avatarId: MyAvatar.sessionUUID + }; + setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); +} + +controller.prototype.deactivateEntity = function(entity) { + var data = { + activated: false, + avatarId: null + }; + setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); +} controller.prototype.onActionEvent = function(action, state) { if (this.pullAction === action && state === 1) { @@ -293,7 +317,9 @@ controller.prototype.onActionEvent = function(action, state) { controller.prototype.cleanup = function() { Entities.deleteEntity(this.pointer); - Entities.deleteAction(this.grabbedEntity, this.actionID); + if (this.grabbedEntity) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + } } function update() { @@ -314,6 +340,7 @@ function cleanup() { } + Script.scriptEnding.connect(cleanup); Script.update.connect(update) Controller.actionEvent.connect(onActionEvent); \ No newline at end of file From 8dc390576710f11d59b9ba6e9d5da70c2da8b73f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Aug 2015 17:15:02 -0700 Subject: [PATCH 097/117] Add common vector constants to JS, add some missing quaternion functionality --- libraries/script-engine/src/Quat.cpp | 20 ++++++++++ libraries/script-engine/src/Quat.h | 4 ++ libraries/script-engine/src/Vec3.cpp | 54 ++++--------------------- libraries/script-engine/src/Vec3.h | 59 ++++++++++++++++++---------- libraries/shared/src/GLMHelpers.cpp | 2 + libraries/shared/src/GLMHelpers.h | 3 +- 6 files changed, 74 insertions(+), 68 deletions(-) diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 9cd659dd5d..14a6573415 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -18,6 +18,26 @@ #include "ScriptEngineLogging.h" #include "Quat.h" +quat Quat::normalize(const glm::quat& q) { + return glm::normalize(q); +} + +glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { + return ::rotationBetween(v1, v2); +} + +glm::quat Quat::lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up) { + return glm::quat_cast(glm::lookAt(eye, center, up)); +} + +glm::quat Quat::lookAtSimple(const glm::vec3& eye, const glm::vec3& center) { + auto dir = glm::normalize(center - eye); + // if the direction is nearly aligned with the Y axis, then use the X axis for 'up' + if (dir.x < 0.001f && dir.z < 0.001f) { + return lookAt(eye, center, Vectors::UNIT_X); + } + return lookAt(eye, center, Vectors::UNIT_Y); +} glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) { return q1 * q2; diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 1f130c57c7..543c93401f 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -25,6 +25,10 @@ class Quat : public QObject { public slots: glm::quat multiply(const glm::quat& q1, const glm::quat& q2); + glm::quat normalize(const glm::quat& q); + glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); + glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); + glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); glm::quat fromVec3Degrees(const glm::vec3& vec3); // degrees glm::quat fromVec3Radians(const glm::vec3& vec3); // radians glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); // degrees diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 4ed16b2ef0..69961bfd8e 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -13,66 +13,26 @@ #include +#include + #include "ScriptEngineLogging.h" #include "NumericalConstants.h" #include "Vec3.h" -glm::vec3 Vec3::reflect(const glm::vec3& v1, const glm::vec3& v2) { - return glm::reflect(v1, v2); -} -glm::vec3 Vec3::cross(const glm::vec3& v1, const glm::vec3& v2) { - return glm::cross(v1,v2); -} - -float Vec3::dot(const glm::vec3& v1, const glm::vec3& v2) { - return glm::dot(v1,v2); -} - -glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) { - return v1 * f; -} - -glm::vec3 Vec3::multiply(float f, const glm::vec3& v1) { - return v1 * f; -} - -glm::vec3 Vec3::multiplyQbyV(const glm::quat& q, const glm::vec3& v) { - return q * v; -} - -glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) { - return v1 + v2; -} -glm::vec3 Vec3::subtract(const glm::vec3& v1, const glm::vec3& v2) { - return v1 - v2; -} - -float Vec3::length(const glm::vec3& v) { - return glm::length(v); -} - -float Vec3::distance(const glm::vec3& v1, const glm::vec3& v2) { - return glm::distance(v1, v2); -} float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { - return glm::degrees(glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3))); + float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3)); + return glm::degrees(radians); } -glm::vec3 Vec3::normalize(const glm::vec3& v) { - return glm::normalize(v); -} - -glm::vec3 Vec3::mix(const glm::vec3& v1, const glm::vec3& v2, float m) { - return glm::mix(v1, v2, m); -} void Vec3::print(const QString& lable, const glm::vec3& v) { qCDebug(scriptengine) << qPrintable(lable) << v.x << "," << v.y << "," << v.z; } -bool Vec3::equal(const glm::vec3& v1, const glm::vec3& v2) { - return v1 == v2; +bool Vec3::withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon) { + float distanceSquared = glm::length2(v1 - v2); + return (epsilon*epsilon) >= distanceSquared; } glm::vec3 Vec3::toPolar(const glm::vec3& v) { diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 82062ca80d..b05e729a49 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -11,40 +11,59 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#pragma once #ifndef hifi_Vec3_h #define hifi_Vec3_h -#include -#include +#include +#include -#include -#include +#include "GLMHelpers.h" /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API class Vec3 : public QObject { Q_OBJECT public slots: - glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2); - glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2); - float dot(const glm::vec3& v1, const glm::vec3& v2); - glm::vec3 multiply(const glm::vec3& v1, float f); - glm::vec3 multiply(float, const glm::vec3& v1); - glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v); - glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2); - glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2); - float length(const glm::vec3& v); - float distance(const glm::vec3& v1, const glm::vec3& v2); + glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); } + glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2) { return glm::cross(v1, v2); } + float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } + glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } + glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; } + glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } + glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } + glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; } + float length(const glm::vec3& v) { return glm::length(v); } + float distance(const glm::vec3& v1, const glm::vec3& v2) { return glm::distance(v1, v2); } float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3); - glm::vec3 normalize(const glm::vec3& v); - glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m); - void print(const QString& lable, const glm::vec3& v); - bool equal(const glm::vec3& v1, const glm::vec3& v2); + glm::vec3 normalize(const glm::vec3& v) { return glm::normalize(v); }; + glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m) { return glm::mix(v1, v2, m); } + void print(const QString& label, const glm::vec3& v); + bool equal(const glm::vec3& v1, const glm::vec3& v2) { return v1 == v2; } + bool withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon); + // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation glm::vec3 toPolar(const glm::vec3& v); glm::vec3 fromPolar(const glm::vec3& polar); glm::vec3 fromPolar(float elevation, float azimuth); + const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } + const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } + const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } + const glm::vec3& UNIT_NEG_X() { return Vectors::UNIT_NEG_X; } + const glm::vec3& UNIT_NEG_Y() { return Vectors::UNIT_NEG_Y; } + const glm::vec3& UNIT_NEG_Z() { return Vectors::UNIT_NEG_Z; } + const glm::vec3& UNIT_XY() { return Vectors::UNIT_XY; } + const glm::vec3& UNIT_XZ() { return Vectors::UNIT_XZ; } + const glm::vec3& UNIT_YZ() { return Vectors::UNIT_YZ; } + const glm::vec3& UNIT_XYZ() { return Vectors::UNIT_XYZ; } + const glm::vec3& FLOAT_MAX() { return Vectors::MAX; } + const glm::vec3& FLOAT_MIN() { return Vectors::MIN; } + const glm::vec3& ZERO() { return Vectors::ZERO; } + const glm::vec3& ONE() { return Vectors::ONE; } + const glm::vec3& TWO() { return Vectors::TWO; } + const glm::vec3& HALF() { return Vectors::HALF; } + const glm::vec3& RIGHT() { return Vectors::RIGHT; } + const glm::vec3& UP() { return Vectors::UNIT_X; } + const glm::vec3& FRONT() { return Vectors::FRONT; } }; - - #endif // hifi_Vec3_h diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 4ca8ed330b..4f2124a343 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -27,6 +27,8 @@ const vec3 Vectors::MAX{ FLT_MAX }; const vec3 Vectors::MIN{ -FLT_MAX }; const vec3 Vectors::ZERO{ 0.0f }; const vec3 Vectors::ONE{ 1.0f }; +const vec3 Vectors::TWO{ 2.0f }; +const vec3 Vectors::HALF{ 0.5f }; const vec3& Vectors::RIGHT = Vectors::UNIT_X; const vec3& Vectors::UP = Vectors::UNIT_Y; const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 4b03ed2525..a35cdfbfb3 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -64,12 +64,13 @@ public: static const vec3 UNIT_XY; static const vec3 UNIT_XZ; static const vec3 UNIT_YZ; - static const vec3 UNIT_ZX; static const vec3 UNIT_XYZ; static const vec3 MAX; static const vec3 MIN; static const vec3 ZERO; static const vec3 ONE; + static const vec3 TWO; + static const vec3 HALF; static const vec3& RIGHT; static const vec3& UP; static const vec3& FRONT; From d53c1d0fba073744d8a976f59b05ebd4f222591e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 3 Sep 2015 13:22:04 -0700 Subject: [PATCH 098/117] Change default avatar collision sound. Note that this only used when Interface.ini does not already have a setting, and the empty string is a valid setting. To reset to default, either delete Interface.ini or the line in it that begins with collisionSoundURL=, or just set Interface|Edit->Preferences->Avatar collision sound URL to https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6939720f32..be984a8053 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -68,7 +68,7 @@ float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f; const int SCRIPTED_MOTOR_CAMERA_FRAME = 0; const int SCRIPTED_MOTOR_AVATAR_FRAME = 1; const int SCRIPTED_MOTOR_WORLD_FRAME = 2; -const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav"; +const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav"; const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; From 36ca789d9243291061ac5bda5c7a823c36a874d4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 14:08:04 -0700 Subject: [PATCH 099/117] fix differential avatar-joint sending to work if more than 2 avatars are in the domain --- assignment-client/src/avatars/AvatarMixer.cpp | 26 ++++++++++++++++++- interface/src/avatar/MyAvatar.cpp | 6 ++--- interface/src/avatar/MyAvatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 23 +++++++++++----- libraries/avatars/src/AvatarData.h | 4 ++- libraries/script-engine/src/ScriptEngine.cpp | 3 ++- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index eefb654737..685e95bbd7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -295,7 +295,8 @@ void AvatarMixer::broadcastAvatarData() { avatarPacketList.startSegment(); numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray(false)); + numAvatarDataBytes += + avatarPacketList.write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO)); avatarPacketList.endSegment(); @@ -361,6 +362,29 @@ void AvatarMixer::broadcastAvatarData() { } else { nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); } + + // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so + // that we can notice differences, next time around. + nodeList->eachMatchingNode( + [&](const SharedNodePointer& otherNode)->bool { + if (!otherNode->getLinkedData()) { + return false; + } + if (otherNode->getUUID() == node->getUUID()) { + return false; + } + + return true; + }, + [&](const SharedNodePointer& otherNode) { + AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + MutexTryLocker lock(otherNodeData->getMutex()); + if (!lock.isLocked()) { + return; + } + AvatarData& otherAvatar = otherNodeData->getAvatar(); + otherAvatar.doneEncoding(); + }); } ); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6939720f32..73ff728747 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -123,18 +123,18 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } -QByteArray MyAvatar::toByteArray(bool cullSmallChanges) { +QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { CameraMode mode = Application::getInstance()->getCamera()->getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = _position; _position = getSkeletonPosition(); - QByteArray array = AvatarData::toByteArray(cullSmallChanges); + QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll); // copy the correct position back _position = oldPosition; return array; } - return AvatarData::toByteArray(cullSmallChanges); + return AvatarData::toByteArray(cullSmallChanges, sendAll); } void MyAvatar::reset() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 89f3b236f4..34e045076d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -200,7 +200,7 @@ private: glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; - QByteArray toByteArray(bool cullSmallChanges); + QByteArray toByteArray(bool cullSmallChanges, bool sendAll); void simulate(float deltaTime); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 123c9707ba..853da6dbe5 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -145,7 +145,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - _position); } -QByteArray AvatarData::toByteArray(bool cullSmallChanges) { +QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { // TODO: DRY this up to a shared method // that can pack any type given the number of bytes // and return the number of bytes to push the pointer @@ -244,11 +244,12 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) { _lastSentJointData.resize(_jointData.size()); - // foreach (const JointData& data, _jointData) { for (int i=0; i < _jointData.size(); i++) { const JointData& data = _jointData.at(i); - if (_lastSentJointData[i].rotation != data.rotation) { - if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) { + if (sendAll || _lastSentJointData[i].rotation != data.rotation) { + if (sendAll || + !cullSmallChanges || + fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) { validity |= (1 << validityBit); } } @@ -267,7 +268,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) { const JointData& data = _jointData[ i ]; if (validity & (1 << validityBit)) { destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation); - _lastSentJointData[i].rotation = data.rotation; } if (++validityBit == BITS_IN_BYTE) { validityBit = 0; @@ -278,6 +278,16 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) { return avatarDataByteArray.left(destinationBuffer - startPosition); } +void AvatarData::doneEncoding() { + // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. + _lastSentJointData.resize(_jointData.size()); + for (int i = 0; i < _jointData.size(); i ++) { + const JointData& data = _jointData[ i ]; + _lastSentJointData[i].rotation = data.rotation; + } +} + + bool AvatarData::shouldLogError(const quint64& now) { if (now > _errorLogExpiry) { _errorLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; @@ -1083,7 +1093,8 @@ void AvatarData::setJointMappingsFromNetworkReply() { void AvatarData::sendAvatarDataPacket() { auto nodeList = DependencyManager::get(); - QByteArray avatarByteArray = toByteArray(true); + QByteArray avatarByteArray = toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); + doneEncoding(); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 278ec2047c..fe6c737a6e 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -111,6 +111,7 @@ const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000; // See also static AvatarData::defaultFullAvatarModelUrl(). const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); +const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02; // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found). // This is the start location in the Sandbox (xyz: 6270, 211, 6000). @@ -171,7 +172,8 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(bool cullSmallChanges); + virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll); + virtual void doneEncoding(); /// \return true if an error should be logged bool shouldLogError(const quint64& now); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index aa395b1b06..ced0742a51 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -599,7 +599,8 @@ void ScriptEngine::run() { / (1000 * 1000)) + 0.5); const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); - QByteArray avatarByteArray = _avatarData->toByteArray(true); + QByteArray avatarByteArray = _avatarData->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); + _avatarData->doneEncoding(); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); From ebe5818c84080abbdc1b5b8edded34710135c9e3 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 3 Sep 2015 14:09:01 -0700 Subject: [PATCH 100/117] relative path --- examples/controllers/hydra/hydraGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 1c0a46f661..bd060ecc14 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -11,7 +11,7 @@ // -Script.include("https://hifi-public.s3.amazonaws.com/scripts/libraries/utils.js"); +Script.include("../../libraries/utils.js"); var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); var rightTriggerAction = RIGHT_HAND_CLICK; From 44c554895d3bb8788d4906a7c09733916b030b35 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 3 Sep 2015 14:33:25 -0700 Subject: [PATCH 101/117] next wave of breakdance work --- examples/libraries/omniTool.js | 4 + .../modules/breakdanceOmniToolModule.js | 35 ++ examples/libraries/utils.js | 27 +- .../breakdanceCore.js} | 378 ++++++++---------- examples/toys/breakdanceToy.js | 17 + 5 files changed, 258 insertions(+), 203 deletions(-) create mode 100644 examples/libraries/omniTool/modules/breakdanceOmniToolModule.js rename examples/{controllers/breakdanceToy.js => toys/breakdanceCore.js} (73%) create mode 100644 examples/toys/breakdanceToy.js diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index c9f041d672..724f30c548 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -65,6 +65,10 @@ OmniTool = function(side) { }); } +OmniTool.prototype.onCleanup = function(action) { + this.unloadModule(); +} + OmniTool.prototype.onActionEvent = function(action, state) { // FIXME figure out the issues when only one spatial controller is active // logDebug("Action: " + action + " " + state); diff --git a/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js b/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js new file mode 100644 index 0000000000..36ee6b1fee --- /dev/null +++ b/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js @@ -0,0 +1,35 @@ +// +// breakdanceOmniToolModule.js +// examples/libraries/omniTool/modules +// +// This is an omniTool module version of the breakdance game +// +// Created by Brad Hefta-Gaub on Sept 3, 2015 +// 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 +// + +Script.include("../toys/breakdanceCore.js"); + +OmniToolModules.Breakdance = function() { + print("OmniToolModules.Breakdance..."); +} + +OmniToolModules.Breakdance.prototype.onLoad = function(deltaTime) { + print("OmniToolModules.Breakdance.prototype.onLoad()..."); + breakdanceStart(); + } + +OmniToolModules.Breakdance.prototype.onUpdate = function(deltaTime) { + print("OmniToolModules.Breakdance.prototype.onUpdate()..."); + breakdanceUpdate(); +} + + OmniToolModules.Breakdance.prototype.onUnload = function() { + print("OmniToolModules.Breakdance.prototype.onUnload()..."); + breakdanceEnd(); +} + +OmniToolModuleType = "Breakdance"; \ No newline at end of file diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 6e6012cfe3..1c2816e7a0 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -101,4 +101,29 @@ logInfo = function(str) { logDebug = function(str) { print(str); -} \ No newline at end of file +} + +// Computes the penetration between a point and a sphere (centered at the origin) +// if point is inside sphere: returns true and stores the result in 'penetration' +// (the vector that would move the point outside the sphere) +// otherwise returns false +findSphereHit = function(point, sphereRadius) { + var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations + var vectorLength = Vec3.length(point); + if (vectorLength < EPSILON) { + return true; + } + var distance = vectorLength - sphereRadius; + if (distance < 0.0) { + return true; + } + return false; +} + +findSpherePointHit = function(sphereCenter, sphereRadius, point) { + return findSphereHit(Vec3.subtract(point,sphereCenter), sphereRadius); +} + +findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) { + return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter); +} diff --git a/examples/controllers/breakdanceToy.js b/examples/toys/breakdanceCore.js similarity index 73% rename from examples/controllers/breakdanceToy.js rename to examples/toys/breakdanceCore.js index db3b418c01..0f346537eb 100644 --- a/examples/controllers/breakdanceToy.js +++ b/examples/toys/breakdanceCore.js @@ -1,7 +1,8 @@ // -// breakdanceToy.js -// examples +// breakdanceCore.js +// examples/toys // +// This is the core breakdance game library, it can be used as part of an entity script, or an omniTool module, or bootstapped on it's own // Created by Brad Hefta-Gaub on August 24, 2015 // Copyright 2015 High Fidelity, Inc. // @@ -9,34 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; - -// helpers -// Computes the penetration between a point and a sphere (centered at the origin) -// if point is inside sphere: returns true and stores the result in 'penetration' -// (the vector that would move the point outside the sphere) -// otherwise returns false -function findSphereHit(point, sphereRadius) { - var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations - var vectorLength = Vec3.length(point); - if (vectorLength < EPSILON) { - return true; - } - var distance = vectorLength - sphereRadius; - if (distance < 0.0) { - return true; - } - return false; -} - -function findSpherePointHit(sphereCenter, sphereRadius, point) { - return findSphereHit(Vec3.subtract(point,sphereCenter), sphereRadius); -} - -function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadius) { - return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter); -} +Script.include("../libraries/utils.js"); function getPositionPuppet() { var DISTANCE_IN_FRONT = 2; @@ -245,13 +220,18 @@ function getPositionRightOnBase() { } -// We will also demonstrate some 3D overlays. We will create a couple of cubes, spheres, and lines -// our 3D cube that moves around... -var handSize = 0.25; -var leftCubePosition = MyAvatar.getLeftPalmPosition(); -var rightCubePosition = MyAvatar.getRightPalmPosition(); +// some globals we will need access to +var HAND_SIZE = 0.25; +var TARGET_SIZE = 0.3; +var TARGET_COLOR = { red: 128, green: 128, blue: 128}; +var TARGET_COLOR_HIT = { red: 0, green: 255, blue: 0}; -var text = Overlays.addOverlay("text", { +var textOverlay, leftHandOverlay, rightHandOverlay, + leftOnBaseOverlay, leftLoweredOverlay, leftOverheadOverlay, leftSideOverlay, leftFrontOverlay, + rightOnBaseOverlay, rightLoweredOverlay, rightOverheadOverlay, rightSideOverlay, rightFrontOverlay; + +function createOverlays() { + textOverlay = Overlays.addOverlay("text", { x: 100, y: 300, width: 900, @@ -265,31 +245,110 @@ var text = Overlays.addOverlay("text", { backgroundAlpha: 0.5 }); -var leftHand= Overlays.addOverlay("cube", { - position: leftCubePosition, - size: handSize, + leftHandOverlay = Overlays.addOverlay("cube", { + position: MyAvatar.getLeftPalmPosition(), + size: HAND_SIZE, color: { red: 0, green: 0, blue: 255}, alpha: 1, solid: false }); -var rightHand= Overlays.addOverlay("cube", { - position: rightCubePosition, - size: handSize, + rightHandOverlay = Overlays.addOverlay("cube", { + position: MyAvatar.getRightPalmPosition(), + size: HAND_SIZE, color: { red: 255, green: 0, blue: 0}, alpha: 1, solid: false }); + leftOnBaseOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftOnBase(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); -var targetSize = 0.3; -var targetColor = { red: 128, green: 128, blue: 128}; -var targetColorHit = { red: 0, green: 255, blue: 0}; -var moveCycleColor = { red: 255, green: 255, blue: 0}; + + leftLoweredOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftLowered(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + leftOverheadOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftOverhead(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + leftSideOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftSide(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + leftFrontOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftFront(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + rightOnBaseOverlay = Overlays.addOverlay("cube", { + position: getPositionRightOnBase(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + rightLoweredOverlay = Overlays.addOverlay("cube", { + position: getPositionRightLowered(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + rightOverheadOverlay = Overlays.addOverlay("cube", { + position: getPositionRightOverhead(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + rightSideOverlay = Overlays.addOverlay("cube", { + position: getPositionRightSide(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + rightFrontOverlay = Overlays.addOverlay("cube", { + position: getPositionRightFront(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); +} var TEMPORARY_LIFETIME = 60; - -var animationSettings = JSON.stringify({ +var ANIMATION_SETTINGS = JSON.stringify({ fps: 30, running: true, loop: true, @@ -297,107 +356,22 @@ var animationSettings = JSON.stringify({ lastFrame: 10000 }); -var naturalDimensions = { x: 1.63, y: 1.67, z: 0.31 }; -var dimensions = Vec3.multiply(naturalDimensions, 0.3); +var NATURAL_DIMENSIONS = { x: 1.63, y: 1.67, z: 0.31 }; +var DIMENSIONS = Vec3.multiply(NATURAL_DIMENSIONS, 0.3); +var puppetEntityID; -var puppetEntityID = Entities.addEntity({ +function createPuppet() { + puppetEntityID = Entities.addEntity({ type: "Model", modelURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/bboy1/bboy1.fbx", animationURL: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx", - animationSettings: animationSettings, + animationSettings: ANIMATION_SETTINGS, position: getPositionPuppet(), ignoreForCollisions: true, - dimensions: dimensions, + dimensions: DIMENSIONS, lifetime: TEMPORARY_LIFETIME }); - -var leftOnBase = Overlays.addOverlay("cube", { - position: getPositionLeftOnBase(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var leftLowered = Overlays.addOverlay("cube", { - position: getPositionLeftLowered(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var leftOverhead = Overlays.addOverlay("cube", { - position: getPositionLeftOverhead(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var leftSide= Overlays.addOverlay("cube", { - position: getPositionLeftSide(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var leftFront= Overlays.addOverlay("cube", { - position: getPositionLeftFront(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var rightOnBase = Overlays.addOverlay("cube", { - position: getPositionRightOnBase(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var rightLowered = Overlays.addOverlay("cube", { - position: getPositionRightLowered(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var rightOverhead = Overlays.addOverlay("cube", { - position: getPositionRightOverhead(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var rightSide= Overlays.addOverlay("cube", { - position: getPositionRightSide(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var rightFront= Overlays.addOverlay("cube", { - position: getPositionRightFront(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var startDate = new Date(); -var lastTime = startDate.getTime(); +} var NO_POSE = 0; var LEFT_ON_BASE = 1; @@ -411,8 +385,6 @@ var RIGHT_LOWERED = 128; var RIGHT_SIDE = 256; var RIGHT_FRONT = 512; -var lastPoseValue = NO_POSE; - //http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx //http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_pose_to_idle.fbx //http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_uprock.fbx @@ -461,8 +433,6 @@ poses[LEFT_ON_BASE + RIGHT_LOWERED ] = { name: "Left On Base + Right Lowered" poses[LEFT_ON_BASE + RIGHT_SIDE ] = { name: "Left On Base + Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" }; poses[LEFT_ON_BASE + RIGHT_FRONT ] = { name: "Left On Base + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" }; - - poses[LEFT_OVERHEAD + RIGHT_OVERHEAD ] = { name: "Left Overhead + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_uprock.fbx" }; poses[LEFT_LOWERED + RIGHT_OVERHEAD ] = { name: "Left Lowered + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_1.fbx" }; poses[LEFT_SIDE + RIGHT_OVERHEAD ] = { name: "Left Side + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_2.fbx" }; @@ -484,43 +454,46 @@ poses[LEFT_SIDE + RIGHT_FRONT ] = { name: "Left Side + Right Front", poses[LEFT_FRONT + RIGHT_FRONT ] = { name: "Left Front + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1_end.fbx" }; -Script.update.connect(function(deltaTime) { - var date= new Date(); - var now= date.getTime(); - var elapsed = now - lastTime; - var inMoveCycle = false; +breakdanceStart = function() { + print("breakdanceStart..."); + createOverlays(); + createPuppet(); +} + +breakdanceUpdate = function(deltaTime) { + //print("breakdanceUpdate..."); var leftHandPos = MyAvatar.getLeftPalmPosition(); var rightHandPos = MyAvatar.getRightPalmPosition(); - Overlays.editOverlay(leftHand, { position: leftHandPos } ); - Overlays.editOverlay(rightHand, { position: rightHandPos } ); + Overlays.editOverlay(leftHandOverlay, { position: leftHandPos } ); + Overlays.editOverlay(rightHandOverlay, { position: rightHandPos } ); - var hitTargetLeftOnBase = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftOnBase(), targetSize/2); - var hitTargetLeftOverhead = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftOverhead(), targetSize/2); - var hitTargetLeftLowered = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftLowered(), targetSize/2); - var hitTargetLeftSide = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftSide(), targetSize/2); - var hitTargetLeftFront = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftFront(), targetSize/2); + var hitTargetLeftOnBase = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftOnBase(), TARGET_SIZE/2); + var hitTargetLeftOverhead = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftOverhead(), TARGET_SIZE/2); + var hitTargetLeftLowered = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftLowered(), TARGET_SIZE/2); + var hitTargetLeftSide = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftSide(), TARGET_SIZE/2); + var hitTargetLeftFront = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftFront(), TARGET_SIZE/2); - var hitTargetRightOnBase = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightOnBase(), targetSize/2); - var hitTargetRightOverhead = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightOverhead(), targetSize/2); - var hitTargetRightLowered = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightLowered(), targetSize/2); - var hitTargetRightSide = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightSide(), targetSize/2); - var hitTargetRightFront = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightFront(), targetSize/2); + var hitTargetRightOnBase = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightOnBase(), TARGET_SIZE/2); + var hitTargetRightOverhead = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightOverhead(), TARGET_SIZE/2); + var hitTargetRightLowered = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightLowered(), TARGET_SIZE/2); + var hitTargetRightSide = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightSide(), TARGET_SIZE/2); + var hitTargetRightFront = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightFront(), TARGET_SIZE/2); // determine target colors - var targetColorLeftOnBase = hitTargetLeftOnBase ? targetColorHit : targetColor; - var targetColorLeftOverhead = hitTargetLeftOverhead ? targetColorHit : targetColor; - var targetColorLeftLowered = hitTargetLeftLowered ? targetColorHit : targetColor; - var targetColorLeftSide = hitTargetLeftSide ? targetColorHit : targetColor; - var targetColorLeftFront = hitTargetLeftFront ? targetColorHit : targetColor; + var targetColorLeftOnBase = hitTargetLeftOnBase ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftOverhead = hitTargetLeftOverhead ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftLowered = hitTargetLeftLowered ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftSide = hitTargetLeftSide ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftFront = hitTargetLeftFront ? TARGET_COLOR_HIT : TARGET_COLOR; - var targetColorRightOnBase = hitTargetRightOnBase ? targetColorHit : targetColor; - var targetColorRightOverhead = hitTargetRightOverhead ? targetColorHit : targetColor; - var targetColorRightLowered = hitTargetRightLowered ? targetColorHit : targetColor; - var targetColorRightSide = hitTargetRightSide ? targetColorHit : targetColor; - var targetColorRightFront = hitTargetRightFront ? targetColorHit : targetColor; + var targetColorRightOnBase = hitTargetRightOnBase ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightOverhead = hitTargetRightOverhead ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightLowered = hitTargetRightLowered ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightSide = hitTargetRightSide ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightFront = hitTargetRightFront ? TARGET_COLOR_HIT : TARGET_COLOR; // calculate a combined arm pose based on left and right hits var poseValue = NO_POSE; @@ -536,47 +509,48 @@ Script.update.connect(function(deltaTime) { poseValue += hitTargetRightFront ? RIGHT_FRONT : 0; if (poses[poseValue] == undefined) { - Overlays.editOverlay(text, { text: "no pose -- value:" + poseValue }); + Overlays.editOverlay(textOverlay, { text: "no pose -- value:" + poseValue }); } else { - Overlays.editOverlay(text, { text: "pose:" + poses[poseValue].name + "\n" + "animation:" + poses[poseValue].animation }); + Overlays.editOverlay(textOverlay, { text: "pose:" + poses[poseValue].name + "\n" + "animation:" + poses[poseValue].animation }); var props = Entities.getEntityProperties(puppetEntityID); + print("puppetEntityID:" + puppetEntityID + "age:"+props.age); Entities.editEntity(puppetEntityID, { animationURL: poses[poseValue].animation, lifetime: TEMPORARY_LIFETIME + props.age // renew lifetime }); } - lastPoseValue = poseValue; - - Overlays.editOverlay(leftOnBase, { position: getPositionLeftOnBase(), color: targetColorLeftOnBase } ); - Overlays.editOverlay(leftOverhead, { position: getPositionLeftOverhead(), color: targetColorLeftOverhead } ); - Overlays.editOverlay(leftLowered, { position: getPositionLeftLowered(), color: targetColorLeftLowered } ); - Overlays.editOverlay(leftSide, { position: getPositionLeftSide() , color: targetColorLeftSide } ); - Overlays.editOverlay(leftFront, { position: getPositionLeftFront() , color: targetColorLeftFront } ); + Overlays.editOverlay(leftOnBaseOverlay, { position: getPositionLeftOnBase(), color: targetColorLeftOnBase } ); + Overlays.editOverlay(leftOverheadOverlay, { position: getPositionLeftOverhead(), color: targetColorLeftOverhead } ); + Overlays.editOverlay(leftLoweredOverlay, { position: getPositionLeftLowered(), color: targetColorLeftLowered } ); + Overlays.editOverlay(leftSideOverlay, { position: getPositionLeftSide() , color: targetColorLeftSide } ); + Overlays.editOverlay(leftFrontOverlay, { position: getPositionLeftFront() , color: targetColorLeftFront } ); - Overlays.editOverlay(rightOnBase, { position: getPositionRightOnBase(), color: targetColorRightOnBase } ); - Overlays.editOverlay(rightOverhead, { position: getPositionRightOverhead(), color: targetColorRightOverhead } ); - Overlays.editOverlay(rightLowered, { position: getPositionRightLowered(), color: targetColorRightLowered } ); - Overlays.editOverlay(rightSide, { position: getPositionRightSide() , color: targetColorRightSide } ); - Overlays.editOverlay(rightFront, { position: getPositionRightFront() , color: targetColorRightFront } ); - }); + Overlays.editOverlay(rightOnBaseOverlay, { position: getPositionRightOnBase(), color: targetColorRightOnBase } ); + Overlays.editOverlay(rightOverheadOverlay, { position: getPositionRightOverhead(), color: targetColorRightOverhead } ); + Overlays.editOverlay(rightLoweredOverlay, { position: getPositionRightLowered(), color: targetColorRightLowered } ); + Overlays.editOverlay(rightSideOverlay, { position: getPositionRightSide() , color: targetColorRightSide } ); + Overlays.editOverlay(rightFrontOverlay, { position: getPositionRightFront() , color: targetColorRightFront } ); + } -Script.scriptEnding.connect(function() { - Overlays.deleteOverlay(leftHand); - Overlays.deleteOverlay(rightHand); - Overlays.deleteOverlay(text); - Overlays.deleteOverlay(leftOnBase); - Overlays.deleteOverlay(leftOverhead); - Overlays.deleteOverlay(leftLowered); - Overlays.deleteOverlay(leftSide); - Overlays.deleteOverlay(leftFront); - Overlays.deleteOverlay(rightOnBase); - Overlays.deleteOverlay(rightOverhead); - Overlays.deleteOverlay(rightLowered); - Overlays.deleteOverlay(rightSide); - Overlays.deleteOverlay(rightFront); +breakdanceEnd= function() { + print("breakdanceEnd..."); + + Overlays.deleteOverlay(leftHandOverlay); + Overlays.deleteOverlay(rightHandOverlay); + + Overlays.deleteOverlay(textOverlay); + Overlays.deleteOverlay(leftOnBaseOverlay); + Overlays.deleteOverlay(leftOverheadOverlay); + Overlays.deleteOverlay(leftLoweredOverlay); + Overlays.deleteOverlay(leftSideOverlay); + Overlays.deleteOverlay(leftFrontOverlay); + Overlays.deleteOverlay(rightOnBaseOverlay); + Overlays.deleteOverlay(rightOverheadOverlay); + Overlays.deleteOverlay(rightLoweredOverlay); + Overlays.deleteOverlay(rightSideOverlay); + Overlays.deleteOverlay(rightFrontOverlay); - print("puppetEntityID:"+puppetEntityID); Entities.deleteEntity(puppetEntityID); -}); \ No newline at end of file +} \ No newline at end of file diff --git a/examples/toys/breakdanceToy.js b/examples/toys/breakdanceToy.js new file mode 100644 index 0000000000..841d5ad111 --- /dev/null +++ b/examples/toys/breakdanceToy.js @@ -0,0 +1,17 @@ +// +// breakdanceToy.js +// examples/toys +// +// This is an local script version of the breakdance game +// +// Created by Brad Hefta-Gaub on Sept 3, 2015 +// 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 +// + +Script.include("breakdanceCore.js"); +breakdanceStart(); +Script.update.connect(breakdanceUpdate); +Script.scriptEnding.connect(breakdanceEnd); From 1071b98260354ce836d5c9fea85502d0f1f8dde6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 14:38:46 -0700 Subject: [PATCH 102/117] fix differential avatar-joint sending to work if more than 2 avatars are in the domain --- assignment-client/src/avatars/AvatarMixer.cpp | 48 ++++++++++--------- libraries/avatars/src/AvatarData.cpp | 6 +-- libraries/avatars/src/AvatarData.h | 4 ++ 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 685e95bbd7..64969b55ce 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -362,32 +362,34 @@ void AvatarMixer::broadcastAvatarData() { } else { nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); } - - // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so - // that we can notice differences, next time around. - nodeList->eachMatchingNode( - [&](const SharedNodePointer& otherNode)->bool { - if (!otherNode->getLinkedData()) { - return false; - } - if (otherNode->getUUID() == node->getUUID()) { - return false; - } - - return true; - }, - [&](const SharedNodePointer& otherNode) { - AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); - MutexTryLocker lock(otherNodeData->getMutex()); - if (!lock.isLocked()) { - return; - } - AvatarData& otherAvatar = otherNodeData->getAvatar(); - otherAvatar.doneEncoding(); - }); } ); + // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so + // that we can notice differences, next time around. + nodeList->eachMatchingNode( + [&](const SharedNodePointer& otherNode)->bool { + if (!otherNode->getLinkedData()) { + return false; + } + if (otherNode->getType() != NodeType::Agent) { + return false; + } + if (!otherNode->getActiveSocket()) { + return false; + } + return true; + }, + [&](const SharedNodePointer& otherNode) { + AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + MutexTryLocker lock(otherNodeData->getMutex()); + if (!lock.isLocked()) { + return; + } + AvatarData& otherAvatar = otherNodeData->getAvatar(); + otherAvatar.doneEncoding(); + }); + _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 853da6dbe5..555ca42d60 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -31,10 +31,6 @@ quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; -// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer -const float MIN_ROTATION_DOT = 0.9999999f; - - using namespace std; const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f); @@ -249,7 +245,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { if (sendAll || _lastSentJointData[i].rotation != data.rotation) { if (sendAll || !cullSmallChanges || - fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) { + fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { validity |= (1 << validityBit); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fe6c737a6e..8976074ec0 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -111,7 +111,11 @@ const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000; // See also static AvatarData::defaultFullAvatarModelUrl(). const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); +// how often should we send a full report about joint rotations, even if they haven't changed? const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02; +// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer +const float AVATAR_MIN_ROTATION_DOT = 0.99999999f; + // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found). // This is the start location in the Sandbox (xyz: 6270, 211, 6000). From 85cb5031525429e6ec34425ca18e1e5c5db645d4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 3 Sep 2015 14:46:44 -0700 Subject: [PATCH 103/117] Made Enable Anim Graph menu option dynamic. It will load the anim graph when enabled and destroy the anim graph when disabled. --- interface/src/avatar/MyAvatar.cpp | 31 +++++++++++++++++++++---------- interface/src/avatar/MyAvatar.h | 2 ++ libraries/animation/src/Rig.cpp | 11 ++++++----- libraries/animation/src/Rig.h | 5 +++-- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f6ec1242f2..d35a1a08f6 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -714,8 +714,12 @@ 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 + if (isEnabled) { + if (_skeletonModel.readyToAddToScene()) { + initAnimGraph(); + } + } else { + destroyAnimGraph(); } } @@ -1234,6 +1238,20 @@ void MyAvatar::initHeadBones() { } } +void MyAvatar::initAnimGraph() { + // 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/e2cb37aee601b6fba31d60eac3f6ae3ef72d4a66/avatar.json"); + _skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); +} + +void MyAvatar::destroyAnimGraph() { + _rig->destroyAnimGraph(); + AnimDebugDraw::getInstance().removeSkeleton("myAvatar"); + AnimDebugDraw::getInstance().removeAnimNode("myAvatar"); +} + void MyAvatar::preRender(RenderArgs* renderArgs) { render::ScenePointer scene = Application::getInstance()->getMain3DScene(); @@ -1241,15 +1259,8 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { if (_skeletonModel.initWhenReady(scene)) { initHeadBones(); - _skeletonModel.setCauterizeBoneSet(_headBoneSet); - - // 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/e2cb37aee601b6fba31d60eac3f6ae3ef72d4a66/avatar.json"); - - _skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); + initAnimGraph(); } if (_enableDebugDrawBindPose || _enableDebugDrawAnimPose) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1774eec71f..83bfb0f808 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -291,6 +291,8 @@ private: void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void maybeUpdateBillboard(); void initHeadBones(); + void initAnimGraph(); + void destroyAnimGraph(); // Avatar Preferences QUrl _fullAvatarURLFromPreferences; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b4652d24ef..4abef61ddc 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -187,12 +187,13 @@ void Rig::deleteAnimations() { removeAnimationHandle(animation); } _animationHandles.clear(); + destroyAnimGraph(); +} - if (_enableAnimGraph) { - _animSkeleton = nullptr; - _animLoader = nullptr; - _animNode = nullptr; - } +void Rig::destroyAnimGraph() { + _animSkeleton = nullptr; + _animLoader = nullptr; + _animNode = nullptr; } void Rig::initJointStates(QVector states, glm::mat4 rootTransform, diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 109605587e..c264d348f4 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -83,6 +83,7 @@ public: bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation. const QList& getRunningAnimations() const { return _runningAnimations; } void deleteAnimations(); + void destroyAnimGraph(); const QList& getAnimationHandles() const { return _animationHandles; } 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()); @@ -193,8 +194,8 @@ public: QList _animationHandles; QList _runningAnimations; - bool _enableRig; - bool _enableAnimGraph; + bool _enableRig = false; + bool _enableAnimGraph = false; glm::vec3 _lastFront; glm::vec3 _lastPosition; From 0c8848a463428e80e949a7f475141ddafb67c27d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 15:41:40 -0700 Subject: [PATCH 104/117] don't update _lastSendJointData if we didn't send --- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 17 ++++++++++++----- libraries/avatars/src/AvatarData.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 64969b55ce..1ab637b57f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -387,7 +387,7 @@ void AvatarMixer::broadcastAvatarData() { return; } AvatarData& otherAvatar = otherNodeData->getAvatar(); - otherAvatar.doneEncoding(); + otherAvatar.doneEncoding(false); }); _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 555ca42d60..46a323733a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -274,16 +274,20 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { return avatarDataByteArray.left(destinationBuffer - startPosition); } -void AvatarData::doneEncoding() { +void AvatarData::doneEncoding(bool cullSmallChanges) { // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. _lastSentJointData.resize(_jointData.size()); for (int i = 0; i < _jointData.size(); i ++) { const JointData& data = _jointData[ i ]; - _lastSentJointData[i].rotation = data.rotation; + if (_lastSentJointData[i].rotation != data.rotation) { + if (!cullSmallChanges || + fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { + _lastSentJointData[i].rotation = data.rotation; + } + } } } - bool AvatarData::shouldLogError(const quint64& now) { if (now > _errorLogExpiry) { _errorLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; @@ -1089,8 +1093,11 @@ void AvatarData::setJointMappingsFromNetworkReply() { void AvatarData::sendAvatarDataPacket() { auto nodeList = DependencyManager::get(); - QByteArray avatarByteArray = toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); - doneEncoding(); + // about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed. + // this is to guard against a joint moving once, the packet getting lost, and the joint never moving again. + bool sendFullUpdate = randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO; + QByteArray avatarByteArray = toByteArray(true, sendFullUpdate); + doneEncoding(true); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8976074ec0..c4cc3fcaac 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -177,7 +177,7 @@ public: void setHandPosition(const glm::vec3& handPosition); virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll); - virtual void doneEncoding(); + virtual void doneEncoding(bool cullSmallChanges); /// \return true if an error should be logged bool shouldLogError(const quint64& now); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index ced0742a51..eeff6c15f0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -600,7 +600,7 @@ void ScriptEngine::run() { const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); QByteArray avatarByteArray = _avatarData->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); - _avatarData->doneEncoding(); + _avatarData->doneEncoding(true); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); From 218521fc021c4e3565ef35cf56926de89a004525 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 16:08:46 -0700 Subject: [PATCH 105/117] put AVATAR_MIN_ROTATION_DOT back to previous value --- libraries/avatars/src/AvatarData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c4cc3fcaac..af97180cbd 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -114,7 +114,7 @@ const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); // how often should we send a full report about joint rotations, even if they haven't changed? const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02; // this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer -const float AVATAR_MIN_ROTATION_DOT = 0.99999999f; +const float AVATAR_MIN_ROTATION_DOT = 0.9999999f; // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found). From 063320771ca7d19b916f8ce373d4bf3f01c5d0fa Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 3 Sep 2015 16:37:24 -0700 Subject: [PATCH 106/117] fix a bug in entity script includes and file based includes on windows --- libraries/script-engine/src/BatchLoader.cpp | 4 ---- libraries/shared/src/RegisteredMetaTypes.cpp | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index db0743808f..e6115324c9 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -58,11 +58,7 @@ void BatchLoader::start() { connect(this, &QObject::destroyed, reply, &QObject::deleteLater); } else { -#ifdef _WIN32 - QString fileName = url.toString(); -#else QString fileName = url.toLocalFile(); -#endif qCDebug(scriptengine) << "Reading file at " << fileName; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 2c4b213fcb..b2389f4db6 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -26,8 +26,7 @@ static int quatMetaTypeId = qRegisterMetaType(); static int xColorMetaTypeId = qRegisterMetaType(); static int pickRayMetaTypeId = qRegisterMetaType(); static int collisionMetaTypeId = qRegisterMetaType(); - - +static int qMapURLStringMetaTypeId = qRegisterMetaType>(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); From 16dfc39f5f7fb6351dba84b39feaaf97beb9cd48 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 3 Sep 2015 18:22:35 -0700 Subject: [PATCH 107/117] fix collision shape of avatars --- interface/src/avatar/SkeletonModel.cpp | 13 +++----- libraries/fbx/src/FBXReader.cpp | 44 ++++++++++---------------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 35536eabd1..181816ed5f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -547,14 +547,11 @@ void SkeletonModel::computeBoundingShape() { // compute the default transform of this joint const JointState& state = _rig->getJointState(i); - // HACK WORKAROUND: ignore joints that may have bad translation (e.g. have been flagged as such with zero radius) - if (state.getBoneRadius() > 0.0f) { - // Each joint contributes a sphere at its position - glm::vec3 axis(state.getBoneRadius()); - glm::vec3 jointPosition = state.getPosition(); - totalExtents.addPoint(jointPosition + axis); - totalExtents.addPoint(jointPosition - axis); - } + // Each joint contributes a sphere at its position + glm::vec3 axis(state.getBoneRadius()); + glm::vec3 jointPosition = state.getPosition(); + totalExtents.addPoint(jointPosition + axis); + totalExtents.addPoint(jointPosition - axis); } // compute bounding shape parameters diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6d83a87a1c..de28aee4c8 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2557,6 +2557,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping int maxJointIndex = firstFBXCluster.jointIndex; glm::mat4 inverseModelTransform = glm::inverse(modelTransform); if (clusterIDs.size() > 1) { + // this is a multi-mesh joint extracted.mesh.clusterIndices.resize(extracted.mesh.vertices.size()); extracted.mesh.clusterWeights.resize(extracted.mesh.vertices.size()); float maxWeight = 0.0f; @@ -2640,6 +2641,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping } } } else { + // this is a single-mesh joint int jointIndex = maxJointIndex; FBXJoint& joint = geometry.joints[jointIndex]; JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex]; @@ -2660,6 +2662,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping } float radiusScale = extractUniformScale(joint.transform * firstFBXCluster.inverseBindMatrix); + // compute average vertex glm::vec3 averageVertex(0.0f); foreach (const glm::vec3& vertex, extracted.mesh.vertices) { float proj = glm::dot(boneDirection, boneEnd - vertex); @@ -2669,29 +2672,26 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping ++jointShapeInfo.numVertexWeights; averageVertex += vertex; } + + // compute joint's radius int numVertices = extracted.mesh.vertices.size(); jointShapeInfo.numVertices = numVertices; if (numVertices > 0) { + // compute average radius averageVertex /= (float)jointShapeInfo.numVertices; float averageRadius = 0.0f; foreach (const glm::vec3& vertex, extracted.mesh.vertices) { averageRadius += glm::distance(vertex, averageVertex); } - jointShapeInfo.averageRadius = averageRadius * radiusScale / (float)jointShapeInfo.numVertices; + averageRadius *= radiusScale / (float)jointShapeInfo.numVertices; + + // final radius is minimum of average and weighted + float weightedRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights; + jointShapeInfo.averageRadius = glm::min(weightedRadius, averageRadius); } - // BUG: the boneBegin and/or boneEnd are incorrect for meshes that are "connected - // under the bone" without weights. Unfortunately we haven't been able to find it yet. - // Although the the mesh vertices are correct in the model-frame, the joint's transform - // in the same frame is just BAD. - // - // HACK WORKAROUND: prevent these shapes from contributing to the collision capsule by setting - // some key members of jointShapeInfo to zero: - jointShapeInfo.numVertices = 0; + // clear sumVertexWeights (this flags it as a single-mesh joint for later) jointShapeInfo.sumVertexWeights = 0.0f; - jointShapeInfo.numVertexWeights = 0; - jointShapeInfo.boneBegin = glm::vec3(0.0f); - jointShapeInfo.averageRadius = 0.0f; } extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); @@ -2727,22 +2727,12 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping } if (jointShapeInfo.sumVertexWeights > 0.0f) { + // mutiple meshes contributed to the bone radius and now that all + // contributing meshes are done we can finally compute the boneRadius joint.boneRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights; - } - - // the joint is "capsule-like" if it had ANY mesh vertices successfully projected onto the bone - // AND its boneRadius is not too close to zero - bool collideLikeCapsule = jointShapeInfo.numVertexWeights > 0 - && glm::length(jointShapeInfo.boneBegin) > EPSILON; - - if (!collideLikeCapsule) { - // this joint's mesh did not successfully project onto the bone axis - // so it isn't "capsule-like" and we need to estimate its radius a different way: - // the average radius to the average point. - if (jointShapeInfo.numVertexWeights == 0 - && jointShapeInfo.numVertices > 0) { - joint.boneRadius = jointShapeInfo.averageRadius; - } + } else { + // single-mesh joint + joint.boneRadius = jointShapeInfo.averageRadius; } } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); From b0f239b89dca2799f7d3707caf3133877c56716a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 3 Sep 2015 19:06:26 -0700 Subject: [PATCH 108/117] also make sure local file includes work for non-entity scripts --- libraries/script-engine/src/BatchLoader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index e6115324c9..ac0cafa6a9 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -60,6 +60,12 @@ void BatchLoader::start() { } else { QString fileName = url.toLocalFile(); + // sometimes on windows, we see the toLocalFile() return null, + // in this case we will attempt to simply use the url as a string + if (fileName.isEmpty()) { + fileName = url.toString(); + } + qCDebug(scriptengine) << "Reading file at " << fileName; QFile scriptFile(fileName); From dcecd7b73a7c97ac51fe1a49daf244b2c08c3c11 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 3 Sep 2015 19:13:39 -0700 Subject: [PATCH 109/117] Added hysteresis to the state machine for turning and moving states. This should help a bit with the jerkiness, as state transitions occur less often due to noise or the user straddling the boundary between two states. --- libraries/animation/src/Rig.cpp | 34 +++++++++++++++++++++++++++------ libraries/animation/src/Rig.h | 6 ++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4abef61ddc..9f22a54c29 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -427,10 +427,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos glm::vec3 positionDelta = worldPosition - _lastPosition; glm::vec3 workingVelocity = positionDelta / deltaTime; +#if !WANT_DEBUG // But for smoothest (non-hmd standing) results, go ahead and use velocity: if (!positionDelta.x && !positionDelta.y && !positionDelta.z) { workingVelocity = worldVelocity; } +#endif if (_enableAnimGraph) { @@ -454,12 +456,29 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); const float ANIM_WALK_SPEED = 1.4f; // m/s - _animVars.set("walkTimeScale", glm::clamp(0.1f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); + _animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); - const float MOVE_SPEED_THRESHOLD = 0.01f; // m/sec - const float TURN_SPEED_THRESHOLD = 0.5f; // rad/sec - if (glm::length(localVel) > MOVE_SPEED_THRESHOLD) { - if (fabs(forwardSpeed) > fabs(lateralSpeed)) { + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + const float TURN_ENTER_SPEED_THRESHOLD = 0.5f; // rad/sec + const float TURN_EXIT_SPEED_THRESHOLD = 0.2f; // rad/sec + + float moveThresh; + if (_state != RigRole::Move) { + moveThresh = MOVE_ENTER_SPEED_THRESHOLD; + } else { + moveThresh = MOVE_EXIT_SPEED_THRESHOLD; + } + + float turnThresh; + if (_state != RigRole::Turn) { + turnThresh = TURN_ENTER_SPEED_THRESHOLD; + } else { + turnThresh = TURN_EXIT_SPEED_THRESHOLD; + } + + if (glm::length(localVel) > moveThresh) { + if (fabs(forwardSpeed) > 0.5f * fabs(lateralSpeed)) { if (forwardSpeed > 0.0f) { // forward _animVars.set("isMovingForward", true); @@ -481,8 +500,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotMoving", false); } } + _state = RigRole::Move; } else { - if (fabs(turningSpeed) > TURN_SPEED_THRESHOLD) { + if (fabs(turningSpeed) > turnThresh) { if (turningSpeed > 0.0f) { // turning right _animVars.set("isTurningRight", true); @@ -492,8 +512,10 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isTurningLeft", true); _animVars.set("isNotTurning", false); } + _state = RigRole::Turn; } else { // idle + _state = RigRole::Idle; } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c264d348f4..0bf0645b4d 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -203,6 +203,12 @@ public: std::shared_ptr _animSkeleton; std::unique_ptr _animLoader; AnimVariantMap _animVars; + enum class RigRole { + Idle = 0, + Turn, + Move + }; + RigRole _state = RigRole::Idle; }; #endif /* defined(__hifi__Rig__) */ From c8069ba73ce3ee80cf63d96cf4c49dc4dde38b95 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 2 Sep 2015 22:37:09 -0700 Subject: [PATCH 110/117] Working on magballs polish --- examples/defaultScripts.js | 1 + examples/html/magBalls/addMode.html | 25 ++ examples/html/magBalls/deleteMode.html | 25 ++ examples/html/magBalls/magBalls.css | 14 + examples/html/magBalls/moveMode.html | 25 ++ examples/libraries/avatarRelativeOverlays.js | 55 ++++ examples/libraries/constants.js | 2 + examples/libraries/highlighter.js | 25 +- examples/libraries/omniTool.js | 73 ++++- .../omniTool/models/invisibleWand.js | 6 + .../libraries/omniTool/models/modelBase.js | 6 + examples/libraries/omniTool/models/wand.js | 34 +-- examples/libraries/omniTool/modules/test.js | 31 +- examples/libraries/utils.js | 19 ++ examples/toys/magBalls.js | 287 +++++++++++++++--- examples/toys/magBalls/constants.js | 25 +- examples/toys/magBalls/debugUtils.js | 95 ------ examples/toys/magBalls/edgeSpring.js | 5 +- examples/toys/magBalls/magBalls.js | 125 +++++++- interface/src/ui/overlays/Web3DOverlay.cpp | 7 +- libraries/render-utils/src/simple.slv | 2 + 21 files changed, 681 insertions(+), 206 deletions(-) create mode 100644 examples/html/magBalls/addMode.html create mode 100644 examples/html/magBalls/deleteMode.html create mode 100644 examples/html/magBalls/magBalls.css create mode 100644 examples/html/magBalls/moveMode.html create mode 100644 examples/libraries/avatarRelativeOverlays.js create mode 100644 examples/libraries/omniTool/models/invisibleWand.js diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index c602c36382..44801ef737 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -17,3 +17,4 @@ Script.load("users.js"); Script.load("grab.js"); Script.load("directory.js"); Script.load("dialTone.js"); +Script.load("libraries/omniTool.js"); diff --git a/examples/html/magBalls/addMode.html b/examples/html/magBalls/addMode.html new file mode 100644 index 0000000000..f1db142daa --- /dev/null +++ b/examples/html/magBalls/addMode.html @@ -0,0 +1,25 @@ + + + + + + + +
Add
+ + \ No newline at end of file diff --git a/examples/html/magBalls/deleteMode.html b/examples/html/magBalls/deleteMode.html new file mode 100644 index 0000000000..94f23fdef8 --- /dev/null +++ b/examples/html/magBalls/deleteMode.html @@ -0,0 +1,25 @@ + + + + + + + +
Delete
+ + \ No newline at end of file diff --git a/examples/html/magBalls/magBalls.css b/examples/html/magBalls/magBalls.css new file mode 100644 index 0000000000..339f094ea7 --- /dev/null +++ b/examples/html/magBalls/magBalls.css @@ -0,0 +1,14 @@ +.container { + display:table; + position:absolute; + width: 100%; + height: 100%; +} + +span { + font-size: 25vw; + display: table-cell; + vertical-align: middle; + line-height: normal; + text-align: center; +} \ No newline at end of file diff --git a/examples/html/magBalls/moveMode.html b/examples/html/magBalls/moveMode.html new file mode 100644 index 0000000000..eae53be635 --- /dev/null +++ b/examples/html/magBalls/moveMode.html @@ -0,0 +1,25 @@ + + + + + + + +
Move
+ + \ No newline at end of file diff --git a/examples/libraries/avatarRelativeOverlays.js b/examples/libraries/avatarRelativeOverlays.js new file mode 100644 index 0000000000..b12971037c --- /dev/null +++ b/examples/libraries/avatarRelativeOverlays.js @@ -0,0 +1,55 @@ + +AvatarRelativeOverlays = function() { + // id -> position & rotation + this.overlays = {}; + this.lastAvatarTransform = { + position: ZERO_VECTOR, + rotation: IDENTITY_QUATERNION, + }; +} + +// FIXME judder in movement is annoying.... add an option to +// automatically hide all overlays when the position or orientation change and then +// restore the ones that were previously visible once the movement stops. +AvatarRelativeOverlays.prototype.onUpdate = function(deltaTime) { + // cache avatar position and orientation and only update on change + if (Vec3.equal(this.lastAvatarTransform.position, MyAvatar.position) && + Quat.equal(this.lastAvatarTransform.rotation, MyAvatar.orientation)) { + return; + } + + this.lastAvatarTransform.position = MyAvatar.position; + this.lastAvatarTransform.rotation = MyAvatar.orientation; + for (var overlayId in this.overlays) { + this.updateOverlayTransform(overlayId); + } +} + +AvatarRelativeOverlays.prototype.updateOverlayTransform = function(overlayId) { + Overlays.editOverlay(overlayId, { + position: getEyeRelativePosition(this.overlays[overlayId].position), + rotation: getAvatarRelativeRotation(this.overlays[overlayId].rotation), + }) +} + +AvatarRelativeOverlays.prototype.addOverlay = function(type, overlayDefinition) { + var overlayId = Overlays.addOverlay(type, overlayDefinition); + if (!overlayId) { + logDebug("Failed to create overlay of type " + type); + return; + } + this.overlays[overlayId] = { + position: overlayDefinition.position || ZERO_VECTOR, + rotation: overlayDefinition.rotation || IDENTITY_QUATERNION, + }; + this.updateOverlayTransform(overlayId); + return overlayId; +} + +AvatarRelativeOverlays.prototype.deleteAll = function() { + for (var overlayId in this.overlays) { + Overlays.deleteOverlay(overlayId); + } + this.overlays = {}; +} + diff --git a/examples/libraries/constants.js b/examples/libraries/constants.js index 3ed7c02633..117e72267c 100644 --- a/examples/libraries/constants.js +++ b/examples/libraries/constants.js @@ -11,6 +11,8 @@ STICK_URL = HIFI_PUBLIC_BUCKET + "models/props/geo_stick.fbx"; ZERO_VECTOR = { x: 0, y: 0, z: 0 }; +IDENTITY_QUATERNION = { w: 1, x: 0, y: 0, z: 0 }; + COLORS = { WHITE: { red: 255, diff --git a/examples/libraries/highlighter.js b/examples/libraries/highlighter.js index b3550b6c8a..33aa445b88 100644 --- a/examples/libraries/highlighter.js +++ b/examples/libraries/highlighter.js @@ -29,7 +29,7 @@ var SELECTION_OVERLAY = { Highlighter = function() { this.highlightCube = Overlays.addOverlay("cube", this.SELECTION_OVERLAY); - this.hightlighted = null; + this.highlighted = null; var _this = this; Script.scriptEnding.connect(function() { _this.onCleanup(); @@ -40,9 +40,9 @@ Highlighter.prototype.onCleanup = function() { Overlays.deleteOverlay(this.highlightCube); } -Highlighter.prototype.highlight = function(entityId) { - if (entityId != this.hightlighted) { - this.hightlighted = entityId; +Highlighter.prototype.highlight = function(entityIdOrPosition) { + if (entityIdOrPosition != this.highlighted) { + this.highlighted = entityIdOrPosition; this.updateHighlight(); } } @@ -53,6 +53,13 @@ Highlighter.prototype.setSize = function(newSize) { }); } +Highlighter.prototype.setColor = function(color) { + Overlays.editOverlay(this.highlightCube, { + color: color + }); +} + + Highlighter.prototype.setRotation = function(newRotation) { Overlays.editOverlay(this.highlightCube, { rotation: newRotation @@ -60,11 +67,15 @@ Highlighter.prototype.setRotation = function(newRotation) { } Highlighter.prototype.updateHighlight = function() { - if (this.hightlighted) { - var properties = Entities.getEntityProperties(this.hightlighted); + if (this.highlighted) { + var position = this.highlighted; + if (typeof this.highlighted === "string") { + var properties = Entities.getEntityProperties(this.highlighted); + position = properties.position; + } // logDebug("Making highlight " + this.highlightCube + " visible @ " + vec3toStr(properties.position)); Overlays.editOverlay(this.highlightCube, { - position: properties.position, + position: position, visible: true }); } else { diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index 724f30c548..26c299cdfb 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -11,13 +11,14 @@ Script.include("utils.js"); Script.include("highlighter.js"); Script.include("omniTool/models/modelBase.js"); Script.include("omniTool/models/wand.js"); +Script.include("omniTool/models/invisibleWand.js"); OmniToolModules = {}; OmniToolModuleType = null; OmniTool = function(side) { this.OMNI_KEY = "OmniTool"; - this.MAX_FRAMERATE = 30; + this.MAX_FRAMERATE = 60; this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE this.SIDE = side; this.PALM = 2 * side; @@ -28,6 +29,7 @@ OmniTool = function(side) { this.ignoreEntities = {}; this.nearestOmniEntity = { id: null, + inside: false, position: null, distance: Infinity, @@ -38,13 +40,11 @@ OmniTool = function(side) { this.activeOmniEntityId = null; this.lastUpdateInterval = 0; - this.tipLength = 0.4; this.active = false; this.module = null; this.moduleEntityId = null; this.lastScanPosition = ZERO_VECTOR; - this.model = new Wand(); - this.model.setLength(this.tipLength); + this.showWand(false); // Connect to desired events var _this = this; @@ -65,6 +65,23 @@ OmniTool = function(side) { }); } +OmniTool.prototype.showWand = function(show) { + if (this.model && this.model.onCleanup) { + this.model.onCleanup(); + } + logDebug("Showing wand: " + show); + if (show) { + this.model = new Wand(); + this.model.setLength(0.4); + this.model.setVisible(true); + } else { + this.model = new InvisibleWand(); + this.model.setLength(0.1); + this.model.setVisible(true); + } +} + + OmniTool.prototype.onCleanup = function(action) { this.unloadModule(); } @@ -110,10 +127,9 @@ OmniTool.prototype.setActive = function(active) { if (active === this.active) { return; } - logDebug("omnitool changing active state: " + active); + logDebug("OmniTool changing active state: " + active); this.active = active; this.model.setVisible(this.active); - if (this.module && this.module.onActiveChanged) { this.module.onActiveChanged(this.side); } @@ -125,14 +141,25 @@ OmniTool.prototype.onUpdate = function(deltaTime) { this.position = Controller.getSpatialControlPosition(this.PALM); // When on the base, hydras report a position of 0 this.setActive(Vec3.length(this.position) > 0.001); + if (!this.active) { + return; + } + + + if (this.model) { + // Update the wand + var rawRotation = Controller.getSpatialControlRawRotation(this.PALM); + this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation); + this.model.setTransform({ + rotation: this.rotation, + position: this.position, + }); + + if (this.model.onUpdate) { + this.model.onUpdate(deltaTime); + } + } - var rawRotation = Controller.getSpatialControlRawRotation(this.PALM); - this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation); - - this.model.setTransform({ - rotation: this.rotation, - position: this.position, - }); this.scan(); @@ -144,6 +171,19 @@ OmniTool.prototype.onUpdate = function(deltaTime) { OmniTool.prototype.onClick = function() { // First check to see if the user is switching to a new omni module if (this.nearestOmniEntity.inside && this.nearestOmniEntity.omniProperties.script) { + + // If this is already the active entity, turn it off + // FIXME add a flag to allow omni modules to cause this entity to be + // ignored in order to support items that will be picked up. + if (this.moduleEntityId && this.moduleEntityId == this.nearestOmniEntity.id) { + this.showWand(false); + this.unloadModule(); + this.highlighter.setColor("White"); + return; + } + + this.showWand(true); + this.highlighter.setColor("Red"); this.activateNewOmniModule(); return; } @@ -157,12 +197,10 @@ OmniTool.prototype.onClick = function() { } OmniTool.prototype.onRelease = function() { - // FIXME how to I switch to a new module? if (this.module && this.module.onRelease) { this.module.onRelease(); return; } - logDebug("Base omnitool does nothing on release"); } // FIXME resturn a structure of all nearby entities to distances @@ -210,6 +248,11 @@ OmniTool.prototype.getPosition = function() { OmniTool.prototype.onEnterNearestOmniEntity = function() { this.nearestOmniEntity.inside = true; this.highlighter.highlight(this.nearestOmniEntity.id); + if (this.moduleEntityId && this.moduleEntityId == this.nearestOmniEntity.id) { + this.highlighter.setColor("Red"); + } else { + this.highlighter.setColor("White"); + } logDebug("On enter omniEntity " + this.nearestOmniEntity.id); } diff --git a/examples/libraries/omniTool/models/invisibleWand.js b/examples/libraries/omniTool/models/invisibleWand.js new file mode 100644 index 0000000000..63e0945af3 --- /dev/null +++ b/examples/libraries/omniTool/models/invisibleWand.js @@ -0,0 +1,6 @@ + +InvisibleWand = function() { +} + +InvisibleWand.prototype = Object.create( ModelBase.prototype ); + diff --git a/examples/libraries/omniTool/models/modelBase.js b/examples/libraries/omniTool/models/modelBase.js index 7697856d3f..87f93b1204 100644 --- a/examples/libraries/omniTool/models/modelBase.js +++ b/examples/libraries/omniTool/models/modelBase.js @@ -17,3 +17,9 @@ ModelBase.prototype.setTransform = function(transform) { this.tipVector = Vec3.multiplyQbyV(this.rotation, { x: 0, y: this.length, z: 0 }); this.tipPosition = Vec3.sum(this.position, this.tipVector); } + +ModelBase.prototype.setTipColors = function(color1, color2) { +} + +ModelBase.prototype.onCleanup = function() { +} diff --git a/examples/libraries/omniTool/models/wand.js b/examples/libraries/omniTool/models/wand.js index 8f0fe92b53..af28afb81c 100644 --- a/examples/libraries/omniTool/models/wand.js +++ b/examples/libraries/omniTool/models/wand.js @@ -1,22 +1,18 @@ Wand = function() { // Max updates fps - this.MAX_FRAMERATE = 30 - this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE this.DEFAULT_TIP_COLORS = [ { red: 128, green: 128, blue: 128, }, { - red: 64, - green: 64, - blue: 64, + red: 0, + green: 0, + blue: 0, }]; this.POINTER_ROTATION = Quat.fromPitchYawRollDegrees(45, 0, 45); - // FIXME does this need to be a member of this? - this.lastUpdateInterval = 0; - + this.pointers = [ Overlays.addOverlay("cube", { position: ZERO_VECTOR, @@ -45,17 +41,7 @@ Wand = function() { var _this = this; Script.scriptEnding.connect(function() { - Overlays.deleteOverlay(_this.pointers[0]); - Overlays.deleteOverlay(_this.pointers[1]); - Overlays.deleteOverlay(_this.wand); - }); - - Script.update.connect(function(deltaTime) { - _this.lastUpdateInterval += deltaTime; - if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) { - _this.onUpdate(_this.lastUpdateInterval); - _this.lastUpdateInterval = 0; - } + _this.onCleanup(); }); } @@ -76,7 +62,6 @@ Wand.prototype.setVisible = function(visible) { Wand.prototype.setTransform = function(transform) { ModelBase.prototype.setTransform.call(this, transform); - var wandPosition = Vec3.sum(this.position, Vec3.multiply(0.5, this.tipVector)); Overlays.editOverlay(this.pointers[0], { position: this.tipPosition, @@ -106,7 +91,10 @@ Wand.prototype.setTipColors = function(color1, color2) { } Wand.prototype.onUpdate = function(deltaTime) { + logDebug("Z4"); + if (this.visible) { + logDebug("5"); var time = new Date().getTime() / 250; var scale1 = Math.abs(Math.sin(time)); var scale2 = Math.abs(Math.cos(time)); @@ -118,3 +106,9 @@ Wand.prototype.onUpdate = function(deltaTime) { }); } } + +Wand.prototype.onCleanup = function() { + Overlays.deleteOverlay(this.pointers[0]); + Overlays.deleteOverlay(this.pointers[1]); + Overlays.deleteOverlay(this.wand); +} \ No newline at end of file diff --git a/examples/libraries/omniTool/modules/test.js b/examples/libraries/omniTool/modules/test.js index 1ca806affa..9f7191b2d0 100644 --- a/examples/libraries/omniTool/modules/test.js +++ b/examples/libraries/omniTool/modules/test.js @@ -1,9 +1,36 @@ -OmniToolModules.Test = function() { +Script.include("avatarRelativeOverlays.js"); + +OmniToolModules.Test = function(omniTool, activeEntityId) { + this.omniTool = omniTool; + this.activeEntityId = activeEntityId; + this.avatarOverlays = new AvatarRelativeOverlays(); } +OmniToolModules.Test.prototype.onUnload = function() { + if (this.testOverlay) { + Overlays.deleteOverlay(this.testOverlay); + this.testOverlay = 0; + } +} + +var CUBE_POSITION = { + x: 0.1, + y: -0.1, + z: -0.4 +}; + OmniToolModules.Test.prototype.onClick = function() { - logDebug("Test module onClick"); + if (this.testOverlay) { + Overlays.deleteOverlay(this.testOverlay); + this.testOverlay = 0; + } } + +OmniToolModules.Test.prototype.onUpdate = function(deltaTime) { + this.avatarOverlays.onUpdate(deltaTime); +} + + OmniToolModuleType = "Test" \ No newline at end of file diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 1c2816e7a0..c6143a51a8 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -11,6 +11,15 @@ vec3toStr = function (v, digits) { return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }"; } + +colorMix = function(colorA, colorB, mix) { + var result = {}; + for (var key in colorA) { + result[key] = (colorA[key] * (1 - mix)) + (colorB[key] * mix); + } + return result; +} + scaleLine = function (start, end, scale) { var v = Vec3.subtract(end, start); var length = Vec3.length(v); @@ -127,3 +136,13 @@ findSpherePointHit = function(sphereCenter, sphereRadius, point) { findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) { return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter); } + +// Given a vec3 v, return a vec3 that is the same vector relative to the avatars +// DEFAULT eye position, rotated into the avatars reference frame. +getEyeRelativePosition = function(v) { + return Vec3.sum(MyAvatar.getDefaultEyePosition(), Vec3.multiplyQbyV(MyAvatar.orientation, v)); +} + +getAvatarRelativeRotation = function(q) { + return Quat.multiply(MyAvatar.orientation, q); +} diff --git a/examples/toys/magBalls.js b/examples/toys/magBalls.js index 8e441901a2..7c11a94e06 100644 --- a/examples/toys/magBalls.js +++ b/examples/toys/magBalls.js @@ -11,21 +11,10 @@ Script.include("../toys/magBalls/constants.js"); Script.include("../toys/magBalls/graph.js"); Script.include("../toys/magBalls/edgeSpring.js"); Script.include("../toys/magBalls/magBalls.js"); +Script.include("avatarRelativeOverlays.js"); OmniToolModuleType = "MagBallsController" - -OmniToolModules.MagBallsController = function(omniTool, entityId) { - this.omniTool = omniTool; - this.entityId = entityId; - this.highlighter = new Highlighter(); - this.magBalls = new MagBalls(); - this.highlighter.setSize(BALL_SIZE); - this.ghostEdges = {}; -} - -var MAG_BALLS_DATA_NAME = "magBalls"; - getMagBallsData = function(id) { return getEntityCustomData(MAG_BALLS_DATA_NAME, id, {}); } @@ -34,54 +23,177 @@ setMagBallsData = function(id, value) { setEntityCustomData(MAG_BALLS_DATA_NAME, id, value); } -//var magBalls = new MagBalls(); -// DEBUGGING ONLY - Clear any previous balls -// magBalls.clear(); +var UI_BALL_RADIUS = 0.01; +var MODE_INFO = { }; -OmniToolModules.MagBallsController.prototype.onClick = function() { - logDebug("MagBallsController onClick: " + vec3toStr(this.tipPosition)); +MODE_INFO[BALL_EDIT_MODE_ADD] = { + uiPosition: { + x: 0.15, + y: -0.08, + z: -0.35, + }, + colors: [ COLORS.GREEN, COLORS.BLUE ], + // FIXME use an http path or find a way to get the relative path to the file + url: "file:///" + Script.resolvePath('../html/magBalls/addMode.html').replace("c:", "C:"), +}; + +MODE_INFO[BALL_EDIT_MODE_DELETE] = { + uiPosition: { + x: 0.20, + y: -0.08, + z: -0.32, + }, + colors: [ COLORS.RED, COLORS.BLUE ], + // FIXME use an http path or find a way to get the relative path to the file + url: "file:///" + Script.resolvePath('../html/magBalls/deleteMode.html').replace("c:", "C:"), +}; + + +var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5, + Vec3.sum(MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition, + MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition)); + +UI_POSITION_MODE_LABEL.y = -0.02; + +var UI_BALL_PROTOTYPE = { + size: UI_BALL_RADIUS * 2.0, + alpha: 1.0, + solid: true, + visible: true, +} + +OmniToolModules.MagBallsController = function(omniTool, entityId) { + this.omniTool = omniTool; + this.entityId = entityId; + + // In hold mode, holding a ball requires that you keep the action + // button pressed, while if this is false, clicking on a ball selects + // it and clicking again will drop it. + this.holdMode = true; + + this.highlighter = new Highlighter(); + this.magBalls = new MagBalls(); + this.highlighter.setSize(BALL_SIZE); + this.ghostEdges = {}; + this.selectionRadiusMultipler = 1.5; + this.uiOverlays = new AvatarRelativeOverlays(); - this.selected = this.highlighter.hightlighted; - logDebug("This selected: " + this.selected); - if (!this.selected) { - this.selected = this.magBalls.createBall(this.tipPosition); - } - this.magBalls.selectBall(this.selected); - this.highlighter.highlight(null); - logDebug("Selected " + this.selected); + + // create the overlay relative to the avatar + this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, { + color: MODE_INFO[BALL_EDIT_MODE_ADD].colors[0], + position: MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition, + })); + this.uiOverlays.addOverlay("sphere", mergeObjects(UI_BALL_PROTOTYPE, { + color: MODE_INFO[BALL_EDIT_MODE_DELETE].colors[0], + position: MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition, + })); + + // FIXME find the proper URLs to use + this.modeLabel = this.uiOverlays.addOverlay("web3d", { + isFacingAvatar: true, + alpha: 1.0, + dimensions: { x: 0.16, y: 0.12, z: 0.001}, + color: "White", + position: UI_POSITION_MODE_LABEL, + }); + + this.setMode(BALL_EDIT_MODE_ADD); + + // DEBUGGING ONLY - Fix old, bad edge bounding boxes + //for (var edgeId in this.magBalls.edges) { + // Entities.editEntity(edgeId, { + // dimensions: LINE_DIMENSIONS, + // }); + //} + // DEBUGGING ONLY - Clear any previous balls + // this.magBalls.clear(); + // DEBUGGING ONLY - Attempt to fix connections between balls + // and delete bad connections. Warning... if you haven't looked around + // and caused the domain server to send you all the nearby balls as well as the connections, + // this can break your structures + // this.magBalls.repair(); } -OmniToolModules.MagBallsController.prototype.onRelease = function() { - logDebug("MagBallsController onRelease: " + vec3toStr(this.tipPosition)); +OmniToolModules.MagBallsController.prototype.onUnload = function() { this.clearGhostEdges(); - if (this.selected) { - this.magBalls.releaseBall(this.selected); - this.selected = null; - } + this.uiOverlays.deleteAll(); } -OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) { - this.tipPosition = this.omniTool.getPosition(); - if (!this.selected) { - // Find the highlight target and set it. - var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS); - this.highlighter.highlight(target); - if (!target) { - this.magBalls.onUpdate(deltaTime); + +OmniToolModules.MagBallsController.prototype.setMode = function(mode) { + if (mode === this.mode) { + return; + } + + logDebug("Changing mode to '" + mode + "'"); + Overlays.editOverlay(this.modeLabel, { + url: MODE_INFO[mode].url + }); + + this.mode = mode; + var color1; + var color2; + switch (this.mode) { + case BALL_EDIT_MODE_ADD: + color1 = COLORS.BLUE; + color2 = COLORS.GREEN; + break; + + case BALL_EDIT_MODE_MOVE: + color1 = COLORS.GREEN; + color2 = COLORS.LIGHT_GREEN; + break; + + case BALL_EDIT_MODE_DELETE: + color1 = COLORS.RED; + color2 = COLORS.BLUE; + break; + + case BALL_EDIT_MODE_DELETE_SHAPE: + color1 = COLORS.RED; + color2 = COLORS.YELLOW; + break; + } + this.omniTool.model.setTipColors(color1, color2); + +} + +OmniToolModules.MagBallsController.prototype.findUiBallHit = function() { + var result = null; + for (var mode in MODE_INFO) { + var modeInfo = MODE_INFO[mode]; + var spherePoint = getEyeRelativePosition(modeInfo.uiPosition); + if (findSpherePointHit(spherePoint, UI_BALL_RADIUS * 2, this.tipPosition)) { + this.highlighter.highlight(spherePoint); + this.highlighter.setColor("White"); + // FIXME why doesn't this work? + this.highlighter.setSize(UI_BALL_RADIUS * 4); + return mode; } + } + return; +} + +OmniToolModules.MagBallsController.prototype.onUpdateSelected = function(deltaTime) { + if (!this.selected) { return; } - this.highlighter.highlight(null); Entities.editEntity(this.selected, { position: this.tipPosition }); var targetBalls = this.magBalls.findPotentialEdges(this.selected); for (var ballId in targetBalls) { + var targetPosition = this.magBalls.getNodePosition(ballId); + var distance = Vec3.distance(targetPosition, this.tipPosition); + var variance = this.magBalls.getVariance(distance); + var mix = Math.abs(variance) / this.magBalls.MAX_VARIANCE; + var color = colorMix(COLORS.YELLOW, COLORS.RED, mix); if (!this.ghostEdges[ballId]) { // create the ovleray this.ghostEdges[ballId] = Overlays.addOverlay("line3d", { start: this.magBalls.getNodePosition(ballId), end: this.tipPosition, - color: COLORS.RED, + color: color, alpha: 1, lineWidth: 5, visible: true, @@ -89,6 +201,7 @@ OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) { } else { Overlays.editOverlay(this.ghostEdges[ballId], { end: this.tipPosition, + color: color, }); } } @@ -100,6 +213,95 @@ OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) { } } +OmniToolModules.MagBallsController.prototype.onUpdate = function(deltaTime) { + this.tipPosition = this.omniTool.getPosition(); + this.uiOverlays.onUpdate(deltaTime); + + this.onUpdateSelected(); + + if (this.findUiBallHit()) { + return; + } + + if (!this.selected) { + // Find the highlight target and set it. + var target = this.magBalls.findNearestNode(this.tipPosition, BALL_RADIUS * this.selectionRadiusMultipler); + this.highlighter.highlight(target); + this.highlighter.setColor(MODE_INFO[this.mode].colors[0]); + if (!target) { + this.magBalls.onUpdate(deltaTime); + } + return; + } +} + +OmniToolModules.MagBallsController.prototype.deselect = function() { + if (!this.selected) { + return false + } + this.clearGhostEdges(); + this.magBalls.releaseBall(this.selected); + this.selected = null; + return true; +} + + +OmniToolModules.MagBallsController.prototype.onClick = function() { + var newMode = this.findUiBallHit(); + if (newMode) { + if (this.selected) { + this.magBalls.destroyNode(highlighted); + this.selected = null; + } + this.setMode(newMode); + return; + } + + if (this.deselect()) { + return; + } + + logDebug("MagBallsController onClick: " + vec3toStr(this.tipPosition)); + + // TODO add checking against UI shapes for adding or deleting balls. + var highlighted = this.highlighter.highlighted; + if (this.mode == BALL_EDIT_MODE_ADD && !highlighted) { + highlighted = this.magBalls.createBall(this.tipPosition); + } + + // Nothing to select or create means we're done here. + if (!highlighted) { + return; + } + + switch (this.mode) { + case BALL_EDIT_MODE_ADD: + case BALL_EDIT_MODE_MOVE: + this.magBalls.selectBall(highlighted); + this.selected = highlighted; + logDebug("Selected " + this.selected); + break; + + case BALL_EDIT_MODE_DELETE: + this.magBalls.destroyNode(highlighted); + break; + + case BALL_EDIT_MODE_DELETE_SHAPE: + logDebug("Not implemented yet"); + break; + } + + if (this.selected) { + this.highlighter.highlight(null); + } +} + +OmniToolModules.MagBallsController.prototype.onRelease = function() { + if (this.holdMode) { + this.deselect(); + } +} + OmniToolModules.MagBallsController.prototype.clearGhostEdges = function() { for(var ballId in this.ghostEdges) { Overlays.deleteOverlay(this.ghostEdges[ballId]); @@ -108,6 +310,3 @@ OmniToolModules.MagBallsController.prototype.clearGhostEdges = function() { } -BallController.prototype.onUnload = function() { - this.clearGhostEdges(); -} diff --git a/examples/toys/magBalls/constants.js b/examples/toys/magBalls/constants.js index b692f0908c..d9dee94329 100644 --- a/examples/toys/magBalls/constants.js +++ b/examples/toys/magBalls/constants.js @@ -1,8 +1,10 @@ +MAG_BALLS_DATA_NAME = "magBalls"; + // FIXME make this editable through some script UI, so the user can customize the size of the structure built -SCALE = 0.5; -BALL_SIZE = 0.08 * SCALE; -STICK_LENGTH = 0.24 * SCALE; +MAG_BALLS_SCALE = 0.5; +BALL_SIZE = 0.08 * MAG_BALLS_SCALE; +STICK_LENGTH = 0.24 * MAG_BALLS_SCALE; DEBUG_MAGSTICKS = true; @@ -11,8 +13,6 @@ EDGE_NAME = "MagStick"; BALL_RADIUS = BALL_SIZE / 2.0; -BALL_SELECTION_RADIUS = BALL_RADIUS * 1.5; - BALL_DIMENSIONS = { x: BALL_SIZE, y: BALL_SIZE, @@ -45,10 +45,13 @@ BALL_PROTOTYPE = { // 2 millimeters BALL_EPSILON = (.002) / BALL_DISTANCE; +// FIXME better handling of the line bounding box would require putting the +// origin in the middle of the line, not at one end +LINE_DIAGONAL = Math.sqrt((STICK_LENGTH * STICK_LENGTH * 2) * 3) * 1.1; LINE_DIMENSIONS = { - x: 5, - y: 5, - z: 5 + x: LINE_DIAGONAL, + y: LINE_DIAGONAL, + z: LINE_DIAGONAL } LINE_PROTOTYPE = { @@ -75,3 +78,9 @@ EDGE_PROTOTYPE = LINE_PROTOTYPE; // ignoreCollisions: true, // collisionsWillMove: false // } + + +BALL_EDIT_MODE_ADD = "add"; +BALL_EDIT_MODE_MOVE = "move"; +BALL_EDIT_MODE_DELETE = "delete"; +BALL_EDIT_MODE_DELETE_SHAPE = "deleteShape"; diff --git a/examples/toys/magBalls/debugUtils.js b/examples/toys/magBalls/debugUtils.js index 8dadd34679..e69de29bb2 100644 --- a/examples/toys/magBalls/debugUtils.js +++ b/examples/toys/magBalls/debugUtils.js @@ -1,95 +0,0 @@ -findMatchingNode = function(position, nodePositions) { - for (var nodeId in nodePositions) { - var nodePos = nodePositions[nodeId]; - var distance = Vec3.distance(position, nodePos); - if (distance < 0.03) { - return nodeId; - } - } -} - -repairConnections = function() { - var ids = Entities.findEntities(MyAvatar.position, 50); - - // Find all the balls and record their positions - var nodePositions = {}; - for (var i in ids) { - var id = ids[i]; - var properties = Entities.getEntityProperties(id); - if (properties.name == BALL_NAME) { - nodePositions[id] = properties.position; - } - } - - // Now check all the edges to see if they're valid (point to balls) - // and ensure that the balls point back to them - var ballsToEdges = {}; - for (var i in ids) { - var id = ids[i]; - var properties = Entities.getEntityProperties(id); - if (properties.name == EDGE_NAME) { - var startPos = properties.position; - var endPos = Vec3.sum(startPos, properties.linePoints[1]); - var magBallData = getMagBallsData(id); - var update = false; - if (!magBallData.start) { - var startNode = findMatchingNode(startPos, nodePositions); - if (startNode) { - logDebug("Found start node " + startNode) - magBallData.start = startNode; - update = true; - } - } - if (!magBallData.end) { - var endNode = findMatchingNode(endPos, nodePositions); - if (endNode) { - logDebug("Found end node " + endNode) - magBallData.end = endNode; - update = true; - } - } - if (!magBallData.start || !magBallData.end) { - logDebug("Didn't find both ends"); - Entities.deleteEntity(id); - continue; - } - if (!ballsToEdges[magBallData.start]) { - ballsToEdges[magBallData.start] = [ id ]; - } else { - ballsToEdges[magBallData.start].push(id); - } - if (!ballsToEdges[magBallData.end]) { - ballsToEdges[magBallData.end] = [ id ]; - } else { - ballsToEdges[magBallData.end].push(id); - } - if (update) { - logDebug("Updating incomplete edge " + id); - magBallData.length = BALL_DISTANCE; - setMagBallsData(id, magBallData); - } - } - } - for (var nodeId in ballsToEdges) { - var magBallData = getMagBallsData(nodeId); - var edges = magBallData.edges || []; - var edgeHash = {}; - for (var i in edges) { - edgeHash[edges[i]] = true; - } - var update = false; - for (var i in ballsToEdges[nodeId]) { - var edgeId = ballsToEdges[nodeId][i]; - if (!edgeHash[edgeId]) { - update = true; - edgeHash[edgeId] = true; - edges.push(edgeId); - } - } - if (update) { - logDebug("Fixing node with missing edge data"); - magBallData.edges = edges; - setMagBallsData(nodeId, magBallData); - } - } -} diff --git a/examples/toys/magBalls/edgeSpring.js b/examples/toys/magBalls/edgeSpring.js index 852c9257c2..e1b717c39b 100644 --- a/examples/toys/magBalls/edgeSpring.js +++ b/examples/toys/magBalls/edgeSpring.js @@ -9,7 +9,10 @@ EdgeSpring = function(edgeId, graph) { this.desiredLength = magBallsData.length || BALL_DISTANCE; } -EdgeSpring.prototype.adjust = function(results) { +// FIXME as iterations increase, start introducing some randomness +// to the adjustment so that we avoid false equilibriums +// Alternatively, larger iterations could increase the acceptable variance +EdgeSpring.prototype.adjust = function(results, iterations) { var startPos = this.getAdjustedPosition(this.start, results); var endPos = this.getAdjustedPosition(this.end, results); var vector = Vec3.subtract(endPos, startPos); diff --git a/examples/toys/magBalls/magBalls.js b/examples/toys/magBalls/magBalls.js index 307be9f5e1..b689fb1272 100644 --- a/examples/toys/magBalls/magBalls.js +++ b/examples/toys/magBalls/magBalls.js @@ -25,10 +25,6 @@ MagBalls = function() { this.refresh(); var _this = this; - //Script.update.connect(function(deltaTime) { - // _this.onUpdate(deltaTime); - //}); - Script.scriptEnding.connect(function() { _this.onCleanup(); }); @@ -61,8 +57,13 @@ MagBalls.prototype.onUpdate = function(deltaTime) { if (!this.unstableEdges[edgeId]) { continue; } - adjusted |= this.edgeObjects[edgeId].adjust(nodeAdjustResults); + // FIXME need to add some randomness to this so that objects don't hit a + // false equilibrium + // FIXME should this be done node-wise, to more easily account for the number of edge + // connections for a node? + adjusted |= this.edgeObjects[edgeId].adjust(nodeAdjustResults, this.adjustIterations); } + for (var nodeId in nodeAdjustResults) { var curPos = this.getNodePosition(nodeId); var newPos = nodeAdjustResults[nodeId]; @@ -71,18 +72,24 @@ MagBalls.prototype.onUpdate = function(deltaTime) { fixupEdges[edgeId] = true; } // logDebug("Moving node Id " + nodeId + " " + (distance * 1000).toFixed(3) + " mm"); - Entities.editEntity(nodeId, { position: newPos, color: COLORS.RED }); + Entities.editEntity(nodeId, { + position: newPos, + // DEBUGGING, flashes moved balls + // color: COLORS.RED + }); } + // DEBUGGING, flashes moved balls + //Script.setTimeout(function(){ + // for (var nodeId in nodeAdjustResults) { + // Entities.editEntity(nodeId, { color: BALL_COLOR }); + // } + //}, ((UPDATE_INTERVAL * 1000) / 2)); + for (var edgeId in fixupEdges) { this.fixupEdge(edgeId); } - - Script.setTimeout(function(){ - for (var nodeId in nodeAdjustResults) { - Entities.editEntity(nodeId, { color: BALL_COLOR }); - } - }, ((UPDATE_INTERVAL * 1000) / 2)); + if (!adjusted || this.adjustIterations > this.MAX_ADJUST_ITERATIONS) { if (adjusted) { @@ -357,4 +364,96 @@ MagBalls.prototype.onEntityAdded = function(entityId) { if (properties.name == BALL_NAME || properties.name == EDGE_NAME) { this.refreshNeeded = 1; } -} \ No newline at end of file +} + +findMatchingNode = function(position, nodePositions) { + for (var nodeId in nodePositions) { + var nodePos = nodePositions[nodeId]; + var distance = Vec3.distance(position, nodePos); + if (distance < 0.03) { + return nodeId; + } + } +} + + +MagBalls.prototype.repair = function() { + // Find all the balls and record their positions + var nodePositions = {}; + for (var nodeId in this.nodes) { + nodePositions[nodeId] = this.getNodePosition(nodeId); + } + + // Now check all the edges to see if they're valid (point to balls) + // and ensure that the balls point back to them + var ballsToEdges = {}; + + // WARNING O(n^2) algorithm, every edge that is broken does + // an O(N) search against the nodes + for (var edgeId in this.edges) { + var properties = Entities.getEntityProperties(edgeId); + var startPos = properties.position; + var endPos = Vec3.sum(startPos, properties.linePoints[1]); + var magBallData = getMagBallsData(edgeId); + var update = false; + if (!magBallData.start) { + var startNode = findMatchingNode(startPos, nodePositions); + if (startNode) { + logDebug("Found start node " + startNode) + magBallData.start = startNode; + update = true; + } + } + if (!magBallData.end) { + var endNode = findMatchingNode(endPos, nodePositions); + if (endNode) { + logDebug("Found end node " + endNode) + magBallData.end = endNode; + update = true; + } + } + if (!magBallData.start || !magBallData.end) { + logDebug("Didn't find both ends"); + this.destroyEdge(edgeId); + continue; + } + if (!ballsToEdges[magBallData.start]) { + ballsToEdges[magBallData.start] = [ edgeId ]; + } else { + ballsToEdges[magBallData.start].push(edgeId); + } + if (!ballsToEdges[magBallData.end]) { + ballsToEdges[magBallData.end] = [ edgeId ]; + } else { + ballsToEdges[magBallData.end].push(edgeId); + } + if (update) { + logDebug("Updating incomplete edge " + edgeId); + magBallData.length = BALL_DISTANCE; + setMagBallsData(edgeId, magBallData); + } + } + for (var nodeId in ballsToEdges) { + var magBallData = getMagBallsData(nodeId); + var edges = magBallData.edges || []; + var edgeHash = {}; + for (var i in edges) { + edgeHash[edges[i]] = true; + } + var update = false; + for (var i in ballsToEdges[nodeId]) { + var edgeId = ballsToEdges[nodeId][i]; + if (!edgeHash[edgeId]) { + update = true; + edgeHash[edgeId] = true; + edges.push(edgeId); + } + } + if (update) { + logDebug("Fixing node with missing edge data"); + magBallData.edges = edges; + setMagBallsData(nodeId, magBallData); + } + } +} + diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index c173c5927f..f14fcf4dc0 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -147,7 +147,12 @@ QScriptValue Web3DOverlay::getProperty(const QString& property) { void Web3DOverlay::setURL(const QString& url) { _url = url; - _isLoaded = false; + if (_webSurface) { + AbstractViewStateInterface::instance()->postLambdaEvent([this, url] { + _webSurface->getRootItem()->setProperty("url", url); + }); + } + } bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) { diff --git a/libraries/render-utils/src/simple.slv b/libraries/render-utils/src/simple.slv index 99f404eaec..b22bb36d83 100644 --- a/libraries/render-utils/src/simple.slv +++ b/libraries/render-utils/src/simple.slv @@ -23,10 +23,12 @@ out vec3 _normal; out vec3 _color; out vec2 _texCoord0; +out vec4 _position; void main(void) { _color = inColor.rgb; _texCoord0 = inTexCoord0.st; + _position = inPosition; // standard transform TransformCamera cam = getTransformCamera(); From 4a28361e058ed2ef6f827111b179bf7be41710ae Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 3 Sep 2015 22:12:21 -0700 Subject: [PATCH 111/117] Fix mirror to display on Oculus --- .../oculus/OculusDisplayPlugin.cpp | 52 +++++-------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 2ed5e69fe7..c8d9993e5a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -310,8 +310,6 @@ void OculusDisplayPlugin::activate() { // not needed since the structure was zeroed on init, but explicit sceneLayer.ColorTexture[1] = nullptr; - PerformanceTimer::setActive(true); - if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { qFatal("Could not attach to sensor device"); @@ -322,15 +320,12 @@ void OculusDisplayPlugin::activate() { void OculusDisplayPlugin::customizeContext() { WindowOpenGLDisplayPlugin::customizeContext(); #if (OVR_MAJOR_VERSION >= 6) - //_texture = DependencyManager::get()-> - // getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png"); - uvec2 mirrorSize = toGlm(_window->geometry().size()); - _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd)); _sceneFbo->Init(getRecommendedRenderSize()); #endif enableVsync(false); - isVsyncEnabled(); + // Only enable mirroring if we know vsync is disabled + _enableMirror = !isVsyncEnabled(); } void OculusDisplayPlugin::deactivate() { @@ -338,7 +333,6 @@ void OculusDisplayPlugin::deactivate() { makeCurrent(); _sceneFbo.reset(); doneCurrent(); - PerformanceTimer::setActive(false); WindowOpenGLDisplayPlugin::deactivate(); @@ -355,6 +349,16 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi // controlling vsync wglSwapIntervalEXT(0); + // screen mirroring + if (_enableMirror) { + auto windowSize = toGlm(_window->size()); + Context::Viewport(windowSize.x, windowSize.y); + glBindTexture(GL_TEXTURE_2D, finalTexture); + GLenum err = glGetError(); + Q_ASSERT(0 == err); + drawUnitQuad(); + } + _sceneFbo->Bound([&] { auto size = _sceneFbo->size; Context::Viewport(size.x, size.y); @@ -369,25 +373,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi }); auto windowSize = toGlm(_window->size()); - - /* - Two alternatives for mirroring to the screen, the first is to copy our own composited - scene to the window framebuffer, before distortion. Note this only works if we're doing - ui compositing ourselves, and not relying on the Oculus SDK compositor (or we don't want - the UI visible in the output window (unlikely). This should be done before - _sceneFbo->Increment or we're be using the wrong texture - */ - if (_enableMirror) { - _sceneFbo->Bound(Framebuffer::Target::Read, [&] { - glBlitFramebuffer( - 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, - 0, 0, windowSize.x, windowSize.y, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - }); - } - { - PerformanceTimer("OculusSubmit"); ovrViewScaleDesc viewScaleDesc; viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0]; @@ -401,20 +387,6 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi } _sceneFbo->Increment(); - /* - The other alternative for mirroring is to use the Oculus mirror texture support, which - will contain the post-distorted and fully composited scene regardless of how many layers - we send. - Currently generates an error. - */ - //auto mirrorSize = _mirrorFbo->size; - //_mirrorFbo->Bound(Framebuffer::Target::Read, [&] { - // Context::BlitFramebuffer( - // 0, mirrorSize.y, mirrorSize.x, 0, - // 0, 0, windowSize.x, windowSize.y, - // BufferSelectBit::ColorBuffer, BlitFilter::Nearest); - //}); - ++_frameIndex; #endif } From 6b2897ca8fe233b8c725f24105d9c2eba5e34801 Mon Sep 17 00:00:00 2001 From: Raffi Bedikian Date: Thu, 3 Sep 2015 23:11:53 -0700 Subject: [PATCH 112/117] Fix AA bugs with display plugins and mini-mirror --- libraries/render-utils/src/AntialiasingEffect.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 283561fc57..0e362bfb49 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -61,6 +61,13 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { // Good to go add the brand new pipeline _antialiasingPipeline.reset(gpu::Pipeline::create(program, state)); } + + int w = DependencyManager::get()->getFrameBufferSize().width(); + int h = DependencyManager::get()->getFrameBufferSize().height(); + if (w != _antialiasingBuffer->getWidth() || h != _antialiasingBuffer->getHeight()) { + _antialiasingBuffer->resize(w, h); + } + return _antialiasingPipeline; } @@ -89,7 +96,14 @@ void Antialiasing::run(const render::SceneContextPointer& sceneContext, const re assert(renderContext->args); assert(renderContext->args->_viewFrustum); + if (renderContext->args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + return; + } + gpu::Batch batch; + + batch.enableStereo(false); + RenderArgs* args = renderContext->args; auto framebufferCache = DependencyManager::get(); From 2f2558724f9e91918e5077b8dfa08b057a83fffe Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 4 Sep 2015 09:28:28 -0600 Subject: [PATCH 113/117] Including Visual Studio 2010 Redistributable in installer --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a4d193324d..a2eb058ae6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -High Fidelity (hifi) is an early-stage technology -lab experimenting with Virtual Worlds and VR. +High Fidelity (hifi) is an early-stage technology lab experimenting with Virtual Worlds and VR. In this repository you'll find the source to many of the components in our alpha-stage virtual world. The project embraces distributed development From 895028134b736f061ddf32e234cf345767ddc59c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 4 Sep 2015 09:40:09 -0700 Subject: [PATCH 114/117] add breakdance game as entity script that works with hydra grab --- examples/entityScripts/breakdanceEntity.js | 88 +++++++++++++++++++++ examples/entityScripts/detectGrabExample.js | 81 +++++++++++++++++++ examples/toys/breakdanceCore.js | 4 +- examples/toys/breakdanceToy.js | 1 + 4 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 examples/entityScripts/breakdanceEntity.js create mode 100644 examples/entityScripts/detectGrabExample.js diff --git a/examples/entityScripts/breakdanceEntity.js b/examples/entityScripts/breakdanceEntity.js new file mode 100644 index 0000000000..77980c0d13 --- /dev/null +++ b/examples/entityScripts/breakdanceEntity.js @@ -0,0 +1,88 @@ +// +// breakdanceEntity.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 9/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, will start the breakdance game if you grab and hold the entity +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../toys/breakdanceCore.js"); + Script.include("../libraries/utils.js"); + + var _this; + + // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember + // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) + BreakdanceEntity = function() { + _this = this; + print("BreakdanceEntity constructor"); + }; + + BreakdanceEntity.prototype = { + + // update() will be called regulary, because we've hooked the update signal in our preload() function + // we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us + // if we're currently being grabbed and if the person grabbing us is the current interfaces avatar. + // we will watch this for state changes and print out if we're being grabbed or released when it changes. + update: function() { + var GRAB_USER_DATA_KEY = "grabKey"; + + // because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID + var entityID = _this.entityID; + + // we want to assume that if there is no grab data, then we are not being grabbed + var defaultGrabData = { activated: false, avatarId: null }; + + // this handy function getEntityCustomData() is available in utils.js and it will return just the specific section + // of user data we asked for. If it's not available it returns our default data. + var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData); + + // if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface + if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) { + + if (!_this.beingGrabbed) { + // remember we're being grabbed so we can detect being released + _this.beingGrabbed = true; + breakdanceStart(); + print("I'm was grabbed..."); + } else { + breakdanceUpdate(); + } + + } else if (_this.beingGrabbed) { + + // if we are not being grabbed, and we previously were, then we were just released, remember that + // and print out a message + _this.beingGrabbed = false; + print("I'm was released..."); + breakdanceEnd(); + } + }, + + // preload() will be called when the entity has become visible (or known) to the interface + // it gives us a chance to set our local JavaScript object up. In this case it means: + // * remembering our entityID, so we can access it in cases where we're called without an entityID + // * connecting to the update signal so we can check our grabbed state + preload: function(entityID) { + this.entityID = entityID; + Script.update.connect(this.update); + }, + + // unload() will be called when our entity is no longer available. It may be because we were deleted, + // or because we've left the domain or quit the application. In all cases we want to unhook our connection + // to the update signal + unload: function(entityID) { + Script.update.disconnect(this.update); + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new BreakdanceEntity(); +}) diff --git a/examples/entityScripts/detectGrabExample.js b/examples/entityScripts/detectGrabExample.js new file mode 100644 index 0000000000..cdc79e119d --- /dev/null +++ b/examples/entityScripts/detectGrabExample.js @@ -0,0 +1,81 @@ +// +// detectGrabExample.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 9/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, will detect when the entity is being grabbed by the hydraGrab script +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../libraries/utils.js"); + + var _this; + + // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember + // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) + DetectGrabbed = function() { + _this = this; + }; + + DetectGrabbed.prototype = { + + // update() will be called regulary, because we've hooked the update signal in our preload() function + // we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us + // if we're currently being grabbed and if the person grabbing us is the current interfaces avatar. + // we will watch this for state changes and print out if we're being grabbed or released when it changes. + update: function() { + var GRAB_USER_DATA_KEY = "grabKey"; + + // because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID + var entityID = _this.entityID; + + // we want to assume that if there is no grab data, then we are not being grabbed + var defaultGrabData = { activated: false, avatarId: null }; + + // this handy function getEntityCustomData() is available in utils.js and it will return just the specific section + // of user data we asked for. If it's not available it returns our default data. + var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData); + + // if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface + if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) { + + // remember we're being grabbed so we can detect being released + _this.beingGrabbed = true; + + // print out that we're being grabbed + print("I'm being grabbed..."); + + } else if (_this.beingGrabbed) { + + // if we are not being grabbed, and we previously were, then we were just released, remember that + // and print out a message + _this.beingGrabbed = false; + print("I'm was released..."); + } + }, + + // preload() will be called when the entity has become visible (or known) to the interface + // it gives us a chance to set our local JavaScript object up. In this case it means: + // * remembering our entityID, so we can access it in cases where we're called without an entityID + // * connecting to the update signal so we can check our grabbed state + preload: function(entityID) { + this.entityID = entityID; + Script.update.connect(this.update); + }, + + // unload() will be called when our entity is no longer available. It may be because we were deleted, + // or because we've left the domain or quit the application. In all cases we want to unhook our connection + // to the update signal + unload: function(entityID) { + Script.update.disconnect(this.update); + }, + }; + + // entity scripts always need to return a newly constructed object of our type + return new DetectGrabbed(); +}) diff --git a/examples/toys/breakdanceCore.js b/examples/toys/breakdanceCore.js index 0f346537eb..9bf63850ab 100644 --- a/examples/toys/breakdanceCore.js +++ b/examples/toys/breakdanceCore.js @@ -11,8 +11,6 @@ // -Script.include("../libraries/utils.js"); - function getPositionPuppet() { var DISTANCE_IN_FRONT = 2; var DISTANCE_UP = 0.4; @@ -513,7 +511,7 @@ breakdanceUpdate = function(deltaTime) { } else { Overlays.editOverlay(textOverlay, { text: "pose:" + poses[poseValue].name + "\n" + "animation:" + poses[poseValue].animation }); var props = Entities.getEntityProperties(puppetEntityID); - print("puppetEntityID:" + puppetEntityID + "age:"+props.age); + //print("puppetEntityID:" + puppetEntityID + "age:"+props.age); Entities.editEntity(puppetEntityID, { animationURL: poses[poseValue].animation, lifetime: TEMPORARY_LIFETIME + props.age // renew lifetime diff --git a/examples/toys/breakdanceToy.js b/examples/toys/breakdanceToy.js index 841d5ad111..0190ab74e9 100644 --- a/examples/toys/breakdanceToy.js +++ b/examples/toys/breakdanceToy.js @@ -11,6 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.include("../libraries/utils.js"); Script.include("breakdanceCore.js"); breakdanceStart(); Script.update.connect(breakdanceUpdate); From 1b92ae95d7428bc7e4655aea4acf71fbdb2a6cf4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 4 Sep 2015 09:41:53 -0700 Subject: [PATCH 115/117] put glm:: before vec3 --- libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 43f97c23ee..5ddee32f88 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -384,7 +384,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o return false; } - glm::vec3 result3 = vec3(result); + glm::vec3 result3 = glm::vec3(result); AABox voxelBox; voxelBox += result3 - Vectors::HALF; From 80f2ecbab6cad9310523d5842b681676132d37c7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 4 Sep 2015 09:59:42 -0700 Subject: [PATCH 116/117] Cleaning up some debugging spew and fixing UI HTML paths --- examples/libraries/omniTool/models/wand.js | 3 --- examples/toys/magBalls.js | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/libraries/omniTool/models/wand.js b/examples/libraries/omniTool/models/wand.js index af28afb81c..e14c90b36a 100644 --- a/examples/libraries/omniTool/models/wand.js +++ b/examples/libraries/omniTool/models/wand.js @@ -91,10 +91,7 @@ Wand.prototype.setTipColors = function(color1, color2) { } Wand.prototype.onUpdate = function(deltaTime) { - logDebug("Z4"); - if (this.visible) { - logDebug("5"); var time = new Date().getTime() / 250; var scale1 = Math.abs(Math.sin(time)); var scale2 = Math.abs(Math.cos(time)); diff --git a/examples/toys/magBalls.js b/examples/toys/magBalls.js index 7c11a94e06..e163aa5ffd 100644 --- a/examples/toys/magBalls.js +++ b/examples/toys/magBalls.js @@ -34,7 +34,7 @@ MODE_INFO[BALL_EDIT_MODE_ADD] = { }, colors: [ COLORS.GREEN, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file - url: "file:///" + Script.resolvePath('../html/magBalls/addMode.html').replace("c:", "C:"), + url: Script.resolvePath('../html/magBalls/addMode.html'), }; MODE_INFO[BALL_EDIT_MODE_DELETE] = { @@ -45,10 +45,9 @@ MODE_INFO[BALL_EDIT_MODE_DELETE] = { }, colors: [ COLORS.RED, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file - url: "file:///" + Script.resolvePath('../html/magBalls/deleteMode.html').replace("c:", "C:"), + url: Script.resolvePath('../html/magBalls/deleteMode.html'), }; - var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5, Vec3.sum(MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition, MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition)); From 3f258c89b42ea64722506f46bbe629b09878d375 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 4 Sep 2015 11:03:59 -0700 Subject: [PATCH 117/117] Fix transition from AG+Rig to AG only. --- libraries/animation/src/Rig.cpp | 1 - libraries/render-utils/src/Model.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9f22a54c29..f2ea922ab7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -187,7 +187,6 @@ void Rig::deleteAnimations() { removeAnimationHandle(animation); } _animationHandles.clear(); - destroyAnimGraph(); } void Rig::destroyAnimGraph() { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 14b1c58321..3f0516b415 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1419,6 +1419,7 @@ void Model::deleteGeometry() { _rig->clearJointStates(); _meshStates.clear(); _rig->deleteAnimations(); + _rig->destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); }