From 340096d45735da353de600f082ba8cd906eb08cb Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 1 Nov 2015 15:16:00 -0800 Subject: [PATCH 01/86] Initial version of AnimExpression class with minimal tokenizer --- libraries/animation/src/AnimExpression.cpp | 228 +++++++++++++++++++++ libraries/animation/src/AnimExpression.h | 78 +++++++ tests/animation/src/AnimTests.cpp | 53 ++++- tests/animation/src/AnimTests.h | 1 + 4 files changed, 354 insertions(+), 6 deletions(-) create mode 100644 libraries/animation/src/AnimExpression.cpp create mode 100644 libraries/animation/src/AnimExpression.h diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp new file mode 100644 index 0000000000..f2ec0426be --- /dev/null +++ b/libraries/animation/src/AnimExpression.cpp @@ -0,0 +1,228 @@ +// +// AnimExpression.cpp +// +// Created by Anthony J. Thibault on 11/1/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 +// + +#include +#include + +#include "AnimExpression.h" +#include "AnimationLogging.h" + + +#ifndef NDEBUG +void AnimExpression::Token::dump() { + switch (type) { + case End: + qCDebug(animation) << " End"; + break; + case Identifier: + qCDebug(animation) << " Identifer =" << strVal; + break; + case LiteralInt: + qCDebug(animation) << " LiteralInt =" << intVal; + break; + case LiteralFloat: + qCDebug(animation) << " LiteralFloat" << floatVal; + break; + case LiteralVec3: + qCDebug(animation) << " LiteralVec3" << vec3Val; + break; + case LiteralVec4: + qCDebug(animation) << " LiteralVec4" << vec4Val; + break; + case LiteralQuat: + qCDebug(animation) << " LiteralQuat" << quatVal; + break; + case And: + qCDebug(animation) << " And"; + break; + case Or: + qCDebug(animation) << " Or"; + break; + case GreaterThan: + qCDebug(animation) << " GreaterThan"; + break; + case GreaterThanEqual: + qCDebug(animation) << " GreaterThanEqual"; + break; + case LessThan: + qCDebug(animation) << " LessThan"; + break; + case LessThanEqual: + qCDebug(animation) << " LessThanEqual"; + break; + case Equal: + qCDebug(animation) << " Equal"; + break; + case NotEqual: + qCDebug(animation) << " NotEqual"; + break; + case LeftParen: + qCDebug(animation) << " LeftParen"; + break; + case RightParen: + qCDebug(animation) << " RightParen"; + break; + case Not: + qCDebug(animation) << " Not"; + break; + case Minus: + qCDebug(animation) << " Minus"; + break; + case Plus: + qCDebug(animation) << " Plus"; + break; + case Multiply: + qCDebug(animation) << " Multiply"; + break; + case Modulus: + qCDebug(animation) << " Modulus"; + break; + case Error: + qCDebug(animation) << " Error"; + break; + } +} +#endif + +AnimExpression::AnimExpression(const QString& str) : + _expression(str) { + parseExpression(_expression); +} + +bool AnimExpression::parseExpression(const QString& str) { + Token token(Token::End); + auto iter = str.begin(); + do { + token = consumeToken(str, iter); + switch(token.type) { + case Token::Error: + case Token::End: + return false; + } + } while(iter != str.end()); +} + +AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::const_iterator& iter) const { + while (iter != str.end()) { + if (iter->isSpace()) { + ++iter; + } else if (iter->isLetter()) { + return consumeIdentifier(str, iter); + } else if (iter->isDigit()) { + return consumeNumber(str, iter); + } else { + switch (iter->unicode()) { + case '&': return consumeAnd(str, iter); + case '|': return consumeOr(str, iter); + case '>': return consumeGreaterThan(str, iter); + case '<': return consumeLessThan(str, iter); + case '(': ++iter; return Token(Token::LeftParen); + case ')': ++iter; return Token(Token::RightParen); + case '!': return consumeNot(str, iter); + case '-': ++iter; return Token(Token::Minus); + case '+': ++iter; return Token(Token::Plus); + case '*': ++iter; return Token(Token::Multiply); + case '%': ++iter; return Token(Token::Modulus); + default: + qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); + return Token(Token::Error); + } + } + } + return Token(Token::End); +} + +AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->isLetter()); + auto begin = iter; + while (iter->isLetter() && iter != str.end()) { + ++iter; + } + int pos = (int)(begin - str.begin()); + int len = (int)(iter - begin); + return Token(QStringRef(const_cast(&str), pos, len)); +} + +AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->isDigit()); + auto begin = iter; + while (iter->isDigit() && iter != str.end()) { + ++iter; + } + int pos = (int)(begin - str.begin()); + int len = (int)(iter - begin); + QString sub = QStringRef(const_cast(&str), pos, len).toString(); + return Token(sub.toInt()); +} + +AnimExpression::Token AnimExpression::consumeAnd(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '&'); + iter++; + if (iter->unicode() == '&') { + iter++; + return Token(Token::And); + } else { + qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); + return Token(Token::Error); + } +} + +AnimExpression::Token AnimExpression::consumeOr(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '|'); + iter++; + if (iter->unicode() == '|') { + iter++; + return Token(Token::Or); + } else { + qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); + return Token(Token::Error); + } +} + +AnimExpression::Token AnimExpression::consumeGreaterThan(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '>'); + iter++; + if (iter->unicode() == '=') { + iter++; + return Token(Token::GreaterThanEqual); + } else { + return Token(Token::GreaterThan); + } +} + +AnimExpression::Token AnimExpression::consumeLessThan(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '<'); + iter++; + if (iter->unicode() == '=') { + iter++; + return Token(Token::LessThanEqual); + } else { + return Token(Token::LessThan); + } +} + +AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::const_iterator& iter) const { + assert(iter != str.end()); + assert(iter->unicode() == '!'); + iter++; + if (iter->unicode() == '=') { + iter++; + return Token(Token::NotEqual); + } else { + return Token(Token::Not); + } +} + diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h new file mode 100644 index 0000000000..a69b13c286 --- /dev/null +++ b/libraries/animation/src/AnimExpression.h @@ -0,0 +1,78 @@ +// +// AnimExpression.h +// +// Created by Anthony J. Thibault on 11/1/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 +// + +#ifndef hifi_AnimExpression +#define hifi_AnimExpression + +#include +#include +#include +#include + +class AnimExpression { +public: + friend class AnimTests; + AnimExpression(const QString& str); +protected: + struct Token { + enum Type { + End = 0, + Identifier, + LiteralInt, + LiteralFloat, + LiteralVec3, + LiteralVec4, + LiteralQuat, + And, + Or, + GreaterThan, + GreaterThanEqual, + LessThan, + LessThanEqual, + Equal, + NotEqual, + LeftParen, + RightParen, + Not, + Minus, + Plus, + Multiply, + Modulus, + Error + }; + Token(Type type) : type(type) {} + Token(const QStringRef& strRef) : type(Type::Identifier), strVal(strRef.toString()) {} + Token(int val) : type(Type::LiteralInt), intVal(val) {} + Type type = End; + QString strVal; + int intVal; + float floatVal; + glm::vec3 vec3Val; + glm::vec4 vec4Val; + glm::quat quatVal; +#ifndef NDEBUG + void dump(); +#endif + }; + bool parseExpression(const QString& str); + Token consumeToken(const QString& str, QString::const_iterator& iter) const; + Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const; + Token consumeNumber(const QString& str, QString::const_iterator& iter) const; + Token consumeAnd(const QString& str, QString::const_iterator& iter) const; + Token consumeOr(const QString& str, QString::const_iterator& iter) const; + Token consumeGreaterThan(const QString& str, QString::const_iterator& iter) const; + Token consumeLessThan(const QString& str, QString::const_iterator& iter) const; + Token consumeNot(const QString& str, QString::const_iterator& iter) const; + + QString _expression; +}; + +#endif + diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 1b5bb4739a..08f13b7ec7 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -8,12 +8,13 @@ // #include "AnimTests.h" -#include "AnimNodeLoader.h" -#include "AnimClip.h" -#include "AnimBlendLinear.h" -#include "AnimationLogging.h" -#include "AnimVariant.h" -#include "AnimUtil.h" +#include +#include +#include +#include +#include +#include +#include #include <../QTestExtensions.h> @@ -322,4 +323,44 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram QVERIFY(resultFrame == startFrame + 0.5f); QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); triggers.clear(); + +void AnimTests::testTokenizer() { + QString str = "(10 + x) >= 20 && (y != !z)"; + AnimExpression e(""); + auto iter = str.cbegin(); + AnimExpression::Token token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::LeftParen); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::LiteralInt); + QVERIFY(token.intVal == 10); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::Plus); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::Identifier); + QVERIFY(token.strVal == "x"); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::RightParen); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::GreaterThanEqual); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::LiteralInt); + QVERIFY(token.intVal == 20); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::And); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::LeftParen); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::Identifier); + QVERIFY(token.strVal == "y"); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::NotEqual); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::Not); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::Identifier); + QVERIFY(token.strVal == "z"); + token = e.consumeToken(str, iter); + QVERIFY(token.type == AnimExpression::Token::RightParen); + token = e.consumeToken(str, iter); } + diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index 7bd05369c7..bfea4eb086 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -26,6 +26,7 @@ private slots: void testLoader(); void testVariant(); void testAccumulateTime(); + void testTokenizer(); }; #endif // hifi_AnimTests_h From 7f0fc4f6eb5bc9a4f28f55c6641c516e32cddd81 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 1 Nov 2015 15:51:57 -0800 Subject: [PATCH 02/86] Added limited floating point support --- libraries/animation/src/AnimExpression.cpp | 38 +++++++++++++++++++++- libraries/animation/src/AnimExpression.h | 1 + tests/animation/src/AnimTests.cpp | 6 ++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp index f2ec0426be..14b3e9da87 100644 --- a/libraries/animation/src/AnimExpression.cpp +++ b/libraries/animation/src/AnimExpression.cpp @@ -151,6 +151,21 @@ AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QStr return Token(QStringRef(const_cast(&str), pos, len)); } +// TODO: not very efficient or accruate, but it's close enough for now. +static float computeFractionalPart(int fractionalPart) +{ + float frac = (float)fractionalPart; + while (fractionalPart) { + fractionalPart /= 10; + frac /= 10.0f; + } + return frac; +} + +static float computeFloat(int whole, int fraction) { + return (float)whole + computeFractionalPart(fraction); +} + AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString::const_iterator& iter) const { assert(iter != str.end()); assert(iter->isDigit()); @@ -158,10 +173,31 @@ AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString: while (iter->isDigit() && iter != str.end()) { ++iter; } + + // parse whole integer part int pos = (int)(begin - str.begin()); int len = (int)(iter - begin); QString sub = QStringRef(const_cast(&str), pos, len).toString(); - return Token(sub.toInt()); + int whole = sub.toInt(); + + // parse optional fractional part + if (iter->unicode() == '.') { + iter++; + auto begin = iter; + while (iter->isDigit() && iter != str.end()) { + ++iter; + } + + int pos = (int)(begin - str.begin()); + int len = (int)(iter - begin); + QString sub = QStringRef(const_cast(&str), pos, len).toString(); + int fraction = sub.toInt(); + + return Token(computeFloat(whole, fraction)); + + } else { + return Token(whole); + } } AnimExpression::Token AnimExpression::consumeAnd(const QString& str, QString::const_iterator& iter) const { diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index a69b13c286..25e1803721 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -50,6 +50,7 @@ protected: Token(Type type) : type(type) {} Token(const QStringRef& strRef) : type(Type::Identifier), strVal(strRef.toString()) {} Token(int val) : type(Type::LiteralInt), intVal(val) {} + Token(float val) : type(Type::LiteralFloat), floatVal(val) {} Type type = End; QString strVal; int intVal; diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 08f13b7ec7..5b6806ec09 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -325,7 +325,7 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram triggers.clear(); void AnimTests::testTokenizer() { - QString str = "(10 + x) >= 20 && (y != !z)"; + QString str = "(10 + x) >= 20.1 && (y != !z)"; AnimExpression e(""); auto iter = str.cbegin(); AnimExpression::Token token = e.consumeToken(str, iter); @@ -343,8 +343,8 @@ void AnimTests::testTokenizer() { token = e.consumeToken(str, iter); QVERIFY(token.type == AnimExpression::Token::GreaterThanEqual); token = e.consumeToken(str, iter); - QVERIFY(token.type == AnimExpression::Token::LiteralInt); - QVERIFY(token.intVal == 20); + QVERIFY(token.type == AnimExpression::Token::LiteralFloat); + QVERIFY(fabsf(token.floatVal - 20.1f) < 0.0001f); token = e.consumeToken(str, iter); QVERIFY(token.type == AnimExpression::Token::And); token = e.consumeToken(str, iter); From 4394083138744a47fd8f2a14c46e1fd008b38772 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 1 Nov 2015 15:57:20 -0800 Subject: [PATCH 03/86] Added comma token --- libraries/animation/src/AnimExpression.cpp | 79 +--------------------- libraries/animation/src/AnimExpression.h | 4 +- 2 files changed, 2 insertions(+), 81 deletions(-) diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp index 14b3e9da87..fe056be81d 100644 --- a/libraries/animation/src/AnimExpression.cpp +++ b/libraries/animation/src/AnimExpression.cpp @@ -14,83 +14,6 @@ #include "AnimExpression.h" #include "AnimationLogging.h" - -#ifndef NDEBUG -void AnimExpression::Token::dump() { - switch (type) { - case End: - qCDebug(animation) << " End"; - break; - case Identifier: - qCDebug(animation) << " Identifer =" << strVal; - break; - case LiteralInt: - qCDebug(animation) << " LiteralInt =" << intVal; - break; - case LiteralFloat: - qCDebug(animation) << " LiteralFloat" << floatVal; - break; - case LiteralVec3: - qCDebug(animation) << " LiteralVec3" << vec3Val; - break; - case LiteralVec4: - qCDebug(animation) << " LiteralVec4" << vec4Val; - break; - case LiteralQuat: - qCDebug(animation) << " LiteralQuat" << quatVal; - break; - case And: - qCDebug(animation) << " And"; - break; - case Or: - qCDebug(animation) << " Or"; - break; - case GreaterThan: - qCDebug(animation) << " GreaterThan"; - break; - case GreaterThanEqual: - qCDebug(animation) << " GreaterThanEqual"; - break; - case LessThan: - qCDebug(animation) << " LessThan"; - break; - case LessThanEqual: - qCDebug(animation) << " LessThanEqual"; - break; - case Equal: - qCDebug(animation) << " Equal"; - break; - case NotEqual: - qCDebug(animation) << " NotEqual"; - break; - case LeftParen: - qCDebug(animation) << " LeftParen"; - break; - case RightParen: - qCDebug(animation) << " RightParen"; - break; - case Not: - qCDebug(animation) << " Not"; - break; - case Minus: - qCDebug(animation) << " Minus"; - break; - case Plus: - qCDebug(animation) << " Plus"; - break; - case Multiply: - qCDebug(animation) << " Multiply"; - break; - case Modulus: - qCDebug(animation) << " Modulus"; - break; - case Error: - qCDebug(animation) << " Error"; - break; - } -} -#endif - AnimExpression::AnimExpression(const QString& str) : _expression(str) { parseExpression(_expression); @@ -130,6 +53,7 @@ AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString:: case '+': ++iter; return Token(Token::Plus); case '*': ++iter; return Token(Token::Multiply); case '%': ++iter; return Token(Token::Modulus); + case ',': ++iter; return Token(Token::Comma); default: qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); return Token(Token::Error); @@ -261,4 +185,3 @@ AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::co return Token(Token::Not); } } - diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 25e1803721..7e6c42f08a 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -45,6 +45,7 @@ protected: Plus, Multiply, Modulus, + Comma, Error }; Token(Type type) : type(type) {} @@ -58,9 +59,6 @@ protected: glm::vec3 vec3Val; glm::vec4 vec4Val; glm::quat quatVal; -#ifndef NDEBUG - void dump(); -#endif }; bool parseExpression(const QString& str); Token consumeToken(const QString& str, QString::const_iterator& iter) const; From 32c40d37c02694326abfd784010fc4c328764251 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 1 Nov 2015 16:01:29 -0800 Subject: [PATCH 04/86] Removed vec literals tokens and renamed int and float token types --- libraries/animation/src/AnimExpression.h | 11 ++++------- tests/animation/src/AnimTests.cpp | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 7e6c42f08a..7ba099ce2e 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -25,11 +25,8 @@ protected: enum Type { End = 0, Identifier, - LiteralInt, - LiteralFloat, - LiteralVec3, - LiteralVec4, - LiteralQuat, + Int, + Float, And, Or, GreaterThan, @@ -50,8 +47,8 @@ protected: }; Token(Type type) : type(type) {} Token(const QStringRef& strRef) : type(Type::Identifier), strVal(strRef.toString()) {} - Token(int val) : type(Type::LiteralInt), intVal(val) {} - Token(float val) : type(Type::LiteralFloat), floatVal(val) {} + Token(int val) : type(Type::Int), intVal(val) {} + Token(float val) : type(Type::Float), floatVal(val) {} Type type = End; QString strVal; int intVal; diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 5b6806ec09..44f9c05c22 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -331,7 +331,7 @@ void AnimTests::testTokenizer() { AnimExpression::Token token = e.consumeToken(str, iter); QVERIFY(token.type == AnimExpression::Token::LeftParen); token = e.consumeToken(str, iter); - QVERIFY(token.type == AnimExpression::Token::LiteralInt); + QVERIFY(token.type == AnimExpression::Token::Int); QVERIFY(token.intVal == 10); token = e.consumeToken(str, iter); QVERIFY(token.type == AnimExpression::Token::Plus); @@ -343,7 +343,7 @@ void AnimTests::testTokenizer() { token = e.consumeToken(str, iter); QVERIFY(token.type == AnimExpression::Token::GreaterThanEqual); token = e.consumeToken(str, iter); - QVERIFY(token.type == AnimExpression::Token::LiteralFloat); + QVERIFY(token.type == AnimExpression::Token::Float); QVERIFY(fabsf(token.floatVal - 20.1f) < 0.0001f); token = e.consumeToken(str, iter); QVERIFY(token.type == AnimExpression::Token::And); From a80ab0003c424e3c85ef36424a58e14f08f9b4c1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 2 Nov 2015 20:46:29 -0800 Subject: [PATCH 05/86] Removed vec literal values --- libraries/animation/src/AnimExpression.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 7ba099ce2e..145350547b 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -12,7 +12,6 @@ #define hifi_AnimExpression #include -#include #include #include @@ -53,11 +52,7 @@ protected: QString strVal; int intVal; float floatVal; - glm::vec3 vec3Val; - glm::vec4 vec4Val; - glm::quat quatVal; }; - bool parseExpression(const QString& str); Token consumeToken(const QString& str, QString::const_iterator& iter) const; Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const; Token consumeNumber(const QString& str, QString::const_iterator& iter) const; @@ -67,6 +62,8 @@ protected: Token consumeLessThan(const QString& str, QString::const_iterator& iter) const; Token consumeNot(const QString& str, QString::const_iterator& iter) const; + bool parseExpression(const QString& str); + QString _expression; }; From 61d1dd00d1d1d949887f230a84ea1d31518ad334 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 4 Nov 2015 12:04:34 -0800 Subject: [PATCH 06/86] Fixes for Mac build and crash on startup For some reason the qt code gen is getting confused by the #if 0 in 3DConnextionClient.h, so I added a stub implementation. In Application.cpp the change of moving idle into paintGL had a sideeffect of calling OffscreenGlCanvas before it was initialized. To work around this I added a guard to prevent calling idle before all the gl windows/widgets have been initialized. (cherry picked from commit 69f1cfbcb9125aa9d449a5d01b3734bcd51309e8) --- interface/src/Application.cpp | 12 ++++++------ interface/src/Application.h | 3 ++- interface/src/devices/3DConnexionClient.h | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d31d9de4a0..556664ec10 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -940,14 +940,12 @@ void Application::initializeGL() { qCDebug(interfaceapp) << "Created Display Window."; // initialize glut for shape drawing; Qt apparently initializes it on OS X - #ifndef __APPLE__ - static bool isInitialized = false; - if (isInitialized) { + if (_isGLInitialized) { return; } else { - isInitialized = true; + _isGLInitialized = true; } - #endif + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned gpu::Context::init(); _gpuContext = std::make_shared(); @@ -1059,7 +1057,9 @@ void Application::paintGL() { _lastFramesPerSecondUpdate = now; } - idle(now); + if (_isGLInitialized) { + idle(now); + } PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("paintGL"); diff --git a/interface/src/Application.h b/interface/src/Application.h index 212687c11e..5a5d5a015c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -414,7 +414,7 @@ private: bool _dependencyManagerIsSetup; - OffscreenGlCanvas* _offscreenContext; + OffscreenGlCanvas* _offscreenContext {nullptr}; DisplayPluginPointer _displayPlugin; InputPluginList _activeInputPlugins; @@ -548,6 +548,7 @@ private: quint64 _lastSimsPerSecondUpdate = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; + bool _isGLInitialized {false}; }; #endif // hifi_Application_h diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index 03a43d4c64..b6fa6a37c3 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -220,4 +220,19 @@ public: #endif +#include +#include + +// stub +class ConnexionClient : public QObject { + Q_OBJECT +public: + static ConnexionClient& getInstance(); + void init() {}; + void destroy() {}; + bool Is3dmouseAttached() { return false; }; +public slots: + void toggleConnexion(bool shouldEnable) {}; +}; + #endif // defined(hifi_3DConnexionClient_h) From 04d8a598da5d8f2ee684619cfb7492349853fdcc Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 4 Nov 2015 16:56:34 -0800 Subject: [PATCH 07/86] First step toward evaluation * added OpCodes * added first parser rules * removed mat4 support from AnimVariantMap --- libraries/animation/src/AnimExpression.cpp | 164 ++++++++++++++++----- libraries/animation/src/AnimExpression.h | 75 +++++++++- libraries/animation/src/AnimVariant.h | 74 ++++++---- libraries/shared/src/GLMHelpers.h | 1 + tests/animation/src/AnimTests.cpp | 34 +++-- tests/animation/src/AnimTests.h | 3 +- 6 files changed, 265 insertions(+), 86 deletions(-) diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp index fe056be81d..8807262028 100644 --- a/libraries/animation/src/AnimExpression.cpp +++ b/libraries/animation/src/AnimExpression.cpp @@ -16,58 +16,56 @@ AnimExpression::AnimExpression(const QString& str) : _expression(str) { - parseExpression(_expression); + auto iter = str.begin(); + parseExpression(_expression, iter); } -bool AnimExpression::parseExpression(const QString& str) { - Token token(Token::End); - auto iter = str.begin(); - do { - token = consumeToken(str, iter); - switch(token.type) { - case Token::Error: - case Token::End: - return false; - } - } while(iter != str.end()); +void AnimExpression::unconsumeToken(const Token& token) { + _tokenStack.push(token); } AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::const_iterator& iter) const { - while (iter != str.end()) { - if (iter->isSpace()) { - ++iter; - } else if (iter->isLetter()) { - return consumeIdentifier(str, iter); - } else if (iter->isDigit()) { - return consumeNumber(str, iter); - } else { - switch (iter->unicode()) { - case '&': return consumeAnd(str, iter); - case '|': return consumeOr(str, iter); - case '>': return consumeGreaterThan(str, iter); - case '<': return consumeLessThan(str, iter); - case '(': ++iter; return Token(Token::LeftParen); - case ')': ++iter; return Token(Token::RightParen); - case '!': return consumeNot(str, iter); - case '-': ++iter; return Token(Token::Minus); - case '+': ++iter; return Token(Token::Plus); - case '*': ++iter; return Token(Token::Multiply); - case '%': ++iter; return Token(Token::Modulus); - case ',': ++iter; return Token(Token::Comma); - default: - qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); - return Token(Token::Error); + if (!_tokenStack.empty()) { + Token top = _tokenStack.top(); + _tokenStack.pop(); + return top; + } else { + while (iter != str.end()) { + if (iter->isSpace()) { + ++iter; + } else if (iter->isLetter()) { + return consumeIdentifier(str, iter); + } else if (iter->isDigit()) { + return consumeNumber(str, iter); + } else { + switch (iter->unicode()) { + case '&': return consumeAnd(str, iter); + case '|': return consumeOr(str, iter); + case '>': return consumeGreaterThan(str, iter); + case '<': return consumeLessThan(str, iter); + case '(': ++iter; return Token(Token::LeftParen); + case ')': ++iter; return Token(Token::RightParen); + case '!': return consumeNot(str, iter); + case '-': ++iter; return Token(Token::Minus); + case '+': ++iter; return Token(Token::Plus); + case '*': ++iter; return Token(Token::Multiply); + case '%': ++iter; return Token(Token::Modulus); + case ',': ++iter; return Token(Token::Comma); + default: + qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin()); + return Token(Token::Error); + } } } + return Token(Token::End); } - return Token(Token::End); } AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QString::const_iterator& iter) const { assert(iter != str.end()); assert(iter->isLetter()); auto begin = iter; - while (iter->isLetter() && iter != str.end()) { + while ((iter->isLetter() || iter->isDigit()) && iter != str.end()) { ++iter; } int pos = (int)(begin - str.begin()); @@ -185,3 +183,93 @@ AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::co return Token(Token::Not); } } + +bool AnimExpression::parseExpression(const QString& str, QString::const_iterator& iter) { + auto token = consumeToken(str, iter); + if (token.type == Token::Identifier) { + if (token.strVal == "true") { + _opCodes.push_back(OpCode {true}); + } else if (token.strVal == "false") { + _opCodes.push_back(OpCode {false}); + } else { + _opCodes.push_back(OpCode {token.strVal}); + } + return true; + } else if (token.type == Token::Int) { + _opCodes.push_back(OpCode {token.intVal}); + return true; + } else if (token.type == Token::Float) { + _opCodes.push_back(OpCode {token.floatVal}); + return true; + } else if (token.type == Token::LeftParen) { + if (parseUnaryExpression(str, iter)) { + token = consumeToken(str, iter); + if (token.type != Token::RightParen) { + qCCritical(animation) << "Error parsing expression, expected ')'"; + return false; + } else { + return true; + } + } else { + return false; + } + } else { + qCCritical(animation) << "Error parsing expression, unexpected symbol"; + return false; + } +} + +bool AnimExpression::parseUnaryExpression(const QString& str, QString::const_iterator& iter) { + auto token = consumeToken(str, iter); + if (token.type == Token::Plus) { + if (parseExpression(str, iter)) { + _opCodes.push_back(OpCode {OpCode::UnaryPlus}); + return true; + } else { + return false; + } + } else if (token.type == Token::Minus) { + if (parseExpression(str, iter)) { + _opCodes.push_back(OpCode {OpCode::UnaryMinus}); + return true; + } else { + return false; + } + } else if (token.type == Token::Not) { + if (parseExpression(str, iter)) { + _opCodes.push_back(OpCode {OpCode::Not}); + return true; + } else { + return false; + } + } else { + unconsumeToken(token); + return parseExpression(str, iter); + } +} + +AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const { + std::stack stack; + for (auto& opCode : _opCodes) { + switch (opCode.type) { + case OpCode::Identifier: + case OpCode::Int: + case OpCode::Float: + stack.push(opCode); + break; + default: + switch (opCode.type) { + case OpCode::Not: + evalNot(map, stack); + break; + } + } + } + return stack.top(); +} + +void AnimExpression::evalNot(const AnimVariantMap& map, std::stack& stack) const { + bool lhs = stack.top().coerceBool(map); + stack.pop(); + stack.push(OpCode {!lhs}); +} diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 145350547b..8d216ca412 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include "AnimVariant.h" class AnimExpression { public: @@ -44,15 +47,64 @@ protected: Comma, Error }; - Token(Type type) : type(type) {} - Token(const QStringRef& strRef) : type(Type::Identifier), strVal(strRef.toString()) {} - Token(int val) : type(Type::Int), intVal(val) {} - Token(float val) : type(Type::Float), floatVal(val) {} - Type type = End; + Token(Type type) : type {type} {} + Token(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} + Token(int val) : type {Type::Int}, intVal {val} {} + Token(float val) : type {Type::Float}, floatVal {val} {} + Type type {End}; QString strVal; - int intVal; - float floatVal; + int intVal {0}; + float floatVal {0.0f}; }; + + struct OpCode { + enum Type { + Identifier, + Bool, + Int, + Float, + And, + Or, + GreaterThan, + GreaterThanEqual, + LessThan, + LessThanEqual, + Equal, + NotEqual, + LeftParen, + RightParen, + Not, + Minus, + Plus, + Multiply, + Modulus, + UnaryPlus, + UnaryMinus + }; + OpCode(Type type) : type {type} {} + OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} + OpCode(const QString& str) : type {Type::Identifier}, strVal {str} {} + OpCode(int val) : type {Type::Int}, intVal {val} {} + OpCode(bool val) : type {Type::Bool}, intVal {(int)val} {} + OpCode(float val) : type {Type::Float}, floatVal {val} {} + + bool coerceBool(const AnimVariantMap& map) const { + if (type == Int || type == Bool) { + return intVal != 0; + } else if (type == Identifier) { + return map.lookup(strVal, false); + } else { + return true; + } + } + + Type type {Int}; + QString strVal; + int intVal {0}; + float floatVal {0.0f}; + }; + + void unconsumeToken(const Token& token); Token consumeToken(const QString& str, QString::const_iterator& iter) const; Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const; Token consumeNumber(const QString& str, QString::const_iterator& iter) const; @@ -62,9 +114,16 @@ protected: Token consumeLessThan(const QString& str, QString::const_iterator& iter) const; Token consumeNot(const QString& str, QString::const_iterator& iter) const; - bool parseExpression(const QString& str); + bool parseExpression(const QString& str, QString::const_iterator& iter); + bool parseUnaryExpression(const QString& str, QString::const_iterator& iter); + + OpCode evaluate(const AnimVariantMap& map) const; + void evalNot(const AnimVariantMap& map, std::stack& stack) const; QString _expression; + mutable std::stack _tokenStack; + std::vector _opCodes; + }; #endif diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 0d7c657058..ff7794a16a 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -18,8 +18,9 @@ #include #include #include +#include +#include #include "AnimationLogging.h" -#include "StreamUtils.h" class AnimVariant { public: @@ -29,7 +30,6 @@ public: Float, Vec3, Quat, - Mat4, String, NumTypes }; @@ -40,7 +40,6 @@ public: 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 QString& value) : _type(Type::String) { _stringVal = value; } bool isBool() const { return _type == Type::Bool; } @@ -48,7 +47,6 @@ public: 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; } Type getType() const { return _type; } @@ -57,17 +55,52 @@ public: 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 QString& value) { assert(_type == Type::String); _stringVal = value; } - bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } - int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; } - float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _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 QString& getString() const { assert(_type == Type::String); return _stringVal; } + bool getBool() const { + if (_type == Type::Bool) { + return _val.boolVal; + } else if (_type == Type::Int) { + return _val.intVal != 0; + } else { + return false; + } + } + int getInt() const { + if (_type == Type::Int) { + return _val.intVal; + } else if (_type == Type::Float) { + return (int)_val.floats[0]; + } else { + return 0; + } + } + float getFloat() const { + if (_type == Type::Float) { + return _val.floats[0]; + } else if (_type == Type::Int) { + return (float)_val.intVal; + } else { + return 0.0f; + } + } + const glm::vec3& getVec3() const { + if (_type == Type::Vec3) { + return *reinterpret_cast(&_val); + } else { + return Vectors::ZERO; + } + } + const glm::quat& getQuat() const { + if (_type == Type::Quat) { + return *reinterpret_cast(&_val); + } else { + return Quaternions::IDENTITY; + } + } + const QString& getString() const { + return _stringVal; + } protected: Type _type; @@ -75,7 +108,7 @@ protected: union { bool boolVal; int intVal; - float floats[16]; + float floats[4]; } _val; }; @@ -130,15 +163,6 @@ public: } } - const glm::mat4& lookup(const QString& key, const glm::mat4& defaultValue) const { - if (key.isEmpty()) { - return defaultValue; - } else { - auto iter = _map.find(key); - return iter != _map.end() ? iter->second.getMat4() : defaultValue; - } - } - const QString& lookup(const QString& key, const QString& defaultValue) const { if (key.isEmpty()) { return defaultValue; @@ -153,7 +177,6 @@ public: void set(const QString& key, float value) { _map[key] = AnimVariant(value); } void set(const QString& key, const glm::vec3& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const glm::quat& value) { _map[key] = AnimVariant(value); } - void set(const QString& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); } void unset(const QString& key) { _map.erase(key); } @@ -189,9 +212,6 @@ public: case AnimVariant::Type::Quat: qCDebug(animation) << " " << pair.first << "=" << pair.second.getQuat(); break; - case AnimVariant::Type::Mat4: - qCDebug(animation) << " " << pair.first << "=" << pair.second.getMat4(); - break; case AnimVariant::Type::String: qCDebug(animation) << " " << pair.first << "=" << pair.second.getString(); break; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 9c1bbe23a4..e69d11629e 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -80,6 +80,7 @@ public: static const vec3& RIGHT; static const vec3& UP; static const vec3& FRONT; + static const vec3 ZERO4; }; // These pack/unpack functions are designed to start specific known types in as efficient a manner diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 44f9c05c22..94caed66f5 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -195,10 +195,7 @@ void AnimTests::testVariant() { auto floatVarNegative = 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); @@ -233,12 +230,6 @@ void AnimTests::testVariant() { 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[1].z == -7.0f); - QVERIFY(m[3].w == 16.0f); } void AnimTests::testAccumulateTime() { @@ -323,10 +314,11 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram QVERIFY(resultFrame == startFrame + 0.5f); QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); triggers.clear(); +} -void AnimTests::testTokenizer() { +void AnimTests::testExpressionTokenizer() { QString str = "(10 + x) >= 20.1 && (y != !z)"; - AnimExpression e(""); + AnimExpression e("x"); auto iter = str.cbegin(); AnimExpression::Token token = e.consumeToken(str, iter); QVERIFY(token.type == AnimExpression::Token::LeftParen); @@ -364,3 +356,21 @@ void AnimTests::testTokenizer() { token = e.consumeToken(str, iter); } +void AnimTests::testExpressionParser() { + QString str = "(!x)"; + AnimExpression e(str); + QVERIFY(e._opCodes.size() == 2); + if (e._opCodes.size() == 2) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); + QVERIFY(e._opCodes[0].strVal == "x"); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not); + } + + auto vars = AnimVariantMap(); + vars.set("x", false); + + auto opCode = e.evaluate(vars); + QVERIFY(opCode.type == AnimExpression::OpCode::Bool); + QVERIFY(opCode.coerceBool(vars) == true); +} + diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index bfea4eb086..94b3eddd25 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -26,7 +26,8 @@ private slots: void testLoader(); void testVariant(); void testAccumulateTime(); - void testTokenizer(); + void testExpressionTokenizer(); + void testExpressionParser(); }; #endif // hifi_AnimTests_h From 431a108c351431345d9aab4144ef55f80de84ed3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 4 Nov 2015 20:13:17 -0800 Subject: [PATCH 08/86] Bugfixes to expression for !!x expressions Added stub eval methods. only boolean not, boolean and, boolean or and unary minus are implemented. --- libraries/animation/src/AnimExpression.cpp | 186 +++++++++++++++++++-- libraries/animation/src/AnimExpression.h | 25 ++- libraries/animation/src/AnimVariant.cpp | 2 + libraries/animation/src/AnimVariant.h | 11 ++ tests/animation/src/AnimTests.cpp | 87 +++++++++- 5 files changed, 280 insertions(+), 31 deletions(-) diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp index 8807262028..a03925f8f9 100644 --- a/libraries/animation/src/AnimExpression.cpp +++ b/libraries/animation/src/AnimExpression.cpp @@ -214,23 +214,27 @@ bool AnimExpression::parseExpression(const QString& str, QString::const_iterator return false; } } else { - qCCritical(animation) << "Error parsing expression, unexpected symbol"; - return false; + unconsumeToken(token); + if (parseUnaryExpression(str, iter)) { + return true; + } else { + qCCritical(animation) << "Error parsing expression"; + return false; + } } } bool AnimExpression::parseUnaryExpression(const QString& str, QString::const_iterator& iter) { auto token = consumeToken(str, iter); - if (token.type == Token::Plus) { + if (token.type == Token::Plus) { // unary plus is a no op. if (parseExpression(str, iter)) { - _opCodes.push_back(OpCode {OpCode::UnaryPlus}); return true; } else { return false; } } else if (token.type == Token::Minus) { if (parseExpression(str, iter)) { - _opCodes.push_back(OpCode {OpCode::UnaryMinus}); + _opCodes.push_back(OpCode {OpCode::Minus}); return true; } else { return false; @@ -255,21 +259,173 @@ AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const case OpCode::Identifier: case OpCode::Int: case OpCode::Float: + case OpCode::Bool: stack.push(opCode); break; - default: - switch (opCode.type) { - case OpCode::Not: - evalNot(map, stack); - break; - } + case OpCode::And: evalAnd(map, stack); break; + case OpCode::Or: evalOr(map, stack); break; + case OpCode::GreaterThan: evalGreaterThan(map, stack); break; + case OpCode::GreaterThanEqual: evalGreaterThanEqual(map, stack); break; + case OpCode::LessThan: evalLessThan(map, stack); break; + case OpCode::LessThanEqual: evalLessThanEqual(map, stack); break; + case OpCode::Equal: evalEqual(map, stack); break; + case OpCode::NotEqual: evalNotEqual(map, stack); break; + case OpCode::Not: evalNot(map, stack); break; + case OpCode::Subtract: evalSubtract(map, stack); break; + case OpCode::Add: evalAdd(map, stack); break; + case OpCode::Multiply: evalMultiply(map, stack); break; + case OpCode::Modulus: evalModulus(map, stack); break; + case OpCode::Minus: evalMinus(map, stack); break; } } return stack.top(); } -void AnimExpression::evalNot(const AnimVariantMap& map, std::stack& stack) const { - bool lhs = stack.top().coerceBool(map); - stack.pop(); - stack.push(OpCode {!lhs}); +#define POP_BOOL(NAME) \ + const OpCode& NAME##_temp = stack.top(); \ + bool NAME = NAME##_temp.coerceBool(map); \ + stack.pop() + +#define PUSH(EXPR) \ + stack.push(OpCode {(EXPR)}) + +void AnimExpression::evalAnd(const AnimVariantMap& map, std::stack& stack) const { + POP_BOOL(lhs); + POP_BOOL(rhs); + PUSH(lhs && rhs); +} + +void AnimExpression::evalOr(const AnimVariantMap& map, std::stack& stack) const { + POP_BOOL(lhs); + POP_BOOL(rhs); + PUSH(lhs || rhs); +} + +void AnimExpression::evalGreaterThan(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalGreaterThanEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalLessThan(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalLessThanEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalNotEqual(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(false); +} + +void AnimExpression::evalNot(const AnimVariantMap& map, std::stack& stack) const { + POP_BOOL(rhs); + PUSH(!rhs); +} + +void AnimExpression::evalSubtract(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(0.0f); +} + +void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(0.0f); +} + +void AnimExpression::evalMultiply(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH(0.0f); +} + +void AnimExpression::evalModulus(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = stack.top(); stack.pop(); + OpCode rhs = stack.top(); stack.pop(); + + // TODO: + PUSH((int)0); +} + +void AnimExpression::evalMinus(const AnimVariantMap& map, std::stack& stack) const { + OpCode rhs = stack.top(); stack.pop(); + + switch (rhs.type) { + case OpCode::Identifier: { + const AnimVariant& var = map.get(rhs.strVal); + switch (var.getType()) { + case AnimVariant::Type::Bool: + qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool"; + // interpret this as boolean not. + PUSH(!var.getBool()); + break; + case AnimVariant::Type::Int: + PUSH(-var.getInt()); + break; + case AnimVariant::Type::Float: + PUSH(-var.getFloat()); + break; + default: + // TODO: Vec3, Quat are unsupported + assert(false); + PUSH(false); + break; + } + } + case OpCode::Int: + PUSH(-rhs.intVal); + break; + case OpCode::Float: + PUSH(-rhs.floatVal); + break; + case OpCode::Bool: + qCWarning(animation) << "AnimExpression: type missmatch for unary minus, expected a number not a bool"; + // interpret this as boolean not. + PUSH(!rhs.coerceBool(map)); + break; + default: + qCCritical(animation) << "AnimExpression: ERRROR for unary minus, expected a number, type = " << rhs.type; + assert(false); + PUSH(false); + break; + } } diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 8d216ca412..afada44e86 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -71,15 +71,12 @@ protected: LessThanEqual, Equal, NotEqual, - LeftParen, - RightParen, Not, - Minus, - Plus, + Subtract, + Add, Multiply, Modulus, - UnaryPlus, - UnaryMinus + Minus }; OpCode(Type type) : type {type} {} OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} @@ -118,12 +115,24 @@ protected: bool parseUnaryExpression(const QString& str, QString::const_iterator& iter); OpCode evaluate(const AnimVariantMap& map) const; + void evalAnd(const AnimVariantMap& map, std::stack& stack) const; + void evalOr(const AnimVariantMap& map, std::stack& stack) const; + void evalGreaterThan(const AnimVariantMap& map, std::stack& stack) const; + void evalGreaterThanEqual(const AnimVariantMap& map, std::stack& stack) const; + void evalLessThan(const AnimVariantMap& map, std::stack& stack) const; + void evalLessThanEqual(const AnimVariantMap& map, std::stack& stack) const; + void evalEqual(const AnimVariantMap& map, std::stack& stack) const; + void evalNotEqual(const AnimVariantMap& map, std::stack& stack) const; void evalNot(const AnimVariantMap& map, std::stack& stack) const; + void evalSubtract(const AnimVariantMap& map, std::stack& stack) const; + void evalAdd(const AnimVariantMap& map, std::stack& stack) const; + void evalMultiply(const AnimVariantMap& map, std::stack& stack) const; + void evalModulus(const AnimVariantMap& map, std::stack& stack) const; + void evalMinus(const AnimVariantMap& map, std::stack& stack) const; QString _expression; - mutable std::stack _tokenStack; + mutable std::stack _tokenStack; // TODO: remove, only needed during parsing std::vector _opCodes; - }; #endif diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 234e9cef09..cae6dce23d 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -15,6 +15,8 @@ #include #include "AnimVariant.h" // which has AnimVariant/AnimVariantMap +const AnimVariant AnimVariant::FALSE = AnimVariant(); + QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const { if (QThread::currentThread() != engine->thread()) { qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread(); diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index ff7794a16a..b15b25f4e0 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -34,6 +34,8 @@ public: NumTypes }; + static const AnimVariant FALSE; + 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; } @@ -186,6 +188,15 @@ public: void clearMap() { _map.clear(); } bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } + const AnimVariant& get(const QString& key) const { + auto iter = _map.find(key); + if (iter != _map.end()) { + return iter->second; + } else { + return AnimVariant::FALSE; + } + } + // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties. QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const; // Side-effect us with the value of object's own properties. (No inherited properties.) diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 94caed66f5..58fd3a1d35 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -357,20 +357,91 @@ void AnimTests::testExpressionTokenizer() { } void AnimTests::testExpressionParser() { - QString str = "(!x)"; - AnimExpression e(str); + + auto vars = AnimVariantMap(); + vars.set("f", false); + vars.set("t", true); + vars.set("ten", (int)10); + vars.set("twenty", (int)20); + vars.set("five", (float)5.0f); + vars.set("forty", (float)40.0f); + + AnimExpression e("(!f)"); QVERIFY(e._opCodes.size() == 2); if (e._opCodes.size() == 2) { QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); - QVERIFY(e._opCodes[0].strVal == "x"); + QVERIFY(e._opCodes[0].strVal == "f"); QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not); + + auto opCode = e.evaluate(vars); + QVERIFY(opCode.type == AnimExpression::OpCode::Bool); + QVERIFY((opCode.intVal != 0) == true); } - auto vars = AnimVariantMap(); - vars.set("x", false); + e = AnimExpression("!!f"); + QVERIFY(e._opCodes.size() == 3); + if (e._opCodes.size() == 3) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); + QVERIFY(e._opCodes[0].strVal == "f"); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not); + QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not); - auto opCode = e.evaluate(vars); - QVERIFY(opCode.type == AnimExpression::OpCode::Bool); - QVERIFY(opCode.coerceBool(vars) == true); + auto opCode = e.evaluate(vars); + QVERIFY(opCode.type == AnimExpression::OpCode::Bool); + QVERIFY((opCode.intVal != 0) == false); + } + + e = AnimExpression("!!(!f)"); + QVERIFY(e._opCodes.size() == 4); + if (e._opCodes.size() == 4) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); + QVERIFY(e._opCodes[0].strVal == "f"); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not); + QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not); + QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Not); + + auto opCode = e.evaluate(vars); + QVERIFY(opCode.type == AnimExpression::OpCode::Bool); + QVERIFY((opCode.intVal != 0) == true); + } +/* + e = AnimExpression("f || !f"); + QVERIFY(e._opCodes.size() == 4); + if (e._opCodes.size() == 4) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); + QVERIFY(e._opCodes[0].strVal == "f"); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Identifier); + QVERIFY(e._opCodes[1].strVal == "f"); + QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not); + QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Or); + + auto opCode = e.evaluate(vars); + QVERIFY(opCode.type == AnimExpression::OpCode::Bool); + QVERIFY((opCode.intVal != 0) == true); + } +*/ + e = AnimExpression("-10"); + QVERIFY(e._opCodes.size() == 2); + if (e._opCodes.size() == 1) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[0].intVal == 10); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Minus); + + auto opCode = e.evaluate(vars); + QVERIFY(opCode.type == AnimExpression::OpCode::Int); + QVERIFY(opCode.intVal == 10); + } + + e = AnimExpression("-ten"); + QVERIFY(e._opCodes.size() == 2); + if (e._opCodes.size() == 1) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); + QVERIFY(e._opCodes[0].strVal == "ten"); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Minus); + + auto opCode = e.evaluate(vars); + QVERIFY(opCode.type == AnimExpression::OpCode::Int); + QVERIFY(opCode.intVal == 10); + } } From 99223d0a3c8848fbef5d8ce16f834747c8efe2e5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 3 Dec 2015 15:02:00 -0800 Subject: [PATCH 09/86] AnimExpression: support for parsing simple expressions supports parens, binary +, -, / and *. / and * have higher precedence then + and - --- libraries/animation/src/AnimExpression.cpp | 349 +++++++++++++++++---- libraries/animation/src/AnimExpression.h | 24 +- tests/animation/src/AnimTests.cpp | 174 ++++++---- tests/animation/src/AnimTests.h | 1 + 4 files changed, 417 insertions(+), 131 deletions(-) diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp index a03925f8f9..b76aaff3f9 100644 --- a/libraries/animation/src/AnimExpression.cpp +++ b/libraries/animation/src/AnimExpression.cpp @@ -17,9 +17,16 @@ AnimExpression::AnimExpression(const QString& str) : _expression(str) { auto iter = str.begin(); - parseExpression(_expression, iter); + parseExpr(_expression, iter); + while(!_tokenStack.empty()) { + _tokenStack.pop(); + } } +// +// Tokenizer +// + void AnimExpression::unconsumeToken(const Token& token) { _tokenStack.push(token); } @@ -49,6 +56,7 @@ AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString:: case '-': ++iter; return Token(Token::Minus); case '+': ++iter; return Token(Token::Plus); case '*': ++iter; return Token(Token::Multiply); + case '/': ++iter; return Token(Token::Divide); case '%': ++iter; return Token(Token::Modulus); case ',': ++iter; return Token(Token::Comma); default: @@ -184,73 +192,137 @@ AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::co } } -bool AnimExpression::parseExpression(const QString& str, QString::const_iterator& iter) { +// +// Parser +// + +/* +Expr → Term Expr' +Expr' → + Term Expr' + | – Term Expr' + | ε +Term → Factor Term' +Term' → * Term' + | / Term' + | ε +Factor → INT + | FLOAT + | IDENTIFIER + | (Expr) +*/ + +bool AnimExpression::parseExpr(const QString& str, QString::const_iterator& iter) { + if (!parseTerm(str, iter)) { + return false; + } + if (!parseExprPrime(str, iter)) { + return false; + } + return true; +} + +bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& iter) { auto token = consumeToken(str, iter); - if (token.type == Token::Identifier) { - if (token.strVal == "true") { - _opCodes.push_back(OpCode {true}); - } else if (token.strVal == "false") { - _opCodes.push_back(OpCode {false}); - } else { - _opCodes.push_back(OpCode {token.strVal}); + if (token.type == Token::Plus) { + if (!parseTerm(str, iter)) { + unconsumeToken(token); + return false; } + if (!parseExprPrime(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::Add}); return true; - } else if (token.type == Token::Int) { + } else if (token.type == Token::Minus) { + if (!parseTerm(str, iter)) { + unconsumeToken(token); + return false; + } + if (!parseExprPrime(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::Subtract}); + return true; + } else { + unconsumeToken(token); + return true; + } +} + +bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter) { + if (!parseFactor(str, iter)) { + return false; + } + if (!parseTermPrime(str, iter)) { + return false; + } + return true; +} + +bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& iter) { + auto token = consumeToken(str, iter); + if (token.type == Token::Multiply) { + if (!parseTerm(str, iter)) { + unconsumeToken(token); + return false; + } + if (!parseTermPrime(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::Multiply}); + return true; + } else if (token.type == Token::Divide) { + if (!parseTerm(str, iter)) { + unconsumeToken(token); + return false; + } + if (!parseTermPrime(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::Divide}); + return true; + } else { + unconsumeToken(token); + return true; + } +} + +bool AnimExpression::parseFactor(const QString& str, QString::const_iterator& iter) { + auto token = consumeToken(str, iter); + if (token.type == Token::Int) { _opCodes.push_back(OpCode {token.intVal}); return true; } else if (token.type == Token::Float) { _opCodes.push_back(OpCode {token.floatVal}); return true; + } else if (token.type == Token::Identifier) { + _opCodes.push_back(OpCode {token.strVal}); + return true; } else if (token.type == Token::LeftParen) { - if (parseUnaryExpression(str, iter)) { - token = consumeToken(str, iter); - if (token.type != Token::RightParen) { - qCCritical(animation) << "Error parsing expression, expected ')'"; - return false; - } else { - return true; - } - } else { + if (!parseExpr(str, iter)) { + unconsumeToken(token); return false; } + auto nextToken = consumeToken(str, iter); + if (nextToken.type != Token::RightParen) { + unconsumeToken(nextToken); + unconsumeToken(token); + return false; + } + return true; } else { unconsumeToken(token); - if (parseUnaryExpression(str, iter)) { - return true; - } else { - qCCritical(animation) << "Error parsing expression"; - return false; - } + return false; } } -bool AnimExpression::parseUnaryExpression(const QString& str, QString::const_iterator& iter) { - auto token = consumeToken(str, iter); - if (token.type == Token::Plus) { // unary plus is a no op. - if (parseExpression(str, iter)) { - return true; - } else { - return false; - } - } else if (token.type == Token::Minus) { - if (parseExpression(str, iter)) { - _opCodes.push_back(OpCode {OpCode::Minus}); - return true; - } else { - return false; - } - } else if (token.type == Token::Not) { - if (parseExpression(str, iter)) { - _opCodes.push_back(OpCode {OpCode::Not}); - return true; - } else { - return false; - } - } else { - unconsumeToken(token); - return parseExpression(str, iter); - } -} +// +// Evaluator +// AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const { std::stack stack; @@ -274,8 +346,9 @@ AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const case OpCode::Subtract: evalSubtract(map, stack); break; case OpCode::Add: evalAdd(map, stack); break; case OpCode::Multiply: evalMultiply(map, stack); break; + case OpCode::Divide: evalDivide(map, stack); break; case OpCode::Modulus: evalModulus(map, stack); break; - case OpCode::Minus: evalMinus(map, stack); break; + case OpCode::UnaryMinus: evalUnaryMinus(map, stack); break; } } return stack.top(); @@ -362,15 +435,107 @@ void AnimExpression::evalSubtract(const AnimVariantMap& map, std::stack& PUSH(0.0f); } -void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack& stack) const { - OpCode lhs = stack.top(); stack.pop(); - OpCode rhs = stack.top(); stack.pop(); +void AnimExpression::add(int lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs + rhs.intVal); + break; + case OpCode::Float: + PUSH((float)lhs + rhs.floatVal); + break; + default: + PUSH(lhs); + } +} - // TODO: - PUSH(0.0f); +void AnimExpression::add(float lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs + (float)rhs.intVal); + break; + case OpCode::Float: + PUSH(lhs + rhs.floatVal); + break; + default: + PUSH(lhs); + } +} + +void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = coerseToValue(map, stack.top()); + stack.pop(); + OpCode rhs = coerseToValue(map, stack.top()); + stack.pop(); + + switch (lhs.type) { + case OpCode::Bool: + add(lhs.intVal, rhs, stack); + break; + case OpCode::Int: + add(lhs.intVal, rhs, stack); + break; + case OpCode::Float: + add(lhs.floatVal, rhs, stack); + break; + default: + add(0, rhs, stack); + break; + } } void AnimExpression::evalMultiply(const AnimVariantMap& map, std::stack& stack) const { + OpCode lhs = coerseToValue(map, stack.top()); + stack.pop(); + OpCode rhs = coerseToValue(map, stack.top()); + stack.pop(); + + switch(lhs.type) { + case OpCode::Bool: + mul(lhs.intVal, rhs, stack); + break; + case OpCode::Int: + mul(lhs.intVal, rhs, stack); + break; + case OpCode::Float: + mul(lhs.floatVal, rhs, stack); + break; + default: + mul(0, rhs, stack); + break; + } +} + +void AnimExpression::mul(int lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs * rhs.intVal); + break; + case OpCode::Float: + PUSH((float)lhs * rhs.floatVal); + break; + default: + PUSH(lhs); + } +} + +void AnimExpression::mul(float lhs, const OpCode& rhs, std::stack& stack) const { + switch (rhs.type) { + case OpCode::Bool: + case OpCode::Int: + PUSH(lhs * (float)rhs.intVal); + break; + case OpCode::Float: + PUSH(lhs * rhs.floatVal); + break; + default: + PUSH(lhs); + } +} + +void AnimExpression::evalDivide(const AnimVariantMap& map, std::stack& stack) const { OpCode lhs = stack.top(); stack.pop(); OpCode rhs = stack.top(); stack.pop(); @@ -386,7 +551,7 @@ void AnimExpression::evalModulus(const AnimVariantMap& map, std::stack& PUSH((int)0); } -void AnimExpression::evalMinus(const AnimVariantMap& map, std::stack& stack) const { +void AnimExpression::evalUnaryMinus(const AnimVariantMap& map, std::stack& stack) const { OpCode rhs = stack.top(); stack.pop(); switch (rhs.type) { @@ -429,3 +594,69 @@ void AnimExpression::evalMinus(const AnimVariantMap& map, std::stack& st break; } } + +AnimExpression::OpCode AnimExpression::coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const { + switch (opCode.type) { + case OpCode::Identifier: + { + const AnimVariant& var = map.get(opCode.strVal); + switch (var.getType()) { + case AnimVariant::Type::Bool: + qCWarning(animation) << "AnimExpression: type missmatch, expected a number not a bool"; + return OpCode(0); + break; + case AnimVariant::Type::Int: + return OpCode(var.getInt()); + break; + case AnimVariant::Type::Float: + return OpCode(var.getFloat()); + break; + default: + // TODO: Vec3, Quat are unsupported + assert(false); + return OpCode(0); + break; + } + } + break; + case OpCode::Bool: + case OpCode::Int: + case OpCode::Float: + return opCode; + default: + qCCritical(animation) << "AnimExpression: ERROR expected a number, type = " << opCode.type; + assert(false); + return OpCode(0); + break; + } +} + +void AnimExpression::dumpOpCodes() const { + QString tmp; + for (auto& op : _opCodes) { + switch (op.type) { + case OpCode::Identifier: tmp += QString(" %1").arg(op.strVal); break; + case OpCode::Bool: tmp += QString(" %1").arg(op.intVal ? "true" : "false"); break; + case OpCode::Int: tmp += QString(" %1").arg(op.intVal); break; + case OpCode::Float: tmp += QString(" %1").arg(op.floatVal); break; + case OpCode::And: tmp += " &&"; break; + case OpCode::Or: tmp += " ||"; break; + case OpCode::GreaterThan: tmp += " >"; break; + case OpCode::GreaterThanEqual: tmp += " >="; break; + case OpCode::LessThan: tmp += " <"; break; + case OpCode::LessThanEqual: tmp += " <="; break; + case OpCode::Equal: tmp += " =="; break; + case OpCode::NotEqual: tmp += " !="; break; + case OpCode::Not: tmp += " !"; break; + case OpCode::Subtract: tmp += " -"; break; + case OpCode::Add: tmp += " +"; break; + case OpCode::Multiply: tmp += " *"; break; + case OpCode::Divide: tmp += " /"; break; + case OpCode::Modulus: tmp += " %"; break; + case OpCode::UnaryMinus: tmp += " unary-"; break; + default: tmp += " ???"; break; + } + } + qCDebug(animation).nospace().noquote() << "opCodes =" << tmp; + qCDebug(animation).resetFormat(); +} diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index afada44e86..6e204483d5 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -43,6 +43,7 @@ protected: Minus, Plus, Multiply, + Divide, Modulus, Comma, Error @@ -75,8 +76,9 @@ protected: Subtract, Add, Multiply, + Divide, Modulus, - Minus + UnaryMinus }; OpCode(Type type) : type {type} {} OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} @@ -111,8 +113,11 @@ protected: Token consumeLessThan(const QString& str, QString::const_iterator& iter) const; Token consumeNot(const QString& str, QString::const_iterator& iter) const; - bool parseExpression(const QString& str, QString::const_iterator& iter); - bool parseUnaryExpression(const QString& str, QString::const_iterator& iter); + bool parseExpr(const QString& str, QString::const_iterator& iter); + bool parseExprPrime(const QString& str, QString::const_iterator& iter); + bool parseTerm(const QString& str, QString::const_iterator& iter); + bool parseTermPrime(const QString& str, QString::const_iterator& iter); + bool parseFactor(const QString& str, QString::const_iterator& iter); OpCode evaluate(const AnimVariantMap& map) const; void evalAnd(const AnimVariantMap& map, std::stack& stack) const; @@ -126,13 +131,24 @@ protected: void evalNot(const AnimVariantMap& map, std::stack& stack) const; void evalSubtract(const AnimVariantMap& map, std::stack& stack) const; void evalAdd(const AnimVariantMap& map, std::stack& stack) const; + void add(int lhs, const OpCode& rhs, std::stack& stack) const; + void add(float lhs, const OpCode& rhs, std::stack& stack) const; void evalMultiply(const AnimVariantMap& map, std::stack& stack) const; + void mul(int lhs, const OpCode& rhs, std::stack& stack) const; + void mul(float lhs, const OpCode& rhs, std::stack& stack) const; + void evalDivide(const AnimVariantMap& map, std::stack& stack) const; void evalModulus(const AnimVariantMap& map, std::stack& stack) const; - void evalMinus(const AnimVariantMap& map, std::stack& stack) const; + void evalUnaryMinus(const AnimVariantMap& map, std::stack& stack) const; + + OpCode coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const; QString _expression; mutable std::stack _tokenStack; // TODO: remove, only needed during parsing std::vector _opCodes; + +#ifndef NDEBUG + void dumpOpCodes() const; +#endif }; #endif diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 58fd3a1d35..6765553a25 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -366,82 +366,120 @@ void AnimTests::testExpressionParser() { vars.set("five", (float)5.0f); vars.set("forty", (float)40.0f); - AnimExpression e("(!f)"); - QVERIFY(e._opCodes.size() == 2); - if (e._opCodes.size() == 2) { - QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); - QVERIFY(e._opCodes[0].strVal == "f"); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not); - - auto opCode = e.evaluate(vars); - QVERIFY(opCode.type == AnimExpression::OpCode::Bool); - QVERIFY((opCode.intVal != 0) == true); - } - - e = AnimExpression("!!f"); - QVERIFY(e._opCodes.size() == 3); - if (e._opCodes.size() == 3) { - QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); - QVERIFY(e._opCodes[0].strVal == "f"); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not); - QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not); - - auto opCode = e.evaluate(vars); - QVERIFY(opCode.type == AnimExpression::OpCode::Bool); - QVERIFY((opCode.intVal != 0) == false); - } - - e = AnimExpression("!!(!f)"); - QVERIFY(e._opCodes.size() == 4); - if (e._opCodes.size() == 4) { - QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); - QVERIFY(e._opCodes[0].strVal == "f"); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not); - QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not); - QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Not); - - auto opCode = e.evaluate(vars); - QVERIFY(opCode.type == AnimExpression::OpCode::Bool); - QVERIFY((opCode.intVal != 0) == true); - } -/* - e = AnimExpression("f || !f"); - QVERIFY(e._opCodes.size() == 4); - if (e._opCodes.size() == 4) { - QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); - QVERIFY(e._opCodes[0].strVal == "f"); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Identifier); - QVERIFY(e._opCodes[1].strVal == "f"); - QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not); - QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Or); - - auto opCode = e.evaluate(vars); - QVERIFY(opCode.type == AnimExpression::OpCode::Bool); - QVERIFY((opCode.intVal != 0) == true); - } -*/ - e = AnimExpression("-10"); - QVERIFY(e._opCodes.size() == 2); + AnimExpression e("10"); + QVERIFY(e._opCodes.size() == 1); if (e._opCodes.size() == 1) { QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); QVERIFY(e._opCodes[0].intVal == 10); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Minus); - - auto opCode = e.evaluate(vars); - QVERIFY(opCode.type == AnimExpression::OpCode::Int); - QVERIFY(opCode.intVal == 10); } - e = AnimExpression("-ten"); - QVERIFY(e._opCodes.size() == 2); + e = AnimExpression("(10)"); + QVERIFY(e._opCodes.size() == 1); + if (e._opCodes.size() == 1) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[0].intVal == 10); + } + + e = AnimExpression("((10))"); + QVERIFY(e._opCodes.size() == 1); + if (e._opCodes.size() == 1) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[0].intVal == 10); + } + + e = AnimExpression("12.5"); + QVERIFY(e._opCodes.size() == 1); + if (e._opCodes.size() == 1) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Float); + QVERIFY(e._opCodes[0].floatVal == 12.5f); + } + + e = AnimExpression("twenty"); + QVERIFY(e._opCodes.size() == 1); if (e._opCodes.size() == 1) { QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier); - QVERIFY(e._opCodes[0].strVal == "ten"); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Minus); + QVERIFY(e._opCodes[0].strVal == "twenty"); + } - auto opCode = e.evaluate(vars); - QVERIFY(opCode.type == AnimExpression::OpCode::Int); - QVERIFY(opCode.intVal == 10); + e = AnimExpression("2 + 3"); + QVERIFY(e._opCodes.size() == 3); + if (e._opCodes.size() == 3) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[0].intVal == 2); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[1].intVal == 3); + QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Add); + } + + e = AnimExpression("2 + 3 * 10"); + QVERIFY(e._opCodes.size() == 5); + if (e._opCodes.size() == 5) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[0].intVal == 2); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[1].intVal == 3); + QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[2].intVal == 10); + QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Multiply); + QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Add); + } + + e = AnimExpression("(2 + 3) * 10"); + QVERIFY(e._opCodes.size() == 5); + if (e._opCodes.size() == 5) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[0].intVal == 2); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[1].intVal == 3); + QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Add); + QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Int); + QVERIFY(e._opCodes[3].intVal == 10); + QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Multiply); } } +void AnimTests::testExpressionEvaluator() { + auto vars = AnimVariantMap(); + vars.set("f", false); + vars.set("t", true); + vars.set("ten", (int)10); + vars.set("twenty", (int)20); + vars.set("five", (float)5.0f); + vars.set("forty", (float)40.0f); + + AnimExpression::OpCode result = AnimExpression("10").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Int); + QVERIFY(result.intVal == 10); + + result = AnimExpression("(10)").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Int); + QVERIFY(result.intVal == 10); + + result = AnimExpression("2 + 3").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Int); + QVERIFY(result.intVal == 2 + 3); + + result = AnimExpression("2 + 3 * 5").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Int); + QVERIFY(result.intVal == 2 + 3 * 5); + + result = AnimExpression("(2 + 3) * 5").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Int); + QVERIFY(result.intVal == (2 + 3) * 5); + + result = AnimExpression("(2 + 3) * (5 + 3)").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Int); + QVERIFY(result.intVal == (2 + 3) * (5 + 3)); + + result = AnimExpression("(ten + twenty) * 5").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Int); + QVERIFY(result.intVal == (10 + 20) * 5); + + result = AnimExpression("(ten + twenty) * 5.0").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Float); + QVERIFY(result.floatVal == (10 + 20) * 5.0f); + + result = AnimExpression("five * forty").evaluate(vars); + QVERIFY(result.type == AnimExpression::OpCode::Float); + QVERIFY(result.floatVal == 5.0f * 40.0f); +} diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index 94b3eddd25..a70acc21f7 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -28,6 +28,7 @@ private slots: void testAccumulateTime(); void testExpressionTokenizer(); void testExpressionParser(); + void testExpressionEvaluator(); }; #endif // hifi_AnimTests_h From 0fe4803b04dc9e2554d1ef5335ce98e498c05cde Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 12 Dec 2015 16:17:00 -0800 Subject: [PATCH 10/86] start --- examples/lights/box.js | 31 +++++++++ examples/lights/light_modifier.js | 103 ++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 examples/lights/box.js create mode 100644 examples/lights/light_modifier.js diff --git a/examples/lights/box.js b/examples/lights/box.js new file mode 100644 index 0000000000..711f5487b2 --- /dev/null +++ b/examples/lights/box.js @@ -0,0 +1,31 @@ +(function() { + + function Box () { + this.oldColor = {}; + this.oldColorKnown = false; + } + + Box.prototype = { + preload: function(entityID) { + print("preload"); + + this.entityID = entityID; + this.storeOldColor(entityID); + }, + startDistantGrab:function(){ + + }, + continueDistantGrab:function(){ + + }, + releaseGrab:function(){ + + } + setHand:function(){ + + } + + }; + + return new Box(); +}); \ No newline at end of file diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js new file mode 100644 index 0000000000..fd49690088 --- /dev/null +++ b/examples/lights/light_modifier.js @@ -0,0 +1,103 @@ +// given a selected light, instantiate some entities that represent various values you can dynamically adjust +// + +var BOX_SCRIPT_URL = Script.resolvePath('box.js'); + +function entitySlider(color) { + this.color = color; + return this; +} + +var RED = { + r: 255, + g: 0, + b: 0 +}; + +var GREEN = { + r: 0, + g: 255, + b: 0 +}; + +var BLUE = { + r: 0, + g: 0, + b: 255 +}; + +var PURPLE = { + r: 255, + g: 0, + b: 255 +}; + +var WHITE = { + r: 255 + g: 255, + b: 255 +}; + +//what's the ux for adjusting values? start with simple entities, try image overlays etc +entitySlider.prototype = { + createAxis: function() { + var properties = { + type: 'Line', + color: this.color, + collisionsWillMove: false, + ignoreForCollisions: true, + }; + + this.axis = Entities.addEntity(properties); + }, + createBoxIndicator: function() { + var properties = { + type: 'Box', + dimensions: { + x: 0.04, + y: 0.04, + z: 0.04 + }, + color: this.color, + position: position, + script: BOX_SCRIPT_URL + }; + + + + this.boxIndicator = Entities.addEntity(properties); + }, + moveIndicatorAlongAxis: function(direction) { + + } +}; + +//create them for this light +function makeSliders(light) { + if (light.type === 'spotlight') { + var USE_COLOR_SLIDER = true; + var USE_INTENSITY_SLIDER = true; + var USE_CUTOFF_SLIDER = true; + var USE_EXPONENT_SLIDER = true; + } + if (light.type === 'pointlight') { + var USE_COLOR_SLIDER = true; + var USE_INTENSITY_SLIDER = true; + var USE_CUTOFF_SLIDER = false; + var USE_EXPONENT_SLIDER = false; + } + if (USE_COLOR_SLIDER === true) { + var r = new entitySlider(RED); + var g = new entitySlider(GREEN); + var b = new entitySlider(BLUE); + } + if (USE_INTENSITY_SLIDER === true) { + var intensity = new entitySlider(WHITE); + } + if (USE_CUTOFF_SLIDER === true) { + var cutoff = new entitySlider(PURPLE); + } + if (USE_EXPONENT_SLIDER === true) { + var exponent = new entitySlider(PURPLE); + } +}; \ No newline at end of file From 7cace240025b3f950a91cbf072e2dbd664fa3235 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 12 Dec 2015 17:14:43 -0800 Subject: [PATCH 11/86] light editing framework --- examples/lights/box.js | 65 ++++++++++++----- examples/lights/light_modifier.js | 116 +++++++++++++++++++++++++----- 2 files changed, 148 insertions(+), 33 deletions(-) diff --git a/examples/lights/box.js b/examples/lights/box.js index 711f5487b2..3a24ef1568 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -1,29 +1,62 @@ (function() { - function Box () { - this.oldColor = {}; - this.oldColorKnown = false; + function Box() { + return this; } Box.prototype = { preload: function(entityID) { - print("preload"); - this.entityID = entityID; - this.storeOldColor(entityID); }, - startDistantGrab:function(){ - + startNearGrab: function() { + this.setInitialProperties(); }, - continueDistantGrab:function(){ - + startDistantGrab: function() { + this.setInitialProperties(); }, - releaseGrab:function(){ - - } - setHand:function(){ - - } + setInitialProperties: function() { + this.initialProperties = Entities.getEntityProperties(this.entityID); + }, + getClampedPosition: function() { + return position; + }, + getClampedRotation: function() { + var rotation = initialProperties.rotation; + return rotation; + }, + continueDistantGrab: function() { + var currentPosition = this.getClampedPosition(); + var distance = Vec3.distance(this.initialProperties.position, currentPosition); + this.sliderValue = scaleValueBasedOnDistanceFromStart(distance); + Entities.editEntity(this.entityID) { + position: currentPosition, + rotation: this.getClampedRotation() + } + }, + releaseGrab: function() { + Entities.editEntity(this.entityID, { + velocity: { + x: 0, + y: 0, + z: 0 + } + }) + this.sendValueToSlider(); + }, + scaleValueBasedOnDistanceFromStart: function(value, min1, max1, min2, max2) { + var min1 = 0; + var max1 = 1; + var min2 = 0; + var max2 = 255; + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); + }, + sendValueToSlider: function() { + var message = { + lightID: this.entityID, + sliderValue: this.sliderValue + } + Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); + }; }; diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index fd49690088..71d3d38c64 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -3,8 +3,10 @@ var BOX_SCRIPT_URL = Script.resolvePath('box.js'); -function entitySlider(color) { +function entitySlider(color, sliderType, verticalOffset) { this.color = color; + this.sliderType = sliderType; + this.verticalOffset = verticalOffset; return this; } @@ -38,6 +40,13 @@ var WHITE = { b: 255 }; +var AXIS_SCALE = 1; +var BOX_DIMENSIONS = { + x: 0.04, + y: 0.04, + z: 0.04 +} + //what's the ux for adjusting values? start with simple entities, try image overlays etc entitySlider.prototype = { createAxis: function() { @@ -53,27 +62,98 @@ entitySlider.prototype = { createBoxIndicator: function() { var properties = { type: 'Box', - dimensions: { - x: 0.04, - y: 0.04, - z: 0.04 - }, + dimensions: BOX_DIMENSIONS, color: this.color, position: position, script: BOX_SCRIPT_URL }; - - this.boxIndicator = Entities.addEntity(properties); }, - moveIndicatorAlongAxis: function(direction) { + handleValueMessages: function(channel, message, sender) { + //easily protect from other people editing your values, but group editing might be fun so lets try that first. + // if (sender !== MyAvatar.sessionUUID) { + // return; + // } + var parsedMessage = JSON.parse(message); + setValueFromMessage(parsedMessage); + }, + setValueFromMessage: function(message) { + var lightProperties = Entities.getEntitiyProperties(message.lightID); + if (this.sliderType === 'color_red') { + Entities.editEntity(message.lightID, { + color: { + red: message.sliderValue, + green: lightProperties.color.g, + blue: lightProperties.color.b + } + }) + } + + if (this.sliderType === 'color_green') { + Entities.editEntity(message.lightID, { + color: { + red: lightProperties.color.r + green: message.sliderValue, + blue: lightProperties.color.b + } + }) + } + + if (this.sliderType === 'color_blue') { + Entities.editEntity(message.lightID, { + color: { + red: lightProperties.color.r, + green: lightProperties.color.g, + blue: message.sliderValue, + } + }) + } + + if (this.sliderType === 'intensity') { + Entities.editEntity(message.lightID, { + intensity: message.sliderValue + }) + } + + if (this.sliderType === 'cutoff') { + Entities.editEntity(message.lightID, { + cutoff: message.sliderValue + }) + } + + if (this.sliderType === 'exponent') { + Entities.editEntity(message.lightID, { + exponent: message.sliderValue + }) + } + }, + subscribeToBoxMessages: function() { + Messages.subscribe('Hifi-Slider-Value-Reciever'); + Messages.messageReceived.connect(this.handleValueMessages); + }, + cleanup: function() { + Entities.deleteEntity(this.boxIndicator); + Entities.deleteEntity(this.axis); + Messages.messageReceived.disconnect(this.handleValueMessages); } }; -//create them for this light +//create them for a given light function makeSliders(light) { + var initialPosition = { + x: 0, + y: 0, + z: 0 + }; + + var perRowOffset = { + x: 0, + y: 0.2, + z: 0 + }; + if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; var USE_INTENSITY_SLIDER = true; @@ -87,17 +167,19 @@ function makeSliders(light) { var USE_EXPONENT_SLIDER = false; } if (USE_COLOR_SLIDER === true) { - var r = new entitySlider(RED); - var g = new entitySlider(GREEN); - var b = new entitySlider(BLUE); + var r = new entitySlider(RED, 'color_red', Vec3.multiply(1, perRowOffset)); + var g = new entitySlider(GREEN, 'color_green', Vec3.multiply(2, perRowOffset)); + var b = new entitySlider(BLUE, 'color_blue', Vec3.multiply(3, perRowOffset)); } if (USE_INTENSITY_SLIDER === true) { - var intensity = new entitySlider(WHITE); + var intensity = new entitySlider(WHITE, 'intensity', Vec3.multiply(4, perRowOffset)); } if (USE_CUTOFF_SLIDER === true) { - var cutoff = new entitySlider(PURPLE); + var cutoff = new entitySlider(PURPLE, 'cutoff', Vec3.multiply(5, perRowOffset)); } if (USE_EXPONENT_SLIDER === true) { - var exponent = new entitySlider(PURPLE); + var exponent = new entitySlider(PURPLE, 'exponent', Vec3.multiply(6, perRowOffset)); } -}; \ No newline at end of file +}; + +makeSliders(light) \ No newline at end of file From 231bcdb8f0fbf0eb8b608f3f89e9f64ed6098f54 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 12 Dec 2015 17:22:57 -0800 Subject: [PATCH 12/86] name some things better and cleanup --- examples/lights/box.js | 4 +- examples/lights/light_modifier.js | 74 +++++++++++++++---------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/examples/lights/box.js b/examples/lights/box.js index 3a24ef1568..27bffe6e33 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -28,6 +28,7 @@ var currentPosition = this.getClampedPosition(); var distance = Vec3.distance(this.initialProperties.position, currentPosition); this.sliderValue = scaleValueBasedOnDistanceFromStart(distance); + Entities.editEntity(this.entityID) { position: currentPosition, rotation: this.getClampedRotation() @@ -41,6 +42,7 @@ z: 0 } }) + this.sendValueToSlider(); }, scaleValueBasedOnDistanceFromStart: function(value, min1, max1, min2, max2) { @@ -52,7 +54,7 @@ }, sendValueToSlider: function() { var message = { - lightID: this.entityID, + boxID: this.entityID, sliderValue: this.sliderValue } Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index 71d3d38c64..9c841eeb24 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -3,10 +3,11 @@ var BOX_SCRIPT_URL = Script.resolvePath('box.js'); -function entitySlider(color, sliderType, verticalOffset) { +function entitySlider(light, color, sliderType, row) { + this.light = light; this.color = color; this.sliderType = sliderType; - this.verticalOffset = verticalOffset; + this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET);; return this; } @@ -42,20 +43,27 @@ var WHITE = { var AXIS_SCALE = 1; var BOX_DIMENSIONS = { - x: 0.04, - y: 0.04, - z: 0.04 -} + x: 0.05, + y: 0.05, + z: 0.05 +}; +var PER_ROW_OFFSET = { + x: 0, + y: 0.2, + z: 0 +}; //what's the ux for adjusting values? start with simple entities, try image overlays etc entitySlider.prototype = { createAxis: function() { - var properties = { - type: 'Line', - color: this.color, - collisionsWillMove: false, - ignoreForCollisions: true, - }; + var position = + var properties = { + type: 'Line', + color: this.color, + collisionsWillMove: false, + ignoreForCollisions: true, + position: position, + }; this.axis = Entities.addEntity(properties); }, @@ -79,54 +87,54 @@ entitySlider.prototype = { setValueFromMessage(parsedMessage); }, setValueFromMessage: function(message) { - var lightProperties = Entities.getEntitiyProperties(message.lightID); + var lightProperties = Entities.getEntityProperties(this.lightID); if (this.sliderType === 'color_red') { - Entities.editEntity(message.lightID, { + Entities.editEntity(this.lightID, { color: { red: message.sliderValue, green: lightProperties.color.g, blue: lightProperties.color.b } - }) + }); } if (this.sliderType === 'color_green') { - Entities.editEntity(message.lightID, { + Entities.editEntity(this.lightID, { color: { red: lightProperties.color.r green: message.sliderValue, blue: lightProperties.color.b } - }) + }); } if (this.sliderType === 'color_blue') { - Entities.editEntity(message.lightID, { + Entities.editEntity(this.lightID, { color: { red: lightProperties.color.r, green: lightProperties.color.g, blue: message.sliderValue, } - }) + }); } if (this.sliderType === 'intensity') { - Entities.editEntity(message.lightID, { + Entities.editEntity(this.lightID, { intensity: message.sliderValue - }) + }); } if (this.sliderType === 'cutoff') { - Entities.editEntity(message.lightID, { + Entities.editEntity(this.lightID, { cutoff: message.sliderValue - }) + }); } if (this.sliderType === 'exponent') { - Entities.editEntity(message.lightID, { + Entities.editEntity(this.lightID, { exponent: message.sliderValue - }) + }); } }, subscribeToBoxMessages: function() { @@ -142,17 +150,6 @@ entitySlider.prototype = { //create them for a given light function makeSliders(light) { - var initialPosition = { - x: 0, - y: 0, - z: 0 - }; - - var perRowOffset = { - x: 0, - y: 0.2, - z: 0 - }; if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; @@ -167,7 +164,7 @@ function makeSliders(light) { var USE_EXPONENT_SLIDER = false; } if (USE_COLOR_SLIDER === true) { - var r = new entitySlider(RED, 'color_red', Vec3.multiply(1, perRowOffset)); + var r = new entitySlider(RED, 'color_red', 1); var g = new entitySlider(GREEN, 'color_green', Vec3.multiply(2, perRowOffset)); var b = new entitySlider(BLUE, 'color_blue', Vec3.multiply(3, perRowOffset)); } @@ -182,4 +179,5 @@ function makeSliders(light) { } }; -makeSliders(light) \ No newline at end of file + +makeSliders(light); \ No newline at end of file From 77969e14810ab20d689dff5e65c3bcd6591b65d9 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 12 Dec 2015 17:31:49 -0800 Subject: [PATCH 13/86] breaktime --- examples/lights/light_modifier.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index 9c841eeb24..d6c964bf55 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -179,5 +179,4 @@ function makeSliders(light) { } }; - makeSliders(light); \ No newline at end of file From 1698e903c94ef65de1366e30de692beafc3352c9 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 11:25:30 -0800 Subject: [PATCH 14/86] midday --- examples/lights/box.js | 6 +- examples/lights/light_modifier.js | 114 ++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 24 deletions(-) diff --git a/examples/lights/box.js b/examples/lights/box.js index 27bffe6e33..ac3cac7ab3 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -18,7 +18,11 @@ this.initialProperties = Entities.getEntityProperties(this.entityID); }, getClampedPosition: function() { - return position; + dPosition = Vec3.subtract(MyAvatar.position, previousPosition); + //convert to localFrame + dPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), dPosition); + + return dPosition; }, getClampedRotation: function() { var rotation = initialProperties.rotation; diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index d6c964bf55..6ae702bf7f 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -3,14 +3,6 @@ var BOX_SCRIPT_URL = Script.resolvePath('box.js'); -function entitySlider(light, color, sliderType, row) { - this.light = light; - this.color = color; - this.sliderType = sliderType; - this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET);; - return this; -} - var RED = { r: 255, g: 0, @@ -42,6 +34,7 @@ var WHITE = { }; var AXIS_SCALE = 1; + var BOX_DIMENSIONS = { x: 0.05, y: 0.05, @@ -53,17 +46,54 @@ var PER_ROW_OFFSET = { z: 0 }; +function entitySlider(light, color, sliderType, row) { + this.light = light; + this.lightID = light.id; + this.initialProperties = light.initialProperties; + this.color = color; + this.sliderType = sliderType; + this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); + + var formattedMessage = { + 'color_red': this.initialProperties.color.r, + 'color_green': this.initialProperties.color.g, + 'color_blue': this.initialProperties.color.b, + 'intensity': this.initialProperties.intensity, + 'exponent': this.initialProperties.exponent, + 'cutoff': this.initialProperties.cutoff, + } + + this.setValueFromMessage(formattedMessage); + this.setInitialSliderPositions(); + + return this; +} + //what's the ux for adjusting values? start with simple entities, try image overlays etc entitySlider.prototype = { createAxis: function() { - var position = - var properties = { - type: 'Line', - color: this.color, - collisionsWillMove: false, - ignoreForCollisions: true, - position: position, - }; + //start of line + var position; + //1 meter along orientationAxis + var endOfAxis; + var properties = { + type: 'Line', + color: this.color, + collisionsWillMove: false, + ignoreForCollisions: true, + dimensions: { + x: 3, + y: 3, + z: 3 + }, + position: position, + linePoints: [{ + x: 0, + y: 0, + z: 0 + }, endOfAxis], + lineWidth: 5, + }; this.axis = Entities.addEntity(properties); }, @@ -79,6 +109,9 @@ entitySlider.prototype = { this.boxIndicator = Entities.addEntity(properties); }, handleValueMessages: function(channel, message, sender) { + if (channel !== 'Hifi-Slider-Value-Reciever') { + return; + } //easily protect from other people editing your values, but group editing might be fun so lets try that first. // if (sender !== MyAvatar.sessionUUID) { // return; @@ -141,6 +174,15 @@ entitySlider.prototype = { Messages.subscribe('Hifi-Slider-Value-Reciever'); Messages.messageReceived.connect(this.handleValueMessages); }, + setInitialSliderPositions:function(){ + + var distanceRed = (this.initialProperties.color.r / 255) * AXIS_SCALE; + var distanceGreen = (this.initialProperties.color.g / 255) * AXIS_SCALE; + var distanceBlue = (this.initialProperties.color.b / 255) * AXIS_SCALE; + var distanceIntensity = (this.initialProperties.intensity / 255) * AXIS_SCALE; + var distanceCutoff = (this.initialProperties.cutoff / 360) * AXIS_SCALE; + var distanceExponent = (this.initialProperties.exponent / 255) * AXIS_SCALE; + }, cleanup: function() { Entities.deleteEntity(this.boxIndicator); Entities.deleteEntity(this.axis); @@ -150,7 +192,6 @@ entitySlider.prototype = { //create them for a given light function makeSliders(light) { - if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; var USE_INTENSITY_SLIDER = true; @@ -165,18 +206,45 @@ function makeSliders(light) { } if (USE_COLOR_SLIDER === true) { var r = new entitySlider(RED, 'color_red', 1); - var g = new entitySlider(GREEN, 'color_green', Vec3.multiply(2, perRowOffset)); - var b = new entitySlider(BLUE, 'color_blue', Vec3.multiply(3, perRowOffset)); + var g = new entitySlider(GREEN, 'color_green', 2); + var b = new entitySlider(BLUE, 'color_blue', 3); } if (USE_INTENSITY_SLIDER === true) { - var intensity = new entitySlider(WHITE, 'intensity', Vec3.multiply(4, perRowOffset)); + var intensity = new entitySlider(WHITE, 'intensity', 4); } if (USE_CUTOFF_SLIDER === true) { - var cutoff = new entitySlider(PURPLE, 'cutoff', Vec3.multiply(5, perRowOffset)); + var cutoff = new entitySlider(PURPLE, 'cutoff', 5); } if (USE_EXPONENT_SLIDER === true) { - var exponent = new entitySlider(PURPLE, 'exponent', Vec3.multiply(6, perRowOffset)); + var exponent = new entitySlider(PURPLE, 'exponent', 6); } }; -makeSliders(light); \ No newline at end of file +function subScribeToNewLights() { + Messages.subscribe('Hifi-Light-Mod-Receiver'); + Messages.messageReceived.connect(handleLightModMessages); +} + +function handleLightModMessages(channel, message, sender) { + if (channel !== 'Hifi-Light-Mod-Receiver') { + return; + } + if (sender !== MyAvatar.sessionUUID) { + return; + } + var parsedMessage = JSON.parse(message); + var light = message.light; + makeSliders(light); +} + +subScribeToNewLights(); + + // diffuseColor: { red: 255, green: 255, blue: 255 }, + // ambientColor: { red: 255, green: 255, blue: 255 }, + // specularColor: { red: 255, green: 255, blue: 255 }, + + // constantAttenuation: 1, + // linearAttenuation: 0, + // quadraticAttenuation: 0, + // exponent: 0, + // cutoff: 180, // in degrees \ No newline at end of file From 18458a843147f55b4163cac6af1e5dd6b37c2468 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 12:04:44 -0800 Subject: [PATCH 15/86] test scene --- examples/lights/testScene.js | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 examples/lights/testScene.js diff --git a/examples/lights/testScene.js b/examples/lights/testScene.js new file mode 100644 index 0000000000..eeb5f4ffd7 --- /dev/null +++ b/examples/lights/testScene.js @@ -0,0 +1,104 @@ + // These constants define the Spotlight position and orientation relative to the model + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + var basePosition, avatarRot; + avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(SPAWN_RANGE * 3, Quat.getFront(avatarRot))); + + var ground = Entities.addEntity({ + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/woodFloor.fbx", + dimensions: { + x: 100, + y: 2, + z: 100 + }, + position: basePosition, + shapeType: 'box' + }); + + var light, block; + + basePosition.y += 2; + + function createLight() { + var lightProperties = { + name: 'Hifi-Spotlight' + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: box, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + rotation: lightTransform.q, + } + light = Entities.addEntity(lightProperties); + + var message = { + light: { + id: light, + type: 'spotlight', + initialProperties:lightProperties + } + }; + Messages.sendMessage('Hifi-Light-Mod-Receiver', JSON.stringify(message)); + + } + + function createBlock() { + var blockProperties = { + name: 'Hifi-Spotlight-Block', + type: 'Box', + dimensions: { + x: 1, + y: 1, + z: 1 + }, + collisionsWillMove: true, + shapeType: 'Box', + color: { + red: 0, + green: 0 + blue: 255 + }, + position: basePosition + } + + block = Entities.addEntity(block); + } + + function evalLightWorldTransform(modelPos, modelRot) { + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + } + + function cleanup() { + Entities.deleteEntity(ground); + Entities.deleteEntity(light); + } + + createBlock(); + createLight(); \ No newline at end of file From ea0ffa589930f3528ea347e33709f865e6e645c4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 16:39:25 -0800 Subject: [PATCH 16/86] support changing values for lights via message --- examples/lights/box.js | 18 ++- examples/lights/light_modifier.js | 197 ++++++++++++++++++++++-------- examples/lights/testScene.js | 38 ++++-- 3 files changed, 183 insertions(+), 70 deletions(-) diff --git a/examples/lights/box.js b/examples/lights/box.js index ac3cac7ab3..a8e708b344 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -1,5 +1,11 @@ (function() { + var AXIS_SCALE = 1; + var COLOR_MAX = 255; + var INTENSITY_MAX = 10; + var CUTOFF_MAX = 360; + var EXPONENT_MAX = 1; + function Box() { return this; } @@ -7,6 +13,8 @@ Box.prototype = { preload: function(entityID) { this.entityID = entityID; + var userData = Entities.getEntityProperties(this.entityID, "userData"); + this.userData = JSON.parse(userData); }, startNearGrab: function() { this.setInitialProperties(); @@ -31,7 +39,8 @@ continueDistantGrab: function() { var currentPosition = this.getClampedPosition(); var distance = Vec3.distance(this.initialProperties.position, currentPosition); - this.sliderValue = scaleValueBasedOnDistanceFromStart(distance); + if () + this.sliderValue = scaleValueBasedOnDistanceFromStart(distance); Entities.editEntity(this.entityID) { position: currentPosition, @@ -49,16 +58,17 @@ this.sendValueToSlider(); }, - scaleValueBasedOnDistanceFromStart: function(value, min1, max1, min2, max2) { + scaleValueBasedOnDistanceFromStart: function(value, max2) { var min1 = 0; var max1 = 1; var min2 = 0; - var max2 = 255; + var max2 = max2; return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); }, sendValueToSlider: function() { var message = { - boxID: this.entityID, + lightID:userData.lightID, + sliderType:userData.sliderType, sliderValue: this.sliderValue } Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index 6ae702bf7f..f1c27b4e4b 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -28,7 +28,7 @@ var PURPLE = { }; var WHITE = { - r: 255 + r: 255, g: 255, b: 255 }; @@ -48,24 +48,48 @@ var PER_ROW_OFFSET = { function entitySlider(light, color, sliderType, row) { this.light = light; - this.lightID = light.id; + this.lightID = light.id.replace(/[{}]/g, ""); this.initialProperties = light.initialProperties; this.color = color; this.sliderType = sliderType; this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); - var formattedMessage = { - 'color_red': this.initialProperties.color.r, - 'color_green': this.initialProperties.color.g, - 'color_blue': this.initialProperties.color.b, - 'intensity': this.initialProperties.intensity, - 'exponent': this.initialProperties.exponent, - 'cutoff': this.initialProperties.cutoff, + var message = { + lightID: this.lightID, + sliderType: this.sliderType, + sliderValue: null + }; + + if (this.sliderType === 'color_red') { + message.sliderValue = this.initialProperties.color.red + this.setValueFromMessage(message); + } + if (this.sliderType === 'color_green') { + message.sliderValue = this.initialProperties.color.green + this.setValueFromMessage(message); + } + if (this.sliderType === 'color_blue') { + message.sliderValue = this.initialProperties.color.blue + this.setValueFromMessage(message); } - this.setValueFromMessage(formattedMessage); - this.setInitialSliderPositions(); + if (this.sliderType === 'intensity') { + message.sliderValue = this.initialProperties.intensity + this.setValueFromMessage(message); + } + if (this.sliderType === 'exponent') { + message.sliderValue = this.initialProperties.exponent + this.setValueFromMessage(message); + } + + if (this.sliderType === 'cutoff') { + message.sliderValue = this.initialProperties.cutoff + this.setValueFromMessage(message); + } + + // this.setInitialSliderPositions(); + this.subscribeToBoxMessages(); return this; } @@ -103,31 +127,44 @@ entitySlider.prototype = { dimensions: BOX_DIMENSIONS, color: this.color, position: position, - script: BOX_SCRIPT_URL + script: BOX_SCRIPT_URL, + userData: JSON.stringify({ + lightModifierKey: { + lightID: this.lightID, + sliderType: this.sliderType + } + }) }; this.boxIndicator = Entities.addEntity(properties); }, - handleValueMessages: function(channel, message, sender) { - if (channel !== 'Hifi-Slider-Value-Reciever') { + setValueFromMessage: function(message) { + print('VALUE MESSAGE::'+JSON.stringify(message)) + print('LIGHT ID::'+this.lightID); + + //message is not for our light + if (message.lightID !== this.lightID) { + print('not our light') return; } - //easily protect from other people editing your values, but group editing might be fun so lets try that first. - // if (sender !== MyAvatar.sessionUUID) { - // return; - // } - var parsedMessage = JSON.parse(message); - setValueFromMessage(parsedMessage); - }, - setValueFromMessage: function(message) { + + //message is not our type + if (message.sliderType !== this.sliderType) { + print('not our slider type') + return + } + + print('SHOULD SET SOME VALUE:::' + message.sliderType); + var lightProperties = Entities.getEntityProperties(this.lightID); + // print('LIGHT PROPERTIES::'+JSON.stringify(lightProperties)); if (this.sliderType === 'color_red') { Entities.editEntity(this.lightID, { color: { red: message.sliderValue, - green: lightProperties.color.g, - blue: lightProperties.color.b + green: lightProperties.color.green, + blue: lightProperties.color.blue } }); } @@ -135,9 +172,9 @@ entitySlider.prototype = { if (this.sliderType === 'color_green') { Entities.editEntity(this.lightID, { color: { - red: lightProperties.color.r + red: lightProperties.color.red, green: message.sliderValue, - blue: lightProperties.color.b + blue: lightProperties.color.blue } }); } @@ -145,14 +182,15 @@ entitySlider.prototype = { if (this.sliderType === 'color_blue') { Entities.editEntity(this.lightID, { color: { - red: lightProperties.color.r, - green: lightProperties.color.g, + red: lightProperties.color.red, + green: lightProperties.color.green, blue: message.sliderValue, } }); } if (this.sliderType === 'intensity') { + print('CHANGING INTENSITY TO::' + message.sliderValue) Entities.editEntity(this.lightID, { intensity: message.sliderValue }); @@ -171,17 +209,22 @@ entitySlider.prototype = { } }, subscribeToBoxMessages: function() { + print('subscribing to box messages'); Messages.subscribe('Hifi-Slider-Value-Reciever'); - Messages.messageReceived.connect(this.handleValueMessages); + Messages.messageReceived.connect(handleValueMessages); }, - setInitialSliderPositions:function(){ + setInitialSliderPositions: function() { + var COLOR_MAX = 255; + var INTENSITY_MAX = 10; + var CUTOFF_MAX = 360; + var EXPONENT_MAX = 1; - var distanceRed = (this.initialProperties.color.r / 255) * AXIS_SCALE; - var distanceGreen = (this.initialProperties.color.g / 255) * AXIS_SCALE; - var distanceBlue = (this.initialProperties.color.b / 255) * AXIS_SCALE; - var distanceIntensity = (this.initialProperties.intensity / 255) * AXIS_SCALE; - var distanceCutoff = (this.initialProperties.cutoff / 360) * AXIS_SCALE; - var distanceExponent = (this.initialProperties.exponent / 255) * AXIS_SCALE; + var distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE; + var distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE; + var distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE; + var distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE; + var distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE; + var distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE; }, cleanup: function() { Entities.deleteEntity(this.boxIndicator); @@ -190,8 +233,18 @@ entitySlider.prototype = { } }; -//create them for a given light +var sliders = []; +var slidersRef = { + 'color_red': null, + 'color_green': null, + 'color_blue': null, + intensity: null, + cutoff: null, + exponent: null +} + function makeSliders(light) { + print('light in makesliders:::' + light) if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; var USE_INTENSITY_SLIDER = true; @@ -205,22 +258,31 @@ function makeSliders(light) { var USE_EXPONENT_SLIDER = false; } if (USE_COLOR_SLIDER === true) { - var r = new entitySlider(RED, 'color_red', 1); - var g = new entitySlider(GREEN, 'color_green', 2); - var b = new entitySlider(BLUE, 'color_blue', 3); + slidersRef.color_red = new entitySlider(light, RED, 'color_red', 1); + slidersRef.color_green = new entitySlider(light, GREEN, 'color_green', 2); + slidersRef.color_blue = new entitySlider(light, BLUE, 'color_blue', 3); + + sliders.push(slidersRef.color_red); + sliders.push(slidersRef.color_green); + sliders.push(slidersRef.color_blue); + } if (USE_INTENSITY_SLIDER === true) { - var intensity = new entitySlider(WHITE, 'intensity', 4); + slidersRef.intensity = new entitySlider(light, WHITE, 'intensity', 4); + sliders.push(slidersRef.intensity); } if (USE_CUTOFF_SLIDER === true) { - var cutoff = new entitySlider(PURPLE, 'cutoff', 5); + slidersRef.cutoff = new entitySlider(light, PURPLE, 'cutoff', 5); + sliders.push(slidersRef.cutoff); } if (USE_EXPONENT_SLIDER === true) { - var exponent = new entitySlider(PURPLE, 'exponent', 6); + slidersRef.exponent = new entitySlider(light, PURPLE, 'exponent', 6); + sliders.push(slidersRef.exponent); } }; function subScribeToNewLights() { + print('subscribing to light messages') Messages.subscribe('Hifi-Light-Mod-Receiver'); Messages.messageReceived.connect(handleLightModMessages); } @@ -233,18 +295,47 @@ function handleLightModMessages(channel, message, sender) { return; } var parsedMessage = JSON.parse(message); - var light = message.light; - makeSliders(light); + + print('MESSAGE LIGHT:::' + message) + makeSliders(parsedMessage.light); } +function handleValueMessages(channel, message, sender) { + + + if (channel !== 'Hifi-Slider-Value-Reciever') { + return; + } + print('HANDLE VALUE MESSAGE') + //easily protect from other people editing your values, but group editing might be fun so lets try that first. + // if (sender !== MyAvatar.sessionUUID) { + // return; + // } + var parsedMessage = JSON.parse(message); + + slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage) + + // this.setValueFromMessage(parsedMessage); +} + +function cleanup() { + while (sliders.length > 0) { + var slider = sliders.pop(); + slider.cleanup(); + } + Messages.messageReceived.disconnect(handleLightModMessages); + delete sliders +} + +Script.scriptEnding.connect(cleanup); subScribeToNewLights(); - // diffuseColor: { red: 255, green: 255, blue: 255 }, - // ambientColor: { red: 255, green: 255, blue: 255 }, - // specularColor: { red: 255, green: 255, blue: 255 }, +// diffuseColor: { red: 255, green: 255, blue: 255 }, +// ambientColor: { red: 255, green: 255, blue: 255 }, +// specularColor: { red: 255, green: 255, blue: 255 }, - // constantAttenuation: 1, - // linearAttenuation: 0, - // quadraticAttenuation: 0, - // exponent: 0, - // cutoff: 180, // in degrees \ No newline at end of file +// constantAttenuation: 1, +// linearAttenuation: 0, +// quadraticAttenuation: 0, +// exponent: 0, +// cutoff: 180, // in degrees \ No newline at end of file diff --git a/examples/lights/testScene.js b/examples/lights/testScene.js index eeb5f4ffd7..4cc0058bea 100644 --- a/examples/lights/testScene.js +++ b/examples/lights/testScene.js @@ -12,7 +12,7 @@ var basePosition, avatarRot; avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); - basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(SPAWN_RANGE * 3, Quat.getFront(avatarRot))); + basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(-3, Quat.getUp(avatarRot))); var ground = Entities.addEntity({ type: "Model", @@ -28,11 +28,14 @@ var light, block; - basePosition.y += 2; + // basePosition.y += 2; function createLight() { + print('making light' + block) + var blockProperties = Entities.getEntityProperties(block, ["position", "rotation"]); + var lightTransform = evalLightWorldTransform(blockProperties.position, blockProperties.rotation); var lightProperties = { - name: 'Hifi-Spotlight' + name: 'Hifi-Spotlight', type: "Light", isSpotlight: true, dimensions: { @@ -40,33 +43,40 @@ y: 2, z: 20 }, - parentID: box, + parentID: block, color: { red: 255, - green: 255, + green: 0, blue: 255 }, intensity: 2, exponent: 0.3, cutoff: 20, - lifetime: LIFETIME, + lifetime: -1, position: lightTransform.p, - rotation: lightTransform.q, - } + rotation: lightTransform.q + }; + light = Entities.addEntity(lightProperties); var message = { light: { id: light, type: 'spotlight', - initialProperties:lightProperties + initialProperties: lightProperties } }; + Messages.sendMessage('Hifi-Light-Mod-Receiver', JSON.stringify(message)); + print('SENT MESSAGE') } function createBlock() { + print('making block'); + + var position = basePosition; + position.y += 5; var blockProperties = { name: 'Hifi-Spotlight-Block', type: 'Box', @@ -76,16 +86,15 @@ z: 1 }, collisionsWillMove: true, - shapeType: 'Box', color: { red: 0, - green: 0 + green: 0, blue: 255 }, - position: basePosition + position: position } - block = Entities.addEntity(block); + block = Entities.addEntity(blockProperties); } function evalLightWorldTransform(modelPos, modelRot) { @@ -96,9 +105,12 @@ } function cleanup() { + Entities.deleteEntity(block); Entities.deleteEntity(ground); Entities.deleteEntity(light); } + Script.scriptEnding.connect(cleanup); + createBlock(); createLight(); \ No newline at end of file From 8bdced85763a6b8643d8f1b0bf63fa921b4747cd Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 16:51:12 -0800 Subject: [PATCH 17/86] more work --- examples/lights/box.js | 22 +++++++++++++++++----- examples/lights/light_modifier.js | 26 +++++++++++++++----------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/examples/lights/box.js b/examples/lights/box.js index a8e708b344..e43ae22a49 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -26,7 +26,8 @@ this.initialProperties = Entities.getEntityProperties(this.entityID); }, getClampedPosition: function() { - dPosition = Vec3.subtract(MyAvatar.position, previousPosition); + + dPosition = Vec3.subtract(MyAvatar.position, this.previousPosition); //convert to localFrame dPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), dPosition); @@ -39,8 +40,19 @@ continueDistantGrab: function() { var currentPosition = this.getClampedPosition(); var distance = Vec3.distance(this.initialProperties.position, currentPosition); - if () - this.sliderValue = scaleValueBasedOnDistanceFromStart(distance); + + if (userData.sliderType === 'color_red' || userData.sliderType === 'color_green' || userData.sliderType === 'color_blue') { + this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, COLOR_MAX); + } + if (userData.sliderType === 'intensity') { + this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, INTENSITY_MAX); + } + if (userData.sliderType === 'cutoff') { + this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, CUTOFF_MAX); + } + if (userData.sliderType === 'exponent') { + this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); + } Entities.editEntity(this.entityID) { position: currentPosition, @@ -67,8 +79,8 @@ }, sendValueToSlider: function() { var message = { - lightID:userData.lightID, - sliderType:userData.sliderType, + lightID: userData.lightID, + sliderType: userData.sliderType, sliderValue: this.sliderValue } Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index f1c27b4e4b..983ea5bcb9 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -1,6 +1,8 @@ // given a selected light, instantiate some entities that represent various values you can dynamically adjust // + + var BOX_SCRIPT_URL = Script.resolvePath('box.js'); var RED = { @@ -54,6 +56,9 @@ function entitySlider(light, color, sliderType, row) { this.sliderType = sliderType; this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); + this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(this.avatarRot))); + var message = { lightID: this.lightID, sliderType: this.sliderType, @@ -97,8 +102,14 @@ function entitySlider(light, color, sliderType, row) { entitySlider.prototype = { createAxis: function() { //start of line - var position; - //1 meter along orientationAxis + var position = Vec3.sum(this.basePosition, this.verticalOffset); + + //line starts on left and goes to right + //set the end of the line to the right + var rightVector = Quat.getRight(this.avatarRot); + var extension = Vec3.multiply(AXIS_SCALE, rightVector); + var endOfAxis = Vec3.sum(position, extension); + var endOfAxis; var properties = { type: 'Line', @@ -139,8 +150,6 @@ entitySlider.prototype = { this.boxIndicator = Entities.addEntity(properties); }, setValueFromMessage: function(message) { - print('VALUE MESSAGE::'+JSON.stringify(message)) - print('LIGHT ID::'+this.lightID); //message is not for our light if (message.lightID !== this.lightID) { @@ -154,10 +163,9 @@ entitySlider.prototype = { return } - print('SHOULD SET SOME VALUE:::' + message.sliderType); + print('should set:::' + this.sliderType); var lightProperties = Entities.getEntityProperties(this.lightID); - // print('LIGHT PROPERTIES::'+JSON.stringify(lightProperties)); if (this.sliderType === 'color_red') { Entities.editEntity(this.lightID, { @@ -190,7 +198,6 @@ entitySlider.prototype = { } if (this.sliderType === 'intensity') { - print('CHANGING INTENSITY TO::' + message.sliderValue) Entities.editEntity(this.lightID, { intensity: message.sliderValue }); @@ -209,7 +216,6 @@ entitySlider.prototype = { } }, subscribeToBoxMessages: function() { - print('subscribing to box messages'); Messages.subscribe('Hifi-Slider-Value-Reciever'); Messages.messageReceived.connect(handleValueMessages); }, @@ -296,17 +302,15 @@ function handleLightModMessages(channel, message, sender) { } var parsedMessage = JSON.parse(message); - print('MESSAGE LIGHT:::' + message) makeSliders(parsedMessage.light); } function handleValueMessages(channel, message, sender) { - + if (channel !== 'Hifi-Slider-Value-Reciever') { return; } - print('HANDLE VALUE MESSAGE') //easily protect from other people editing your values, but group editing might be fun so lets try that first. // if (sender !== MyAvatar.sessionUUID) { // return; From 112dd4209b3efa8032c8b5918429c3b633d2a85f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 17:27:01 -0800 Subject: [PATCH 18/86] prep for incoming --- examples/lights/box.js | 2 +- examples/lights/light_modifier.js | 49 ++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/examples/lights/box.js b/examples/lights/box.js index e43ae22a49..806d6be389 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -72,7 +72,7 @@ }, scaleValueBasedOnDistanceFromStart: function(value, max2) { var min1 = 0; - var max1 = 1; + var max1 = AXIS_SCALE; var min2 = 0; var max2 = max2; return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index 983ea5bcb9..ac29b65f74 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -93,8 +93,10 @@ function entitySlider(light, color, sliderType, row) { this.setValueFromMessage(message); } - // this.setInitialSliderPositions(); + this.setInitialSliderPositions(); this.subscribeToBoxMessages(); + this.createAxis(); + this.createBoxIndicator(); return this; } @@ -133,11 +135,37 @@ entitySlider.prototype = { this.axis = Entities.addEntity(properties); }, createBoxIndicator: function() { + var position = Vec3.sum(this.basePosition, this.verticalOffset); + + //line starts on left and goes to right + //set the end of the line to the right + var rightVector = Quat.getRight(this.avatarRot); + var initialDistance; + if (this.sliderType === 'color_red') { + initialDistance = this.distanceRed; + } + if (this.sliderType === 'color_green') { + initialDistance = this.distanceGreen; + } + if (this.sliderType === 'color_blue') { + initialDistance = this.distanceBlue; + } + if (this.sliderType === 'intensity') { + initialDistance = this.distanceRed; + } + if (this.sliderType === 'cutoff') { + initialDistance = this.distanceCutoff; + } + if (this.sliderType === 'exponent') { + initialDistance = this.distanceExponent; + } + var extension = Vec3.multiply(initialDistance, rightVector); + var endOfAxis = Vec3.sum(position, extension); var properties = { type: 'Box', dimensions: BOX_DIMENSIONS, color: this.color, - position: position, + position: endOfAxis, script: BOX_SCRIPT_URL, userData: JSON.stringify({ lightModifierKey: { @@ -225,12 +253,12 @@ entitySlider.prototype = { var CUTOFF_MAX = 360; var EXPONENT_MAX = 1; - var distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE; - var distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE; - var distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE; - var distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE; - var distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE; - var distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE; + this.distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE; + this.distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE; + this.distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE; + this.distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE; + this.distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE; + this.distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE; }, cleanup: function() { Entities.deleteEntity(this.boxIndicator); @@ -307,7 +335,6 @@ function handleLightModMessages(channel, message, sender) { function handleValueMessages(channel, message, sender) { - if (channel !== 'Hifi-Slider-Value-Reciever') { return; } @@ -318,8 +345,6 @@ function handleValueMessages(channel, message, sender) { var parsedMessage = JSON.parse(message); slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage) - - // this.setValueFromMessage(parsedMessage); } function cleanup() { @@ -334,6 +359,8 @@ function cleanup() { Script.scriptEnding.connect(cleanup); subScribeToNewLights(); + +//other light properties // diffuseColor: { red: 255, green: 255, blue: 255 }, // ambientColor: { red: 255, green: 255, blue: 255 }, // specularColor: { red: 255, green: 255, blue: 255 }, From 109aa89619b10fc108c2980a47933d4596a95631 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 18:42:00 -0800 Subject: [PATCH 19/86] end of day --- examples/lights/box.js | 22 +++++++++++----------- examples/lights/light_modifier.js | 3 +++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/examples/lights/box.js b/examples/lights/box.js index 806d6be389..b1f9252073 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -13,8 +13,9 @@ Box.prototype = { preload: function(entityID) { this.entityID = entityID; - var userData = Entities.getEntityProperties(this.entityID, "userData"); - this.userData = JSON.parse(userData); + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + var parsedUserData = JSON.parse(entityProperties.userData); + var userData = parsedUserData.lightModifierKey; }, startNearGrab: function() { this.setInitialProperties(); @@ -26,10 +27,10 @@ this.initialProperties = Entities.getEntityProperties(this.entityID); }, getClampedPosition: function() { - - dPosition = Vec3.subtract(MyAvatar.position, this.previousPosition); + var dPosition; + // dPosition = Vec3.subtract(MyAvatar.position, this.previousPosition); //convert to localFrame - dPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), dPosition); + // dPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), dPosition); return dPosition; }, @@ -52,12 +53,12 @@ } if (userData.sliderType === 'exponent') { this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); - } + }; - Entities.editEntity(this.entityID) { + Entities.editEntity(this.entityID, { position: currentPosition, - rotation: this.getClampedRotation() - } + // rotation: this.getClampedRotation() + }); }, releaseGrab: function() { Entities.editEntity(this.entityID, { @@ -84,8 +85,7 @@ sliderValue: this.sliderValue } Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); - }; - + } }; return new Box(); diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index ac29b65f74..2e11c5e22e 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -115,6 +115,7 @@ entitySlider.prototype = { var endOfAxis; var properties = { type: 'Line', + name: 'Hifi-Slider-Axis::'+this.sliderType, color: this.color, collisionsWillMove: false, ignoreForCollisions: true, @@ -135,6 +136,7 @@ entitySlider.prototype = { this.axis = Entities.addEntity(properties); }, createBoxIndicator: function() { + print('BOX COLOR IS:::'+JSON.stringify(this.color)); var position = Vec3.sum(this.basePosition, this.verticalOffset); //line starts on left and goes to right @@ -163,6 +165,7 @@ entitySlider.prototype = { var endOfAxis = Vec3.sum(position, extension); var properties = { type: 'Box', + name: 'Hifi-Slider::'+this.sliderType, dimensions: BOX_DIMENSIONS, color: this.color, position: endOfAxis, From 22756d168b25af08f004d5b7b8de24dd59134db4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 15 Dec 2015 10:35:35 -0800 Subject: [PATCH 20/86] Changed grammar to support boolean and and or. --- libraries/animation/src/AnimExpression.cpp | 64 ++++++++++------------ libraries/animation/src/AnimExpression.h | 16 +++--- tests/QTestExtensions.h | 2 +- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp index b76aaff3f9..5fc1bc9b4f 100644 --- a/libraries/animation/src/AnimExpression.cpp +++ b/libraries/animation/src/AnimExpression.cpp @@ -78,7 +78,15 @@ AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QStr } int pos = (int)(begin - str.begin()); int len = (int)(iter - begin); - return Token(QStringRef(const_cast(&str), pos, len)); + + QStringRef stringRef(const_cast(&str), pos, len); + if (stringRef == "true") { + return Token(true); + } else if (stringRef == "false") { + return Token(false); + } else { + return Token(stringRef); + } } // TODO: not very efficient or accruate, but it's close enough for now. @@ -198,19 +206,19 @@ AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::co /* Expr → Term Expr' -Expr' → + Term Expr' - | – Term Expr' +Expr' → '||' Term Expr' | ε Term → Factor Term' -Term' → * Term' - | / Term' +Term' → '&&' Term' | ε Factor → INT + | BOOL | FLOAT | IDENTIFIER - | (Expr) + | '(' Expr ')' */ +// Expr → Term Expr' bool AnimExpression::parseExpr(const QString& str, QString::const_iterator& iter) { if (!parseTerm(str, iter)) { return false; @@ -221,9 +229,10 @@ bool AnimExpression::parseExpr(const QString& str, QString::const_iterator& iter return true; } +// Expr' → '||' Term Expr' | ε bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& iter) { auto token = consumeToken(str, iter); - if (token.type == Token::Plus) { + if (token.type == Token::Or) { if (!parseTerm(str, iter)) { unconsumeToken(token); return false; @@ -232,18 +241,7 @@ bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& unconsumeToken(token); return false; } - _opCodes.push_back(OpCode {OpCode::Add}); - return true; - } else if (token.type == Token::Minus) { - if (!parseTerm(str, iter)) { - unconsumeToken(token); - return false; - } - if (!parseExprPrime(str, iter)) { - unconsumeToken(token); - return false; - } - _opCodes.push_back(OpCode {OpCode::Subtract}); + _opCodes.push_back(OpCode {OpCode::Or}); return true; } else { unconsumeToken(token); @@ -251,6 +249,7 @@ bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& } } +// Term → Factor Term' bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter) { if (!parseFactor(str, iter)) { return false; @@ -261,9 +260,10 @@ bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter return true; } +// Term' → '&&' Term' | ε bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& iter) { auto token = consumeToken(str, iter); - if (token.type == Token::Multiply) { + if (token.type == Token::And) { if (!parseTerm(str, iter)) { unconsumeToken(token); return false; @@ -272,18 +272,7 @@ bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& unconsumeToken(token); return false; } - _opCodes.push_back(OpCode {OpCode::Multiply}); - return true; - } else if (token.type == Token::Divide) { - if (!parseTerm(str, iter)) { - unconsumeToken(token); - return false; - } - if (!parseTermPrime(str, iter)) { - unconsumeToken(token); - return false; - } - _opCodes.push_back(OpCode {OpCode::Divide}); + _opCodes.push_back(OpCode {OpCode::And}); return true; } else { unconsumeToken(token); @@ -291,11 +280,15 @@ bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& } } +// Factor → INT | BOOL | FLOAT | IDENTIFIER | '(' Expr ')' bool AnimExpression::parseFactor(const QString& str, QString::const_iterator& iter) { auto token = consumeToken(str, iter); if (token.type == Token::Int) { _opCodes.push_back(OpCode {token.intVal}); return true; + } else if (token.type == Token::Bool) { + _opCodes.push_back(OpCode {(bool)token.intVal}); + return true; } else if (token.type == Token::Float) { _opCodes.push_back(OpCode {token.floatVal}); return true; @@ -351,7 +344,7 @@ AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const case OpCode::UnaryMinus: evalUnaryMinus(map, stack); break; } } - return stack.top(); + return coerseToValue(map, stack.top()); } #define POP_BOOL(NAME) \ @@ -602,8 +595,7 @@ AnimExpression::OpCode AnimExpression::coerseToValue(const AnimVariantMap& map, const AnimVariant& var = map.get(opCode.strVal); switch (var.getType()) { case AnimVariant::Type::Bool: - qCWarning(animation) << "AnimExpression: type missmatch, expected a number not a bool"; - return OpCode(0); + return OpCode((bool)var.getBool()); break; case AnimVariant::Type::Int: return OpCode(var.getInt()); @@ -631,6 +623,7 @@ AnimExpression::OpCode AnimExpression::coerseToValue(const AnimVariantMap& map, } } +#ifndef NDEBUG void AnimExpression::dumpOpCodes() const { QString tmp; for (auto& op : _opCodes) { @@ -660,3 +653,4 @@ void AnimExpression::dumpOpCodes() const { qCDebug(animation).nospace().noquote() << "opCodes =" << tmp; qCDebug(animation).resetFormat(); } +#endif diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 6e204483d5..8a1961b326 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -27,6 +27,7 @@ protected: enum Type { End = 0, Identifier, + Bool, Int, Float, And, @@ -50,8 +51,9 @@ protected: }; Token(Type type) : type {type} {} Token(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} - Token(int val) : type {Type::Int}, intVal {val} {} - Token(float val) : type {Type::Float}, floatVal {val} {} + explicit Token(int val) : type {Type::Int}, intVal {val} {} + explicit Token(bool val) : type {Type::Bool}, intVal {val} {} + explicit Token(float val) : type {Type::Float}, floatVal {val} {} Type type {End}; QString strVal; int intVal {0}; @@ -81,11 +83,11 @@ protected: UnaryMinus }; OpCode(Type type) : type {type} {} - OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} - OpCode(const QString& str) : type {Type::Identifier}, strVal {str} {} - OpCode(int val) : type {Type::Int}, intVal {val} {} - OpCode(bool val) : type {Type::Bool}, intVal {(int)val} {} - OpCode(float val) : type {Type::Float}, floatVal {val} {} + explicit OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {} + explicit OpCode(const QString& str) : type {Type::Identifier}, strVal {str} {} + explicit OpCode(int val) : type {Type::Int}, intVal {val} {} + explicit OpCode(bool val) : type {Type::Bool}, intVal {(int)val} {} + explicit OpCode(float val) : type {Type::Float}, floatVal {val} {} bool coerceBool(const AnimVariantMap& map) const { if (type == Int || type == Bool) { diff --git a/tests/QTestExtensions.h b/tests/QTestExtensions.h index 16e51b41ee..b7b9795a9a 100644 --- a/tests/QTestExtensions.h +++ b/tests/QTestExtensions.h @@ -274,7 +274,7 @@ struct ByteData { QTextStream & operator << (QTextStream& stream, const ByteData & wrapper) { // Print bytes as hex - stream << QByteArray::fromRawData(wrapper.data, wrapper.length).toHex(); + stream << QByteArray::fromRawData(wrapper.data, (int)wrapper.length).toHex(); return stream; } From ab85e2967a9bca036abac6b49c5199fba4d4e54c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 15 Dec 2015 13:18:30 -0800 Subject: [PATCH 21/86] AnimExpression: support for unary not. --- libraries/animation/src/AnimExpression.cpp | 32 +++++++-- libraries/animation/src/AnimExpression.h | 1 + tests/animation/src/AnimTests.cpp | 77 ++++++++-------------- 3 files changed, 53 insertions(+), 57 deletions(-) diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp index 5fc1bc9b4f..79004a72a6 100644 --- a/libraries/animation/src/AnimExpression.cpp +++ b/libraries/animation/src/AnimExpression.cpp @@ -208,9 +208,11 @@ AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::co Expr → Term Expr' Expr' → '||' Term Expr' | ε -Term → Factor Term' -Term' → '&&' Term' +Term → Unary Term' +Term' → '&&' Unary Term' | ε +Unary → '!' Unary + | Factor Factor → INT | BOOL | FLOAT @@ -249,9 +251,9 @@ bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& } } -// Term → Factor Term' +// Term → Unary Term' bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter) { - if (!parseFactor(str, iter)) { + if (!parseUnary(str, iter)) { return false; } if (!parseTermPrime(str, iter)) { @@ -260,11 +262,11 @@ bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter return true; } -// Term' → '&&' Term' | ε +// Term' → '&&' Unary Term' | ε bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& iter) { auto token = consumeToken(str, iter); if (token.type == Token::And) { - if (!parseTerm(str, iter)) { + if (!parseUnary(str, iter)) { unconsumeToken(token); return false; } @@ -280,6 +282,24 @@ bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& } } +// Unary → '!' Unary | Factor +bool AnimExpression::parseUnary(const QString& str, QString::const_iterator& iter) { + + auto token = consumeToken(str, iter); + if (token.type == Token::Not) { + if (!parseUnary(str, iter)) { + unconsumeToken(token); + return false; + } + _opCodes.push_back(OpCode {OpCode::Not}); + return true; + } + unconsumeToken(token); + + return parseFactor(str, iter); +} + + // Factor → INT | BOOL | FLOAT | IDENTIFIER | '(' Expr ')' bool AnimExpression::parseFactor(const QString& str, QString::const_iterator& iter) { auto token = consumeToken(str, iter); diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 8a1961b326..468217f5b3 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -119,6 +119,7 @@ protected: bool parseExprPrime(const QString& str, QString::const_iterator& iter); bool parseTerm(const QString& str, QString::const_iterator& iter); bool parseTermPrime(const QString& str, QString::const_iterator& iter); + bool parseUnary(const QString& str, QString::const_iterator& iter); bool parseFactor(const QString& str, QString::const_iterator& iter); OpCode evaluate(const AnimVariantMap& map) const; diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 63eb39dc48..6812bb63b6 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -522,43 +522,19 @@ void AnimTests::testExpressionParser() { QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::And); } - /* - e = AnimExpression("2 + 3"); - QVERIFY(e._opCodes.size() == 3); - if (e._opCodes.size() == 3) { - QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[0].intVal == 2); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[1].intVal == 3); - QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Add); + e = AnimExpression("!(true || false) && true"); + QVERIFY(e._opCodes.size() == 6); + if (e._opCodes.size() == 6) { + QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Bool); + QVERIFY(e._opCodes[0].intVal == (int)true); + QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Bool); + QVERIFY(e._opCodes[1].intVal == (int)false); + QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Or); + QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Not); + QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Bool); + QVERIFY(e._opCodes[4].intVal == (int)true); + QVERIFY(e._opCodes[5].type == AnimExpression::OpCode::And); } - - e = AnimExpression("2 + 3 * 10"); - QVERIFY(e._opCodes.size() == 5); - if (e._opCodes.size() == 5) { - QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[0].intVal == 2); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[1].intVal == 3); - QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[2].intVal == 10); - QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Multiply); - QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Add); - } - - e = AnimExpression("(2 + 3) * 10"); - QVERIFY(e._opCodes.size() == 5); - if (e._opCodes.size() == 5) { - QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[0].intVal == 2); - QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[1].intVal == 3); - QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Add); - QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Int); - QVERIFY(e._opCodes[3].intVal == 10); - QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Multiply); - } - */ } #define TEST_BOOL_EXPR(EXPR) \ @@ -630,23 +606,22 @@ void AnimTests::testExpressionEvaluator() { TEST_BOOL_EXPR(f || false); TEST_BOOL_EXPR(f || true); -/* - result = AnimExpression("(2 + 3) * (5 + 3)").evaluate(vars); - QVERIFY(result.type == AnimExpression::OpCode::Int); - QVERIFY(result.intVal == (2 + 3) * (5 + 3)); + TEST_BOOL_EXPR(!true); + TEST_BOOL_EXPR(!false); + TEST_BOOL_EXPR(!true || true); - result = AnimExpression("(ten + twenty) * 5").evaluate(vars); - QVERIFY(result.type == AnimExpression::OpCode::Int); - QVERIFY(result.intVal == (10 + 20) * 5); + TEST_BOOL_EXPR(!true && !false || !true); + TEST_BOOL_EXPR(!true && !false || true); + TEST_BOOL_EXPR(!true && false || !true); + TEST_BOOL_EXPR(!true && false || true); + TEST_BOOL_EXPR(true && !false || !true); + TEST_BOOL_EXPR(true && !false || true); + TEST_BOOL_EXPR(true && false || !true); + TEST_BOOL_EXPR(true && false || true); - result = AnimExpression("(ten + twenty) * 5.0").evaluate(vars); - QVERIFY(result.type == AnimExpression::OpCode::Float); - QVERIFY(result.floatVal == (10 + 20) * 5.0f); - - result = AnimExpression("five * forty").evaluate(vars); - QVERIFY(result.type == AnimExpression::OpCode::Float); - QVERIFY(result.floatVal == 5.0f * 40.0f); -*/ + TEST_BOOL_EXPR(!(true && f) || !t); + TEST_BOOL_EXPR(!!!(t) && (!!f || true)); + TEST_BOOL_EXPR(!(true && f) && true); } From d4f55ed6d2da961544888a74b22fb45777cbbf8a Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 15 Dec 2015 15:55:03 -0800 Subject: [PATCH 22/86] working lsiders --- examples/controllers/handControllerGrab.js | 56 ++++++++++++++++- examples/lights/box.js | 73 +++++++++++++--------- examples/lights/light_modifier.js | 61 ++++++++++-------- examples/lights/testScene.js | 1 + 4 files changed, 135 insertions(+), 56 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 07894b46d1..d9e75c8836 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -816,6 +816,19 @@ function MyController(hand) { Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + var defaultConstrainData = { + axisBasePosition:false, + endOfAxis: false, + } + + var constraintData = getEntityCustomData('lightModifierKey', this.grabbedEntity, defaultConstrainData); + + // var constrainX = constraintData.constrain.x; + // var constrainY = constraintData.constrain.y; + // var constrainZ = constraintData.constrain.z; + + // print('constrainY'+constrainY); + // mix in head motion if (MOVE_WITH_HEAD) { var objDistance = Vec3.length(objectToAvatar); @@ -826,8 +839,21 @@ function MyController(hand) { this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); } + var clampedVector; + var targetPosition; + if (constraintData.axisBasePosition !== false) { + clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisBasePosition, constraintData.endOfAxis); + targetPosition = clampedVector; + } else { + targetPosition = { + x: this.currentObjectPosition.x, + y: this.currentObjectPosition.y, + z: this.currentObjectPosition.z + } + } + Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, + targetPosition: targetPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, @@ -836,6 +862,34 @@ function MyController(hand) { this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); }; + this.projectVectorAlongAxis = function(position, axisStart, axisEnd) { + + var aPrime = Vec3.subtract(position, axisStart); + + var bPrime = Vec3.subtract(axisEnd, axisStart); + + var bPrimeMagnitude = Vec3.length(bPrime); + + var dotProduct = Vec3.dot(aPrime, bPrime); + + var scalar = dotProduct / bPrimeMagnitude; + + print('SCALAR:::'+scalar); + + if(scalar<0){ + scalar = 0; + } + + if(scalar>1){ + scalar = 1; + } + + var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime))); + + return projection + + }, + this.nearGrabbing = function() { var now = Date.now(); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); diff --git a/examples/lights/box.js b/examples/lights/box.js index b1f9252073..3bcebc64d3 100644 --- a/examples/lights/box.js +++ b/examples/lights/box.js @@ -14,51 +14,64 @@ preload: function(entityID) { this.entityID = entityID; var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + print('USER DATA:::' + entityProperties.userData) var parsedUserData = JSON.parse(entityProperties.userData); - var userData = parsedUserData.lightModifierKey; + this.userData = parsedUserData.lightModifierKey; + this.bPrime = Vec3.subtract(this.userData.endOfAxis, this.userData.axisBasePosition); + this.bPrimeMagnitude = Vec3.length(this.bPrime); + }, startNearGrab: function() { this.setInitialProperties(); }, startDistantGrab: function() { + // Entities.editEntity(this.entityID, { + // parentID: MyAvatar.sessionUUID, + // parentJointIndex: MyAvatar.getJointIndex("LeftHand") + // }); this.setInitialProperties(); }, setInitialProperties: function() { this.initialProperties = Entities.getEntityProperties(this.entityID); }, - getClampedPosition: function() { - var dPosition; - // dPosition = Vec3.subtract(MyAvatar.position, this.previousPosition); - //convert to localFrame - // dPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), dPosition); + clampPosition: function() { + + var currentProperties = Entities.getEntityProperties(this.entityID); + + var aPrime = Vec3.subtract(this.userData.axisBasePosition, currentProperties.position); + + var dotProduct = Vec3.dot(aPrime, this.bPrime); + + var scalar = dotProduct / this.bPrimeMagnitude; + + print('SCALAR:::' + scalar); + + var projection = Vec3.sum(this.userData.axisBasePosition, Vec3.multiply(scalar, Vec3.normalize(this.bPrime))); + + this.currentProjection = projection; - return dPosition; - }, - getClampedRotation: function() { - var rotation = initialProperties.rotation; - return rotation; }, continueDistantGrab: function() { - var currentPosition = this.getClampedPosition(); - var distance = Vec3.distance(this.initialProperties.position, currentPosition); + // this.clampPosition(); + print('distant grab') + var currentPosition = Entities.getEntityProperties(this.entityID, "position").position; - if (userData.sliderType === 'color_red' || userData.sliderType === 'color_green' || userData.sliderType === 'color_blue') { - this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, COLOR_MAX); + var distance = Vec3.distance(this.axisBasePosition, this.currentProjection); + + if (this.userData.sliderType === 'color_red' || this.userData.sliderType === 'color_green' || this.userData.sliderType === 'color_blue') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, COLOR_MAX); } - if (userData.sliderType === 'intensity') { - this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, INTENSITY_MAX); + if (this.userData.sliderType === 'intensity') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, INTENSITY_MAX); } - if (userData.sliderType === 'cutoff') { - this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, CUTOFF_MAX); + if (this.userData.sliderType === 'cutoff') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, CUTOFF_MAX); } - if (userData.sliderType === 'exponent') { - this.sliderValue = scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); + if (this.userData.sliderType === 'exponent') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); }; - Entities.editEntity(this.entityID, { - position: currentPosition, - // rotation: this.getClampedRotation() - }); + }, releaseGrab: function() { Entities.editEntity(this.entityID, { @@ -66,7 +79,8 @@ x: 0, y: 0, z: 0 - } + }, + parentID: null }) this.sendValueToSlider(); @@ -79,10 +93,11 @@ return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); }, sendValueToSlider: function() { + var _t = this; var message = { - lightID: userData.lightID, - sliderType: userData.sliderType, - sliderValue: this.sliderValue + lightID: _t.userData.lightID, + sliderType: _t.userData.sliderType, + sliderValue: _t.sliderValue } Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); } diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index 2e11c5e22e..f54af5b54f 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -3,36 +3,36 @@ -var BOX_SCRIPT_URL = Script.resolvePath('box.js'); +var BOX_SCRIPT_URL = Script.resolvePath('box.js?'+Math.random(0,100)); var RED = { - r: 255, - g: 0, - b: 0 + red: 255, + green: 0, + blue: 0 }; var GREEN = { - r: 0, - g: 255, - b: 0 + red: 0, + green: 255, + blue: 0 }; var BLUE = { - r: 0, - g: 0, - b: 255 + red: 0, + green: 0, + blue: 255 }; var PURPLE = { - r: 255, - g: 0, - b: 255 + red: 255, + green: 0, + blue: 255 }; var WHITE = { - r: 255, - g: 255, - b: 255 + red: 255, + green: 255, + blue: 255 }; var AXIS_SCALE = 1; @@ -44,7 +44,7 @@ var BOX_DIMENSIONS = { }; var PER_ROW_OFFSET = { x: 0, - y: 0.2, + y: -0.2, z: 0 }; @@ -55,6 +55,7 @@ function entitySlider(light, color, sliderType, row) { this.color = color; this.sliderType = sliderType; this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); + print('slider : ' + this.sliderType + "should have an offset of : " + this.verticalOffset); this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(this.avatarRot))); @@ -111,11 +112,11 @@ entitySlider.prototype = { var rightVector = Quat.getRight(this.avatarRot); var extension = Vec3.multiply(AXIS_SCALE, rightVector); var endOfAxis = Vec3.sum(position, extension); - - var endOfAxis; + this.endOfAxis = endOfAxis; + print('endOfAxis:::' + JSON.stringify(endOfAxis)) var properties = { type: 'Line', - name: 'Hifi-Slider-Axis::'+this.sliderType, + name: 'Hifi-Slider-Axis::' + this.sliderType, color: this.color, collisionsWillMove: false, ignoreForCollisions: true, @@ -129,14 +130,14 @@ entitySlider.prototype = { x: 0, y: 0, z: 0 - }, endOfAxis], + }, extension], lineWidth: 5, }; this.axis = Entities.addEntity(properties); }, createBoxIndicator: function() { - print('BOX COLOR IS:::'+JSON.stringify(this.color)); + print('BOX COLOR IS:::' + JSON.stringify(this.color)); var position = Vec3.sum(this.basePosition, this.verticalOffset); //line starts on left and goes to right @@ -162,18 +163,26 @@ entitySlider.prototype = { initialDistance = this.distanceExponent; } var extension = Vec3.multiply(initialDistance, rightVector); - var endOfAxis = Vec3.sum(position, extension); + var sliderPosition = Vec3.sum(position, extension); var properties = { type: 'Box', - name: 'Hifi-Slider::'+this.sliderType, + name: 'Hifi-Slider::' + this.sliderType, dimensions: BOX_DIMENSIONS, + collisionsWillMove: true, color: this.color, - position: endOfAxis, + position: sliderPosition, script: BOX_SCRIPT_URL, userData: JSON.stringify({ lightModifierKey: { lightID: this.lightID, - sliderType: this.sliderType + sliderType: this.sliderType, + axisBasePosition: position, + endOfAxis: this.endOfAxis, + }, + constraintKey: { + constrain: { + y: position.y + } } }) }; diff --git a/examples/lights/testScene.js b/examples/lights/testScene.js index 4cc0058bea..0e0a226c0b 100644 --- a/examples/lights/testScene.js +++ b/examples/lights/testScene.js @@ -15,6 +15,7 @@ basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(-3, Quat.getUp(avatarRot))); var ground = Entities.addEntity({ + name:'Hifi-Light-Mod-Floor', type: "Model", modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/woodFloor.fbx", dimensions: { From 9a2d9997e90c99edd49ce1b7452511c80e1bccb5 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 15 Dec 2015 18:20:12 -0800 Subject: [PATCH 23/86] prep for feedback --- examples/controllers/handControllerGrab.js | 234 ++++++++++--------- examples/lights/light_loader.js | 8 + examples/lights/light_modifier.js | 113 ++++----- examples/lights/light_modifier_test_scene.js | 128 ++++++++++ examples/lights/{box.js => slider.js} | 49 ++-- examples/lights/testScene.js | 117 ---------- 6 files changed, 331 insertions(+), 318 deletions(-) create mode 100644 examples/lights/light_loader.js create mode 100644 examples/lights/light_modifier_test_scene.js rename examples/lights/{box.js => slider.js} (67%) delete mode 100644 examples/lights/testScene.js diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index d9e75c8836..c452519522 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -218,6 +218,7 @@ function getSpatialOffsetPosition(hand, spatialKey) { } var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + function getSpatialOffsetRotation(hand, spatialKey) { var rotation = Quat.IDENTITY; @@ -261,9 +262,9 @@ function MyController(hand) { this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; - + this.overlayLine = null; - + this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; this.offsetRotation = Quat.IDENTITY; @@ -816,33 +817,36 @@ function MyController(hand) { Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - var defaultConstrainData = { - axisBasePosition:false, - endOfAxis: false, - } - var constraintData = getEntityCustomData('lightModifierKey', this.grabbedEntity, defaultConstrainData); - - // var constrainX = constraintData.constrain.x; - // var constrainY = constraintData.constrain.y; - // var constrainZ = constraintData.constrain.z; - - // print('constrainY'+constrainY); // mix in head motion if (MOVE_WITH_HEAD) { var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { x: 0.0, y: 0.0, z: objDistance }); - var after = Vec3.multiplyQbyV(Camera.orientation, { x: 0.0, y: 0.0, z: objDistance }); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); var change = Vec3.subtract(before, after); this.currentCameraOrientation = Camera.orientation; this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); } + var defaultConstraintData = { + axisStart: false, + axisEnd: false, + } + + var constraintData = getEntityCustomData('lightModifierKey', this.grabbedEntity, defaultConstraintData); var clampedVector; var targetPosition; - if (constraintData.axisBasePosition !== false) { - clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisBasePosition, constraintData.endOfAxis); + if (constraintData.startAxis !== false) { + clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisStart, constraintData.axisEnd); targetPosition = clampedVector; } else { targetPosition = { @@ -859,121 +863,121 @@ function MyController(hand) { angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, ttl: ACTION_TTL }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + }; this.projectVectorAlongAxis = function(position, axisStart, axisEnd) { - var aPrime = Vec3.subtract(position, axisStart); + var aPrime = Vec3.subtract(position, axisStart); - var bPrime = Vec3.subtract(axisEnd, axisStart); + var bPrime = Vec3.subtract(axisEnd, axisStart); - var bPrimeMagnitude = Vec3.length(bPrime); + var bPrimeMagnitude = Vec3.length(bPrime); - var dotProduct = Vec3.dot(aPrime, bPrime); + var dotProduct = Vec3.dot(aPrime, bPrime); - var scalar = dotProduct / bPrimeMagnitude; + var scalar = dotProduct / bPrimeMagnitude; - print('SCALAR:::'+scalar); + if (scalar < 0) { + scalar = 0; + } - if(scalar<0){ - scalar = 0; - } + if (scalar > 1) { + scalar = 1; + } - if(scalar>1){ - scalar = 1; - } + var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime))); - var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime))); + return projection - return projection + }, - }, + this.nearGrabbing = function() { + var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - this.nearGrabbing = function() { - var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } + this.lineOff(); + this.overlayLineOff(); - this.lineOff(); - this.overlayLineOff(); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - collisionsWillMove: false + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. + this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.ignoreIK = false; + + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK }); - } - - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { - // if an object is "equipped" and has a spatialKey, use it. - this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - } else { - this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } else { - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - if (this.state == STATE_NEAR_GRABBING) { - this.setState(STATE_CONTINUE_NEAR_GRABBING); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; } else { - // equipping - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - this.startHandGrasp(); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + this.startHandGrasp(); + + this.setState(STATE_CONTINUE_EQUIP_BD); + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - this.setState(STATE_CONTINUE_EQUIP_BD); } - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - - } - - this.currentHandControllerTipPosition = - (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - - this.currentObjectTime = Date.now(); - }; + this.currentObjectTime = Date.now(); + }; this.continueNearGrabbing = function() { if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { @@ -1358,10 +1362,10 @@ Controller.enableMapping(MAPPING_NAME); var handToDisable = 'none'; function update() { - if (handToDisable !== LEFT_HAND && handToDisable!=='both') { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { leftController.update(); } - if (handToDisable !== RIGHT_HAND && handToDisable!=='both') { + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { rightController.update(); } } @@ -1369,7 +1373,7 @@ function update() { Messages.subscribe('Hifi-Hand-Disabler'); handleHandDisablerMessages = function(channel, message, sender) { - + if (sender === MyAvatar.sessionUUID) { if (message === 'left') { handToDisable = LEFT_HAND; @@ -1377,11 +1381,11 @@ handleHandDisablerMessages = function(channel, message, sender) { if (message === 'right') { handToDisable = RIGHT_HAND; } - if(message==='both'){ - handToDisable='both'; + if (message === 'both') { + handToDisable = 'both'; } - if(message==='none'){ - handToDisable='none'; + if (message === 'none') { + handToDisable = 'none'; } } @@ -1396,4 +1400,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); +Script.update.connect(update); \ No newline at end of file diff --git a/examples/lights/light_loader.js b/examples/lights/light_loader.js new file mode 100644 index 0000000000..8edaa20f57 --- /dev/null +++ b/examples/lights/light_loader.js @@ -0,0 +1,8 @@ +var grabScript = Script.resolvePath('../controllers/handControllerGrab.js'); +Script.load(grabScript); +var lightModifier = Script.resolvePath('light_modifier.js'); +Script.load(lightModifier); +Script.setTimeout(function() { + var lightModifierTestScene = Script.resolvePath('light_modifier_test_scene.js'); + Script.load(lightModifierTestScene); +}, 750) \ No newline at end of file diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index f54af5b54f..cbb4e3ff10 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -1,9 +1,22 @@ -// given a selected light, instantiate some entities that represent various values you can dynamically adjust +// +// light_modifier.js +// +// Created byJames Pollack @imgntn on 10/19/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Given a selected light, instantiate some entities that represent various values you can dynamically adjust by grabbing and moving. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var AXIS_SCALE = 1; +var COLOR_MAX = 255; +var INTENSITY_MAX = 0.05; +var CUTOFF_MAX = 360; +var EXPONENT_MAX = 1; - -var BOX_SCRIPT_URL = Script.resolvePath('box.js?'+Math.random(0,100)); +var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); var RED = { red: 255, @@ -35,13 +48,12 @@ var WHITE = { blue: 255 }; -var AXIS_SCALE = 1; - -var BOX_DIMENSIONS = { - x: 0.05, - y: 0.05, - z: 0.05 +var SLIDER_DIMENSIONS = { + x: 0.075, + y: 0.075, + z: 0.075 }; + var PER_ROW_OFFSET = { x: 0, y: -0.2, @@ -55,10 +67,8 @@ function entitySlider(light, color, sliderType, row) { this.color = color; this.sliderType = sliderType; this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); - print('slider : ' + this.sliderType + "should have an offset of : " + this.verticalOffset); - this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); - this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(this.avatarRot))); + this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); var message = { lightID: this.lightID, @@ -95,9 +105,8 @@ function entitySlider(light, color, sliderType, row) { } this.setInitialSliderPositions(); - this.subscribeToBoxMessages(); this.createAxis(); - this.createBoxIndicator(); + this.createSliderIndicator(); return this; } @@ -113,7 +122,6 @@ entitySlider.prototype = { var extension = Vec3.multiply(AXIS_SCALE, rightVector); var endOfAxis = Vec3.sum(position, extension); this.endOfAxis = endOfAxis; - print('endOfAxis:::' + JSON.stringify(endOfAxis)) var properties = { type: 'Line', name: 'Hifi-Slider-Axis::' + this.sliderType, @@ -136,10 +144,8 @@ entitySlider.prototype = { this.axis = Entities.addEntity(properties); }, - createBoxIndicator: function() { - print('BOX COLOR IS:::' + JSON.stringify(this.color)); + createSliderIndicator: function() { var position = Vec3.sum(this.basePosition, this.verticalOffset); - //line starts on left and goes to right //set the end of the line to the right var rightVector = Quat.getRight(this.avatarRot); @@ -154,7 +160,7 @@ entitySlider.prototype = { initialDistance = this.distanceBlue; } if (this.sliderType === 'intensity') { - initialDistance = this.distanceRed; + initialDistance = this.distanceIntensity; } if (this.sliderType === 'cutoff') { initialDistance = this.distanceCutoff; @@ -165,30 +171,26 @@ entitySlider.prototype = { var extension = Vec3.multiply(initialDistance, rightVector); var sliderPosition = Vec3.sum(position, extension); var properties = { - type: 'Box', + type: 'Sphere', name: 'Hifi-Slider::' + this.sliderType, - dimensions: BOX_DIMENSIONS, + dimensions: SLIDER_DIMENSIONS, collisionsWillMove: true, color: this.color, position: sliderPosition, - script: BOX_SCRIPT_URL, + script: SLIDER_SCRIPT_URL, userData: JSON.stringify({ lightModifierKey: { lightID: this.lightID, sliderType: this.sliderType, - axisBasePosition: position, - endOfAxis: this.endOfAxis, - }, - constraintKey: { - constrain: { - y: position.y - } + axisStart: position, + axisEnd: this.endOfAxis, } }) }; - this.boxIndicator = Entities.addEntity(properties); + this.sliderIndicator = Entities.addEntity(properties); }, + setValueFromMessage: function(message) { //message is not for our light @@ -203,8 +205,6 @@ entitySlider.prototype = { return } - print('should set:::' + this.sliderType); - var lightProperties = Entities.getEntityProperties(this.lightID); if (this.sliderType === 'color_red') { @@ -255,28 +255,15 @@ entitySlider.prototype = { }); } }, - subscribeToBoxMessages: function() { - Messages.subscribe('Hifi-Slider-Value-Reciever'); - Messages.messageReceived.connect(handleValueMessages); - }, setInitialSliderPositions: function() { - var COLOR_MAX = 255; - var INTENSITY_MAX = 10; - var CUTOFF_MAX = 360; - var EXPONENT_MAX = 1; - this.distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE; this.distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE; this.distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE; this.distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE; this.distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE; this.distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE; - }, - cleanup: function() { - Entities.deleteEntity(this.boxIndicator); - Entities.deleteEntity(this.axis); - Messages.messageReceived.disconnect(this.handleValueMessages); } + }; var sliders = []; @@ -288,9 +275,9 @@ var slidersRef = { cutoff: null, exponent: null } +var light = null; function makeSliders(light) { - print('light in makesliders:::' + light) if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; var USE_INTENSITY_SLIDER = true; @@ -325,14 +312,19 @@ function makeSliders(light) { slidersRef.exponent = new entitySlider(light, PURPLE, 'exponent', 6); sliders.push(slidersRef.exponent); } + subscribeToSliderMessages(); }; function subScribeToNewLights() { - print('subscribing to light messages') Messages.subscribe('Hifi-Light-Mod-Receiver'); Messages.messageReceived.connect(handleLightModMessages); } +function subscribeToSliderMessages() { + Messages.subscribe('Hifi-Slider-Value-Reciever'); + Messages.messageReceived.connect(handleValueMessages); +} + function handleLightModMessages(channel, message, sender) { if (channel !== 'Hifi-Light-Mod-Receiver') { return; @@ -343,6 +335,7 @@ function handleLightModMessages(channel, message, sender) { var parsedMessage = JSON.parse(message); makeSliders(parsedMessage.light); + light = parsedMessage.light.id } function handleValueMessages(channel, message, sender) { @@ -356,27 +349,39 @@ function handleValueMessages(channel, message, sender) { // } var parsedMessage = JSON.parse(message); - slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage) + slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage); } function cleanup() { - while (sliders.length > 0) { - var slider = sliders.pop(); - slider.cleanup(); + var i; + for (i = 0; i < sliders.length; i++) { + Entities.deleteEntity(sliders[i].axis); + Entities.deleteEntity(sliders[i].sliderIndicator); } + Messages.messageReceived.disconnect(handleLightModMessages); - delete sliders + Messages.messageReceived.disconnect(handleValueMessages); + Entities.deletingEntity.disconnect(deleteEntity); + } Script.scriptEnding.connect(cleanup); subScribeToNewLights(); +function deleteEntity(entityID) { + if (entityID === light) { + // cleanup(); + } +} + + +Entities.deletingEntity.connect(deleteEntity); + //other light properties // diffuseColor: { red: 255, green: 255, blue: 255 }, // ambientColor: { red: 255, green: 255, blue: 255 }, // specularColor: { red: 255, green: 255, blue: 255 }, - // constantAttenuation: 1, // linearAttenuation: 0, // quadraticAttenuation: 0, diff --git a/examples/lights/light_modifier_test_scene.js b/examples/lights/light_modifier_test_scene.js new file mode 100644 index 0000000000..d997816957 --- /dev/null +++ b/examples/lights/light_modifier_test_scene.js @@ -0,0 +1,128 @@ +// +// light_modifier_test_scene.js +// +// Created byJames Pollack @imgntn on 10/19/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Given a selected light, instantiate some entities that represent various values you can dynamically adjust. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 +}; +var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 +}); + +var basePosition, avatarRot; +avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); +basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(-3, Quat.getUp(avatarRot))); + +var ground = Entities.addEntity({ + name: 'Hifi-Light-Mod-Floor', + //type: "Model", + type: 'Box', + color: { + red: 100, + green: 100, + blue: 100 + }, + //modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/woodFloor.fbx", + dimensions: { + x: 100, + y: 2, + z: 100 + }, + position: basePosition, + shapeType: 'box' +}); + +var light, block; + +function createLight() { + var blockProperties = Entities.getEntityProperties(block, ["position", "rotation"]); + var lightTransform = evalLightWorldTransform(blockProperties.position, blockProperties.rotation); + var lightProperties = { + name: 'Hifi-Spotlight', + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: block, + color: { + red: 255, + green: 0, + blue: 255 + }, + intensity: 0.035, + exponent: 1, + cutoff: 30, + lifetime: -1, + position: lightTransform.p, + rotation: lightTransform.q + }; + + light = Entities.addEntity(lightProperties); + + var message = { + light: { + id: light, + type: 'spotlight', + initialProperties: lightProperties + } + }; + + Messages.sendMessage('Hifi-Light-Mod-Receiver', JSON.stringify(message)); + +} + +function createBlock() { + var position = basePosition; + position.y += 5; + var blockProperties = { + name: 'Hifi-Spotlight-Block', + type: 'Box', + dimensions: { + x: 1, + y: 1, + z: 1 + }, + collisionsWillMove: true, + color: { + red: 0, + green: 0, + blue: 255 + }, + position: position + } + + block = Entities.addEntity(blockProperties); +} + +function evalLightWorldTransform(modelPos, modelRot) { + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; +} + +function cleanup() { + print('CLEANUP TEST SCENE SCRIPT') + Entities.deleteEntity(block); + Entities.deleteEntity(ground); + Entities.deleteEntity(light); +} + +Script.scriptEnding.connect(cleanup); + +createBlock(); +createLight(); \ No newline at end of file diff --git a/examples/lights/box.js b/examples/lights/slider.js similarity index 67% rename from examples/lights/box.js rename to examples/lights/slider.js index 3bcebc64d3..ab90019b9f 100644 --- a/examples/lights/box.js +++ b/examples/lights/slider.js @@ -2,61 +2,40 @@ var AXIS_SCALE = 1; var COLOR_MAX = 255; - var INTENSITY_MAX = 10; + var INTENSITY_MAX = 0.05; var CUTOFF_MAX = 360; var EXPONENT_MAX = 1; - function Box() { + function Slider() { return this; } - Box.prototype = { + Slider.prototype = { preload: function(entityID) { this.entityID = entityID; var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); - print('USER DATA:::' + entityProperties.userData) var parsedUserData = JSON.parse(entityProperties.userData); this.userData = parsedUserData.lightModifierKey; - this.bPrime = Vec3.subtract(this.userData.endOfAxis, this.userData.axisBasePosition); - this.bPrimeMagnitude = Vec3.length(this.bPrime); - }, startNearGrab: function() { this.setInitialProperties(); }, startDistantGrab: function() { - // Entities.editEntity(this.entityID, { - // parentID: MyAvatar.sessionUUID, - // parentJointIndex: MyAvatar.getJointIndex("LeftHand") - // }); this.setInitialProperties(); }, setInitialProperties: function() { this.initialProperties = Entities.getEntityProperties(this.entityID); }, - clampPosition: function() { - - var currentProperties = Entities.getEntityProperties(this.entityID); - - var aPrime = Vec3.subtract(this.userData.axisBasePosition, currentProperties.position); - - var dotProduct = Vec3.dot(aPrime, this.bPrime); - - var scalar = dotProduct / this.bPrimeMagnitude; - - print('SCALAR:::' + scalar); - - var projection = Vec3.sum(this.userData.axisBasePosition, Vec3.multiply(scalar, Vec3.normalize(this.bPrime))); - - this.currentProjection = projection; - + continueNearGrab: function() { + // this.continueDistantGrab(); }, continueDistantGrab: function() { - // this.clampPosition(); - print('distant grab') + this.setSliderValueBasedOnDistance(); + }, + setSliderValueBasedOnDistance: function() { var currentPosition = Entities.getEntityProperties(this.entityID, "position").position; - var distance = Vec3.distance(this.axisBasePosition, this.currentProjection); + var distance = Vec3.distance(this.userData.axisStart, currentPosition); if (this.userData.sliderType === 'color_red' || this.userData.sliderType === 'color_green' || this.userData.sliderType === 'color_blue') { this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, COLOR_MAX); @@ -71,6 +50,8 @@ this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); }; + print('SLIDER VALUE:::' + this.sliderValue) + this.sendValueToSlider(); }, releaseGrab: function() { @@ -80,7 +61,11 @@ y: 0, z: 0 }, - parentID: null + angularVelocity: { + x: 0, + y: 0, + z: 0 + } }) this.sendValueToSlider(); @@ -103,5 +88,5 @@ } }; - return new Box(); + return new Slider(); }); \ No newline at end of file diff --git a/examples/lights/testScene.js b/examples/lights/testScene.js deleted file mode 100644 index 0e0a226c0b..0000000000 --- a/examples/lights/testScene.js +++ /dev/null @@ -1,117 +0,0 @@ - // These constants define the Spotlight position and orientation relative to the model - var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 - }; - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 - }); - - var basePosition, avatarRot; - avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); - basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(-3, Quat.getUp(avatarRot))); - - var ground = Entities.addEntity({ - name:'Hifi-Light-Mod-Floor', - type: "Model", - modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/woodFloor.fbx", - dimensions: { - x: 100, - y: 2, - z: 100 - }, - position: basePosition, - shapeType: 'box' - }); - - var light, block; - - // basePosition.y += 2; - - function createLight() { - print('making light' + block) - var blockProperties = Entities.getEntityProperties(block, ["position", "rotation"]); - var lightTransform = evalLightWorldTransform(blockProperties.position, blockProperties.rotation); - var lightProperties = { - name: 'Hifi-Spotlight', - type: "Light", - isSpotlight: true, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: block, - color: { - red: 255, - green: 0, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: -1, - position: lightTransform.p, - rotation: lightTransform.q - }; - - light = Entities.addEntity(lightProperties); - - var message = { - light: { - id: light, - type: 'spotlight', - initialProperties: lightProperties - } - }; - - Messages.sendMessage('Hifi-Light-Mod-Receiver', JSON.stringify(message)); - print('SENT MESSAGE') - - } - - function createBlock() { - print('making block'); - - var position = basePosition; - position.y += 5; - var blockProperties = { - name: 'Hifi-Spotlight-Block', - type: 'Box', - dimensions: { - x: 1, - y: 1, - z: 1 - }, - collisionsWillMove: true, - color: { - red: 0, - green: 0, - blue: 255 - }, - position: position - } - - block = Entities.addEntity(blockProperties); - } - - function evalLightWorldTransform(modelPos, modelRot) { - return { - p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), - q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) - }; - } - - function cleanup() { - Entities.deleteEntity(block); - Entities.deleteEntity(ground); - Entities.deleteEntity(light); - } - - Script.scriptEnding.connect(cleanup); - - createBlock(); - createLight(); \ No newline at end of file From adf6cf8aa3f3d41ad79013ae8d2ee724de3b738c Mon Sep 17 00:00:00 2001 From: James Pollack Date: Wed, 16 Dec 2015 00:58:00 -0800 Subject: [PATCH 24/86] vary colors --- examples/lights/light_modifier.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index cbb4e3ff10..ed11b9c618 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -48,6 +48,12 @@ var WHITE = { blue: 255 }; +var ORANGE={ + red:255, + green:0, + blue:128 +} + var SLIDER_DIMENSIONS = { x: 0.075, y: 0.075, @@ -309,7 +315,7 @@ function makeSliders(light) { sliders.push(slidersRef.cutoff); } if (USE_EXPONENT_SLIDER === true) { - slidersRef.exponent = new entitySlider(light, PURPLE, 'exponent', 6); + slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 6); sliders.push(slidersRef.exponent); } subscribeToSliderMessages(); From 5a9549a2d951442bdc0b3fbf269f7984d126599b Mon Sep 17 00:00:00 2001 From: James Pollack Date: Wed, 16 Dec 2015 01:21:22 -0800 Subject: [PATCH 25/86] headers --- examples/lights/light_modifier.js | 2 +- examples/lights/light_modifier_test_scene.js | 2 +- examples/lights/slider.js | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/lights/light_modifier.js b/examples/lights/light_modifier.js index ed11b9c618..132934f5d2 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/light_modifier.js @@ -1,7 +1,7 @@ // // light_modifier.js // -// Created byJames Pollack @imgntn on 10/19/2015 +// Created byJames Pollack @imgntn on 12/15/2015 // Copyright 2015 High Fidelity, Inc. // // Given a selected light, instantiate some entities that represent various values you can dynamically adjust by grabbing and moving. diff --git a/examples/lights/light_modifier_test_scene.js b/examples/lights/light_modifier_test_scene.js index d997816957..e70812cb45 100644 --- a/examples/lights/light_modifier_test_scene.js +++ b/examples/lights/light_modifier_test_scene.js @@ -1,7 +1,7 @@ // // light_modifier_test_scene.js // -// Created byJames Pollack @imgntn on 10/19/2015 +// Created byJames Pollack @imgntn on 12/15/2015 // Copyright 2015 High Fidelity, Inc. // // Given a selected light, instantiate some entities that represent various values you can dynamically adjust. diff --git a/examples/lights/slider.js b/examples/lights/slider.js index ab90019b9f..ab59b6eed1 100644 --- a/examples/lights/slider.js +++ b/examples/lights/slider.js @@ -1,3 +1,13 @@ +// +// slider.js +// +// Created byJames Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html (function() { var AXIS_SCALE = 1; From 28a5dba2d32a0a2a96ff161fff58a4647a07f07e Mon Sep 17 00:00:00 2001 From: James Pollack Date: Wed, 16 Dec 2015 01:31:24 -0800 Subject: [PATCH 26/86] rename files --- examples/lights/lightLoader.js | 20 +++++++++++++++++++ .../{light_modifier.js => lightModifier.js} | 8 ++++---- ...est_scene.js => lightModifierTestScene.js} | 5 ++--- examples/lights/light_loader.js | 8 -------- examples/lights/slider.js | 5 +++-- 5 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 examples/lights/lightLoader.js rename examples/lights/{light_modifier.js => lightModifier.js} (98%) rename examples/lights/{light_modifier_test_scene.js => lightModifierTestScene.js} (95%) delete mode 100644 examples/lights/light_loader.js diff --git a/examples/lights/lightLoader.js b/examples/lights/lightLoader.js new file mode 100644 index 0000000000..e4022e7bc1 --- /dev/null +++ b/examples/lights/lightLoader.js @@ -0,0 +1,20 @@ +// +// lightLoader.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Loads a test scene showing sliders that you can grab and move to change entity properties. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var grabScript = Script.resolvePath('../controllers/handControllerGrab.js'); +Script.load(grabScript); +var lightModifier = Script.resolvePath('lightModifier.js'); +Script.load(lightModifier); +Script.setTimeout(function() { + var lightModifierTestScene = Script.resolvePath('lightModifierTestScene.js'); + Script.load(lightModifierTestScene); +}, 750) \ No newline at end of file diff --git a/examples/lights/light_modifier.js b/examples/lights/lightModifier.js similarity index 98% rename from examples/lights/light_modifier.js rename to examples/lights/lightModifier.js index 132934f5d2..ffd1469a4a 100644 --- a/examples/lights/light_modifier.js +++ b/examples/lights/lightModifier.js @@ -1,7 +1,7 @@ // -// light_modifier.js +// lightModifier.js // -// Created byJames Pollack @imgntn on 12/15/2015 +// Created by James Pollack @imgntn on 12/15/2015 // Copyright 2015 High Fidelity, Inc. // // Given a selected light, instantiate some entities that represent various values you can dynamically adjust by grabbing and moving. @@ -201,13 +201,13 @@ entitySlider.prototype = { //message is not for our light if (message.lightID !== this.lightID) { - print('not our light') + // print('not our light') return; } //message is not our type if (message.sliderType !== this.sliderType) { - print('not our slider type') + // print('not our slider type') return } diff --git a/examples/lights/light_modifier_test_scene.js b/examples/lights/lightModifierTestScene.js similarity index 95% rename from examples/lights/light_modifier_test_scene.js rename to examples/lights/lightModifierTestScene.js index e70812cb45..214d900130 100644 --- a/examples/lights/light_modifier_test_scene.js +++ b/examples/lights/lightModifierTestScene.js @@ -1,7 +1,7 @@ // -// light_modifier_test_scene.js +// lightModifierTestScene.js // -// Created byJames Pollack @imgntn on 12/15/2015 +// Created by James Pollack @imgntn on 12/15/2015 // Copyright 2015 High Fidelity, Inc. // // Given a selected light, instantiate some entities that represent various values you can dynamically adjust. @@ -116,7 +116,6 @@ function evalLightWorldTransform(modelPos, modelRot) { } function cleanup() { - print('CLEANUP TEST SCENE SCRIPT') Entities.deleteEntity(block); Entities.deleteEntity(ground); Entities.deleteEntity(light); diff --git a/examples/lights/light_loader.js b/examples/lights/light_loader.js deleted file mode 100644 index 8edaa20f57..0000000000 --- a/examples/lights/light_loader.js +++ /dev/null @@ -1,8 +0,0 @@ -var grabScript = Script.resolvePath('../controllers/handControllerGrab.js'); -Script.load(grabScript); -var lightModifier = Script.resolvePath('light_modifier.js'); -Script.load(lightModifier); -Script.setTimeout(function() { - var lightModifierTestScene = Script.resolvePath('light_modifier_test_scene.js'); - Script.load(lightModifierTestScene); -}, 750) \ No newline at end of file diff --git a/examples/lights/slider.js b/examples/lights/slider.js index ab59b6eed1..c17704b0db 100644 --- a/examples/lights/slider.js +++ b/examples/lights/slider.js @@ -1,13 +1,14 @@ // // slider.js // -// Created byJames Pollack @imgntn on 12/15/2015 +// Created by James Pollack @imgntn on 12/15/2015 // Copyright 2015 High Fidelity, Inc. // // Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// (function() { var AXIS_SCALE = 1; @@ -60,7 +61,7 @@ this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); }; - print('SLIDER VALUE:::' + this.sliderValue) + //print('SLIDER VALUE:::' + this.sliderValue) this.sendValueToSlider(); }, From b0bcbb03f2bee5fa5c299c9f20774fd0d7ddea04 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 11:57:54 -0800 Subject: [PATCH 27/86] remove floor and move sliders up a bit --- examples/lights/lightModifier.js | 84 +++++++++++++++++++++-- examples/lights/lightModifierTestScene.js | 46 ++++--------- examples/lights/slider.js | 3 +- 3 files changed, 92 insertions(+), 41 deletions(-) diff --git a/examples/lights/lightModifier.js b/examples/lights/lightModifier.js index ffd1469a4a..0a584b127c 100644 --- a/examples/lights/lightModifier.js +++ b/examples/lights/lightModifier.js @@ -10,6 +10,19 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +// Script.include('../libraries/lightOverlayManager.js'); +// var lightOverlayManager = new LightOverlayManager(); +// lightOverlayManager.setVisible(true); + + // var pickRay = Camera.computePickRay(event.x, event.y); + // var lightResult = lightOverlayManager.findRayIntersection(pickRay) + + +var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}' +var SHOULD_STAY_WITH_AVATAR = false; +var VERTICAL_SLIDERS = false; + var AXIS_SCALE = 1; var COLOR_MAX = 255; var INTENSITY_MAX = 0.05; @@ -48,10 +61,10 @@ var WHITE = { blue: 255 }; -var ORANGE={ - red:255, - green:0, - blue:128 +var ORANGE = { + red: 255, + green: 0, + blue: 128 } var SLIDER_DIMENSIONS = { @@ -75,6 +88,7 @@ function entitySlider(light, color, sliderType, row) { this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); + this.basePosition.y +=1; var message = { lightID: this.lightID, @@ -190,6 +204,10 @@ entitySlider.prototype = { sliderType: this.sliderType, axisStart: position, axisEnd: this.endOfAxis, + }, + releaseVelocityKey: { + disableReleaseVelocity: true, + customReleaseVelocity: false } }) }; @@ -201,13 +219,13 @@ entitySlider.prototype = { //message is not for our light if (message.lightID !== this.lightID) { - // print('not our light') + // print('not our light') return; } //message is not our type if (message.sliderType !== this.sliderType) { - // print('not our slider type') + // print('not our slider type') return } @@ -381,8 +399,62 @@ function deleteEntity(entityID) { } + Entities.deletingEntity.connect(deleteEntity); +// search for lights to make grabbable + +// var USE_DEBOUNCE = true; +// var sinceLastUpdate = 0; + +// function searchForLightsToVisualize() { + +// var deltaTime = interval(); + +// if (USE_DEBOUNCE === true) { +// sinceLastUpdate = sinceLastUpdate + deltaTime; + +// if (sinceLastUpdate > 60) { +// sinceLastUpdate = 0; +// } else { +// return; +// } +// } + +// print('SEARCHING FOR LIGHTS'); + +// var entitites = Entities.findEntities(MyAvatar.position, 50); +// for (i = 0; i < entities.length; i++) { +// var entityProperties = Entities.getEntityProperties(entities[i], ['type', 'parentID']) +// var parentID = entityProperties.parentID; +// var type = entityProperties.type; + +// if (type !== 'Light') { +// return; +// } + +// if (type === "Light" && parentID !== DEFAULT_PARENT_ID && parentID !== null) { +// var light = entities[i]; +// //do something with the light. +// } + +// } + +// } + +// function interval() { +// var lastTime = new Date().getTime(); + +// return function getInterval() { +// var newTime = new Date().getTime(); +// var delta = newTime - lastTime; +// lastTime = newTime; +// return delta; +// }; +// } + + + //other light properties // diffuseColor: { red: 255, green: 255, blue: 255 }, diff --git a/examples/lights/lightModifierTestScene.js b/examples/lights/lightModifierTestScene.js index 214d900130..5748f0d4ed 100644 --- a/examples/lights/lightModifierTestScene.js +++ b/examples/lights/lightModifierTestScene.js @@ -9,39 +9,10 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 -}; -var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 -}); var basePosition, avatarRot; avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); -basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(-3, Quat.getUp(avatarRot))); - -var ground = Entities.addEntity({ - name: 'Hifi-Light-Mod-Floor', - //type: "Model", - type: 'Box', - color: { - red: 100, - green: 100, - blue: 100 - }, - //modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/woodFloor.fbx", - dimensions: { - x: 100, - y: 2, - z: 100 - }, - position: basePosition, - shapeType: 'box' -}); +basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(0, Quat.getUp(avatarRot))); var light, block; @@ -87,7 +58,7 @@ function createLight() { function createBlock() { var position = basePosition; - position.y += 5; + position.y += 3; var blockProperties = { name: 'Hifi-Spotlight-Block', type: 'Box', @@ -103,12 +74,22 @@ function createBlock() { blue: 255 }, position: position - } + }; block = Entities.addEntity(blockProperties); } function evalLightWorldTransform(modelPos, modelRot) { + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); return { p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) @@ -117,7 +98,6 @@ function evalLightWorldTransform(modelPos, modelRot) { function cleanup() { Entities.deleteEntity(block); - Entities.deleteEntity(ground); Entities.deleteEntity(light); } diff --git a/examples/lights/slider.js b/examples/lights/slider.js index c17704b0db..dc02e0bdba 100644 --- a/examples/lights/slider.js +++ b/examples/lights/slider.js @@ -9,6 +9,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + (function() { var AXIS_SCALE = 1; @@ -61,9 +62,7 @@ this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); }; - //print('SLIDER VALUE:::' + this.sliderValue) this.sendValueToSlider(); - }, releaseGrab: function() { Entities.editEntity(this.entityID, { From 6cd86e8a6bf2371079416dfa3b5c387eddc05696 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 12:23:10 -0800 Subject: [PATCH 28/86] midday --- examples/lights/lightModifier.js | 68 +++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/examples/lights/lightModifier.js b/examples/lights/lightModifier.js index 0a584b127c..e928679f08 100644 --- a/examples/lights/lightModifier.js +++ b/examples/lights/lightModifier.js @@ -11,17 +11,20 @@ // +//some experimental options +var ONLY_I_CAN_EDIT = false; +var SLIDERS_SHOULD_STAY_WITH_AVATAR = false; +var VERTICAL_SLIDERS = false; + // Script.include('../libraries/lightOverlayManager.js'); // var lightOverlayManager = new LightOverlayManager(); // lightOverlayManager.setVisible(true); - // var pickRay = Camera.computePickRay(event.x, event.y); - // var lightResult = lightOverlayManager.findRayIntersection(pickRay) - +// var pickRay = Camera.computePickRay(event.x, event.y); +// var lightResult = lightOverlayManager.findRayIntersection(pickRay) var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}' -var SHOULD_STAY_WITH_AVATAR = false; -var VERTICAL_SLIDERS = false; + var AXIS_SCALE = 1; var COLOR_MAX = 255; @@ -88,7 +91,7 @@ function entitySlider(light, color, sliderType, row) { this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); - this.basePosition.y +=1; + this.basePosition.y += 1; var message = { lightID: this.lightID, @@ -134,14 +137,23 @@ function entitySlider(light, color, sliderType, row) { entitySlider.prototype = { createAxis: function() { //start of line - var position = Vec3.sum(this.basePosition, this.verticalOffset); + var position; + var extension; - //line starts on left and goes to right - //set the end of the line to the right - var rightVector = Quat.getRight(this.avatarRot); - var extension = Vec3.multiply(AXIS_SCALE, rightVector); - var endOfAxis = Vec3.sum(position, extension); - this.endOfAxis = endOfAxis; + if (VERTICAL_SLIDERS == true) { + position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot))))); + //line starts on bottom and goes up + var upVector = Quat.getUp(this.avatarRot); + extension = Vec3.multiply(AXIS_SCALE, upVector); + } else { + position = Vec3.sum(this.basePosition, this.verticalOffset); + //line starts on left and goes to right + //set the end of the line to the right + var rightVector = Quat.getRight(this.avatarRot); + extension = Vec3.multiply(AXIS_SCALE, rightVector); + } + + this.endOfAxis = Vec3.sum(position, extension); var properties = { type: 'Line', name: 'Hifi-Slider-Axis::' + this.sliderType, @@ -165,10 +177,18 @@ entitySlider.prototype = { this.axis = Entities.addEntity(properties); }, createSliderIndicator: function() { - var position = Vec3.sum(this.basePosition, this.verticalOffset); - //line starts on left and goes to right - //set the end of the line to the right - var rightVector = Quat.getRight(this.avatarRot); + var extensionVector; + var position; + if (VERTICAL_SLIDERS == true) { + position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot))))); + extensionVector = Quat.getUp(this.avatarRot); + + } else { + position = Vec3.sum(this.basePosition, this.verticalOffset); + extensionVector = Quat.getRight(this.avatarRot); + + } + var initialDistance; if (this.sliderType === 'color_red') { initialDistance = this.distanceRed; @@ -188,11 +208,13 @@ entitySlider.prototype = { if (this.sliderType === 'exponent') { initialDistance = this.distanceExponent; } - var extension = Vec3.multiply(initialDistance, rightVector); + + var extension = Vec3.multiply(initialDistance, extensionVector); var sliderPosition = Vec3.sum(position, extension); + var properties = { type: 'Sphere', - name: 'Hifi-Slider::' + this.sliderType, + name: 'Hifi-Slider-' + this.sliderType, dimensions: SLIDER_DIMENSIONS, collisionsWillMove: true, color: this.color, @@ -367,10 +389,9 @@ function handleValueMessages(channel, message, sender) { if (channel !== 'Hifi-Slider-Value-Reciever') { return; } - //easily protect from other people editing your values, but group editing might be fun so lets try that first. - // if (sender !== MyAvatar.sessionUUID) { - // return; - // } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } var parsedMessage = JSON.parse(message); slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage); @@ -455,7 +476,6 @@ Entities.deletingEntity.connect(deleteEntity); - //other light properties // diffuseColor: { red: 255, green: 255, blue: 255 }, // ambientColor: { red: 255, green: 255, blue: 255 }, From b25fc5be6cfde2bf383b2050693ff00da92f2452 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 17 Dec 2015 09:52:34 +1300 Subject: [PATCH 29/86] Don't send a domain-server check in when shutting down --- libraries/networking/src/NodeList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e591ee8a31..bc79ddd5fd 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -234,6 +234,7 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) void NodeList::sendDomainServerCheckIn() { if (_isShuttingDown) { qCDebug(networking) << "Refusing to send a domain-server check in while shutting down."; + return; } if (_publicSockAddr.isNull()) { From e37d68449bb3b344936bc5ad0845098c878cc57f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 13:57:40 -0800 Subject: [PATCH 30/86] rename folder --- examples/controllers/handControllerGrab.js | 4 ++-- examples/{lights => light_modifier}/lightLoader.js | 0 examples/{lights => light_modifier}/lightModifier.js | 0 examples/{lights => light_modifier}/lightModifierTestScene.js | 0 examples/{lights => light_modifier}/slider.js | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename examples/{lights => light_modifier}/lightLoader.js (100%) rename examples/{lights => light_modifier}/lightModifier.js (100%) rename examples/{lights => light_modifier}/lightModifierTestScene.js (100%) rename examples/{lights => light_modifier}/slider.js (100%) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index c452519522..e7d1b5f84b 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -944,7 +944,7 @@ function MyController(hand) { relativeRotation: this.offsetRotation, ttl: ACTION_TTL, kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, + kinematicSetVelocity: false, ignoreIK: this.ignoreIK }); if (this.actionID === NULL_ACTION_ID) { @@ -1029,7 +1029,7 @@ function MyController(hand) { relativeRotation: this.offsetRotation, ttl: ACTION_TTL, kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, + kinematicSetVelocity: false, ignoreIK: this.ignoreIK }); this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); diff --git a/examples/lights/lightLoader.js b/examples/light_modifier/lightLoader.js similarity index 100% rename from examples/lights/lightLoader.js rename to examples/light_modifier/lightLoader.js diff --git a/examples/lights/lightModifier.js b/examples/light_modifier/lightModifier.js similarity index 100% rename from examples/lights/lightModifier.js rename to examples/light_modifier/lightModifier.js diff --git a/examples/lights/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js similarity index 100% rename from examples/lights/lightModifierTestScene.js rename to examples/light_modifier/lightModifierTestScene.js diff --git a/examples/lights/slider.js b/examples/light_modifier/slider.js similarity index 100% rename from examples/lights/slider.js rename to examples/light_modifier/slider.js From 40e6e4b13389fb57d5ef627113beda458eabcf76 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 14:01:50 -0800 Subject: [PATCH 31/86] add readme --- examples/light_modifier/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 examples/light_modifier/README.md diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md new file mode 100644 index 0000000000..d978c73b5b --- /dev/null +++ b/examples/light_modifier/README.md @@ -0,0 +1,24 @@ +This PR demonstrates one way in-world editing of objects might work. We start with a spotlight. When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached. + +To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js +To reset, I recommend stopping all scripts then re-loading lightLoader.js + +When you run the lightLoader.js script, 4 scripts will be loaded: +- handControllerGrab.js (custom) +- lightModifier.js (listens for message to create sliders for a given light) +- lightModifierTestScene.js (creates a light and parents it to a block, then sends a message ^^) +- slider.js (attached to each slider entity) + + + +Current sliders are (top to bottom): +red +green +blue +intensity +cutoff +exponent + +To-Do: Determine how to enter / exit edit mode , support near grab, add other input types (checkbox, etc), prevent velocity drift on slider release,button to hide entity + +![capture](https://cloud.githubusercontent.com/assets/843228/11830366/2f2dfe70-a359-11e5-84f0-33a380ebeac7.PNG) From e0b8c6b48b46e1f380a5940a16c6dcb702ae3c89 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 14:24:31 -0800 Subject: [PATCH 32/86] show light volumes --- examples/light_modifier/lightModifier.js | 32 +++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index e928679f08..b33b6fc0f1 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -10,22 +10,37 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - //some experimental options var ONLY_I_CAN_EDIT = false; var SLIDERS_SHOULD_STAY_WITH_AVATAR = false; var VERTICAL_SLIDERS = false; +var SHOW_OVERLAYS = true; +var SHOW_LIGHT_VOLUME = true; -// Script.include('../libraries/lightOverlayManager.js'); -// var lightOverlayManager = new LightOverlayManager(); -// lightOverlayManager.setVisible(true); +//variables for managing overlays +var selectionDisplay; +var selectionManager; +var lightOverlayManager; +if (SHOW_OVERLAYS === true) { + Script.include('../libraries/entitySelectionTool.js'); + + selectionDisplay = SelectionDisplay; + selectionManager = SelectionManager; + Script.include('../libraries/lightOverlayManager.js'); + lightOverlayManager = new LightOverlayManager(); + selectionManager.addEventListener(function() { + selectionDisplay.updateHandles(); + lightOverlayManager.updatePositions(); + }); + + lightOverlayManager.setVisible(true); +} // var pickRay = Camera.computePickRay(event.x, event.y); // var lightResult = lightOverlayManager.findRayIntersection(pickRay) var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}' - var AXIS_SCALE = 1; var COLOR_MAX = 255; var INTENSITY_MAX = 0.05; @@ -323,7 +338,9 @@ var slidersRef = { } var light = null; -function makeSliders(light) { +function makeSliders(light) { // selectionManager.setSelections([entityID]); + + if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; var USE_INTENSITY_SLIDER = true; @@ -382,6 +399,9 @@ function handleLightModMessages(channel, message, sender) { makeSliders(parsedMessage.light); light = parsedMessage.light.id + if (SHOW_LIGHT_VOLUME === true) { + selectionManager.setSelections([parsedMessage.light.id]); + } } function handleValueMessages(channel, message, sender) { From 40a096ca8d9c2e1166387531b154da2d263c3759 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 14:34:01 -0800 Subject: [PATCH 33/86] kinematic grab things --- examples/controllers/handControllerGrab.js | 25 ++++++++++++++++--- examples/light_modifier/lightModifier.js | 3 +-- .../light_modifier/lightModifierTestScene.js | 8 +++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index e7d1b5f84b..ce519880af 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -944,7 +944,7 @@ function MyController(hand) { relativeRotation: this.offsetRotation, ttl: ACTION_TTL, kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: false, + kinematicSetVelocity: true, ignoreIK: this.ignoreIK }); if (this.actionID === NULL_ACTION_ID) { @@ -1029,7 +1029,7 @@ function MyController(hand) { relativeRotation: this.offsetRotation, ttl: ACTION_TTL, kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: false, + kinematicSetVelocity: true, ignoreIK: this.ignoreIK }); this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); @@ -1243,7 +1243,26 @@ function MyController(hand) { this.overlayLineOff(); if (this.grabbedEntity !== null) { if (this.actionID !== null) { - Entities.deleteAction(this.grabbedEntity, this.actionID); + //add velocity whatnot + var defaultReleaseVelocityData = { + disableReleaseVelocity: false + }; + + var releaseVelocityData = getEntityCustomData('releaseVelocityKey', this.grabbedEntity, defaultReleaseVelocityData); + if (releaseVelocityData.disableReleaseVelocity === true) { + Entities.updateAction(this.grabbedEntity, this.actionID, { + ttl: 1, + kinematic: false, + kinematicSetVelocity: false, + + }); + // Entities.deleteAction(this.grabbedEntity, this.actionID); + + } else { + //don't make adjustments + Entities.deleteAction(this.grabbedEntity, this.actionID); + + } } } diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index b33b6fc0f1..b79c00da92 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -243,8 +243,7 @@ entitySlider.prototype = { axisEnd: this.endOfAxis, }, releaseVelocityKey: { - disableReleaseVelocity: true, - customReleaseVelocity: false + disableReleaseVelocity: true } }) }; diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js index 5748f0d4ed..e939353b8c 100644 --- a/examples/light_modifier/lightModifierTestScene.js +++ b/examples/light_modifier/lightModifierTestScene.js @@ -73,7 +73,13 @@ function createBlock() { green: 0, blue: 255 }, - position: position + position: position, + userData: JSON.stringify({ + + releaseVelocityKey: { + disableReleaseVelocity: true + } + }) }; block = Entities.addEntity(blockProperties); From f1ec28e169896581a1eb5788d9e89453d6c93378 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 14:55:01 -0800 Subject: [PATCH 34/86] visualize volumes --- examples/libraries/lightOverlayManager.js | 19 +++++++++++++------ examples/light_modifier/lightModifier.js | 2 ++ .../light_modifier/lightModifierTestScene.js | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/examples/libraries/lightOverlayManager.js b/examples/libraries/lightOverlayManager.js index 0942fae723..2d3618096b 100644 --- a/examples/libraries/lightOverlayManager.js +++ b/examples/libraries/lightOverlayManager.js @@ -53,7 +53,9 @@ LightOverlayManager = function() { if (visible != isVisible) { visible = isVisible; for (var id in entityOverlays) { - Overlays.editOverlay(entityOverlays[id], { visible: visible }); + Overlays.editOverlay(entityOverlays[id], { + visible: visible + }); } } }; @@ -61,8 +63,7 @@ LightOverlayManager = function() { // Allocate or get an unused overlay function getOverlay() { if (unusedOverlays.length == 0) { - var overlay = Overlays.addOverlay("image3d", { - }); + var overlay = Overlays.addOverlay("image3d", {}); allOverlays.push(overlay); } else { var overlay = unusedOverlays.pop(); @@ -72,7 +73,9 @@ LightOverlayManager = function() { function releaseOverlay(overlay) { unusedOverlays.push(overlay); - Overlays.editOverlay(overlay, { visible: false }); + Overlays.editOverlay(overlay, { + visible: false + }); } function addEntity(entityID) { @@ -88,7 +91,11 @@ LightOverlayManager = function() { visible: visible, alpha: 0.9, scale: 0.5, - color: { red: 255, green: 255, blue: 255 } + color: { + red: 255, + green: 255, + blue: 255 + } }); } } @@ -123,4 +130,4 @@ LightOverlayManager = function() { Overlays.deleteOverlay(allOverlays[i]); } }); -}; +}; \ No newline at end of file diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index b79c00da92..4d5a326cae 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -36,6 +36,7 @@ if (SHOW_OVERLAYS === true) { lightOverlayManager.setVisible(true); } +// var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking // var pickRay = Camera.computePickRay(event.x, event.y); // var lightResult = lightOverlayManager.findRayIntersection(pickRay) @@ -427,6 +428,7 @@ function cleanup() { Messages.messageReceived.disconnect(handleValueMessages); Entities.deletingEntity.disconnect(deleteEntity); + lightOverlayManager.setVisible(false); } Script.scriptEnding.connect(cleanup); diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js index e939353b8c..84fb779469 100644 --- a/examples/light_modifier/lightModifierTestScene.js +++ b/examples/light_modifier/lightModifierTestScene.js @@ -26,7 +26,7 @@ function createLight() { dimensions: { x: 2, y: 2, - z: 20 + z: 8 }, parentID: block, color: { From f55994b99317472a9b39e4ddeee619700899b96f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 16 Dec 2015 15:07:31 -0800 Subject: [PATCH 35/86] cleanup --- examples/light_modifier/lightModifier.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 4d5a326cae..7cd442e9ed 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -23,19 +23,27 @@ var selectionManager; var lightOverlayManager; if (SHOW_OVERLAYS === true) { + + Script.include('../libraries/gridTool.js'); Script.include('../libraries/entitySelectionTool.js'); + Script.include('../libraries/lightOverlayManager.js'); + + var grid = Grid(); + gridTool = GridTool({ + horizontalGrid: grid + }); + gridTool.setVisible(false); selectionDisplay = SelectionDisplay; selectionManager = SelectionManager; - Script.include('../libraries/lightOverlayManager.js'); lightOverlayManager = new LightOverlayManager(); selectionManager.addEventListener(function() { selectionDisplay.updateHandles(); lightOverlayManager.updatePositions(); }); - lightOverlayManager.setVisible(true); } + // var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking // var pickRay = Camera.computePickRay(event.x, event.y); // var lightResult = lightOverlayManager.findRayIntersection(pickRay) From cd911b31d29413a56ae1d54e215e70b4ef895ea5 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 17 Dec 2015 10:26:54 -0800 Subject: [PATCH 36/86] dont accidentally constrain all objects --- examples/controllers/handControllerGrab.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index ce519880af..9efbff7c58 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -845,7 +845,8 @@ function MyController(hand) { var constraintData = getEntityCustomData('lightModifierKey', this.grabbedEntity, defaultConstraintData); var clampedVector; var targetPosition; - if (constraintData.startAxis !== false) { + if (constraintData.axisStart !== false) { + print('CONSTRAINING OBJECT') clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisStart, constraintData.axisEnd); targetPosition = clampedVector; } else { From 5cc5a2ab33aa2df0edfb622849d17b220c3c1a00 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 17 Dec 2015 10:58:14 -0800 Subject: [PATCH 37/86] working non kinematic release --- examples/controllers/handControllerGrab.js | 71 ++++++++++++------- examples/light_modifier/lightLoader.js | 6 +- examples/light_modifier/lightModifier.js | 8 +-- .../light_modifier/lightModifierTestScene.js | 3 +- 4 files changed, 54 insertions(+), 34 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9efbff7c58..f21df807b4 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -817,26 +817,35 @@ function MyController(hand) { Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + var defaultMoveWithHeadData = { + disableMoveWithHead: false + }; + var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); - // mix in head motion - if (MOVE_WITH_HEAD) { - var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var after = Vec3.multiplyQbyV(Camera.orientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var change = Vec3.subtract(before, after); - this.currentCameraOrientation = Camera.orientation; - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + if (handControllerData.disableMoveWithHead !== true) { + // mix in head motion + if (MOVE_WITH_HEAD) { + var objDistance = Vec3.length(objectToAvatar); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var change = Vec3.subtract(before, after); + this.currentCameraOrientation = Camera.orientation; + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + } + } else { + print('should not head move!'); } + var defaultConstraintData = { axisStart: false, axisEnd: false, @@ -846,7 +855,6 @@ function MyController(hand) { var clampedVector; var targetPosition; if (constraintData.axisStart !== false) { - print('CONSTRAINING OBJECT') clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisStart, constraintData.axisEnd); targetPosition = clampedVector; } else { @@ -1249,15 +1257,29 @@ function MyController(hand) { disableReleaseVelocity: false }; - var releaseVelocityData = getEntityCustomData('releaseVelocityKey', this.grabbedEntity, defaultReleaseVelocityData); + var releaseVelocityData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultReleaseVelocityData); if (releaseVelocityData.disableReleaseVelocity === true) { - Entities.updateAction(this.grabbedEntity, this.actionID, { - ttl: 1, - kinematic: false, - kinematicSetVelocity: false, + print('SHOULD NOT BE KINEMATIC AT RELEASE') + // Entities.updateAction(this.grabbedEntity, this.actionID, { + // ttl: 1, + // kinematic: false, + // kinematicSetVelocity: false, + // }); + Entities.deleteAction(this.grabbedEntity, this.actionID); - }); - // Entities.deleteAction(this.grabbedEntity, this.actionID); + Entities.editEntity(this.grabbedEntity,{ + velocity:{ + x:0, + y:0, + z:0 + }, + angularVelocity:{ + x:0, + y:0, + z:0 + } + }) + Entities.deleteAction(this.grabbedEntity, this.actionID); } else { //don't make adjustments @@ -1266,7 +1288,6 @@ function MyController(hand) { } } } - this.deactivateEntity(this.grabbedEntity); this.grabbedEntity = null; diff --git a/examples/light_modifier/lightLoader.js b/examples/light_modifier/lightLoader.js index e4022e7bc1..83618f85c2 100644 --- a/examples/light_modifier/lightLoader.js +++ b/examples/light_modifier/lightLoader.js @@ -10,11 +10,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var grabScript = Script.resolvePath('../controllers/handControllerGrab.js'); +var grabScript = Script.resolvePath('../controllers/handControllerGrab.js?' + Math.random(0 - 100)); Script.load(grabScript); -var lightModifier = Script.resolvePath('lightModifier.js'); +var lightModifier = Script.resolvePath('lightModifier.js?' + Math.random(0 - 100)); Script.load(lightModifier); Script.setTimeout(function() { - var lightModifierTestScene = Script.resolvePath('lightModifierTestScene.js'); + var lightModifierTestScene = Script.resolvePath('lightModifierTestScene.js?' + Math.random(0 - 100)); Script.load(lightModifierTestScene); }, 750) \ No newline at end of file diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 7cd442e9ed..8eae5cc2a4 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -251,15 +251,15 @@ entitySlider.prototype = { axisStart: position, axisEnd: this.endOfAxis, }, - releaseVelocityKey: { - disableReleaseVelocity: true + handControllerKey: { + disableReleaseVelocity: true, + disableMoveWithHead: true } - }) + }), }; this.sliderIndicator = Entities.addEntity(properties); }, - setValueFromMessage: function(message) { //message is not for our light diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js index 84fb779469..761eb4786d 100644 --- a/examples/light_modifier/lightModifierTestScene.js +++ b/examples/light_modifier/lightModifierTestScene.js @@ -75,8 +75,7 @@ function createBlock() { }, position: position, userData: JSON.stringify({ - - releaseVelocityKey: { + handControllerKey: { disableReleaseVelocity: true } }) From 8e2128c69226e629291db183bf1fc77317826ee7 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 17 Dec 2015 11:59:21 -0800 Subject: [PATCH 38/86] update light volume when you move light parent --- examples/controllers/handControllerGrab.js | 29 +- examples/libraries/entitySelectionTool.js | 3101 +++++++++++++---- examples/light_modifier/lightModifier.js | 2 +- .../light_modifier/lightModifierTestScene.js | 2 + examples/light_modifier/lightParent.js | 42 + 5 files changed, 2387 insertions(+), 789 deletions(-) create mode 100644 examples/light_modifier/lightParent.js diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f21df807b4..f53a66444f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1256,27 +1256,21 @@ function MyController(hand) { var defaultReleaseVelocityData = { disableReleaseVelocity: false }; - + //sometimes we want things to stay right where they are when we let go. var releaseVelocityData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultReleaseVelocityData); if (releaseVelocityData.disableReleaseVelocity === true) { - print('SHOULD NOT BE KINEMATIC AT RELEASE') - // Entities.updateAction(this.grabbedEntity, this.actionID, { - // ttl: 1, - // kinematic: false, - // kinematicSetVelocity: false, - // }); - Entities.deleteAction(this.grabbedEntity, this.actionID); + Entities.deleteAction(this.grabbedEntity, this.actionID); - Entities.editEntity(this.grabbedEntity,{ - velocity:{ - x:0, - y:0, - z:0 + Entities.editEntity(this.grabbedEntity, { + velocity: { + x: 0, + y: 0, + z: 0 }, - angularVelocity:{ - x:0, - y:0, - z:0 + angularVelocity: { + x: 0, + y: 0, + z: 0 } }) Entities.deleteAction(this.grabbedEntity, this.actionID); @@ -1288,6 +1282,7 @@ function MyController(hand) { } } } + this.deactivateEntity(this.grabbedEntity); this.grabbedEntity = null; diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 6edbe6844b..9b213760c2 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -16,23 +16,73 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; SPACE_LOCAL = "local"; SPACE_WORLD = "world"; + SelectionManager = (function() { var that = {}; + + function subscribeToUpdateMessages() { + Messages.subscribe('entityToolUpdates'); + Messages.messageReceived.connect(handleEntitySelectionToolUpdates); + } + + function handleEntitySelectionToolUpdates(channel, message, sender) { + if (channel !== 'entityToolUpdates') { + return; + } + if (sender !== MyAvatar.sessionUUID) { + return; + } + + if (message === 'callUpdate') { + that._update(); + } + } + + subscribeToUpdateMessages(); + that.savedProperties = {}; that.selections = []; var listeners = []; that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - that.localPosition = { x: 0, y: 0, z: 0 }; - that.localDimensions = { x: 0, y: 0, z: 0 }; - that.localRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; + that.localPosition = { + x: 0, + y: 0, + z: 0 + }; + that.localDimensions = { + x: 0, + y: 0, + z: 0 + }; + that.localRegistrationPoint = { + x: 0.5, + y: 0.5, + z: 0.5 + }; that.worldRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - that.worldPosition = { x: 0, y: 0, z: 0 }; - that.worldDimensions = { x: 0, y: 0, z: 0 }; - that.worldRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; - that.centerPosition = { x: 0, y: 0, z: 0 }; + that.worldPosition = { + x: 0, + y: 0, + z: 0 + }; + that.worldDimensions = { + x: 0, + y: 0, + z: 0 + }; + that.worldRegistrationPoint = { + x: 0.5, + y: 0.5, + z: 0.5 + }; + that.centerPosition = { + x: 0, + y: 0, + z: 0 + }; that.saveProperties = function() { that.savedProperties = {}; @@ -177,9 +227,9 @@ function getRelativeCenterPosition(dimensions, registrationPoint) { } } -SelectionDisplay = (function () { +SelectionDisplay = (function() { var that = {}; - + var MINIMUM_DIMENSION = 0.001; var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075; @@ -194,14 +244,18 @@ SelectionDisplay = (function () { var ROTATE_ARROW_WEST_SOUTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-south.svg"; var showExtendedStretchHandles = false; - + var spaceMode = SPACE_LOCAL; var mode = "UNKNOWN"; var overlayNames = new Array(); var lastCameraPosition = Camera.getPosition(); var lastCameraOrientation = Camera.getOrientation(); - var handleHoverColor = { red: 224, green: 67, blue: 36 }; + var handleHoverColor = { + red: 224, + green: 67, + blue: 36 + }; var handleHoverAlpha = 1.0; var rotateOverlayTargetSize = 10000; // really big target @@ -221,19 +275,27 @@ SelectionDisplay = (function () { var pitchNormal; var rollNormal; var rotationNormal; - + var originalRotation; var originalPitch; var originalYaw; var originalRoll; - - var handleColor = { red: 255, green: 255, blue: 255 }; + + var handleColor = { + red: 255, + green: 255, + blue: 255 + }; var handleAlpha = 0.7; - var highlightedHandleColor = { red: 183, green: 64, blue: 44 }; + var highlightedHandleColor = { + red: 183, + green: 64, + blue: 44 + }; var highlightedHandleAlpha = 0.9; - + var previousHandle = false; var previousHandleColor; var previousHandleAlpha; @@ -242,115 +304,182 @@ SelectionDisplay = (function () { var grabberSizeEdge = 0.015; var grabberSizeFace = 0.025; var grabberAlpha = 1; - var grabberColorCorner = { red: 120, green: 120, blue: 120 }; - var grabberColorEdge = { red: 0, green: 0, blue: 0 }; - var grabberColorFace = { red: 120, green: 120, blue: 120 }; + var grabberColorCorner = { + red: 120, + green: 120, + blue: 120 + }; + var grabberColorEdge = { + red: 0, + green: 0, + blue: 0 + }; + var grabberColorFace = { + red: 120, + green: 120, + blue: 120 + }; var grabberLineWidth = 0.5; var grabberSolid = true; - var grabberMoveUpPosition = { x: 0, y: 0, z: 0 }; + var grabberMoveUpPosition = { + x: 0, + y: 0, + z: 0 + }; - var lightOverlayColor = { red: 255, green: 153, blue: 0 }; + var lightOverlayColor = { + red: 255, + green: 153, + blue: 0 + }; var grabberPropertiesCorner = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeCorner, - color: grabberColorCorner, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeCorner, + color: grabberColorCorner, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; var grabberPropertiesEdge = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeEdge, - color: grabberColorEdge, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeEdge, + color: grabberColorEdge, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; var grabberPropertiesFace = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeFace, - color: grabberColorFace, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; - + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeFace, + color: grabberColorFace, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; + var spotLightLineProperties = { color: lightOverlayColor, lineWidth: 1.5, }; - + var highlightBox = Overlays.addOverlay("cube", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 90, green: 90, blue: 90}, - alpha: 1, - solid: false, - visible: false, - dashed: true, - lineWidth: 2.0, - ignoreRayIntersection: true, // this never ray intersects - drawInFront: true - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 90, + green: 90, + blue: 90 + }, + alpha: 1, + solid: false, + visible: false, + dashed: true, + lineWidth: 2.0, + ignoreRayIntersection: true, // this never ray intersects + drawInFront: true + }); var selectionBox = Overlays.addOverlay("cube", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 255, green: 0, blue: 0}, - alpha: 1, - solid: false, - visible: false, - dashed: false, - lineWidth: 1.0, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 255, + green: 0, + blue: 0 + }, + alpha: 1, + solid: false, + visible: false, + dashed: false, + lineWidth: 1.0, + }); var selectionBoxes = []; var rotationDegreesDisplay = Overlays.addOverlay("text3d", { - position: { x:0, y: 0, z: 0}, - text: "", - color: { red: 0, green: 0, blue: 0}, - backgroundColor: { red: 255, green: 255, blue: 255 }, - alpha: 0.7, - backgroundAlpha: 0.7, - visible: false, - isFacingAvatar: true, - drawInFront: true, - ignoreRayIntersection: true, - dimensions: { x: 0, y: 0 }, - lineHeight: 0.0, - topMargin: 0, - rightMargin: 0, - bottomMargin: 0, - leftMargin: 0, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + text: "", + color: { + red: 0, + green: 0, + blue: 0 + }, + backgroundColor: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 0.7, + backgroundAlpha: 0.7, + visible: false, + isFacingAvatar: true, + drawInFront: true, + ignoreRayIntersection: true, + dimensions: { + x: 0, + y: 0 + }, + lineHeight: 0.0, + topMargin: 0, + rightMargin: 0, + bottomMargin: 0, + leftMargin: 0, + }); var grabberMoveUp = Overlays.addOverlay("image3d", { - url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: true, - drawInFront: true, - }); + url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: true, + drawInFront: true, + }); // var normalLine = Overlays.addOverlay("line3d", { // visible: true, @@ -360,7 +489,7 @@ SelectionDisplay = (function () { // color: { red: 255, green: 255, blue: 0 }, // ignoreRayIntersection: true, // }); - + var grabberLBN = Overlays.addOverlay("cube", grabberPropertiesCorner); var grabberRBN = Overlays.addOverlay("cube", grabberPropertiesCorner); var grabberLBF = Overlays.addOverlay("cube", grabberPropertiesCorner); @@ -481,171 +610,324 @@ SelectionDisplay = (function () { ]; - var baseOverlayAngles = { x: 0, y: 0, z: 0 }; + var baseOverlayAngles = { + x: 0, + y: 0, + z: 0 + }; var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", { - position: { x: 1, y: 0, z: 0}, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.5, - solid: true, - visible: false, - width: 300, height: 200, - rotation: baseOverlayRotation, - ignoreRayIntersection: true, // always ignore this - }); + position: { + x: 1, + y: 0, + z: 0 + }, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.5, + solid: true, + visible: false, + width: 300, + height: 200, + rotation: baseOverlayRotation, + ignoreRayIntersection: true, // always ignore this + }); - var yawOverlayAngles = { x: 90, y: 0, z: 0 }; + var yawOverlayAngles = { + x: 90, + y: 0, + z: 0 + }; var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles); - var pitchOverlayAngles = { x: 0, y: 90, z: 0 }; + var pitchOverlayAngles = { + x: 0, + y: 90, + z: 0 + }; var pitchOverlayRotation = Quat.fromVec3Degrees(pitchOverlayAngles); - var rollOverlayAngles = { x: 0, y: 180, z: 0 }; + var rollOverlayAngles = { + x: 0, + y: 180, + z: 0 + }; var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); var xRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var yRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 255, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 255, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var zRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 0, blue: 255 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 0, + blue: 255 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var rotateZeroOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 2.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + visible: false, + lineWidth: 2.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateCurrentOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 2.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 0, blue: 255 }, - ignoreRayIntersection: true, // always ignore this - }); + visible: false, + lineWidth: 2.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 0, + blue: 255 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayTarget = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: rotateOverlayTargetSize, - color: { red: 0, green: 0, blue: 0 }, - alpha: 0.0, - solid: true, - visible: false, - rotation: yawOverlayRotation, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: rotateOverlayTargetSize, + color: { + red: 0, + green: 0, + blue: 0 + }, + alpha: 0.0, + solid: true, + visible: false, + rotation: yawOverlayRotation, + }); var rotateOverlayInner = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.2, + solid: true, + visible: false, + rotation: yawOverlayRotation, + hasTickMarks: true, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayOuter = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.2, + solid: true, + visible: false, + rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + hasTickMarks: true, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayCurrent = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 224, green: 67, blue: 36}, - alpha: 0.8, - solid: true, - visible: false, - rotation: yawOverlayRotation, - ignoreRayIntersection: true, // always ignore this - hasTickMarks: true, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 224, + green: 67, + blue: 36 + }, + alpha: 0.8, + solid: true, + visible: false, + rotation: yawOverlayRotation, + ignoreRayIntersection: true, // always ignore this + hasTickMarks: true, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + }); var yawHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var pitchHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var rollHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var allOverlays = [ highlightBox, @@ -703,7 +985,7 @@ SelectionDisplay = (function () { overlayNames[grabberEdgeNL] = "grabberEdgeNL"; overlayNames[grabberEdgeFR] = "grabberEdgeFR"; overlayNames[grabberEdgeFL] = "grabberEdgeFL"; - + overlayNames[yawHandle] = "yawHandle"; overlayNames[pitchHandle] = "pitchHandle"; overlayNames[rollHandle] = "rollHandle"; @@ -718,6 +1000,7 @@ SelectionDisplay = (function () { var activeTool = null; var grabberTools = {}; + function addGrabberTool(overlay, tool) { grabberTools[overlay] = { mode: tool.mode, @@ -727,8 +1010,8 @@ SelectionDisplay = (function () { } } - - that.cleanup = function () { + + that.cleanup = function() { for (var i = 0; i < allOverlays.length; i++) { Overlays.deleteOverlay(allOverlays[i]); } @@ -741,19 +1024,21 @@ SelectionDisplay = (function () { var properties = Entities.getEntityProperties(entityID); Overlays.editOverlay(highlightBox, { visible: true, - position: properties.boundingBox.center, + position: properties.boundingBox.center, dimensions: properties.dimensions, rotation: properties.rotation }); }; that.unhighlightSelectable = function(entityID) { - Overlays.editOverlay(highlightBox,{ visible: false}); + Overlays.editOverlay(highlightBox, { + visible: false + }); }; that.select = function(entityID, event) { var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - + lastCameraPosition = Camera.getPosition(); lastCameraOrientation = Camera.getOrientation(); @@ -766,12 +1051,16 @@ SelectionDisplay = (function () { print(" event.y:" + event.y); Vec3.print(" current position:", properties.position); } - - + + } - Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); - Overlays.editOverlay(highlightBox, { visible: false }); + Entities.editEntity(entityID, { + localRenderAlpha: 0.1 + }); + Overlays.editOverlay(highlightBox, { + visible: false + }); that.updateHandles(); } @@ -789,7 +1078,7 @@ SelectionDisplay = (function () { } var rotateHandleOffset = 0.05; - + var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; var dimensions, rotation; @@ -828,136 +1117,320 @@ SelectionDisplay = (function () { * ------------------------------*/ - + var cameraPosition = Camera.getPosition(); if (cameraPosition.x > objectCenter.x) { // must be BRF or BRN if (cameraPosition.z < objectCenter.z) { - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 90, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 90, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 0 + }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - yawCorner = { x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: left + rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: near - rotateHandleOffset + }; - pitchCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset}; + pitchCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - rollCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; + rollCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: right, y: boundsCenter.y, z: boundsCenter.z}; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: far }; - + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: right, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: far + }; + + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_SOUTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_SOUTH_URL + }); - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_SOUTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_SOUTH_URL }); - } else { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 0, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 90 }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 0, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 180, + y: 270, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 90 + }); + + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - yawCorner = { x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset }; + yawCorner = { + x: left + rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: far + rotateHandleOffset + }; - pitchCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; + pitchCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - rollCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset}; + rollCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: right, y: boundsCenter.y, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: near}; + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: right, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: near + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } } else { - + // must be BLF or BLN if (cameraPosition.z < objectCenter.z) { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 180, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 180, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 90, + y: 0, + z: 90 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 180 + }); - yawCorner = { x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - pitchCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: right - rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: near - rotateHandleOffset + }; - rollCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset}; + pitchCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: left, y: boundsCenter.y, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: far}; + rollCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: left, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: far + }; + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } else { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 270, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawNormal = { x: 0, y: 1, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; - pitchNormal = { x: 1, y: 0, z: 0 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 270, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 180, + y: 270, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 180 + }); - yawCorner = { x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; - rollCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: right - rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: far + rotateHandleOffset + }; - pitchCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset}; + rollCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: near }; - pitchCenter = { x: left, y: boundsCenter.y, z: boundsCenter.z}; + pitchCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: near + }; + pitchCenter = { + x: left, + y: boundsCenter.y, + z: boundsCenter.z + }; + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } } - + var rotateHandlesVisible = true; var rotationOverlaysVisible = false; var translateHandlesVisible = true; @@ -984,20 +1457,38 @@ SelectionDisplay = (function () { rotateHandlesVisible = false; translateHandlesVisible = false; } - + var rotation = selectionManager.worldRotation; var dimensions = selectionManager.worldDimensions; var position = selectionManager.worldPosition; - - Overlays.editOverlay(rotateOverlayTarget, { visible: rotationOverlaysVisible }); - Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible }); - Overlays.editOverlay(rotateCurrentOverlay, { visible: rotationOverlaysVisible }); + + Overlays.editOverlay(rotateOverlayTarget, { + visible: rotationOverlaysVisible + }); + Overlays.editOverlay(rotateZeroOverlay, { + visible: rotationOverlaysVisible + }); + Overlays.editOverlay(rotateCurrentOverlay, { + visible: rotationOverlaysVisible + }); // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden - Overlays.editOverlay(yawHandle, { visible: rotateHandlesVisible, position: yawCorner, rotation: yawHandleRotation}); - Overlays.editOverlay(pitchHandle, { visible: rotateHandlesVisible, position: pitchCorner, rotation: pitchHandleRotation}); - Overlays.editOverlay(rollHandle, { visible: rotateHandlesVisible, position: rollCorner, rotation: rollHandleRotation}); + Overlays.editOverlay(yawHandle, { + visible: rotateHandlesVisible, + position: yawCorner, + rotation: yawHandleRotation + }); + Overlays.editOverlay(pitchHandle, { + visible: rotateHandlesVisible, + position: pitchCorner, + rotation: pitchHandleRotation + }); + Overlays.editOverlay(rollHandle, { + visible: rotateHandlesVisible, + position: rollCorner, + rotation: rollHandleRotation + }); }; that.setSpaceMode = function(newSpaceMode) { @@ -1016,8 +1507,7 @@ SelectionDisplay = (function () { that.updateHandles(); }; - that.unselectAll = function () { - }; + that.unselectAll = function() {}; that.updateHandles = function() { if (SelectionManager.selections.length == 0) { @@ -1061,34 +1551,138 @@ SelectionDisplay = (function () { var worldTop = SelectionManager.worldDimensions.y / 2; - var LBN = { x: left, y: bottom, z: near }; - var RBN = { x: right, y: bottom, z: near }; - var LBF = { x: left, y: bottom, z: far }; - var RBF = { x: right, y: bottom, z: far }; - var LTN = { x: left, y: top, z: near }; - var RTN = { x: right, y: top, z: near }; - var LTF = { x: left, y: top, z: far }; - var RTF = { x: right, y: top, z: far }; + var LBN = { + x: left, + y: bottom, + z: near + }; + var RBN = { + x: right, + y: bottom, + z: near + }; + var LBF = { + x: left, + y: bottom, + z: far + }; + var RBF = { + x: right, + y: bottom, + z: far + }; + var LTN = { + x: left, + y: top, + z: near + }; + var RTN = { + x: right, + y: top, + z: near + }; + var LTF = { + x: left, + y: top, + z: far + }; + var RTF = { + x: right, + y: top, + z: far + }; - var TOP = { x: center.x, y: top, z: center.z }; - var BOTTOM = { x: center.x, y: bottom, z: center.z }; - var LEFT = { x: left, y: center.y, z: center.z }; - var RIGHT = { x: right, y: center.y, z: center.z }; - var NEAR = { x: center.x, y: center.y, z: near }; - var FAR = { x: center.x, y: center.y, z: far }; + var TOP = { + x: center.x, + y: top, + z: center.z + }; + var BOTTOM = { + x: center.x, + y: bottom, + z: center.z + }; + var LEFT = { + x: left, + y: center.y, + z: center.z + }; + var RIGHT = { + x: right, + y: center.y, + z: center.z + }; + var NEAR = { + x: center.x, + y: center.y, + z: near + }; + var FAR = { + x: center.x, + y: center.y, + z: far + }; - var EdgeTR = { x: right, y: top, z: center.z }; - var EdgeTL = { x: left, y: top, z: center.z }; - var EdgeTF = { x: center.x, y: top, z: front }; - var EdgeTN = { x: center.x, y: top, z: near }; - var EdgeBR = { x: right, y: bottom, z: center.z }; - var EdgeBL = { x: left, y: bottom, z: center.z }; - var EdgeBF = { x: center.x, y: bottom, z: front }; - var EdgeBN = { x: center.x, y: bottom, z: near }; - var EdgeNR = { x: right, y: center.y, z: near }; - var EdgeNL = { x: left, y: center.y, z: near }; - var EdgeFR = { x: right, y: center.y, z: front }; - var EdgeFL = { x: left, y: center.y, z: front }; + var EdgeTR = { + x: right, + y: top, + z: center.z + }; + var EdgeTL = { + x: left, + y: top, + z: center.z + }; + var EdgeTF = { + x: center.x, + y: top, + z: front + }; + var EdgeTN = { + x: center.x, + y: top, + z: near + }; + var EdgeBR = { + x: right, + y: bottom, + z: center.z + }; + var EdgeBL = { + x: left, + y: bottom, + z: center.z + }; + var EdgeBF = { + x: center.x, + y: bottom, + z: front + }; + var EdgeBN = { + x: center.x, + y: bottom, + z: near + }; + var EdgeNR = { + x: right, + y: center.y, + z: near + }; + var EdgeNL = { + x: left, + y: center.y, + z: near + }; + var EdgeFR = { + x: right, + y: center.y, + z: front + }; + var EdgeFL = { + x: left, + y: center.y, + z: front + }; LBN = Vec3.multiplyQbyV(rotation, LBN); RBN = Vec3.multiplyQbyV(rotation, RBN); @@ -1151,7 +1745,7 @@ SelectionDisplay = (function () { var stretchHandlesVisible = spaceMode == SPACE_LOCAL; var extendedStretchHandlesVisible = stretchHandlesVisible && showExtendedStretchHandles; - if (selectionManager.selections.length == 1 ) { + if (selectionManager.selections.length == 1) { var properties = Entities.getEntityProperties(selectionManager.selections[0]); if (properties.type == "Light" && properties.isSpotlight == true) { var stretchHandlesVisible = false; @@ -1190,7 +1784,11 @@ SelectionDisplay = (function () { }); Overlays.editOverlay(grabberSpotLightCircle, { position: NEAR, - dimensions: { x: distance, y: distance, z: 1 }, + dimensions: { + x: distance, + y: distance, + z: 1 + }, lineWidth: 1.5, rotation: rotation, visible: true, @@ -1217,15 +1815,33 @@ SelectionDisplay = (function () { visible: true, }); - Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); - Overlays.editOverlay(grabberPointLightT, { visible: false }); - Overlays.editOverlay(grabberPointLightB, { visible: false }); - Overlays.editOverlay(grabberPointLightL, { visible: false }); - Overlays.editOverlay(grabberPointLightR, { visible: false }); - Overlays.editOverlay(grabberPointLightF, { visible: false }); - Overlays.editOverlay(grabberPointLightN, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleX, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleY, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleZ, { + visible: false + }); + Overlays.editOverlay(grabberPointLightT, { + visible: false + }); + Overlays.editOverlay(grabberPointLightB, { + visible: false + }); + Overlays.editOverlay(grabberPointLightL, { + visible: false + }); + Overlays.editOverlay(grabberPointLightR, { + visible: false + }); + Overlays.editOverlay(grabberPointLightF, { + visible: false + }); + Overlays.editOverlay(grabberPointLightN, { + visible: false + }); } else if (properties.type == "Light" && properties.isSpotlight == false) { var stretchHandlesVisible = false; var extendedStretchHandlesVisible = false; @@ -1262,75 +1878,203 @@ SelectionDisplay = (function () { Overlays.editOverlay(grabberPointLightCircleX, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)), - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleY, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleZ, { position: position, rotation: rotation, - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); - Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); - Overlays.editOverlay(grabberSpotLightL, { visible: false }); - Overlays.editOverlay(grabberSpotLightR, { visible: false }); - Overlays.editOverlay(grabberSpotLightT, { visible: false }); - Overlays.editOverlay(grabberSpotLightB, { visible: false }); - Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + Overlays.editOverlay(grabberSpotLightRadius, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightB, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightCircle, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineB, { + visible: false + }); } else { - Overlays.editOverlay(grabberSpotLightCenter, { visible: false }); - Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); - Overlays.editOverlay(grabberSpotLightL, { visible: false }); - Overlays.editOverlay(grabberSpotLightR, { visible: false }); - Overlays.editOverlay(grabberSpotLightT, { visible: false }); - Overlays.editOverlay(grabberSpotLightB, { visible: false }); - Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + Overlays.editOverlay(grabberSpotLightCenter, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightRadius, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightB, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightCircle, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineB, { + visible: false + }); - Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); - Overlays.editOverlay(grabberPointLightT, { visible: false }); - Overlays.editOverlay(grabberPointLightB, { visible: false }); - Overlays.editOverlay(grabberPointLightL, { visible: false }); - Overlays.editOverlay(grabberPointLightR, { visible: false }); - Overlays.editOverlay(grabberPointLightF, { visible: false }); - Overlays.editOverlay(grabberPointLightN, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleX, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleY, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleZ, { + visible: false + }); + Overlays.editOverlay(grabberPointLightT, { + visible: false + }); + Overlays.editOverlay(grabberPointLightB, { + visible: false + }); + Overlays.editOverlay(grabberPointLightL, { + visible: false + }); + Overlays.editOverlay(grabberPointLightR, { + visible: false + }); + Overlays.editOverlay(grabberPointLightF, { + visible: false + }); + Overlays.editOverlay(grabberPointLightN, { + visible: false + }); } } - Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, rotation: rotation, position: LBN }); - Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, rotation: rotation, position: RBN }); - Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, rotation: rotation, position: LBF }); - Overlays.editOverlay(grabberRBF, { visible: stretchHandlesVisible, rotation: rotation, position: RBF }); + Overlays.editOverlay(grabberLBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: LBN + }); + Overlays.editOverlay(grabberRBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: RBN + }); + Overlays.editOverlay(grabberLBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: LBF + }); + Overlays.editOverlay(grabberRBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: RBF + }); - Overlays.editOverlay(grabberLTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LTN }); - Overlays.editOverlay(grabberRTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RTN }); - Overlays.editOverlay(grabberLTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LTF }); - Overlays.editOverlay(grabberRTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RTF }); + Overlays.editOverlay(grabberLTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LTN + }); + Overlays.editOverlay(grabberRTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RTN + }); + Overlays.editOverlay(grabberLTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LTF + }); + Overlays.editOverlay(grabberRTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RTF + }); - Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, rotation: rotation, position: TOP }); - Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, rotation: rotation, position: BOTTOM }); - Overlays.editOverlay(grabberLEFT, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LEFT }); - Overlays.editOverlay(grabberRIGHT, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RIGHT }); - Overlays.editOverlay(grabberNEAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: NEAR }); - Overlays.editOverlay(grabberFAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: FAR }); + Overlays.editOverlay(grabberTOP, { + visible: stretchHandlesVisible, + rotation: rotation, + position: TOP + }); + Overlays.editOverlay(grabberBOTTOM, { + visible: stretchHandlesVisible, + rotation: rotation, + position: BOTTOM + }); + Overlays.editOverlay(grabberLEFT, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LEFT + }); + Overlays.editOverlay(grabberRIGHT, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RIGHT + }); + Overlays.editOverlay(grabberNEAR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: NEAR + }); + Overlays.editOverlay(grabberFAR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: FAR + }); var boxPosition = Vec3.multiplyQbyV(rotation, center); boxPosition = Vec3.sum(position, boxPosition); @@ -1346,9 +2090,17 @@ SelectionDisplay = (function () { for (var i = 0; i < overlaysNeeded; i++) { selectionBoxes.push( Overlays.addOverlay("cube", { - position: { x: 0, y: 0, z: 0 }, + position: { + x: 0, + y: 0, + z: 0 + }, size: 1, - color: { red: 255, green: 153, blue: 0 }, + color: { + red: 255, + green: 153, + blue: 0 + }, alpha: 1, solid: false, visible: false, @@ -1366,7 +2118,11 @@ SelectionDisplay = (function () { // Adjust overlay position to take registrationPoint into account // centeredRP = registrationPoint with range [-0.5, 0.5] - var centeredRP = Vec3.subtract(properties.registrationPoint, { x: 0.5, y: 0.5, z: 0.5 }); + var centeredRP = Vec3.subtract(properties.registrationPoint, { + x: 0.5, + y: 0.5, + z: 0.5 + }); var offset = vec3Mult(properties.dimensions, centeredRP); offset = Vec3.multiply(-1, offset); offset = Vec3.multiplyQbyV(properties.rotation, offset); @@ -1382,25 +2138,81 @@ SelectionDisplay = (function () { } // Hide any remaining selection boxes for (; i < selectionBoxes.length; i++) { - Overlays.editOverlay(selectionBoxes[i], { visible: false }); + Overlays.editOverlay(selectionBoxes[i], { + visible: false + }); } - Overlays.editOverlay(grabberEdgeTR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTR }); - Overlays.editOverlay(grabberEdgeTL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTL }); - Overlays.editOverlay(grabberEdgeTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTF }); - Overlays.editOverlay(grabberEdgeTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTN }); - Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBR }); - Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBL }); - Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBF }); - Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBN }); - Overlays.editOverlay(grabberEdgeNR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeNR }); - Overlays.editOverlay(grabberEdgeNL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeNL }); - Overlays.editOverlay(grabberEdgeFR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeFR }); - Overlays.editOverlay(grabberEdgeFL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeFL }); + Overlays.editOverlay(grabberEdgeTR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTR + }); + Overlays.editOverlay(grabberEdgeTL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTL + }); + Overlays.editOverlay(grabberEdgeTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTF + }); + Overlays.editOverlay(grabberEdgeTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTN + }); + Overlays.editOverlay(grabberEdgeBR, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBR + }); + Overlays.editOverlay(grabberEdgeBL, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBL + }); + Overlays.editOverlay(grabberEdgeBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBF + }); + Overlays.editOverlay(grabberEdgeBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBN + }); + Overlays.editOverlay(grabberEdgeNR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeNR + }); + Overlays.editOverlay(grabberEdgeNL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeNL + }); + Overlays.editOverlay(grabberEdgeFR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeFR + }); + Overlays.editOverlay(grabberEdgeFL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeFL + }); var grabberMoveUpOffset = 0.1; - grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z } - Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" }); + grabberMoveUpPosition = { + x: position.x, + y: position.y + worldTop + grabberMoveUpOffset, + z: position.z + } + Overlays.editOverlay(grabberMoveUp, { + visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" + }); Overlays.editOverlay(baseOfEntityProjectionOverlay, { visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL", @@ -1423,16 +2235,19 @@ SelectionDisplay = (function () { that.setOverlaysVisible = function(isVisible) { var length = allOverlays.length; for (var i = 0; i < length; i++) { - Overlays.editOverlay(allOverlays[i], { visible: isVisible }); + Overlays.editOverlay(allOverlays[i], { + visible: isVisible + }); } length = selectionBoxes.length; for (var i = 0; i < length; i++) { - Overlays.editOverlay(selectionBoxes[i], { visible: isVisible }); + Overlays.editOverlay(selectionBoxes[i], { + visible: isVisible + }); } }; - that.unselect = function (entityID) { - }; + that.unselect = function(entityID) {}; var initialXZPick = null; var isConstrained = false; @@ -1447,7 +2262,11 @@ SelectionDisplay = (function () { var dimensions = SelectionManager.worldDimensions; var pickRay = Camera.computePickRay(event.x, event.y); - initialXZPick = rayPlaneIntersection(pickRay, startPosition, { x: 0, y: 1, z: 0 }); + initialXZPick = rayPlaneIntersection(pickRay, startPosition, { + x: 0, + y: 1, + z: 0 + }); // Duplicate entities if alt is pressed. This will make a // copy of the selected entities and move the _original_ entities, not @@ -1473,13 +2292,21 @@ SelectionDisplay = (function () { onEnd: function(event, reason) { pushCommandForSelections(duplicatedEntityIDs); - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); }, onMove: function(event) { pickRay = Camera.computePickRay(event.x, event.y); - var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { x: 0, y: 1, z: 0 }); + var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { + x: 0, + y: 1, + z: 0 + }); var vector = Vec3.subtract(pick, initialXZPick); // If shifted, constrain to one axis @@ -1490,19 +2317,49 @@ SelectionDisplay = (function () { vector.x = 0; } if (!isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: true }); - var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 }); - var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 }); - var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 }); - var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 }); - Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true }); - Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true }); + Overlays.editOverlay(xRailOverlay, { + visible: true + }); + var xStart = Vec3.sum(startPosition, { + x: -10000, + y: 0, + z: 0 + }); + var xEnd = Vec3.sum(startPosition, { + x: 10000, + y: 0, + z: 0 + }); + var zStart = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: -10000 + }); + var zEnd = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: 10000 + }); + Overlays.editOverlay(xRailOverlay, { + start: xStart, + end: xEnd, + visible: true + }); + Overlays.editOverlay(zRailOverlay, { + start: zStart, + end: zEnd, + visible: true + }); isConstrained = true; } } else { if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); isConstrained = false; } } @@ -1510,14 +2367,18 @@ SelectionDisplay = (function () { constrainMajorOnly = event.isControl; var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, selectionManager.worldDimensions)); vector = Vec3.subtract( - grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), - cornerPosition); + grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), + cornerPosition); var wantDebug = false; for (var i = 0; i < SelectionManager.selections.length; i++) { var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; - var newPosition = Vec3.sum(properties.position, { x: vector.x, y: 0, z: vector.z }); + var newPosition = Vec3.sum(properties.position, { + x: vector.x, + y: 0, + z: vector.z + }); Entities.editEntity(SelectionManager.selections[i], { position: newPosition, }); @@ -1533,7 +2394,7 @@ SelectionDisplay = (function () { SelectionManager._update(); } }; - + var lastXYPick = null var upDownPickNormal = null; addGrabberTool(grabberMoveUp, { @@ -1579,11 +2440,11 @@ SelectionDisplay = (function () { var vector = Vec3.subtract(newIntersection, lastXYPick); vector = grid.snapToGrid(vector); - + // we only care about the Y axis vector.x = 0; vector.z = 0; - + var wantDebug = false; if (wantDebug) { print("translateUpDown... "); @@ -1609,12 +2470,16 @@ SelectionDisplay = (function () { }); var vec3Mult = function(v1, v2) { - return { x: v1.x * v2.x, y: v1.y * v2.y, z: v1.z * v2.z }; - } - // stretchMode - name of mode - // direction - direction to stretch in - // pivot - point to use as a pivot - // offset - the position of the overlay tool relative to the selections center position + return { + x: v1.x * v2.x, + y: v1.y * v2.y, + z: v1.z * v2.z + }; + } + // stretchMode - name of mode + // direction - direction to stretch in + // pivot - point to use as a pivot + // offset - the position of the overlay tool relative to the selections center position var makeStretchTool = function(stretchMode, direction, pivot, offset, customOnMove) { var signs = { x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), @@ -1659,7 +2524,11 @@ SelectionDisplay = (function () { } // Modify range of registrationPoint to be [-0.5, 0.5] - var centeredRP = Vec3.subtract(registrationPoint, { x: 0.5, y: 0.5, z: 0.5 }); + var centeredRP = Vec3.subtract(registrationPoint, { + x: 0.5, + y: 0.5, + z: 0.5 + }); // Scale pivot to be in the same range as registrationPoint var scaledPivot = Vec3.multiply(0.5, pivot) @@ -1675,9 +2544,17 @@ SelectionDisplay = (function () { pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); if (numDimensions == 1 && mask.x) { - var start = Vec3.multiplyQbyV(rotation, { x: -10000, y: 0, z: 0 }); + var start = Vec3.multiplyQbyV(rotation, { + x: -10000, + y: 0, + z: 0 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 10000, y: 0, z: 0 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 10000, + y: 0, + z: 0 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(xRailOverlay, { start: start, @@ -1686,9 +2563,17 @@ SelectionDisplay = (function () { }); } if (numDimensions == 1 && mask.y) { - var start = Vec3.multiplyQbyV(rotation, { x: 0, y: -10000, z: 0 }); + var start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: -10000, + z: 0 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 10000, z: 0 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 10000, + z: 0 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(yRailOverlay, { start: start, @@ -1697,9 +2582,17 @@ SelectionDisplay = (function () { }); } if (numDimensions == 1 && mask.z) { - var start = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: -10000 }); + var start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: -10000 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: 10000 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: 10000 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(zRailOverlay, { start: start, @@ -1709,26 +2602,50 @@ SelectionDisplay = (function () { } if (numDimensions == 1) { if (mask.x == 1) { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } else if (mask.y == 1) { - planeNormal = { x: 1, y: 0, z: 0 }; + planeNormal = { + x: 1, + y: 0, + z: 0 + }; } else { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } } else if (numDimensions == 2) { if (mask.x == 0) { - planeNormal = { x: 1, y: 0, z: 0 }; + planeNormal = { + x: 1, + y: 0, + z: 0 + }; } else if (mask.y == 0) { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } else { - planeNormal = { x: 0, y: 0, z: z }; + planeNormal = { + x: 0, + y: 0, + z: z + }; } } planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); var pickRay = Camera.computePickRay(event.x, event.y); lastPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); + pickRayPosition, + planeNormal); // Overlays.editOverlay(normalLine, { // start: initialPosition, @@ -1739,9 +2656,15 @@ SelectionDisplay = (function () { }; var onEnd = function(event, reason) { - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(yRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(yRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); pushCommandForSelections(); }; @@ -1762,8 +2685,8 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); newPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); + pickRayPosition, + planeNormal); var vector = Vec3.subtract(newPick, lastPick); vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); @@ -1798,14 +2721,14 @@ SelectionDisplay = (function () { } else { newDimensions = Vec3.sum(initialDimensions, changeInDimensions); } - + newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); - + var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(deltaPivot, changeInDimensions)); var newPosition = Vec3.sum(initialPosition, changeInPosition); - + for (var i = 0; i < SelectionManager.selections.length; i++) { Entities.editEntity(SelectionManager.selections[i], { position: newPosition, @@ -1882,7 +2805,11 @@ SelectionDisplay = (function () { size = props.dimensions.z + change.z; } - var newDimensions = { x: size, y: size, z: size }; + var newDimensions = { + x: size, + y: size, + z: size + }; Entities.editEntity(selectionManager.selections[0], { dimensions: newDimensions, @@ -1891,47 +2818,411 @@ SelectionDisplay = (function () { SelectionManager._update(); } - addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); - addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }); - addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }); - addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }); - addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }); - addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }); + addStretchTool(grabberNEAR, "STRETCH_NEAR", { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }); + addStretchTool(grabberFAR, "STRETCH_FAR", { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: 1 + }); + addStretchTool(grabberTOP, "STRETCH_TOP", { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }); + addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }); + addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { + x: -1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }); + addStretchTool(grabberLEFT, "STRETCH_LEFT", { + x: 1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }); - addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); - addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, cutoffStretchFunc); + addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }); + addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { + x: 0, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { + x: 0, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, cutoffStretchFunc); - addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }, radiusStretchFunc); + addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { + x: 0, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { + x: 0, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }, radiusStretchFunc); - addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}, { x: -1, y: -1, z: -1 }); - addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}, { x: 1, y: -1, z: -1 }); - addStretchTool(grabberLBF, "STRETCH_LBF", null, {x: 1, y: 0, z: -1}, { x: -1, y: -1, z: 1 }); - addStretchTool(grabberRBF, "STRETCH_RBF", null, {x: -1, y: 0, z: -1}, { x: 1, y: -1, z: 1 }); - addStretchTool(grabberLTN, "STRETCH_LTN", null, {x: 1, y: 0, z: 1}, { x: -1, y: 1, z: -1 }); - addStretchTool(grabberRTN, "STRETCH_RTN", null, {x: -1, y: 0, z: 1}, { x: 1, y: 1, z: -1 }); - addStretchTool(grabberLTF, "STRETCH_LTF", null, {x: 1, y: 0, z: -1}, { x: -1, y: 1, z: 1 }); - addStretchTool(grabberRTF, "STRETCH_RTF", null, {x: -1, y: 0, z: -1}, { x: 1, y: 1, z: 1 }); + addStretchTool(grabberLBN, "STRETCH_LBN", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: -1, + z: -1 + }); + addStretchTool(grabberRBN, "STRETCH_RBN", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: -1, + z: -1 + }); + addStretchTool(grabberLBF, "STRETCH_LBF", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: -1, + z: 1 + }); + addStretchTool(grabberRBF, "STRETCH_RBF", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: -1, + z: 1 + }); + addStretchTool(grabberLTN, "STRETCH_LTN", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: 1, + z: -1 + }); + addStretchTool(grabberRTN, "STRETCH_RTN", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: 1, + z: -1 + }); + addStretchTool(grabberLTF, "STRETCH_LTF", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: 1, + z: 1 + }); + addStretchTool(grabberRTF, "STRETCH_RTF", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: 1, + z: 1 + }); - addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, {x: 1, y: 1, z: 0}, { x: 1, y: 1, z: 0 }); - addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, {x: -1, y: 1, z: 0}, { x: -1, y: 1, z: 0 }); - addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, {x: 0, y: 1, z: -1}, { x: 0, y: 1, z: -1 }); - addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, {x: 0, y: 1, z: 1}, { x: 0, y: 1, z: 1 }); - addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, {x: -1, y: 0, z: 0}, { x: 1, y: -1, z: 0 }); - addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, {x: 1, y: 0, z: 0}, { x: -1, y: -1, z: 0 }); - addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, {x: 0, y: 0, z: -1}, { x: 0, y: -1, z: -1 }); - addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, {x: 0, y: 0, z: 1}, { x: 0, y: -1, z: 1 }); - addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, {x: -1, y: 0, z: 1}, { x: 1, y: 0, z: -1 }); - addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, {x: 1, y: 0, z: 1}, { x: -1, y: 0, z: -1 }); - addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, {x: -1, y: 0, z: -1}, { x: 1, y: 0, z: 1 }); - addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, {x: 1, y: 0, z: -1}, { x: -1, y: 0, z: 1 }); + addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, { + x: 1, + y: 1, + z: 0 + }, { + x: 1, + y: 1, + z: 0 + }); + addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, { + x: -1, + y: 1, + z: 0 + }, { + x: -1, + y: 1, + z: 0 + }); + addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, { + x: 0, + y: 1, + z: -1 + }, { + x: 0, + y: 1, + z: -1 + }); + addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, { + x: 0, + y: 1, + z: 1 + }, { + x: 0, + y: 1, + z: 1 + }); + addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: -1, + z: 0 + }); + addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: -1, + z: 0 + }); + addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: -1, + z: -1 + }); + addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: -1, + z: 1 + }); + addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: 0, + z: -1 + }); + addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: 0, + z: -1 + }); + addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: 0, + z: 1 + }); + addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: 0, + z: 1 + }); function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { var angle = angleFromZero * (Math.PI / 180); @@ -1967,34 +3258,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2003,19 +3291,35 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, yawHandleRotation, yawCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); + var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2028,7 +3332,11 @@ SelectionDisplay = (function () { var snapToInner = distanceFromCenter < innerRadius; var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); + var yawChange = Quat.fromVec3Degrees({ + x: 0, + y: angleFromZero, + z: 0 + }); // Entities should only reposition if we are rotating multiple selections around // the selections center point. Otherwise, the rotation will be around the entities @@ -2066,19 +3374,43 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } - + } } }); @@ -2096,34 +3428,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2132,18 +3461,34 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, pitchHandleRotation, pitchCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2158,8 +3503,12 @@ SelectionDisplay = (function () { var snapToInner = distanceFromCenter < innerRadius; var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - - var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); + + var pitchChange = Quat.fromVec3Degrees({ + x: angleFromZero, + y: 0, + z: 0 + }); for (var i = 0; i < SelectionManager.selections.length; i++) { var entityID = SelectionManager.selections[i]; @@ -2188,17 +3537,41 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } } } @@ -2217,34 +3590,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2253,18 +3623,34 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, rollHandleRotation, rollCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2280,7 +3666,11 @@ SelectionDisplay = (function () { var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); + var rollChange = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: angleFromZero + }); for (var i = 0; i < SelectionManager.selections.length; i++) { var entityID = SelectionManager.selections[i]; var properties = Entities.getEntityProperties(entityID); @@ -2308,17 +3698,41 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } } } @@ -2329,10 +3743,10 @@ SelectionDisplay = (function () { // FIXME - this cause problems with editing in the entity properties window //SelectionManager._update(); - + if (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation)) { - + that.updateRotationHandles(); } } @@ -2347,12 +3761,20 @@ SelectionDisplay = (function () { var somethingClicked = false; var pickRay = Camera.computePickRay(event.x, event.y); - + // before we do a ray test for grabbers, disable the ray intersection for our selection box - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: true }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: true }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: true + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2377,14 +3799,16 @@ SelectionDisplay = (function () { activeTool.onBegin(event); } } else { - switch(result.overlayID) { + switch (result.overlayID) { case grabberMoveUp: mode = "TRANSLATE_UP_DOWN"; somethingClicked = true; // in translate mode, we hide our stretch handles... for (var i = 0; i < stretchHandles.length; i++) { - Overlays.editOverlay(stretchHandles[i], { visible: false }); + Overlays.editOverlay(stretchHandles[i], { + visible: false + }); } break; @@ -2397,8 +3821,8 @@ SelectionDisplay = (function () { break; case grabberFAR: - case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? - case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? + case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? + case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? mode = "STRETCH_FAR"; somethingClicked = true; break; @@ -2411,14 +3835,14 @@ SelectionDisplay = (function () { somethingClicked = true; break; case grabberRIGHT: - case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? - case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? + case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? + case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? mode = "STRETCH_RIGHT"; somethingClicked = true; break; case grabberLEFT: - case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? - case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? + case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? + case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? mode = "STRETCH_LEFT"; somethingClicked = true; break; @@ -2429,29 +3853,43 @@ SelectionDisplay = (function () { } } } - + // if one of the items above was clicked, then we know we are in translate or stretch mode, and we // should hide our rotate handles... if (somethingClicked) { - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); - + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); + if (mode != "TRANSLATE_UP_DOWN") { - Overlays.editOverlay(grabberMoveUp, { visible: false }); + Overlays.editOverlay(grabberMoveUp, { + visible: false + }); } } - + if (!somethingClicked) { - + print("rotate handle case..."); - + // After testing our stretch handles, then check out rotate handles - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); - + var overlayOrientation; var overlayCenter; @@ -2460,12 +3898,12 @@ SelectionDisplay = (function () { var pitch = angles.x; var yaw = angles.y; var roll = angles.z; - + originalRotation = properties.rotation; originalPitch = pitch; originalYaw = yaw; originalRoll = roll; - + if (result.intersects) { var tool = grabberTools[result.overlayID]; if (tool) { @@ -2476,7 +3914,7 @@ SelectionDisplay = (function () { activeTool.onBegin(event); } } - switch(result.overlayID) { + switch (result.overlayID) { case yawHandle: mode = "ROTATE_YAW"; somethingClicked = true; @@ -2514,59 +3952,147 @@ SelectionDisplay = (function () { print(" somethingClicked:" + somethingClicked); print(" mode:" + mode); - + if (somethingClicked) { - - Overlays.editOverlay(rotateOverlayTarget, { visible: true, rotation: overlayOrientation, position: overlayCenter }); - Overlays.editOverlay(rotateOverlayInner, { visible: true, rotation: overlayOrientation, position: overlayCenter }); - Overlays.editOverlay(rotateOverlayOuter, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 0 }); - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); + + Overlays.editOverlay(rotateOverlayTarget, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter, + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter, + startAt: 0, + endAt: 0 + }); + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); - Overlays.editOverlay(grabberMoveUp, { visible: false }); - Overlays.editOverlay(grabberLBN, { visible: false }); - Overlays.editOverlay(grabberLBF, { visible: false }); - Overlays.editOverlay(grabberRBN, { visible: false }); - Overlays.editOverlay(grabberRBF, { visible: false }); - Overlays.editOverlay(grabberLTN, { visible: false }); - Overlays.editOverlay(grabberLTF, { visible: false }); - Overlays.editOverlay(grabberRTN, { visible: false }); - Overlays.editOverlay(grabberRTF, { visible: false }); + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); + Overlays.editOverlay(grabberMoveUp, { + visible: false + }); + Overlays.editOverlay(grabberLBN, { + visible: false + }); + Overlays.editOverlay(grabberLBF, { + visible: false + }); + Overlays.editOverlay(grabberRBN, { + visible: false + }); + Overlays.editOverlay(grabberRBF, { + visible: false + }); + Overlays.editOverlay(grabberLTN, { + visible: false + }); + Overlays.editOverlay(grabberLTF, { + visible: false + }); + Overlays.editOverlay(grabberRTN, { + visible: false + }); + Overlays.editOverlay(grabberRTF, { + visible: false + }); - Overlays.editOverlay(grabberTOP, { visible: false }); - Overlays.editOverlay(grabberBOTTOM, { visible: false }); - Overlays.editOverlay(grabberLEFT, { visible: false }); - Overlays.editOverlay(grabberRIGHT, { visible: false }); - Overlays.editOverlay(grabberNEAR, { visible: false }); - Overlays.editOverlay(grabberFAR, { visible: false }); + Overlays.editOverlay(grabberTOP, { + visible: false + }); + Overlays.editOverlay(grabberBOTTOM, { + visible: false + }); + Overlays.editOverlay(grabberLEFT, { + visible: false + }); + Overlays.editOverlay(grabberRIGHT, { + visible: false + }); + Overlays.editOverlay(grabberNEAR, { + visible: false + }); + Overlays.editOverlay(grabberFAR, { + visible: false + }); - Overlays.editOverlay(grabberEdgeTR, { visible: false }); - Overlays.editOverlay(grabberEdgeTL, { visible: false }); - Overlays.editOverlay(grabberEdgeTF, { visible: false }); - Overlays.editOverlay(grabberEdgeTN, { visible: false }); - Overlays.editOverlay(grabberEdgeBR, { visible: false }); - Overlays.editOverlay(grabberEdgeBL, { visible: false }); - Overlays.editOverlay(grabberEdgeBF, { visible: false }); - Overlays.editOverlay(grabberEdgeBN, { visible: false }); - Overlays.editOverlay(grabberEdgeNR, { visible: false }); - Overlays.editOverlay(grabberEdgeNL, { visible: false }); - Overlays.editOverlay(grabberEdgeFR, { visible: false }); - Overlays.editOverlay(grabberEdgeFL, { visible: false }); + Overlays.editOverlay(grabberEdgeTR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTF, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTN, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBF, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBN, { + visible: false + }); + Overlays.editOverlay(grabberEdgeNR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeNL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeFR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeFL, { + visible: false + }); } } if (!somethingClicked) { - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - switch(result.overlayID) { + switch (result.overlayID) { case selectionBox: activeTool = translateXZTool; mode = translateXZTool.mode; @@ -2584,17 +4110,25 @@ SelectionDisplay = (function () { if (somethingClicked) { pickRay = Camera.computePickRay(event.x, event.y); if (wantDebug) { - print("mousePressEvent()...... " + overlayNames[result.overlayID]); + print("mousePressEvent()...... " + overlayNames[result.overlayID]); } } // reset everything as intersectable... // TODO: we could optimize this since some of these were already flipped back - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: false }); - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); - + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: false + }); + return somethingClicked; }; @@ -2613,7 +4147,7 @@ SelectionDisplay = (function () { var highlightNeeded = false; if (result.intersects) { - switch(result.overlayID) { + switch (result.overlayID) { case yawHandle: case pitchHandle: case rollHandle: @@ -2621,7 +4155,7 @@ SelectionDisplay = (function () { pickedAlpha = handleAlpha; highlightNeeded = true; break; - + case grabberMoveUp: pickedColor = handleColor; pickedAlpha = handleAlpha; @@ -2682,30 +4216,42 @@ SelectionDisplay = (function () { default: if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } break; } - + if (highlightNeeded) { if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } - Overlays.editOverlay(result.overlayID, { color: highlightedHandleColor, alpha: highlightedHandleAlpha }); + Overlays.editOverlay(result.overlayID, { + color: highlightedHandleColor, + alpha: highlightedHandleAlpha + }); previousHandle = result.overlayID; previousHandleColor = pickedColor; previousHandleAlpha = pickedAlpha; } - + } else { if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } } - + return false; }; @@ -2728,7 +4274,11 @@ SelectionDisplay = (function () { Overlays.editOverlay(rollHandle, { scale: handleSize, }); - var pos = Vec3.sum(grabberMoveUpPosition, { x: 0, y: Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3, z: 0 }); + var pos = Vec3.sum(grabberMoveUpPosition, { + x: 0, + y: Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3, + z: 0 + }); Overlays.editOverlay(grabberMoveUp, { position: pos, scale: handleSize / 2, @@ -2745,34 +4295,43 @@ SelectionDisplay = (function () { activeTool = null; // hide our rotation overlays..., and show our handles if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL") { - Overlays.editOverlay(rotateOverlayTarget, { visible: false }); - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { + visible: false + }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); showHandles = true; } if (mode != "UNKNOWN") { showHandles = true; } - + mode = "UNKNOWN"; - + // if something is selected, then reset the "original" properties for any potential next click+move operation if (SelectionManager.hasSelection()) { if (showHandles) { that.select(SelectionManager.selections[0], event); } } - + }; // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: // Controller.mousePressEvent.connect(that.mousePressEvent); // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); - + + + return that; -}()); - +}()); \ No newline at end of file diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 8eae5cc2a4..8f71f161a2 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -25,7 +25,7 @@ var lightOverlayManager; if (SHOW_OVERLAYS === true) { Script.include('../libraries/gridTool.js'); - Script.include('../libraries/entitySelectionTool.js'); + Script.include('../libraries/entitySelectionTool.js?'+Math.random(0-100)); Script.include('../libraries/lightOverlayManager.js'); var grid = Grid(); diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js index 761eb4786d..8381b9d434 100644 --- a/examples/light_modifier/lightModifierTestScene.js +++ b/examples/light_modifier/lightModifierTestScene.js @@ -10,6 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?'+Math.random(0-100)); var basePosition, avatarRot; avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(0, Quat.getUp(avatarRot))); @@ -74,6 +75,7 @@ function createBlock() { blue: 255 }, position: position, + script:PARENT_SCRIPT_URL, userData: JSON.stringify({ handControllerKey: { disableReleaseVelocity: true diff --git a/examples/light_modifier/lightParent.js b/examples/light_modifier/lightParent.js new file mode 100644 index 0000000000..2d7cfe8042 --- /dev/null +++ b/examples/light_modifier/lightParent.js @@ -0,0 +1,42 @@ + // + // slider.js + // + // Created by James Pollack @imgntn on 12/15/2015 + // Copyright 2015 High Fidelity, Inc. + // + // Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis. + // + // Distributed under the Apache License, Version 2.0. + // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + // + + (function() { + + function LightParent() { + return this; + } + + LightParent.prototype = { + preload: function(entityID) { + print('LIGHT PARENT SCRIPT GO') + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + this.initialProperties = entityProperties + this.userData = JSON.parse(entityProperties.userData); + }, + startNearGrab: function() {}, + startDistantGrab: function() { + + }, + continueNearGrab: function() { + this.continueDistantGrab(); + }, + continueDistantGrab: function() { + print('distant grab, should send message!') + Messages.sendMessage('entityToolUpdates', 'callUpdate'); + }, + + }; + + return new LightParent(); + }); \ No newline at end of file From a25feb5578adfb989824aa6d2e723564a317c6bf Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 17 Dec 2015 16:58:38 -0800 Subject: [PATCH 39/86] switching branches --- examples/controllers/handControllerGrab.js | 2 +- examples/libraries/entitySelectionTool.js | 1 - examples/light_modifier/lightModifier.js | 179 ++++++++++++------ .../light_modifier/lightModifierTestScene.js | 13 +- examples/light_modifier/lightParent.js | 2 - 5 files changed, 131 insertions(+), 66 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f53a66444f..0f927f39c8 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -517,7 +517,7 @@ function MyController(hand) { } var intersection = Entities.findRayIntersection(pickRayBacked, true); - + Messages.sendMessage('Hifi-Light-Overlay-Ray-Check', JSON.stringify(pickRayBacked)); if (intersection.intersects) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 9b213760c2..94ffb48a71 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -20,7 +20,6 @@ SPACE_WORLD = "world"; SelectionManager = (function() { var that = {}; - function subscribeToUpdateMessages() { Messages.subscribe('entityToolUpdates'); Messages.messageReceived.connect(handleEntitySelectionToolUpdates); diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 8f71f161a2..26fb7b7f36 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -22,10 +22,13 @@ var selectionDisplay; var selectionManager; var lightOverlayManager; +//for when we make a block parent for the light +var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100)); + if (SHOW_OVERLAYS === true) { Script.include('../libraries/gridTool.js'); - Script.include('../libraries/entitySelectionTool.js?'+Math.random(0-100)); + Script.include('../libraries/entitySelectionTool.js?' + Math.random(0 - 100)); Script.include('../libraries/lightOverlayManager.js'); var grid = Grid(); @@ -90,8 +93,8 @@ var WHITE = { var ORANGE = { red: 255, - green: 0, - blue: 128 + green: 165, + blue: 0 } var SLIDER_DIMENSIONS = { @@ -100,6 +103,18 @@ var SLIDER_DIMENSIONS = { z: 0.075 }; +var CLOSE_BUTTON_DIMENSIONS ={ + x:0.1, + y:0.1, + z:0.1 +} + +var LIGHT_MODEL_DIMENSIONS={ + x:0.04, + y:0.04, + z:0.09 +} + var PER_ROW_OFFSET = { x: 0, y: -0.2, @@ -348,7 +363,6 @@ var light = null; function makeSliders(light) { // selectionManager.setSelections([entityID]); - if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; var USE_INTENSITY_SLIDER = true; @@ -383,9 +397,25 @@ function makeSliders(light) { // selectionManager.setSelections([entityID]); slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 6); sliders.push(slidersRef.exponent); } + + var closeButtonPosition; + + createCloseButton(closeButtonPosition); subscribeToSliderMessages(); }; +function createCloseButton(position){ + var buttonProperties = { + type:'Model', + modelURL:CLOSE_BUTTON_MODEL_URL, + dimensions:CLOSE_BUTTON_DIMENSIONS, + position:position, + rotation:Quat.fromPitchYawRollDegrees(90,0,0); + } + + var button = Entities.addEntity(buttonProperties); +} + function subScribeToNewLights() { Messages.subscribe('Hifi-Light-Mod-Receiver'); Messages.messageReceived.connect(handleLightModMessages); @@ -396,6 +426,12 @@ function subscribeToSliderMessages() { Messages.messageReceived.connect(handleValueMessages); } +function subscribeToLightOverlayRayCheckMessages() { + Messages.subscribe('Hifi-Light-Overlay-Ray-Check'); + Messages.messageReceived.connect(handleLightOverlayRayCheckMessages); +} + + function handleLightModMessages(channel, message, sender) { if (channel !== 'Hifi-Light-Mod-Receiver') { return; @@ -425,6 +461,86 @@ function handleValueMessages(channel, message, sender) { slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage); } +var currentLight; + +function handleLightOverlayRayCheckMessages(channel, message, sender) { + if (channel !== 'Hifi-Light-Overlay-Ray-Check') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + + print('RAY CHECK GOT MESSAGE::' + message); + var pickRay = JSON.parse(message); + + var doesIntersect = lightOverlayManager.findRayIntersection(pickRay); + print('DOES INTERSECT A LIGHT WE HAVE???' + doesIntersect.intersects); + if (doesIntersect.intersects === true) { + print('FULL MESSAGE:::' + JSON.stringify(doesIntersect)) + + var lightID = doesIntersect.entityID; + if (currentLight === lightID) { + print('ALREADY HAVE A BLOCK, EXIT') + return; + } + print('LIGHT ID::' + lightID); + currentLight = lightID; + var lightProperties = Entities.getEntityProperties(lightID); + var block = createBlock(lightProperties.position); + + var light = { + id: lightID, + type: 'spotlight', + initialProperties: lightProperties + } + + makeSliders(light); + print('AFTER MAKE SLIDERS') + if (SHOW_LIGHT_VOLUME === true) { + selectionManager.setSelections([lightID]); + print('SET SELECTIOIO MANAGER TO::: '+ lightID); + print('hasSelection???' + selectionManager.hasSelection()) + } + print('BLOCK IS:::' + block); + Entities.editEntity(lightID, { + parentID: block + }); + + + } +} +function createBlock(position) { + print('CREATE BLOCK') + + var blockProperties = { + name: 'Hifi-Spotlight-Block', + type: 'Box', + dimensions: { + x: 1, + y: 1, + z: 1 + }, + collisionsWillMove: true, + color: { + red: 0, + green: 0, + blue: 255 + }, + position: position, + script: PARENT_SCRIPT_URL, + userData: JSON.stringify({ + handControllerKey: { + disableReleaseVelocity: true + } + }) + }; + + var block = Entities.addEntity(blockProperties); + + return block +} + function cleanup() { var i; for (i = 0; i < sliders.length; i++) { @@ -440,7 +556,6 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -subScribeToNewLights(); function deleteEntity(entityID) { if (entityID === light) { @@ -448,60 +563,10 @@ function deleteEntity(entityID) { } } - - Entities.deletingEntity.connect(deleteEntity); -// search for lights to make grabbable - -// var USE_DEBOUNCE = true; -// var sinceLastUpdate = 0; - -// function searchForLightsToVisualize() { - -// var deltaTime = interval(); - -// if (USE_DEBOUNCE === true) { -// sinceLastUpdate = sinceLastUpdate + deltaTime; - -// if (sinceLastUpdate > 60) { -// sinceLastUpdate = 0; -// } else { -// return; -// } -// } - -// print('SEARCHING FOR LIGHTS'); - -// var entitites = Entities.findEntities(MyAvatar.position, 50); -// for (i = 0; i < entities.length; i++) { -// var entityProperties = Entities.getEntityProperties(entities[i], ['type', 'parentID']) -// var parentID = entityProperties.parentID; -// var type = entityProperties.type; - -// if (type !== 'Light') { -// return; -// } - -// if (type === "Light" && parentID !== DEFAULT_PARENT_ID && parentID !== null) { -// var light = entities[i]; -// //do something with the light. -// } - -// } - -// } - -// function interval() { -// var lastTime = new Date().getTime(); - -// return function getInterval() { -// var newTime = new Date().getTime(); -// var delta = newTime - lastTime; -// lastTime = newTime; -// return delta; -// }; -// } +subscribeToLightOverlayRayCheckMessages(); +subScribeToNewLights(); diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js index 8381b9d434..4b78484a31 100644 --- a/examples/light_modifier/lightModifierTestScene.js +++ b/examples/light_modifier/lightModifierTestScene.js @@ -19,7 +19,10 @@ var light, block; function createLight() { var blockProperties = Entities.getEntityProperties(block, ["position", "rotation"]); - var lightTransform = evalLightWorldTransform(blockProperties.position, blockProperties.rotation); + var position = basePosition; + position.y += 3; + var lightTransform = evalLightWorldTransform(position,avatarRot); + // var lightTransform = evalLightWorldTransform(blockProperties.position, blockProperties.rotation); var lightProperties = { name: 'Hifi-Spotlight', type: "Light", @@ -29,7 +32,7 @@ function createLight() { y: 2, z: 8 }, - parentID: block, + // parentID: block, color: { red: 255, green: 0, @@ -53,7 +56,7 @@ function createLight() { } }; - Messages.sendMessage('Hifi-Light-Mod-Receiver', JSON.stringify(message)); +// Messages.sendMessage('Hifi-Light-Mod-Receiver', JSON.stringify(message)); } @@ -104,11 +107,11 @@ function evalLightWorldTransform(modelPos, modelRot) { } function cleanup() { - Entities.deleteEntity(block); + //Entities.deleteEntity(block); Entities.deleteEntity(light); } Script.scriptEnding.connect(cleanup); -createBlock(); +//createBlock(); createLight(); \ No newline at end of file diff --git a/examples/light_modifier/lightParent.js b/examples/light_modifier/lightParent.js index 2d7cfe8042..0d91b93f93 100644 --- a/examples/light_modifier/lightParent.js +++ b/examples/light_modifier/lightParent.js @@ -18,7 +18,6 @@ LightParent.prototype = { preload: function(entityID) { - print('LIGHT PARENT SCRIPT GO') this.entityID = entityID; var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); this.initialProperties = entityProperties @@ -32,7 +31,6 @@ this.continueDistantGrab(); }, continueDistantGrab: function() { - print('distant grab, should send message!') Messages.sendMessage('entityToolUpdates', 'callUpdate'); }, From 7a542a678b709d5a68ce0f75cb9a9c874c94c480 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 17 Dec 2015 17:17:04 -0800 Subject: [PATCH 40/86] prep for close button --- examples/light_modifier/closeButton.js | 41 +++++++++++++ examples/light_modifier/lightModifier.js | 76 +++++++++++++++++------- examples/light_modifier/lightParent.js | 22 +++---- 3 files changed, 106 insertions(+), 33 deletions(-) create mode 100644 examples/light_modifier/closeButton.js diff --git a/examples/light_modifier/closeButton.js b/examples/light_modifier/closeButton.js new file mode 100644 index 0000000000..045882fd33 --- /dev/null +++ b/examples/light_modifier/closeButton.js @@ -0,0 +1,41 @@ +// +// closeButton.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that closes sliders when interacted with. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + function CloseButton() { + return this; + } + + CloseButton.prototype = { + preload: function(entityID) { + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + this.initialProperties = entityProperties + this.userData = JSON.parse(entityProperties.userData); + }, + startNearGrab: function() { + + }, + startDistantGrab: function() { + + }, + continueNearGrab: function() { + this.continueDistantGrab(); + }, + continueDistantGrab: function() { + }, + + }; + + return new CloseButton(); +}); \ No newline at end of file diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 26fb7b7f36..29d66ca821 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -60,6 +60,8 @@ var CUTOFF_MAX = 360; var EXPONENT_MAX = 1; var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); +var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx'; +var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100)); var RED = { red: 255, @@ -103,16 +105,16 @@ var SLIDER_DIMENSIONS = { z: 0.075 }; -var CLOSE_BUTTON_DIMENSIONS ={ - x:0.1, - y:0.1, - z:0.1 +var CLOSE_BUTTON_DIMENSIONS = { + x: 0.1, + y: 0.1, + z: 0.1 } -var LIGHT_MODEL_DIMENSIONS={ - x:0.04, - y:0.04, - z:0.09 +var LIGHT_MODEL_DIMENSIONS = { + x: 0.04, + y: 0.04, + z: 0.09 } var PER_ROW_OFFSET = { @@ -361,7 +363,7 @@ var slidersRef = { } var light = null; -function makeSliders(light) { // selectionManager.setSelections([entityID]); +function makeSliders(light) { if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; @@ -398,22 +400,50 @@ function makeSliders(light) { // selectionManager.setSelections([entityID]); sliders.push(slidersRef.exponent); } - var closeButtonPosition; + createCloseButton(7); - createCloseButton(closeButtonPosition); subscribeToSliderMessages(); }; -function createCloseButton(position){ +var closeButtons = []; + +function createCloseButton(row) { + var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); + var verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); + var downPosition = Vec3.sum(basePosition, verticalOffset); + var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + var rightVector = Quat.getRight(avatarRot); + var extension = Vec3.multiply(AXIS_SCALE, rightVector); + var position = Vec3.sum(downPosition, extension); + var buttonProperties = { - type:'Model', - modelURL:CLOSE_BUTTON_MODEL_URL, - dimensions:CLOSE_BUTTON_DIMENSIONS, - position:position, - rotation:Quat.fromPitchYawRollDegrees(90,0,0); + type: 'Model', + modelURL: CLOSE_BUTTON_MODEL_URL, + dimensions: CLOSE_BUTTON_DIMENSIONS, + position: position, + rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), + collisionsWillMove: false + //need to add wantsTrigger stuff so we can interact with it with our beamz } var button = Entities.addEntity(buttonProperties); + + closeButtons.push(button); + + Script.update.connect(rotateCloseButtons); +} + +function rotateCloseButtons() { + closeButtons.forEach(function(button) { + Entities.editEntity(button, { + angularVelocity: { + x: 0, + y: 0.25, + z: 0 + } + }) + + }) } function subScribeToNewLights() { @@ -471,7 +501,6 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { return; } - print('RAY CHECK GOT MESSAGE::' + message); var pickRay = JSON.parse(message); var doesIntersect = lightOverlayManager.findRayIntersection(pickRay); @@ -484,7 +513,7 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { print('ALREADY HAVE A BLOCK, EXIT') return; } - print('LIGHT ID::' + lightID); + currentLight = lightID; var lightProperties = Entities.getEntityProperties(lightID); var block = createBlock(lightProperties.position); @@ -496,13 +525,13 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { } makeSliders(light); - print('AFTER MAKE SLIDERS') + if (SHOW_LIGHT_VOLUME === true) { selectionManager.setSelections([lightID]); - print('SET SELECTIOIO MANAGER TO::: '+ lightID); + print('SET SELECTIOIO MANAGER TO::: ' + lightID); print('hasSelection???' + selectionManager.hasSelection()) } - print('BLOCK IS:::' + block); + Entities.editEntity(lightID, { parentID: block }); @@ -510,6 +539,7 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { } } + function createBlock(position) { print('CREATE BLOCK') @@ -553,6 +583,8 @@ function cleanup() { Entities.deletingEntity.disconnect(deleteEntity); lightOverlayManager.setVisible(false); + Script.update.disconnect(rotateCloseButtons); + } Script.scriptEnding.connect(cleanup); diff --git a/examples/light_modifier/lightParent.js b/examples/light_modifier/lightParent.js index 0d91b93f93..2b53c05d0a 100644 --- a/examples/light_modifier/lightParent.js +++ b/examples/light_modifier/lightParent.js @@ -1,14 +1,14 @@ - // - // slider.js - // - // Created by James Pollack @imgntn on 12/15/2015 - // Copyright 2015 High Fidelity, Inc. - // - // Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis. - // - // Distributed under the Apache License, Version 2.0. - // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - // +// +// lightParent.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that tells the light parent to update the selection tool when we move it. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// (function() { From f84a441c7daeb09c54acef636791986df0353609 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 17 Dec 2015 18:33:17 -0800 Subject: [PATCH 41/86] close button --- examples/light_modifier/lightModifier.js | 44 ++++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 29d66ca821..d82afc09bb 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -60,6 +60,7 @@ var CUTOFF_MAX = 360; var EXPONENT_MAX = 1; var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); +var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_good.fbx'; var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx'; var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100)); @@ -112,9 +113,9 @@ var CLOSE_BUTTON_DIMENSIONS = { } var LIGHT_MODEL_DIMENSIONS = { - x: 0.04, - y: 0.04, - z: 0.09 + x: 0.68, + y: 0.68, + z: 1.54 } var PER_ROW_OFFSET = { @@ -408,15 +409,16 @@ function makeSliders(light) { var closeButtons = []; function createCloseButton(row) { - var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); + var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); var verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); var downPosition = Vec3.sum(basePosition, verticalOffset); - var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); var rightVector = Quat.getRight(avatarRot); var extension = Vec3.multiply(AXIS_SCALE, rightVector); var position = Vec3.sum(downPosition, extension); var buttonProperties = { + name:'Hifi-Close-Button', type: 'Model', modelURL: CLOSE_BUTTON_MODEL_URL, dimensions: CLOSE_BUTTON_DIMENSIONS, @@ -492,6 +494,7 @@ function handleValueMessages(channel, message, sender) { } var currentLight; +var block; function handleLightOverlayRayCheckMessages(channel, message, sender) { if (channel !== 'Hifi-Light-Overlay-Ray-Check') { @@ -516,7 +519,8 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { currentLight = lightID; var lightProperties = Entities.getEntityProperties(lightID); - var block = createBlock(lightProperties.position); + block = createBlock(lightProperties.position); + // block = createLightModel(lightProperties.position); var light = { id: lightID, @@ -540,6 +544,30 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { } } +function createLightModel(position) { + print('CREATE MODEL') + var blockProperties = { + name: 'Hifi-Spotlight-Model', + type: 'Model', + shapeType: 'box', + modelURL: LIGHT_MODEL_URL, + dimensions: LIGHT_MODEL_DIMENSIONS, + collisionsWillMove: true, + position: position, + rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), + script: PARENT_SCRIPT_URL, + userData: JSON.stringify({ + handControllerKey: { + disableReleaseVelocity: true + } + }) + }; + + var block = Entities.addEntity(blockProperties); + + return block +} + function createBlock(position) { print('CREATE BLOCK') @@ -551,12 +579,12 @@ function createBlock(position) { y: 1, z: 1 }, - collisionsWillMove: true, color: { red: 0, green: 0, blue: 255 }, + collisionsWillMove: true, position: position, script: PARENT_SCRIPT_URL, userData: JSON.stringify({ @@ -578,11 +606,13 @@ function cleanup() { Entities.deleteEntity(sliders[i].sliderIndicator); } + Entities.deleteEntity(block); Messages.messageReceived.disconnect(handleLightModMessages); Messages.messageReceived.disconnect(handleValueMessages); Entities.deletingEntity.disconnect(deleteEntity); lightOverlayManager.setVisible(false); + selectionManager.clearSelections(); Script.update.disconnect(rotateCloseButtons); } From 387c30d83fa1abe3fa2a97ed512918ac7ad073ea Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 18 Dec 2015 15:04:13 -0800 Subject: [PATCH 42/86] close button --- examples/controllers/handControllerGrab.js | 2 +- examples/light_modifier/README.md | 6 +- examples/light_modifier/lightModifier.js | 58 +++++++++++-------- .../light_modifier/lightModifierTestScene.js | 2 +- 4 files changed, 41 insertions(+), 27 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 0f927f39c8..1b256f67c0 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -842,7 +842,7 @@ function MyController(hand) { this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); } } else { - print('should not head move!'); + // print('should not head move!'); } diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md index d978c73b5b..3bd456d525 100644 --- a/examples/light_modifier/README.md +++ b/examples/light_modifier/README.md @@ -3,11 +3,15 @@ This PR demonstrates one way in-world editing of objects might work. We start w To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js To reset, I recommend stopping all scripts then re-loading lightLoader.js -When you run the lightLoader.js script, 4 scripts will be loaded: +When you run the lightLoader.js script, several scripts will be loaded: - handControllerGrab.js (custom) - lightModifier.js (listens for message to create sliders for a given light) - lightModifierTestScene.js (creates a light and parents it to a block, then sends a message ^^) - slider.js (attached to each slider entity) +- lightParent.js (attached to the entity to which a light is parented, so you can move it around) +- closeButton.js (for closing the ui) +- ../libraries/lightOverlayManager.js (custom) +- ../libraries/entitySelectionTool.js diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index d82afc09bb..76c1cdc873 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -60,7 +60,7 @@ var CUTOFF_MAX = 360; var EXPONENT_MAX = 1; var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); -var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_good.fbx'; +var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_rotated.fbx'; var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx'; var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100)); @@ -108,14 +108,14 @@ var SLIDER_DIMENSIONS = { var CLOSE_BUTTON_DIMENSIONS = { x: 0.1, - y: 0.1, + y: 0.025, z: 0.1 } var LIGHT_MODEL_DIMENSIONS = { - x: 0.68, - y: 0.68, - z: 1.54 + x: 0.58, + y: 1.21, + z: 0.57 } var PER_ROW_OFFSET = { @@ -262,6 +262,7 @@ entitySlider.prototype = { color: this.color, position: sliderPosition, script: SLIDER_SCRIPT_URL, + ignoreForCollisions:true, userData: JSON.stringify({ lightModifierKey: { lightID: this.lightID, @@ -401,29 +402,34 @@ function makeSliders(light) { sliders.push(slidersRef.exponent); } - createCloseButton(7); + createCloseButton(slidersRef.exponent.endOfAxis); subscribeToSliderMessages(); }; var closeButtons = []; -function createCloseButton(row) { - var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); - var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); - var verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); - var downPosition = Vec3.sum(basePosition, verticalOffset); - var rightVector = Quat.getRight(avatarRot); - var extension = Vec3.multiply(AXIS_SCALE, rightVector); - var position = Vec3.sum(downPosition, extension); +function createCloseButton(endOfAxis) { + // var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + // var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); + // var verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); + // var verticalOffset = lastRowVerticalOffset; + // var downPosition = Vec3.sum(basePosition, verticalOffset); + // var rightVector = Quat.getRight(avatarRot); + // var extension = Vec3.multiply(AXIS_SCALE, rightVector); + // var position = Vec3.sum(downPosition, extension); var buttonProperties = { name:'Hifi-Close-Button', type: 'Model', modelURL: CLOSE_BUTTON_MODEL_URL, dimensions: CLOSE_BUTTON_DIMENSIONS, - position: position, - rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), + position: Vec3.sum(endOfAxis,{ + x:0, + y:-0.15, + z:0 + }), + rotation: Quat.fromPitchYawRollDegrees(0, 45, 90), collisionsWillMove: false //need to add wantsTrigger stuff so we can interact with it with our beamz } @@ -440,7 +446,7 @@ function rotateCloseButtons() { Entities.editEntity(button, { angularVelocity: { x: 0, - y: 0.25, + y: 0.5, z: 0 } }) @@ -519,8 +525,8 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { currentLight = lightID; var lightProperties = Entities.getEntityProperties(lightID); - block = createBlock(lightProperties.position); - // block = createLightModel(lightProperties.position); + // block = createBlock(lightProperties.position); + block = createLightModel(lightProperties.position); var light = { id: lightID, @@ -532,12 +538,11 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { if (SHOW_LIGHT_VOLUME === true) { selectionManager.setSelections([lightID]); - print('SET SELECTIOIO MANAGER TO::: ' + lightID); - print('hasSelection???' + selectionManager.hasSelection()) } Entities.editEntity(lightID, { - parentID: block + parentID: block, + parentJointIndex:-1 }); @@ -551,10 +556,11 @@ function createLightModel(position) { type: 'Model', shapeType: 'box', modelURL: LIGHT_MODEL_URL, + // modelURL:"http://hifi-content.s3.amazonaws.com/james/light_modifier/box4.fbx", dimensions: LIGHT_MODEL_DIMENSIONS, collisionsWillMove: true, position: position, - rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), + // rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), script: PARENT_SCRIPT_URL, userData: JSON.stringify({ handControllerKey: { @@ -576,7 +582,7 @@ function createBlock(position) { type: 'Box', dimensions: { x: 1, - y: 1, + y: 4, z: 1 }, color: { @@ -606,6 +612,10 @@ function cleanup() { Entities.deleteEntity(sliders[i].sliderIndicator); } + while(closeButtons.length>0){ + Entities.deleteEntity(closeButtons.pop()); + } + Entities.deleteEntity(block); Messages.messageReceived.disconnect(handleLightModMessages); Messages.messageReceived.disconnect(handleValueMessages); diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js index 4b78484a31..8df83b09bf 100644 --- a/examples/light_modifier/lightModifierTestScene.js +++ b/examples/light_modifier/lightModifierTestScene.js @@ -20,7 +20,7 @@ var light, block; function createLight() { var blockProperties = Entities.getEntityProperties(block, ["position", "rotation"]); var position = basePosition; - position.y += 3; + position.y += 2; var lightTransform = evalLightWorldTransform(position,avatarRot); // var lightTransform = evalLightWorldTransform(blockProperties.position, blockProperties.rotation); var lightProperties = { From 0e530900d4a40bbe7c741db2812bb1ca4160637e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 18 Dec 2015 16:22:20 -0800 Subject: [PATCH 43/86] sequential editing --- examples/light_modifier/closeButton.js | 12 +- examples/light_modifier/lightModifier.js | 161 +++++++++++------------ 2 files changed, 81 insertions(+), 92 deletions(-) diff --git a/examples/light_modifier/closeButton.js b/examples/light_modifier/closeButton.js index 045882fd33..7b2869cc8e 100644 --- a/examples/light_modifier/closeButton.js +++ b/examples/light_modifier/closeButton.js @@ -24,16 +24,12 @@ this.userData = JSON.parse(entityProperties.userData); }, startNearGrab: function() { - - }, - startDistantGrab: function() { }, - continueNearGrab: function() { - this.continueDistantGrab(); - }, - continueDistantGrab: function() { - }, + startFarTrigger: function() { + print('START FAR TRIGGER ON CLOSE BUTTON!!!') + Messages.sendMessage('Hifi-Light-Modifier-Cleanup', 'callCleanup') + } }; diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 76c1cdc873..5aa367dbbc 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -262,7 +262,7 @@ entitySlider.prototype = { color: this.color, position: sliderPosition, script: SLIDER_SCRIPT_URL, - ignoreForCollisions:true, + ignoreForCollisions: true, userData: JSON.stringify({ lightModifierKey: { lightID: this.lightID, @@ -407,30 +407,52 @@ function makeSliders(light) { subscribeToSliderMessages(); }; + +function createLightModel(position) { + var blockProperties = { + name: 'Hifi-Spotlight-Model', + type: 'Model', + shapeType: 'box', + modelURL: LIGHT_MODEL_URL, + dimensions: LIGHT_MODEL_DIMENSIONS, + collisionsWillMove: true, + position: position, + script: PARENT_SCRIPT_URL, + userData: JSON.stringify({ + handControllerKey: { + disableReleaseVelocity: true + } + }) + }; + + var block = Entities.addEntity(blockProperties); + + return block +} + var closeButtons = []; function createCloseButton(endOfAxis) { - // var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); - // var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); - // var verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); - // var verticalOffset = lastRowVerticalOffset; - // var downPosition = Vec3.sum(basePosition, verticalOffset); - // var rightVector = Quat.getRight(avatarRot); - // var extension = Vec3.multiply(AXIS_SCALE, rightVector); - // var position = Vec3.sum(downPosition, extension); var buttonProperties = { - name:'Hifi-Close-Button', + name: 'Hifi-Close-Button', type: 'Model', modelURL: CLOSE_BUTTON_MODEL_URL, dimensions: CLOSE_BUTTON_DIMENSIONS, - position: Vec3.sum(endOfAxis,{ - x:0, - y:-0.15, - z:0 + position: Vec3.sum(endOfAxis, { + x: 0, + y: -0.15, + z: 0 }), rotation: Quat.fromPitchYawRollDegrees(0, 45, 90), - collisionsWillMove: false + collisionsWillMove: false, + ignoreForCollisions: true, + script: CLOSE_BUTTON_SCRIPT_URL, + userData: JSON.stringify({ + grabbableKey: { + wantsTrigger: true + } + }) //need to add wantsTrigger stuff so we can interact with it with our beamz } @@ -469,6 +491,11 @@ function subscribeToLightOverlayRayCheckMessages() { Messages.messageReceived.connect(handleLightOverlayRayCheckMessages); } +function subscribeToCleanupMessages() { + Messages.subscribe('Hifi-Light-Modifier-Cleanup'); + Messages.messageReceived.connect(handleCleanupMessages); +} + function handleLightModMessages(channel, message, sender) { if (channel !== 'Hifi-Light-Mod-Receiver') { @@ -513,9 +540,9 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { var pickRay = JSON.parse(message); var doesIntersect = lightOverlayManager.findRayIntersection(pickRay); - print('DOES INTERSECT A LIGHT WE HAVE???' + doesIntersect.intersects); + // print('DOES INTERSECT A LIGHT WE HAVE???' + doesIntersect.intersects); if (doesIntersect.intersects === true) { - print('FULL MESSAGE:::' + JSON.stringify(doesIntersect)) + // print('FULL MESSAGE:::' + JSON.stringify(doesIntersect)) var lightID = doesIntersect.entityID; if (currentLight === lightID) { @@ -525,7 +552,6 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { currentLight = lightID; var lightProperties = Entities.getEntityProperties(lightID); - // block = createBlock(lightProperties.position); block = createLightModel(lightProperties.position); var light = { @@ -542,103 +568,70 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { Entities.editEntity(lightID, { parentID: block, - parentJointIndex:-1 + parentJointIndex: -1 }); - } } -function createLightModel(position) { - print('CREATE MODEL') - var blockProperties = { - name: 'Hifi-Spotlight-Model', - type: 'Model', - shapeType: 'box', - modelURL: LIGHT_MODEL_URL, - // modelURL:"http://hifi-content.s3.amazonaws.com/james/light_modifier/box4.fbx", - dimensions: LIGHT_MODEL_DIMENSIONS, - collisionsWillMove: true, - position: position, - // rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), - script: PARENT_SCRIPT_URL, - userData: JSON.stringify({ - handControllerKey: { - disableReleaseVelocity: true - } - }) - }; +function handleCleanupMessages(channel, message, sender) { - var block = Entities.addEntity(blockProperties); - - return block + if (channel !== 'Hifi-Light-Modifier-Cleanup') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + if (message === 'callCleanup') { + print('GOT CLEANUP CALL!!!'); + cleanup(true); + } } -function createBlock(position) { - print('CREATE BLOCK') - - var blockProperties = { - name: 'Hifi-Spotlight-Block', - type: 'Box', - dimensions: { - x: 1, - y: 4, - z: 1 - }, - color: { - red: 0, - green: 0, - blue: 255 - }, - collisionsWillMove: true, - position: position, - script: PARENT_SCRIPT_URL, - userData: JSON.stringify({ - handControllerKey: { - disableReleaseVelocity: true - } - }) - }; - - var block = Entities.addEntity(blockProperties); - - return block -} - -function cleanup() { +function cleanup(fromMessage) { var i; for (i = 0; i < sliders.length; i++) { Entities.deleteEntity(sliders[i].axis); Entities.deleteEntity(sliders[i].sliderIndicator); } - while(closeButtons.length>0){ + while (closeButtons.length > 0) { Entities.deleteEntity(closeButtons.pop()); } - Entities.deleteEntity(block); + //if the light was already parented to something we will want to restore that. or come up with groups or something clever. + Entities.editEntity(currentLight, { + parentID: null, + }); + + if(fromMessage!==true){ Messages.messageReceived.disconnect(handleLightModMessages); Messages.messageReceived.disconnect(handleValueMessages); - Entities.deletingEntity.disconnect(deleteEntity); - + Messages.messageReceived.disconnect(handleLightOverlayRayCheckMessages); lightOverlayManager.setVisible(false); + } + + selectionManager.clearSelections(); Script.update.disconnect(rotateCloseButtons); + print('DELETE LIGHT MODEL::: ' + block); + Entities.deleteEntity(block); + currentLight = null; + + } Script.scriptEnding.connect(cleanup); -function deleteEntity(entityID) { - if (entityID === light) { - // cleanup(); - } -} +Script.scriptEnding.connect(function() { + lightOverlayManager.setVisible(false); +}) -Entities.deletingEntity.connect(deleteEntity); subscribeToLightOverlayRayCheckMessages(); subScribeToNewLights(); +subscribeToCleanupMessages(); From e405311cc8018fc870cf3291a2082174cec89ec1 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 18 Dec 2015 16:35:06 -0800 Subject: [PATCH 44/86] cleanup --- examples/light_modifier/closeButton.js | 1 - examples/light_modifier/lightModifier.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/examples/light_modifier/closeButton.js b/examples/light_modifier/closeButton.js index 7b2869cc8e..72fcfbc382 100644 --- a/examples/light_modifier/closeButton.js +++ b/examples/light_modifier/closeButton.js @@ -27,7 +27,6 @@ }, startFarTrigger: function() { - print('START FAR TRIGGER ON CLOSE BUTTON!!!') Messages.sendMessage('Hifi-Light-Modifier-Cleanup', 'callCleanup') } diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 5aa367dbbc..d813216be5 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -583,7 +583,6 @@ function handleCleanupMessages(channel, message, sender) { return; } if (message === 'callCleanup') { - print('GOT CLEANUP CALL!!!'); cleanup(true); } } @@ -615,7 +614,6 @@ function cleanup(fromMessage) { selectionManager.clearSelections(); Script.update.disconnect(rotateCloseButtons); - print('DELETE LIGHT MODEL::: ' + block); Entities.deleteEntity(block); currentLight = null; From f83476599090eef8ae08488283ebe898e5d0e0da Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 18 Dec 2015 16:39:21 -0800 Subject: [PATCH 45/86] readme --- examples/light_modifier/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md index 3bd456d525..73c2199ec9 100644 --- a/examples/light_modifier/README.md +++ b/examples/light_modifier/README.md @@ -1,5 +1,7 @@ This PR demonstrates one way in-world editing of objects might work. We start with a spotlight. When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached. +Enter edit mode by running your distance beam through a light overlay. Exit using the red X. + To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js To reset, I recommend stopping all scripts then re-loading lightLoader.js @@ -23,6 +25,4 @@ intensity cutoff exponent -To-Do: Determine how to enter / exit edit mode , support near grab, add other input types (checkbox, etc), prevent velocity drift on slider release,button to hide entity - -![capture](https://cloud.githubusercontent.com/assets/843228/11830366/2f2dfe70-a359-11e5-84f0-33a380ebeac7.PNG) +![capture](https://cloud.githubusercontent.com/assets/843228/11910139/afaaf1ae-a5a5-11e5-8b66-0eb3fc6976df.PNG) From 4afdd0242bfaf907b4dbdf4a6c707e1ca2aebd24 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 18 Dec 2015 16:44:20 -0800 Subject: [PATCH 46/86] cleanup --- .../light_modifier/lightModifierTestScene.js | 52 ++----------------- 1 file changed, 4 insertions(+), 48 deletions(-) diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js index 8df83b09bf..58956850f2 100644 --- a/examples/light_modifier/lightModifierTestScene.js +++ b/examples/light_modifier/lightModifierTestScene.js @@ -10,19 +10,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?'+Math.random(0-100)); +var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100)); var basePosition, avatarRot; avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(0, Quat.getUp(avatarRot))); -var light, block; +var light; function createLight() { - var blockProperties = Entities.getEntityProperties(block, ["position", "rotation"]); - var position = basePosition; + var position = basePosition; position.y += 2; - var lightTransform = evalLightWorldTransform(position,avatarRot); - // var lightTransform = evalLightWorldTransform(blockProperties.position, blockProperties.rotation); + var lightTransform = evalLightWorldTransform(position, avatarRot); var lightProperties = { name: 'Hifi-Spotlight', type: "Light", @@ -32,7 +30,6 @@ function createLight() { y: 2, z: 8 }, - // parentID: block, color: { red: 255, green: 0, @@ -48,45 +45,6 @@ function createLight() { light = Entities.addEntity(lightProperties); - var message = { - light: { - id: light, - type: 'spotlight', - initialProperties: lightProperties - } - }; - -// Messages.sendMessage('Hifi-Light-Mod-Receiver', JSON.stringify(message)); - -} - -function createBlock() { - var position = basePosition; - position.y += 3; - var blockProperties = { - name: 'Hifi-Spotlight-Block', - type: 'Box', - dimensions: { - x: 1, - y: 1, - z: 1 - }, - collisionsWillMove: true, - color: { - red: 0, - green: 0, - blue: 255 - }, - position: position, - script:PARENT_SCRIPT_URL, - userData: JSON.stringify({ - handControllerKey: { - disableReleaseVelocity: true - } - }) - }; - - block = Entities.addEntity(blockProperties); } function evalLightWorldTransform(modelPos, modelRot) { @@ -107,11 +65,9 @@ function evalLightWorldTransform(modelPos, modelRot) { } function cleanup() { - //Entities.deleteEntity(block); Entities.deleteEntity(light); } Script.scriptEnding.connect(cleanup); -//createBlock(); createLight(); \ No newline at end of file From b0da1773c246f4d619824355c336c2dac1c2869e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 18 Dec 2015 16:53:41 -0800 Subject: [PATCH 47/86] have right rotation on sequential edits --- examples/light_modifier/lightModifier.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index d813216be5..0d07f2c011 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -60,7 +60,7 @@ var CUTOFF_MAX = 360; var EXPONENT_MAX = 1; var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); -var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_rotated.fbx'; +var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_very_good.fbx'; var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx'; var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100)); @@ -408,7 +408,7 @@ function makeSliders(light) { }; -function createLightModel(position) { +function createLightModel(position,rotation) { var blockProperties = { name: 'Hifi-Spotlight-Model', type: 'Model', @@ -417,6 +417,7 @@ function createLightModel(position) { dimensions: LIGHT_MODEL_DIMENSIONS, collisionsWillMove: true, position: position, + rotation:rotation, script: PARENT_SCRIPT_URL, userData: JSON.stringify({ handControllerKey: { @@ -552,7 +553,7 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { currentLight = lightID; var lightProperties = Entities.getEntityProperties(lightID); - block = createLightModel(lightProperties.position); + block = createLightModel(lightProperties.position,lightProperties.rotation); var light = { id: lightID, From eb03dcd8215aa92737b10ae5371eb181795ebe92 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 19 Dec 2015 12:47:44 -0800 Subject: [PATCH 48/86] keep parent of lights that already have parents --- examples/light_modifier/lightModifier.js | 36 +++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 0d07f2c011..e82897e049 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -47,10 +47,6 @@ if (SHOW_OVERLAYS === true) { lightOverlayManager.setVisible(true); } -// var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking -// var pickRay = Camera.computePickRay(event.x, event.y); -// var lightResult = lightOverlayManager.findRayIntersection(pickRay) - var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}' var AXIS_SCALE = 1; @@ -408,7 +404,7 @@ function makeSliders(light) { }; -function createLightModel(position,rotation) { +function createLightModel(position, rotation) { var blockProperties = { name: 'Hifi-Spotlight-Model', type: 'Model', @@ -417,7 +413,7 @@ function createLightModel(position,rotation) { dimensions: LIGHT_MODEL_DIMENSIONS, collisionsWillMove: true, position: position, - rotation:rotation, + rotation: rotation, script: PARENT_SCRIPT_URL, userData: JSON.stringify({ handControllerKey: { @@ -529,6 +525,7 @@ function handleValueMessages(channel, message, sender) { var currentLight; var block; +var hasParent = false; function handleLightOverlayRayCheckMessages(channel, message, sender) { if (channel !== 'Hifi-Light-Overlay-Ray-Check') { @@ -553,7 +550,16 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { currentLight = lightID; var lightProperties = Entities.getEntityProperties(lightID); - block = createLightModel(lightProperties.position,lightProperties.rotation); + if (lightProperties.parentID !== DEFAULT_PARENT_ID) { + //this light has a parent already. so lets call our block the parent and then make sure not to delete it at the end; + hasParent = true; + block = lightProperties.parentID; + if (lightProperties.parentJointIndex !== -1) { + //should make sure to retain the parent too. but i don't actually know what the + } + } else { + block = createLightModel(lightProperties.position, lightProperties.rotation); + } var light = { id: lightID, @@ -604,18 +610,20 @@ function cleanup(fromMessage) { parentID: null, }); - if(fromMessage!==true){ - Messages.messageReceived.disconnect(handleLightModMessages); - Messages.messageReceived.disconnect(handleValueMessages); - Messages.messageReceived.disconnect(handleLightOverlayRayCheckMessages); - lightOverlayManager.setVisible(false); + if (fromMessage !== true) { + Messages.messageReceived.disconnect(handleLightModMessages); + Messages.messageReceived.disconnect(handleValueMessages); + Messages.messageReceived.disconnect(handleLightOverlayRayCheckMessages); + lightOverlayManager.setVisible(false); } selectionManager.clearSelections(); Script.update.disconnect(rotateCloseButtons); - - Entities.deleteEntity(block); + if (hasParent === false) { + Entities.deleteEntity(block); + } + hasParent = false; currentLight = null; From 378d50d8a3174086ae451e3a048ff3aa96e7ac62 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 19 Dec 2015 12:49:42 -0800 Subject: [PATCH 49/86] update overlays when cutoff slider is used --- examples/light_modifier/slider.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/light_modifier/slider.js b/examples/light_modifier/slider.js index dc02e0bdba..6c67d35204 100644 --- a/examples/light_modifier/slider.js +++ b/examples/light_modifier/slider.js @@ -95,6 +95,9 @@ sliderValue: _t.sliderValue } Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); + if (_t.userData.sliderType === 'cutoff') { + Messages.sendMessage('entityToolUpdates', 'callUpdate'); + } } }; From 05467bd5e1a356afca087c151f0c77f29aa5e238 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 19 Dec 2015 18:19:00 -0800 Subject: [PATCH 50/86] create panel entity and parent panel to it --- examples/light_modifier/lightModifier.js | 47 ++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index e82897e049..14053c7974 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -363,6 +363,10 @@ var light = null; function makeSliders(light) { + if (USE_PARENTED_PANEL === true) { + createPanelEntity(MyAvatar.position); + } + if (light.type === 'spotlight') { var USE_COLOR_SLIDER = true; var USE_INTENSITY_SLIDER = true; @@ -398,12 +402,55 @@ function makeSliders(light) { sliders.push(slidersRef.exponent); } + + createCloseButton(slidersRef.exponent.endOfAxis); subscribeToSliderMessages(); + + if (USE_PARENTED_PANEL === true) { + parentEntitiesToPanel(); + } }; +function parentEntitiesToPanel(panel) { + slidersRef.forEach(function(slider) { + Entities.editEntity(slider.axis, { + parentID: panel + }) + Entities.editEntity(slider.sliderIndicator, { + parentID: panel + }) + }) + + closeButtons.forEach(function(button) { + Entities.editEntity(slider.sliderIndicator, { + parentID: panel + }) + }) +} + +function createPanelEntity(position) { + + var panelProperties = { + name: 'Hifi-Slider-Panel', + type: 'Box', + dimensions: { + x: 0.1, + y: 0.1, + z: 0.1 + }, + visible: false, + collisionsWillMove: false, + ignoreForCollisions: true + } + + var panel = Entities.addEntity(panelProperties); + return panel +} + + function createLightModel(position, rotation) { var blockProperties = { name: 'Hifi-Spotlight-Model', From 9f0d254739df2a9a158c01a138a85717dcad0812 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 19 Dec 2015 19:11:17 -0800 Subject: [PATCH 51/86] panel entity, readme --- examples/light_modifier/README.md | 16 ++++++------- examples/light_modifier/lightModifier.js | 29 ++++++++++++++++-------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md index 73c2199ec9..b12ff7550b 100644 --- a/examples/light_modifier/README.md +++ b/examples/light_modifier/README.md @@ -1,6 +1,8 @@ -This PR demonstrates one way in-world editing of objects might work. We start with a spotlight. When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached. +This PR demonstrates one way in-world editing of objects might work. -Enter edit mode by running your distance beam through a light overlay. Exit using the red X. +Running this script will show light overlay icons in-world. Enter edit mode by running your distance beam through a light overlay. Exit using the red X. + +When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached. To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js To reset, I recommend stopping all scripts then re-loading lightLoader.js @@ -8,14 +10,12 @@ To reset, I recommend stopping all scripts then re-loading lightLoader.js When you run the lightLoader.js script, several scripts will be loaded: - handControllerGrab.js (custom) - lightModifier.js (listens for message to create sliders for a given light) -- lightModifierTestScene.js (creates a light and parents it to a block, then sends a message ^^) +- lightModifierTestScene.js (creates a light) - slider.js (attached to each slider entity) -- lightParent.js (attached to the entity to which a light is parented, so you can move it around) +- lightParent.js (attached to a 3d model of a light, to which a light is parented, so you can move it around. or keep the current parent if a light already has a parent) - closeButton.js (for closing the ui) -- ../libraries/lightOverlayManager.js (custom) -- ../libraries/entitySelectionTool.js - - +- ../libraries/lightOverlayManager.js (shows 2d overlays for lights in the world) +- ../libraries/entitySelectionTool.js (visualizes volume of the lights) Current sliders are (top to bottom): red diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 14053c7974..30f004c922 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -16,13 +16,14 @@ var SLIDERS_SHOULD_STAY_WITH_AVATAR = false; var VERTICAL_SLIDERS = false; var SHOW_OVERLAYS = true; var SHOW_LIGHT_VOLUME = true; +var USE_PARENTED_PANEL = false; //variables for managing overlays var selectionDisplay; var selectionManager; var lightOverlayManager; -//for when we make a block parent for the light +//for when we make a 3d model of a light a parent for the light var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100)); if (SHOW_OVERLAYS === true) { @@ -362,9 +363,9 @@ var slidersRef = { var light = null; function makeSliders(light) { - + var panel; if (USE_PARENTED_PANEL === true) { - createPanelEntity(MyAvatar.position); + panel = createPanelEntity(MyAvatar.position); } if (light.type === 'spotlight') { @@ -409,10 +410,21 @@ function makeSliders(light) { subscribeToSliderMessages(); if (USE_PARENTED_PANEL === true) { - parentEntitiesToPanel(); + parentEntitiesToPanel(panel); + } + + if () { + parentPanelToAvatar(panel) } }; +function parentPanelToAvatar(panel) { + Entities.editEntity(panel, { + parentID: MyAvatar.sessionUUID, + //actually figure out which one to parent it to -- probably a spine or something. + parentJointIndex: 1, + }) +} function parentEntitiesToPanel(panel) { slidersRef.forEach(function(slider) { @@ -493,11 +505,10 @@ function createCloseButton(endOfAxis) { ignoreForCollisions: true, script: CLOSE_BUTTON_SCRIPT_URL, userData: JSON.stringify({ - grabbableKey: { - wantsTrigger: true - } - }) - //need to add wantsTrigger stuff so we can interact with it with our beamz + grabbableKey: { + wantsTrigger: true + } + }) } var button = Entities.addEntity(buttonProperties); From daa262af7d33d18866d304a5c3b9d1c5e33aeb55 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 19 Dec 2015 19:14:19 -0800 Subject: [PATCH 52/86] readme --- examples/light_modifier/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md index b12ff7550b..4a2690cd91 100644 --- a/examples/light_modifier/README.md +++ b/examples/light_modifier/README.md @@ -8,7 +8,7 @@ To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js To reset, I recommend stopping all scripts then re-loading lightLoader.js When you run the lightLoader.js script, several scripts will be loaded: -- handControllerGrab.js (custom) +- handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end) - lightModifier.js (listens for message to create sliders for a given light) - lightModifierTestScene.js (creates a light) - slider.js (attached to each slider entity) From 485bb4ee3f3948f505fdffda30bc67bafa1c3428 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 19 Dec 2015 19:14:56 -0800 Subject: [PATCH 53/86] slider should stay with avatr --- examples/light_modifier/lightModifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 30f004c922..02d764e7e2 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -413,7 +413,7 @@ function makeSliders(light) { parentEntitiesToPanel(panel); } - if () { + if (SLIDERS_SHOULD_STAY_WITH_AVATAR) { parentPanelToAvatar(panel) } }; From da57e29096069c06f841aa4e6d6fdbed27989e71 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sun, 20 Dec 2015 16:40:41 -0800 Subject: [PATCH 54/86] start options, cleanup --- examples/light_modifier/README.md | 2 +- examples/light_modifier/slider.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md index 4a2690cd91..1358fbe917 100644 --- a/examples/light_modifier/README.md +++ b/examples/light_modifier/README.md @@ -9,7 +9,7 @@ To reset, I recommend stopping all scripts then re-loading lightLoader.js When you run the lightLoader.js script, several scripts will be loaded: - handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end) -- lightModifier.js (listens for message to create sliders for a given light) +- lightModifier.js (listens for message to create sliders for a given light. will start with slider set to the light's initial properties) - lightModifierTestScene.js (creates a light) - slider.js (attached to each slider entity) - lightParent.js (attached to a 3d model of a light, to which a light is parented, so you can move it around. or keep the current parent if a light already has a parent) diff --git a/examples/light_modifier/slider.js b/examples/light_modifier/slider.js index 6c67d35204..e1dfea4e87 100644 --- a/examples/light_modifier/slider.js +++ b/examples/light_modifier/slider.js @@ -50,16 +50,16 @@ var distance = Vec3.distance(this.userData.axisStart, currentPosition); if (this.userData.sliderType === 'color_red' || this.userData.sliderType === 'color_green' || this.userData.sliderType === 'color_blue') { - this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, COLOR_MAX); + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, COLOR_MAX); } if (this.userData.sliderType === 'intensity') { - this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, INTENSITY_MAX); + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, INTENSITY_MAX); } if (this.userData.sliderType === 'cutoff') { - this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, CUTOFF_MAX); + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, CUTOFF_MAX); } if (this.userData.sliderType === 'exponent') { - this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, EXPONENT_MAX); + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, EXPONENT_MAX); }; this.sendValueToSlider(); @@ -80,10 +80,10 @@ this.sendValueToSlider(); }, - scaleValueBasedOnDistanceFromStart: function(value, max2) { + scaleValueBasedOnDistanceFromStart: function(value, min2, max2) { var min1 = 0; var max1 = AXIS_SCALE; - var min2 = 0; + var min2 = min2; var max2 = max2; return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); }, From dcde640acdde332a861b6d8d9117855bf1685360 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 18 Dec 2015 19:13:59 -0800 Subject: [PATCH 55/86] Stub Perception Neuron input plugin --- interface/resources/controllers/neuron.json | 7 ++ plugins/neuron/CMakeLists.txt | 13 ++++ plugins/neuron/src/NeuronPlugin.cpp | 75 +++++++++++++++++++++ plugins/neuron/src/NeuronPlugin.h | 57 ++++++++++++++++ plugins/neuron/src/NeuronProvider.cpp | 45 +++++++++++++ plugins/neuron/src/plugin.json | 1 + 6 files changed, 198 insertions(+) create mode 100644 interface/resources/controllers/neuron.json create mode 100644 plugins/neuron/CMakeLists.txt create mode 100644 plugins/neuron/src/NeuronPlugin.cpp create mode 100644 plugins/neuron/src/NeuronPlugin.h create mode 100644 plugins/neuron/src/NeuronProvider.cpp create mode 100644 plugins/neuron/src/plugin.json diff --git a/interface/resources/controllers/neuron.json b/interface/resources/controllers/neuron.json new file mode 100644 index 0000000000..2d61f80c35 --- /dev/null +++ b/interface/resources/controllers/neuron.json @@ -0,0 +1,7 @@ +{ + "name": "Neuron to Standard", + "channels": [ + { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, + { "from": "Hydra.RightHand", "to": "Standard.RightHand" } + ] +} diff --git a/plugins/neuron/CMakeLists.txt b/plugins/neuron/CMakeLists.txt new file mode 100644 index 0000000000..b86d310ab7 --- /dev/null +++ b/plugins/neuron/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Created by Anthony Thibault on 2015/12/18 +# 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 +# + +set(TARGET_NAME neuron) +setup_hifi_plugin(Script Qml Widgets) +link_hifi_libraries(shared controllers plugins input-plugins) +# target_neuron() + diff --git a/plugins/neuron/src/NeuronPlugin.cpp b/plugins/neuron/src/NeuronPlugin.cpp new file mode 100644 index 0000000000..735b81a1ef --- /dev/null +++ b/plugins/neuron/src/NeuronPlugin.cpp @@ -0,0 +1,75 @@ +// +// NeuronPlugin.h +// input-plugins/src/input-plugins +// +// Created by Anthony Thibault on 12/18/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 +// + +#include "NeuronPlugin.h" + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") + +const QString NeuronPlugin::NAME = "Neuron"; +const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; + +bool NeuronPlugin::isSupported() const { + // TODO: + return true; +} + +void NeuronPlugin::activate() { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::activate"; +} + +void NeuronPlugin::deactivate() { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::deactivate"; +} + +void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::pluginUpdate"; +} + +void NeuronPlugin::saveSettings() const { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::saveSettings"; +} + +void NeuronPlugin::loadSettings() { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::loadSettings"; +} + +controller::Input::NamedVector NeuronPlugin::InputDevice::getAvailableInputs() const { + // TODO: + static const controller::Input::NamedVector availableInputs { + makePair(controller::LEFT_HAND, "LeftHand"), + makePair(controller::RIGHT_HAND, "RightHand") + }; + return availableInputs; +} + +QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/neuron.json"; + return MAPPING_JSON; +} + +void NeuronPlugin::InputDevice::update(float deltaTime, bool jointsCaptured) { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::InputDevice::update"; +} + +void NeuronPlugin::InputDevice::focusOutEvent() { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::InputDevice::focusOutEvent"; +} diff --git a/plugins/neuron/src/NeuronPlugin.h b/plugins/neuron/src/NeuronPlugin.h new file mode 100644 index 0000000000..59e0f0a393 --- /dev/null +++ b/plugins/neuron/src/NeuronPlugin.h @@ -0,0 +1,57 @@ +// +// NeuronPlugin.h +// input-plugins/src/input-plugins +// +// Created by Anthony Thibault on 12/18/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 +// + +#ifndef hifi_NeuronPlugin_h +#define hifi_NeuronPlugin_h + +#include +#include +#include + +// Handles interaction with the Neuron SDK +class NeuronPlugin : public InputPlugin { + Q_OBJECT +public: + // Plugin functions + virtual bool isSupported() const override; + virtual bool isJointController() const override { return true; } + const QString& getName() const override { return NAME; } + const QString& getID() const override { return NEURON_ID_STRING; } + + virtual void activate() override; + virtual void deactivate() override; + + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; + + virtual void saveSettings() const override; + virtual void loadSettings() override; + +private: + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Neuron") {} + + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + }; + + std::shared_ptr _inputDevice { std::make_shared() }; + + static const QString NAME; + static const QString NEURON_ID_STRING; +}; + +#endif // hifi_NeuronPlugin_h + diff --git a/plugins/neuron/src/NeuronProvider.cpp b/plugins/neuron/src/NeuronProvider.cpp new file mode 100644 index 0000000000..b171c5150d --- /dev/null +++ b/plugins/neuron/src/NeuronProvider.cpp @@ -0,0 +1,45 @@ +// +// Created by Anthony Thibault on 2015/12/18 +// 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 +#include + +#include "NeuronPlugin.h" + +class NeuronProvider : public QObject, public InputProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + NeuronProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~NeuronProvider() {} + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new NeuronPlugin()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + +private: + InputPluginList _inputPlugins; +}; + +#include "NeuronProvider.moc" diff --git a/plugins/neuron/src/plugin.json b/plugins/neuron/src/plugin.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/plugins/neuron/src/plugin.json @@ -0,0 +1 @@ +{} From b59b8db5c935884238d6c5f269cbc922ccbf2322 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 21 Dec 2015 12:09:18 -0800 Subject: [PATCH 56/86] set defaults --- examples/light_modifier/lightModifier.js | 36 ++++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 02d764e7e2..e8a9f39903 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -120,6 +120,17 @@ var PER_ROW_OFFSET = { y: -0.2, z: 0 }; +var sliders = []; +var slidersRef = { + 'color_red': null, + 'color_green': null, + 'color_blue': null, + intensity: null, + cutoff: null, + exponent: null +}; +var light = null; + function entitySlider(light, color, sliderType, row) { this.light = light; @@ -351,17 +362,6 @@ entitySlider.prototype = { }; -var sliders = []; -var slidersRef = { - 'color_red': null, - 'color_green': null, - 'color_blue': null, - intensity: null, - cutoff: null, - exponent: null -} -var light = null; - function makeSliders(light) { var panel; if (USE_PARENTED_PANEL === true) { @@ -403,8 +403,6 @@ function makeSliders(light) { sliders.push(slidersRef.exponent); } - - createCloseButton(slidersRef.exponent.endOfAxis); subscribeToSliderMessages(); @@ -413,12 +411,13 @@ function makeSliders(light) { parentEntitiesToPanel(panel); } - if (SLIDERS_SHOULD_STAY_WITH_AVATAR) { - parentPanelToAvatar(panel) + if (SLIDERS_SHOULD_STAY_WITH_AVATAR === true) { + parentPanelToAvatar(panel); } }; function parentPanelToAvatar(panel) { + //this is going to need some more work re: the sliders actually being grabbable. probably something to do with updating axis movement Entities.editEntity(panel, { parentID: MyAvatar.sessionUUID, //actually figure out which one to parent it to -- probably a spine or something. @@ -427,7 +426,8 @@ function parentPanelToAvatar(panel) { } function parentEntitiesToPanel(panel) { - slidersRef.forEach(function(slider) { + + sliders.forEach(function(slider) { Entities.editEntity(slider.axis, { parentID: panel }) @@ -437,14 +437,14 @@ function parentEntitiesToPanel(panel) { }) closeButtons.forEach(function(button) { - Entities.editEntity(slider.sliderIndicator, { + Entities.editEntity(button, { parentID: panel }) }) } function createPanelEntity(position) { - + print('CREATING PANEL at ' + JSON.stringify(position)); var panelProperties = { name: 'Hifi-Slider-Panel', type: 'Box', From 0459479c2b1a8130beecee5771d40aac490d1a9f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 21 Dec 2015 18:30:15 -0800 Subject: [PATCH 57/86] NeuronPlugin: Added external project for Neuron SDK Now builds on windows with actual Neuron SDK. Can to TCP server on localhost, and receive joint data. Will debug draw joint 6, (left foot?) --- cmake/externals/neuron/CMakeLists.txt | 49 ++++ cmake/macros/TargetNeuron.cmake | 14 ++ cmake/modules/FindNeuron.cmake | 28 +++ interface/CMakeLists.txt | 4 +- plugins/{neuron => hifiNeuron}/CMakeLists.txt | 4 +- plugins/hifiNeuron/src/NeuronPlugin.cpp | 222 ++++++++++++++++++ .../{neuron => hifiNeuron}/src/NeuronPlugin.h | 19 +- .../src/NeuronProvider.cpp | 0 .../{neuron => hifiNeuron}/src/plugin.json | 0 plugins/neuron/src/NeuronPlugin.cpp | 75 ------ 10 files changed, 336 insertions(+), 79 deletions(-) create mode 100644 cmake/externals/neuron/CMakeLists.txt create mode 100644 cmake/macros/TargetNeuron.cmake create mode 100644 cmake/modules/FindNeuron.cmake rename plugins/{neuron => hifiNeuron}/CMakeLists.txt (88%) create mode 100644 plugins/hifiNeuron/src/NeuronPlugin.cpp rename plugins/{neuron => hifiNeuron}/src/NeuronPlugin.h (79%) rename plugins/{neuron => hifiNeuron}/src/NeuronProvider.cpp (100%) rename plugins/{neuron => hifiNeuron}/src/plugin.json (100%) delete mode 100644 plugins/neuron/src/NeuronPlugin.cpp diff --git a/cmake/externals/neuron/CMakeLists.txt b/cmake/externals/neuron/CMakeLists.txt new file mode 100644 index 0000000000..324b3fb917 --- /dev/null +++ b/cmake/externals/neuron/CMakeLists.txt @@ -0,0 +1,49 @@ +include(ExternalProject) + +set(EXTERNAL_NAME neuron) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.zip") +set(NEURON_URL_MD5 "0ab54ca04c9cc8094e0fa046c226e574") + +ExternalProject_Add(${EXTERNAL_NAME} + URL ${NEURON_URL} + URL_MD5 ${NEURON_URL_MD5} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1) + +ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +# set include dir +if(WIN32) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Windows/include" CACHE TYPE INTERNAL) +elseif(APPLE) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS "${SOURCE_DIR}/NeuronDataReader_Mac/include" CACHE TYPE INTERNAL) +else() + # Unsupported +endif() + +if(WIN32) + + if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(ARCH_DIR "x64") + else() + set(ARCH_DIR "x86") + endif() + + set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Windows/lib/${ARCH_DIR}") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL) + + add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}") +elseif(APPLE) + # TODO +else() + # UNSUPPORTED +endif() + diff --git a/cmake/macros/TargetNeuron.cmake b/cmake/macros/TargetNeuron.cmake new file mode 100644 index 0000000000..01891ef525 --- /dev/null +++ b/cmake/macros/TargetNeuron.cmake @@ -0,0 +1,14 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Anthony J. Thibault on 2015/12/21 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_NEURON) + add_dependency_external_projects(neuron) + find_package(Neuron REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${NEURON_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${NEURON_LIBRARIES}) + add_definitions(-DHAVE_NEURON) +endmacro() diff --git a/cmake/modules/FindNeuron.cmake b/cmake/modules/FindNeuron.cmake new file mode 100644 index 0000000000..6a93b3a016 --- /dev/null +++ b/cmake/modules/FindNeuron.cmake @@ -0,0 +1,28 @@ +# +# FindNeuron.cmake +# +# Try to find the Perception Neuron SDK +# +# You must provide a NEURON_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# NEURON_FOUND - system found Neuron SDK +# NEURON_INCLUDE_DIRS - the Neuron SDK include directory +# NEURON_LIBRARIES - Link this to use Neuron +# +# Created on 12/21/2015 by Anthony J. Thibault +# 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(SelectLibraryConfigurations) +select_library_configurations(NEURON) + +set(NEURON_REQUIREMENTS NEURON_INCLUDE_DIRS NEURON_LIBRARIES) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Neuron DEFAULT_MSG NEURON_INCLUDE_DIRS NEURON_LIBRARIES) +mark_as_advanced(NEURON_LIBRARIES NEURON_INCLUDE_DIRS NEURON_SEARCH_DIRS) + diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 1d9557a835..5d96b95624 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -109,7 +109,9 @@ add_dependency_external_projects(sdl2) if (WIN32) add_dependency_external_projects(OpenVR) endif() - +if(WIN32 OR APPLE) + add_dependency_external_projects(neuron) +endif() # disable /OPT:REF and /OPT:ICF for the Debug builds # This will prevent the following linker warnings diff --git a/plugins/neuron/CMakeLists.txt b/plugins/hifiNeuron/CMakeLists.txt similarity index 88% rename from plugins/neuron/CMakeLists.txt rename to plugins/hifiNeuron/CMakeLists.txt index b86d310ab7..9c512fc877 100644 --- a/plugins/neuron/CMakeLists.txt +++ b/plugins/hifiNeuron/CMakeLists.txt @@ -6,8 +6,8 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -set(TARGET_NAME neuron) +set(TARGET_NAME hifiNeuron) setup_hifi_plugin(Script Qml Widgets) link_hifi_libraries(shared controllers plugins input-plugins) -# target_neuron() +target_neuron() diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp new file mode 100644 index 0000000000..c3f764da05 --- /dev/null +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -0,0 +1,222 @@ +// +// NeuronPlugin.h +// input-plugins/src/input-plugins +// +// Created by Anthony Thibault on 12/18/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 +// + +#include "NeuronPlugin.h" + +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") + +#define __OS_XUN__ 1 +#define BOOL int +#include + +const QString NeuronPlugin::NAME = "Neuron"; +const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; + +enum JointIndex { + HipsPosition = 0, + Hips, + RightUpLeg, + RightLeg, + RightFoot, + LeftUpLeg, + LeftLeg, + LeftFoot, + Spine, + Spine1, + Spine2, + Spine3, + Neck, + Head, + RightShoulder, + RightArm, + RightForeArm, + RightHand, + RightHandThumb1, + RightHandThumb2, + RightHandThumb3, + RightInHandIndex, + RightHandIndex1, + RightHandIndex2, + RightHandIndex3, + RightInHandMiddle, + RightHandMiddle1, + RightHandMiddle2, + RightHandMiddle3, + RightInHandRing, + RightHandRing1, + RightHandRing2, + RightHandRing3, + RightInHandPinky, + RightHandPinky1, + RightHandPinky2, + RightHandPinky3, + LeftShoulder, + LeftArm, + LeftForeArm, + LeftHand, + LeftHandThumb1, + LeftHandThumb2, + LeftHandThumb3, + LeftInHandIndex, + LeftHandIndex1, + LeftHandIndex2, + LeftHandIndex3, + LeftInHandMiddle, + LeftHandMiddle1, + LeftHandMiddle2, + LeftHandMiddle3, + LeftInHandRing, + LeftHandRing1, + LeftHandRing2, + LeftHandRing3, + LeftInHandPinky, + LeftHandPinky1, + LeftHandPinky2, + LeftHandPinky3 +}; + +bool NeuronPlugin::isSupported() const { + // Because it's a client/server network architecture, we can't tell + // if the neuron is actually connected until we connect to the server. + return true; +} + +// NOTE: must be thread-safe +void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx* header, float* data) { + qCDebug(inputplugins) << "NeuronPlugin: received frame data, DataCount = " << header->DataCount; + + auto neuronPlugin = reinterpret_cast(context); + std::lock_guard guard(neuronPlugin->_jointsMutex); + + // Data is 6 floats: 3 position values, 3 rotation euler angles (degrees) + + // resize vector if necessary + const size_t NUM_FLOATS_PER_JOINT = 6; + const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT; + if (neuronPlugin->_joints.size() != NUM_JOINTS) { + neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + } + + assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float))); + + // copy the data + memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); +} + +// NOTE: must be thread-safe +static void CommandDataReceivedCallback(void* context, SOCKET_REF sender, CommandPack* pack, void* data) { + +} + +// NOTE: must be thread-safe +static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, SocketStatus status, char* message) { + qCDebug(inputplugins) << "NeuronPlugin: socket status = " << message; +} + +void NeuronPlugin::activate() { + InputPlugin::activate(); + qCDebug(inputplugins) << "NeuronPlugin::activate"; + + // register c-style callbacks + BRRegisterFrameDataCallback((void*)this, FrameDataReceivedCallback); + BRRegisterCommandDataCallback((void*)this, CommandDataReceivedCallback); + BRRegisterSocketStatusCallback((void*)this, SocketStatusChangedCallback); + + // TODO: pull these from prefs! + _serverAddress = "localhost"; + _serverPort = 7001; + _socketRef = BRConnectTo((char*)_serverAddress.c_str(), _serverPort); + if (!_socketRef) { + // error + qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << "error = " << BRGetLastErrorMessage(); + } + qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort; +} + +void NeuronPlugin::deactivate() { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::deactivate"; + + if (_socketRef) { + BRCloseSocket(_socketRef); + } + InputPlugin::deactivate(); +} + +// convert between euler in degrees to quaternion +static quat eulerToQuat(vec3 euler) { + return (glm::angleAxis(euler.y * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * + glm::angleAxis(euler.x * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(euler.z * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); +} + +void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) { + + std::vector joints; + // copy the shared data + { + std::lock_guard guard(_jointsMutex); + joints = _joints; + } + + DebugDraw::getInstance().addMyAvatarMarker("LEFT_FOOT", + eulerToQuat(joints[6].rot), + joints[6].pos / 100.0f, + glm::vec4(1)); + + _inputDevice->update(deltaTime, jointsCaptured); +} + +void NeuronPlugin::saveSettings() const { + InputPlugin::saveSettings(); + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::saveSettings"; +} + +void NeuronPlugin::loadSettings() { + InputPlugin::loadSettings(); + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::loadSettings"; +} + +// +// InputDevice +// + +controller::Input::NamedVector NeuronPlugin::InputDevice::getAvailableInputs() const { + // TODO: + static const controller::Input::NamedVector availableInputs { + makePair(controller::LEFT_HAND, "LeftHand"), + makePair(controller::RIGHT_HAND, "RightHand") + }; + return availableInputs; +} + +QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/neuron.json"; + return MAPPING_JSON; +} + +void NeuronPlugin::InputDevice::update(float deltaTime, bool jointsCaptured) { + +} + +void NeuronPlugin::InputDevice::focusOutEvent() { + // TODO: + qCDebug(inputplugins) << "NeuronPlugin::InputDevice::focusOutEvent"; +} diff --git a/plugins/neuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h similarity index 79% rename from plugins/neuron/src/NeuronPlugin.h rename to plugins/hifiNeuron/src/NeuronPlugin.h index 59e0f0a393..5f67502e04 100644 --- a/plugins/neuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -16,10 +16,15 @@ #include #include +struct _BvhDataHeaderEx; +void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data); + // Handles interaction with the Neuron SDK class NeuronPlugin : public InputPlugin { Q_OBJECT public: + friend void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data); + // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } @@ -35,7 +40,7 @@ public: virtual void saveSettings() const override; virtual void loadSettings() override; -private: +protected: class InputDevice : public controller::InputDevice { public: InputDevice() : controller::InputDevice("Neuron") {} @@ -51,6 +56,18 @@ private: static const QString NAME; static const QString NEURON_ID_STRING; + + std::string _serverAddress; + int _serverPort; + void* _socketRef; + + struct NeuronJoint { + glm::vec3 pos; + glm::vec3 rot; + }; + + std::vector _joints; + std::mutex _jointsMutex; }; #endif // hifi_NeuronPlugin_h diff --git a/plugins/neuron/src/NeuronProvider.cpp b/plugins/hifiNeuron/src/NeuronProvider.cpp similarity index 100% rename from plugins/neuron/src/NeuronProvider.cpp rename to plugins/hifiNeuron/src/NeuronProvider.cpp diff --git a/plugins/neuron/src/plugin.json b/plugins/hifiNeuron/src/plugin.json similarity index 100% rename from plugins/neuron/src/plugin.json rename to plugins/hifiNeuron/src/plugin.json diff --git a/plugins/neuron/src/NeuronPlugin.cpp b/plugins/neuron/src/NeuronPlugin.cpp deleted file mode 100644 index 735b81a1ef..0000000000 --- a/plugins/neuron/src/NeuronPlugin.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// NeuronPlugin.h -// input-plugins/src/input-plugins -// -// Created by Anthony Thibault on 12/18/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 -// - -#include "NeuronPlugin.h" - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(inputplugins) -Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") - -const QString NeuronPlugin::NAME = "Neuron"; -const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; - -bool NeuronPlugin::isSupported() const { - // TODO: - return true; -} - -void NeuronPlugin::activate() { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::activate"; -} - -void NeuronPlugin::deactivate() { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::deactivate"; -} - -void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::pluginUpdate"; -} - -void NeuronPlugin::saveSettings() const { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::saveSettings"; -} - -void NeuronPlugin::loadSettings() { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::loadSettings"; -} - -controller::Input::NamedVector NeuronPlugin::InputDevice::getAvailableInputs() const { - // TODO: - static const controller::Input::NamedVector availableInputs { - makePair(controller::LEFT_HAND, "LeftHand"), - makePair(controller::RIGHT_HAND, "RightHand") - }; - return availableInputs; -} - -QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { - static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/neuron.json"; - return MAPPING_JSON; -} - -void NeuronPlugin::InputDevice::update(float deltaTime, bool jointsCaptured) { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::InputDevice::update"; -} - -void NeuronPlugin::InputDevice::focusOutEvent() { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::InputDevice::focusOutEvent"; -} From 00b47cacea4b3af8f8d743f32f1db6b2d828dacc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 22 Dec 2015 09:59:57 -0800 Subject: [PATCH 58/86] Fix crash when getting MyAvatar.sessionUUID from AvatarManager The previous code inadvertently added a default constructed shared pointer to the avatar hash, causing it to crash when dereferencing it in the update loop. --- interface/src/avatar/AvatarManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 312742e778..217cd28e61 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -310,8 +310,8 @@ QVector AvatarManager::getAvatarIdentifiers() { } AvatarData* AvatarManager::getAvatar(QUuid avatarID) { - QReadLocker locker(&_hashLock); - return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar. + // Null/Default-constructed QUuids will return MyAvatar + return getAvatarBySessionID(avatarID).get(); } From 56d654987c1f0fe91731f5671900e927814f124b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 11:48:56 -0800 Subject: [PATCH 59/86] changing branches --- examples/light_modifier/lightModifier.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index e8a9f39903..48efe89a6e 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -8,7 +8,9 @@ // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// +// + +// todo: text labels for property names, panel plane for visibility //some experimental options var ONLY_I_CAN_EDIT = false; @@ -16,7 +18,7 @@ var SLIDERS_SHOULD_STAY_WITH_AVATAR = false; var VERTICAL_SLIDERS = false; var SHOW_OVERLAYS = true; var SHOW_LIGHT_VOLUME = true; -var USE_PARENTED_PANEL = false; +var USE_PARENTED_PANEL = true; //variables for managing overlays var selectionDisplay; @@ -225,6 +227,9 @@ entitySlider.prototype = { }; this.axis = Entities.addEntity(properties); + }, + createLabel:function(){ + }, createSliderIndicator: function() { var extensionVector; @@ -652,6 +657,12 @@ function handleCleanupMessages(channel, message, sender) { } } +function updateSliderAxis(){ + sliders.forEach(function(slider){ + + }) +} + function cleanup(fromMessage) { var i; for (i = 0; i < sliders.length; i++) { From c39f26ad72c340fb09a5e4e4e91c45ca7f9dd1c5 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 16:31:49 -0800 Subject: [PATCH 60/86] new features --- examples/light_modifier/lightModifier.js | 113 ++++++++++++++++++++--- 1 file changed, 98 insertions(+), 15 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 48efe89a6e..4e0507b69f 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -19,6 +19,8 @@ var VERTICAL_SLIDERS = false; var SHOW_OVERLAYS = true; var SHOW_LIGHT_VOLUME = true; var USE_PARENTED_PANEL = true; +var VISIBLE_PANEL = true; +var USE_LABELS = true; //variables for managing overlays var selectionDisplay; @@ -62,6 +64,7 @@ var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_very_good.fbx'; var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx'; var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100)); +var TRANSPARENT_PANEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/transparent_box_alpha_15.fbx'; var RED = { red: 255, @@ -134,12 +137,13 @@ var slidersRef = { var light = null; -function entitySlider(light, color, sliderType, row) { +function entitySlider(light, color, sliderType, displayText, row) { this.light = light; this.lightID = light.id.replace(/[{}]/g, ""); this.initialProperties = light.initialProperties; this.color = color; this.sliderType = sliderType; + this.displayText = displayText; this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); @@ -182,6 +186,9 @@ function entitySlider(light, color, sliderType, row) { this.setInitialSliderPositions(); this.createAxis(); this.createSliderIndicator(); + if (USE_LABELS === true) { + this.createLabel() + } return this; } @@ -205,6 +212,8 @@ entitySlider.prototype = { extension = Vec3.multiply(AXIS_SCALE, rightVector); } + + this.axisStart = position; this.endOfAxis = Vec3.sum(position, extension); var properties = { type: 'Line', @@ -228,8 +237,35 @@ entitySlider.prototype = { this.axis = Entities.addEntity(properties); }, - createLabel:function(){ - + createLabel: function() { + var LABEL_WIDTH = 0.25 + var leftVector = Vec.multiply(-1, Quat.getRight(this.avatarRot)); + var extension = Vec3.multiply(LABEL_WIDTH, leftVector); + var position = Vec3.sum(this.axisStart, extension); + var labelProperties = { + name: 'Hifi-Slider-Label-' + this.sliderType, + type: 'Text', + dimensions: { + x: LABEL_WIDTH, + y: 0.2, + z: 0.1 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + text: this.displayText, + lineHeight: 0.14, + backgroundColor: { + red: 0, + green: 0, + blue: 0 + }, + + } + + this.label = Entities.addEntity(labelProperties); }, createSliderIndicator: function() { var extensionVector; @@ -367,8 +403,12 @@ entitySlider.prototype = { }; + +var panel; +var visiblePanel; + function makeSliders(light) { - var panel; + if (USE_PARENTED_PANEL === true) { panel = createPanelEntity(MyAvatar.position); } @@ -386,9 +426,9 @@ function makeSliders(light) { var USE_EXPONENT_SLIDER = false; } if (USE_COLOR_SLIDER === true) { - slidersRef.color_red = new entitySlider(light, RED, 'color_red', 1); - slidersRef.color_green = new entitySlider(light, GREEN, 'color_green', 2); - slidersRef.color_blue = new entitySlider(light, BLUE, 'color_blue', 3); + slidersRef.color_red = new entitySlider(light, RED, 'color_red', 'Red', 1); + slidersRef.color_green = new entitySlider(light, GREEN, 'color_green', 'Green', 2); + slidersRef.color_blue = new entitySlider(light, BLUE, 'color_blue', 'Blue', 3); sliders.push(slidersRef.color_red); sliders.push(slidersRef.color_green); @@ -396,15 +436,15 @@ function makeSliders(light) { } if (USE_INTENSITY_SLIDER === true) { - slidersRef.intensity = new entitySlider(light, WHITE, 'intensity', 4); + slidersRef.intensity = new entitySlider(light, WHITE, 'intensity', 'Intensity', 4); sliders.push(slidersRef.intensity); } if (USE_CUTOFF_SLIDER === true) { - slidersRef.cutoff = new entitySlider(light, PURPLE, 'cutoff', 5); + slidersRef.cutoff = new entitySlider(light, PURPLE, 'cutoff', 'Cutoff', 5); sliders.push(slidersRef.cutoff); } if (USE_EXPONENT_SLIDER === true) { - slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 6); + slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 'Exponent', 6); sliders.push(slidersRef.exponent); } @@ -419,6 +459,10 @@ function makeSliders(light) { if (SLIDERS_SHOULD_STAY_WITH_AVATAR === true) { parentPanelToAvatar(panel); } + + if (VISIBLE_PANEL === true) { + visiblePanel = createVisiblePanel(); + } }; function parentPanelToAvatar(panel) { @@ -430,6 +474,9 @@ function parentPanelToAvatar(panel) { }) } + +function updateAxisWhe + function parentEntitiesToPanel(panel) { sliders.forEach(function(slider) { @@ -467,6 +514,29 @@ function createPanelEntity(position) { return panel } +function createVisiblePanel(position) { + print('CREATING VISIBLE PANEL at ' + JSON.stringify(position)); + + var totalOffset = Vec3.multiply(sliders.length, PER_ROW_OFFSET); + var panelProperties = { + name: 'Hifi-Visible-Transparent-Panel', + type: 'Model', + modelURL: TRANSPARENT_PANEL_URL, + dimensions: { + x: 1, + y: 1.4, + z: 0.1 + }, + visible: true, + collisionsWillMove: false, + ignoreForCollisions: true, + position: position + } + + var panel = Entities.addEntity(panelProperties); + return panel +} + function createLightModel(position, rotation) { var blockProperties = { @@ -588,6 +658,7 @@ function handleValueMessages(channel, message, sender) { var currentLight; var block; +var oldParent = null; var hasParent = false; function handleLightOverlayRayCheckMessages(channel, message, sender) { @@ -615,6 +686,7 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { var lightProperties = Entities.getEntityProperties(lightID); if (lightProperties.parentID !== DEFAULT_PARENT_ID) { //this light has a parent already. so lets call our block the parent and then make sure not to delete it at the end; + oldParent = lightProperties.parentID; hasParent = true; block = lightProperties.parentID; if (lightProperties.parentJointIndex !== -1) { @@ -657,8 +729,8 @@ function handleCleanupMessages(channel, message, sender) { } } -function updateSliderAxis(){ - sliders.forEach(function(slider){ +function updateSliderAxis() { + sliders.forEach(function(slider) { }) } @@ -675,9 +747,16 @@ function cleanup(fromMessage) { } //if the light was already parented to something we will want to restore that. or come up with groups or something clever. - Entities.editEntity(currentLight, { - parentID: null, - }); + if (oldParent !== null) { + Entities.editEntity(currentLight, { + parentID: oldParent, + }); + } else { + Entities.editEntity(currentLight, { + parentID: null, + }); + } + if (fromMessage !== true) { Messages.messageReceived.disconnect(handleLightModMessages); @@ -687,11 +766,15 @@ function cleanup(fromMessage) { } + Entities.deleteEntity(panel); + Entities.deleteEntity(visiblePanel); + selectionManager.clearSelections(); Script.update.disconnect(rotateCloseButtons); if (hasParent === false) { Entities.deleteEntity(block); } + oldParent = null; hasParent = false; currentLight = null; From aa0d1f0c29a0ae6e14b0bac03307ed218cd84b6e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 17:15:23 -0800 Subject: [PATCH 61/86] temp --- examples/light_modifier/lightModifier.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 4e0507b69f..a347aeadb0 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -10,8 +10,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// todo: text labels for property names, panel plane for visibility - //some experimental options var ONLY_I_CAN_EDIT = false; var SLIDERS_SHOULD_STAY_WITH_AVATAR = false; @@ -514,23 +512,23 @@ function createPanelEntity(position) { return panel } -function createVisiblePanel(position) { +function createVisiblePanel() { print('CREATING VISIBLE PANEL at ' + JSON.stringify(position)); - var totalOffset = Vec3.multiply(sliders.length, PER_ROW_OFFSET); + var totalOffset = -PER_ROW_OFFSET.y * sliders.length; var panelProperties = { name: 'Hifi-Visible-Transparent-Panel', type: 'Model', modelURL: TRANSPARENT_PANEL_URL, dimensions: { x: 1, - y: 1.4, + y: totalOffset, z: 0.1 }, visible: true, collisionsWillMove: false, ignoreForCollisions: true, - position: position + position: this.basePosition } var panel = Entities.addEntity(panelProperties); From 6afe3bae5ecfe0bb7096391a29e8d7ce26326591 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 22 Dec 2015 17:21:33 -0800 Subject: [PATCH 62/86] Copy Neuron joints into controller poses This makes the accessible for controller mapping and to JavaScript. Added 'neuronAvatar.js' as an example of reading joints from the neuron and setting them on the avatar. NOTE: the rotations are currently in the wrong coordinate frame. --- examples/controllers/neuron/neuronAvatar.js | 141 ++++++++++++ libraries/animation/src/Rig.cpp | 6 +- .../src/controllers/StandardControls.h | 61 +++++- plugins/hifiNeuron/src/NeuronPlugin.cpp | 205 ++++++++++++++++-- plugins/hifiNeuron/src/NeuronPlugin.h | 21 +- 5 files changed, 400 insertions(+), 34 deletions(-) create mode 100644 examples/controllers/neuron/neuronAvatar.js diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js new file mode 100644 index 0000000000..a7146e0759 --- /dev/null +++ b/examples/controllers/neuron/neuronAvatar.js @@ -0,0 +1,141 @@ +// maps controller joint names to avatar joint names +var JOINT_NAME_MAP = { + HipsPosition: "", + Hips: "Hips", + RightUpLeg: "RightUpLeg", + RightLeg: "RightLeg", + RightFoot: "RightFoot", + LeftUpLeg: "LeftUpLeg", + LeftLeg: "LeftLeg", + LeftFoot: "LeftFoot", + Spine: "Spine", + Spine1: "Spine1", + Spine2: "Spine2", + Spine3: "Spine3", + Neck: "Neck", + Head: "Head", + RightShoulder: "RightShoulder", + RightArm: "RightArm", + RightForeArm: "RightForeArm", + RightHand: "RightHand", + RightHandThumb1: "RightHandThumb2", + RightHandThumb2: "RightHandThumb3", + RightHandThumb3: "RightHandThumb4", + RightInHandIndex: "RightHandIndex1", + RightHandIndex1: "RightHandIndex2", + RightHandIndex2: "RightHandIndex3", + RightHandIndex3: "RightHandIndex4", + RightInHandMiddle: "RightHandMiddle1", + RightHandMiddle1: "RightHandMiddle2", + RightHandMiddle2: "RightHandMiddle3", + RightHandMiddle3: "RightHandMiddle4", + RightInHandRing: "RightHandRing1", + RightHandRing1: "RightHandRing2", + RightHandRing2: "RightHandRing3", + RightHandRing3: "RightHandRing4", + RightInHandPinky: "RightHandPinky1", + RightHandPinky1: "RightHandPinky2", + RightHandPinky2: "RightHandPinky3", + RightHandPinky3: "RightHandPinky4", + LeftShoulder: "LeftShoulder", + LeftArm: "LeftArm", + LeftForeArm: "LeftForeArm", + LeftHand: "LeftHand", + LeftHandThumb1: "LeftHandThumb2", + LeftHandThumb2: "LeftHandThumb3", + LeftHandThumb3: "LeftHandThumb4", + LeftInHandIndex: "LeftHandIndex1", + LeftHandIndex1: "LeftHandIndex2", + LeftHandIndex2: "LeftHandIndex3", + LeftHandIndex3: "LeftHandIndex4", + LeftInHandMiddle: "LeftHandMiddle1", + LeftHandMiddle1: "LeftHandMiddle2", + LeftHandMiddle2: "LeftHandMiddle3", + LeftHandMiddle3: "LeftHandMiddle4", + LeftInHandRing: "LeftHandRing1", + LeftHandRing1: "LeftHandRing2", + LeftHandRing2: "LeftHandRing3", + LeftHandRing3: "LeftHandRing4", + LeftInHandPinky: "LeftHandPinky1", + LeftHandPinky1: "LeftHandPinky2", + LeftHandPinky2: "LeftHandPinky3", + LeftHandPinky3: "LeftHandPinky4" +}; + +function dumpHardwareMapping() { + Object.keys(Controller.Hardware).forEach(function (deviceName) { + Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { + print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); + }); + }); +} + +// ctor +function NeuronAvatar() { + var self = this; + Script.scriptEnding.connect(function () { + self.shutdown(); + }); + Controller.hardwareChanged.connect(function () { + self.hardwareChanged(); + }); + + if (Controller.Hardware.Neuron) { + this.activate(); + } else { + this.deactivate(); + } +} + +NeuronAvatar.prototype.shutdown = function () { + this.deactivate(); +}; + +NeuronAvatar.prototype.hardwareChanged = function () { + if (Controller.Hardware.Neuron) { + this.activate(); + } else { + this.deactivate(); + } +}; + +NeuronAvatar.prototype.activate = function () { + if (!this._active) { + Script.update.connect(updateCallback); + } + this._active = true; +}; + +NeuronAvatar.prototype.deactivate = function () { + if (this._active) { + var self = this; + Script.update.disconnect(updateCallback); + } + this._active = false; + MyAvatar.clearJointsData(); +}; + +NeuronAvatar.prototype.update = function (deltaTime) { + var keys = Object.keys(JOINT_NAME_MAP); + var i, l = keys.length; + for (i = 0; i < l; i++) { + var channel = Controller.Hardware.Neuron[keys[i]]; + if (channel) { + var pose = Controller.getPoseValue(channel); + var j = MyAvatar.getJointIndex(JOINT_NAME_MAP[keys[i]]); + var defaultRot = MyAvatar.getDefaultJointRotation(j); + if (keys[i] == "Hips") { + MyAvatar.setJointRotation(j, Quat.multiply(pose.rotation, defaultRot)); + } else { + MyAvatar.setJointRotation(j, defaultRot); + } + } + } +}; + +var neuronAvatar = new NeuronAvatar(); + +function updateCallback(deltaTime) { + neuronAvatar.update(deltaTime); +} + diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 68f382d2d9..4dd091f1d6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -289,8 +289,10 @@ void Rig::clearJointState(int index) { void Rig::clearJointStates() { _internalPoseSet._overrideFlags.clear(); - _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints()); - _internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + if (_animSkeleton) { + _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints()); + _internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + } } void Rig::clearJointAnimationPriority(int index) { diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index bbd33c5cb3..feed8a0fad 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -88,9 +88,66 @@ namespace controller { // No correlation to SDL enum StandardPoseChannel { - LEFT_HAND = 0, - RIGHT_HAND, + HIPS_ROOT = 0, + HIPS, + RIGHT_UP_LEG, + RIGHT_LEG, + RIGHT_FOOT, + LEFT_UP_LEG, + LEFT_LEG, + LEFT_FOOT, + SPINE, + SPINE1, + SPINE2, + SPINE3, + NECK, HEAD, + RIGHT_SHOULDER, + RIGHT_ARM, + RIGHT_FORE_ARM, + RIGHT_HAND, + RIGHT_HAND_THUMB1, + RIGHT_HAND_THUMB2, + RIGHT_HAND_THUMB3, + RIGHT_IN_HAND_INDEX, + RIGHT_HAND_INDEX1, + RIGHT_HAND_INDEX2, + RIGHT_HAND_INDEX3, + RIGHT_IN_HAND_MIDDLE, + RIGHT_HAND_MIDDLE1, + RIGHT_HAND_MIDDLE2, + RIGHT_HAND_MIDDLE3, + RIGHT_IN_HANDRING, + RIGHT_HAND_RING1, + RIGHT_HAND_RING2, + RIGHT_HAND_RING3, + RIGHT_IN_HAND_PINKY, + RIGHT_HAND_PINKY1, + RIGHT_HAND_PINKY2, + RIGHT_HAND_PINKY3, + LEFT_SHOULDER, + LEFT_ARM, + LEFT_FORE_ARM, + LEFT_HAND, + LEFT_HAND_THUMB1, + LEFT_HAND_THUMB2, + LEFT_HAND_THUMB3, + LEFT_IN_HAND_INDEX, + LEFT_HAND_INDEX1, + LEFT_HAND_INDEX2, + LEFT_HAND_INDEX3, + LEFT_IN_HAND_MIDDLE, + LEFT_HAND_MIDDLE1, + LEFT_HAND_MIDDLE2, + LEFT_HAND_MIDDLE3, + LEFT_IN_HAND_RING, + LEFT_HAND_RING1, + LEFT_HAND_RING2, + LEFT_HAND_RING3, + LEFT_IN_HAND_PINKY, + LEFT_HAND_PINKY1, + LEFT_HAND_PINKY2, + LEFT_HAND_PINKY3, NUM_STANDARD_POSES }; diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index c3f764da05..4f52d8da98 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -1,5 +1,5 @@ // -// NeuronPlugin.h +// NeuronPlugin.cpp // input-plugins/src/input-plugins // // Created by Anthony Thibault on 12/18/2015. @@ -11,6 +11,7 @@ #include "NeuronPlugin.h" +#include #include #include #include @@ -27,6 +28,7 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") const QString NeuronPlugin::NAME = "Neuron"; const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; +// This matches controller::StandardPoseChannel enum JointIndex { HipsPosition = 0, Hips, @@ -87,7 +89,8 @@ enum JointIndex { LeftInHandPinky, LeftHandPinky1, LeftHandPinky2, - LeftHandPinky3 + LeftHandPinky3, + Size }; bool NeuronPlugin::isSupported() const { @@ -98,29 +101,81 @@ bool NeuronPlugin::isSupported() const { // NOTE: must be thread-safe void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx* header, float* data) { - qCDebug(inputplugins) << "NeuronPlugin: received frame data, DataCount = " << header->DataCount; auto neuronPlugin = reinterpret_cast(context); - std::lock_guard guard(neuronPlugin->_jointsMutex); - // Data is 6 floats: 3 position values, 3 rotation euler angles (degrees) + // version 1.0 + if (header->DataVersion.Major == 1 && header->DataVersion.Minor == 0) { - // resize vector if necessary - const size_t NUM_FLOATS_PER_JOINT = 6; - const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT; - if (neuronPlugin->_joints.size() != NUM_JOINTS) { - neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + std::lock_guard guard(neuronPlugin->_jointsMutex); + + // Data is 6 floats: 3 position values, 3 rotation euler angles (degrees) + + // resize vector if necessary + const size_t NUM_FLOATS_PER_JOINT = 6; + const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT; + if (neuronPlugin->_joints.size() != NUM_JOINTS) { + neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + } + + assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float))); + + // copy the data + memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); + + } else { + static bool ONCE = false; + if (!ONCE) { + qCCritical(inputplugins) << "NeuronPlugin: bad frame version number, expected 1.0"; + ONCE = true; + } } - - assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float))); - - // copy the data - memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); } // NOTE: must be thread-safe static void CommandDataReceivedCallback(void* context, SOCKET_REF sender, CommandPack* pack, void* data) { + DATA_VER version; + version._VersionMask = pack->DataVersion; + if (version.Major == 1 && version.Minor == 0) { + const char* str = "Unknown"; + switch (pack->CommandId) { + case Cmd_BoneSize: // Id can be used to request bone size from server or register avatar name command. + str = "BoneSize"; + break; + case Cmd_AvatarName: // Id can be used to request avatar name from server or register avatar name command. + str = "AvatarName"; + break; + case Cmd_FaceDirection: // Id used to request face direction from server + str = "FaceDirection"; + break; + case Cmd_DataFrequency: // Id can be used to request data frequency from server or register data frequency command. + str = "DataFrequency"; + break; + case Cmd_BvhInheritanceTxt: // Id can be used to request bvh header txt from server or register bvh header txt command. + str = "BvhInheritanceTxt"; + break; + case Cmd_AvatarCount: // Id can be used to request avatar count from server or register avatar count command. + str = "AvatarCount"; + break; + case Cmd_CombinationMode: // Id can be used to request combination mode from server or register combination mode command. + str = "CombinationMode"; + break; + case Cmd_RegisterEvent: // Id can be used to register event. + str = "RegisterEvent"; + break; + case Cmd_UnRegisterEvent: // Id can be used to unregister event. + str = "UnRegisterEvent"; + break; + } + qCDebug(inputplugins) << "NeuronPlugin: command data received CommandID = " << str; + } else { + static bool ONCE = false; + if (!ONCE) { + qCCritical(inputplugins) << "NeuronPlugin: bad command version number, expected 1.0"; + ONCE = true; + } + } } // NOTE: must be thread-safe @@ -130,6 +185,11 @@ static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, Socket void NeuronPlugin::activate() { InputPlugin::activate(); + + // register with userInputMapper + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(_inputDevice); + qCDebug(inputplugins) << "NeuronPlugin::activate"; // register c-style callbacks @@ -149,12 +209,18 @@ void NeuronPlugin::activate() { } void NeuronPlugin::deactivate() { - // TODO: qCDebug(inputplugins) << "NeuronPlugin::deactivate"; + // unregister from userInputMapper + if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(_inputDevice->_deviceID); + } + if (_socketRef) { BRCloseSocket(_socketRef); } + InputPlugin::deactivate(); } @@ -174,12 +240,14 @@ void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) { joints = _joints; } + /* DebugDraw::getInstance().addMyAvatarMarker("LEFT_FOOT", - eulerToQuat(joints[6].rot), + eulerToQuat(joints[6].euler), joints[6].pos / 100.0f, glm::vec4(1)); - - _inputDevice->update(deltaTime, jointsCaptured); + */ + _inputDevice->update(deltaTime, joints, _prevJoints); + _prevJoints = joints; } void NeuronPlugin::saveSettings() const { @@ -198,11 +266,86 @@ void NeuronPlugin::loadSettings() { // InputDevice // +static controller::StandardPoseChannel neuronJointIndexToPoseIndex(JointIndex i) { + // Currently they are the same. + // but that won't always be the case... + return (controller::StandardPoseChannel)i; +} + +static const char* neuronJointName(JointIndex i) { + switch (i) { + case HipsPosition: return "HipsPosition"; + case Hips: return "Hips"; + case RightUpLeg: return "RightUpLeg"; + case RightLeg: return "RightLeg"; + case RightFoot: return "RightFoot"; + case LeftUpLeg: return "LeftUpLeg"; + case LeftLeg: return "LeftLeg"; + case LeftFoot: return "LeftFoot"; + case Spine: return "Spine"; + case Spine1: return "Spine1"; + case Spine2: return "Spine2"; + case Spine3: return "Spine3"; + case Neck: return "Neck"; + case Head: return "Head"; + case RightShoulder: return "RightShoulder"; + case RightArm: return "RightArm"; + case RightForeArm: return "RightForeArm"; + case RightHand: return "RightHand"; + case RightHandThumb1: return "RightHandThumb1"; + case RightHandThumb2: return "RightHandThumb2"; + case RightHandThumb3: return "RightHandThumb3"; + case RightInHandIndex: return "RightInHandIndex"; + case RightHandIndex1: return "RightHandIndex1"; + case RightHandIndex2: return "RightHandIndex2"; + case RightHandIndex3: return "RightHandIndex3"; + case RightInHandMiddle: return "RightInHandMiddle"; + case RightHandMiddle1: return "RightHandMiddle1"; + case RightHandMiddle2: return "RightHandMiddle2"; + case RightHandMiddle3: return "RightHandMiddle3"; + case RightInHandRing: return "RightInHandRing"; + case RightHandRing1: return "RightHandRing1"; + case RightHandRing2: return "RightHandRing2"; + case RightHandRing3: return "RightHandRing3"; + case RightInHandPinky: return "RightInHandPinky"; + case RightHandPinky1: return "RightHandPinky1"; + case RightHandPinky2: return "RightHandPinky2"; + case RightHandPinky3: return "RightHandPinky3"; + case LeftShoulder: return "LeftShoulder"; + case LeftArm: return "LeftArm"; + case LeftForeArm: return "LeftForeArm"; + case LeftHand: return "LeftHand"; + case LeftHandThumb1: return "LeftHandThumb1"; + case LeftHandThumb2: return "LeftHandThumb2"; + case LeftHandThumb3: return "LeftHandThumb3"; + case LeftInHandIndex: return "LeftInHandIndex"; + case LeftHandIndex1: return "LeftHandIndex1"; + case LeftHandIndex2: return "LeftHandIndex2"; + case LeftHandIndex3: return "LeftHandIndex3"; + case LeftInHandMiddle: return "LeftInHandMiddle"; + case LeftHandMiddle1: return "LeftHandMiddle1"; + case LeftHandMiddle2: return "LeftHandMiddle2"; + case LeftHandMiddle3: return "LeftHandMiddle3"; + case LeftInHandRing: return "LeftInHandRing"; + case LeftHandRing1: return "LeftHandRing1"; + case LeftHandRing2: return "LeftHandRing2"; + case LeftHandRing3: return "LeftHandRing3"; + case LeftInHandPinky: return "LeftInHandPinky"; + case LeftHandPinky1: return "LeftHandPinky1"; + case LeftHandPinky2: return "LeftHandPinky2"; + case LeftHandPinky3: return "LeftHandPinky3"; + default: return "???"; + } +} + controller::Input::NamedVector NeuronPlugin::InputDevice::getAvailableInputs() const { // TODO: - static const controller::Input::NamedVector availableInputs { - makePair(controller::LEFT_HAND, "LeftHand"), - makePair(controller::RIGHT_HAND, "RightHand") + static controller::Input::NamedVector availableInputs; + + if (availableInputs.size() == 0) { + for (int i = 0; i < JointIndex::Size; i++) { + availableInputs.push_back(makePair(neuronJointIndexToPoseIndex((JointIndex)i), neuronJointName((JointIndex)i))); + } }; return availableInputs; } @@ -212,8 +355,24 @@ QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { return MAPPING_JSON; } -void NeuronPlugin::InputDevice::update(float deltaTime, bool jointsCaptured) { +void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector& joints, const std::vector& prevJoints) { + for (int i = 0; i < joints.size(); i++) { + int poseIndex = neuronJointIndexToPoseIndex((JointIndex)i); + glm::vec3 linearVel, angularVel; + glm::vec3 pos = (joints[i].pos * METERS_PER_CENTIMETER); + glm::quat rot = eulerToQuat(joints[i].euler); + if (i < prevJoints.size()) { + linearVel = (pos - (prevJoints[i].pos * METERS_PER_CENTIMETER)) / deltaTime; + // quat log imag part points along the axis of rotation, and it's length will be the half angle. + glm::quat d = glm::log(rot * glm::inverse(eulerToQuat(prevJoints[i].euler))); + angularVel = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); + } + _poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVel, angularVel); + if (glm::length(angularVel) > 0.5f) { + qCDebug(inputplugins) << "Movement in joint" << i << neuronJointName((JointIndex)i); + } + } } void NeuronPlugin::InputDevice::focusOutEvent() { diff --git a/plugins/hifiNeuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h index 5f67502e04..f787838ce2 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -41,15 +41,25 @@ public: virtual void loadSettings() override; protected: + + struct NeuronJoint { + glm::vec3 pos; + glm::vec3 euler; + }; + class InputDevice : public controller::InputDevice { public: + friend class NeuronPlugin; + InputDevice() : controller::InputDevice("Neuron") {} // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void update(float deltaTime, bool jointsCaptured) override {}; virtual void focusOutEvent() override; + + void update(float deltaTime, const std::vector& joints, const std::vector& prevJoints); }; std::shared_ptr _inputDevice { std::make_shared() }; @@ -61,13 +71,10 @@ protected: int _serverPort; void* _socketRef; - struct NeuronJoint { - glm::vec3 pos; - glm::vec3 rot; - }; - std::vector _joints; - std::mutex _jointsMutex; + std::mutex _jointsMutex; // used to guard access to _joints + + std::vector _prevJoints; }; #endif // hifi_NeuronPlugin_h From 65d5e3f60a3a919fd0d0ee00357ce4e2f0e62e3f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 23 Dec 2015 14:11:42 -0800 Subject: [PATCH 63/86] add labels and transparent panel, but panel blocks pick so disable --- examples/light_modifier/lightModifier.js | 83 ++++++++++++++++++++---- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index a347aeadb0..dc9332405e 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -17,8 +17,10 @@ var VERTICAL_SLIDERS = false; var SHOW_OVERLAYS = true; var SHOW_LIGHT_VOLUME = true; var USE_PARENTED_PANEL = true; -var VISIBLE_PANEL = true; +var VISIBLE_PANEL = false; var USE_LABELS = true; +var LEFT_LABELS = false; +var RIGHT_LABELS = true; //variables for managing overlays var selectionDisplay; @@ -134,6 +136,8 @@ var slidersRef = { }; var light = null; +var basePosition; +var avatarRotation; function entitySlider(light, color, sliderType, displayText, row) { this.light = light; @@ -146,6 +150,8 @@ function entitySlider(light, color, sliderType, displayText, row) { this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); this.basePosition.y += 1; + basePosition=this.basePosition; + avatarRot = this.avatarRot; var message = { lightID: this.lightID, @@ -213,6 +219,8 @@ entitySlider.prototype = { this.axisStart = position; this.endOfAxis = Vec3.sum(position, extension); + this.createEndOfAxisEntity(); + var properties = { type: 'Line', name: 'Hifi-Slider-Axis::' + this.sliderType, @@ -235,16 +243,59 @@ entitySlider.prototype = { this.axis = Entities.addEntity(properties); }, + createEndOfAxisEntity: function() { + //we use this to track the end of the axis while parented to a panel + var properties = { + name: 'Hifi-End-Of-Axis', + type: 'Box', + collisionsWillMove: false, + ignoreForCollisions: true, + dimensions: { + x: 0.01, + y: 0.01, + z: 0.01 + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + position: this.endOfAxis, + parentID:this.axis, + visible: false + } + + this.endOfAxisEntity = Entities.addEntity(this.endOfAxis); + }, createLabel: function() { + var LABEL_WIDTH = 0.25 - var leftVector = Vec.multiply(-1, Quat.getRight(this.avatarRot)); - var extension = Vec3.multiply(LABEL_WIDTH, leftVector); - var position = Vec3.sum(this.axisStart, extension); + var PER_LETTER_SPACING = 0.1; + var textWidth = this.displayText.length * PER_LETTER_SPACING; + + var position; + if (LEFT_LABELS === true) { + var leftVector = Vec3.multiply(-1, Quat.getRight(this.avatarRot)); + + var extension = Vec3.multiply(textWidth, leftVector); + + position = Vec3.sum(this.axisStart, extension); + } + + if (RIGHT_LABELS === true) { + var rightVector = Quat.getRight(this.avatarRot); + + var extension = Vec3.multiply(textWidth / 1.75, rightVector); + + position = Vec3.sum(this.endOfAxis, extension); + } + + var labelProperties = { name: 'Hifi-Slider-Label-' + this.sliderType, type: 'Text', dimensions: { - x: LABEL_WIDTH, + x: textWidth, y: 0.2, z: 0.1 }, @@ -260,10 +311,12 @@ entitySlider.prototype = { green: 0, blue: 0 }, - + position: position, + rotation:this.avatarRot, } - + print('BEFORE CREATE LABEL' + JSON.stringify(labelProperties)) this.label = Entities.addEntity(labelProperties); + print('AFTER CREATE LABEL') }, createSliderIndicator: function() { var extensionVector; @@ -473,8 +526,6 @@ function parentPanelToAvatar(panel) { } -function updateAxisWhe - function parentEntitiesToPanel(panel) { sliders.forEach(function(slider) { @@ -513,22 +564,25 @@ function createPanelEntity(position) { } function createVisiblePanel() { - print('CREATING VISIBLE PANEL at ' + JSON.stringify(position)); - var totalOffset = -PER_ROW_OFFSET.y * sliders.length; + + var moveRight =Vec3.sum(basePosition,Vec3.multiply(AXIS_SCALE/2,Quat.getRight(avatarRot))); + + var moveDown = Vec3.sum(moveRight,Vec3.multiply((sliders.length+1)/2,PER_ROW_OFFSET)) var panelProperties = { name: 'Hifi-Visible-Transparent-Panel', type: 'Model', modelURL: TRANSPARENT_PANEL_URL, dimensions: { - x: 1, + x: AXIS_SCALE+0.1, y: totalOffset, - z: 0.1 + z: SLIDER_DIMENSIONS.z/4 }, visible: true, collisionsWillMove: false, ignoreForCollisions: true, - position: this.basePosition + position: moveDown, + rotation:avatarRot } var panel = Entities.addEntity(panelProperties); @@ -738,6 +792,7 @@ function cleanup(fromMessage) { for (i = 0; i < sliders.length; i++) { Entities.deleteEntity(sliders[i].axis); Entities.deleteEntity(sliders[i].sliderIndicator); + Entities.deleteEntity(sliders[i].label); } while (closeButtons.length > 0) { From c44f69b370387b06ab16b27583aaf5940f3cf59d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 23 Dec 2015 17:13:52 -0800 Subject: [PATCH 64/86] WIP checkpoint Changed euler angle compisition based on experiments in maya. Also, the neuronAvatar.js attempts to transform the neuron input quaternions into a pose relative to the avatar's default pose, but doesn't it doesn't work. --- examples/controllers/neuron/neuronAvatar.js | 9 +++----- .../src/controllers/StandardControls.h | 3 +-- plugins/hifiNeuron/src/NeuronPlugin.cpp | 22 +++++++++++++------ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js index a7146e0759..776cabd301 100644 --- a/examples/controllers/neuron/neuronAvatar.js +++ b/examples/controllers/neuron/neuronAvatar.js @@ -1,6 +1,5 @@ // maps controller joint names to avatar joint names var JOINT_NAME_MAP = { - HipsPosition: "", Hips: "Hips", RightUpLeg: "RightUpLeg", RightLeg: "RightLeg", @@ -124,11 +123,9 @@ NeuronAvatar.prototype.update = function (deltaTime) { var pose = Controller.getPoseValue(channel); var j = MyAvatar.getJointIndex(JOINT_NAME_MAP[keys[i]]); var defaultRot = MyAvatar.getDefaultJointRotation(j); - if (keys[i] == "Hips") { - MyAvatar.setJointRotation(j, Quat.multiply(pose.rotation, defaultRot)); - } else { - MyAvatar.setJointRotation(j, defaultRot); - } + var rot = Quat.multiply(Quat.inverse(defaultRot), Quat.multiply(pose.rotation, defaultRot)); + MyAvatar.setJointRotation(j, Quat.multiply(defaultRot, rot)); + //MyAvatar.setJointTranslation(j, Vec3.multiply(100.0, pose.translation)); } } }; diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index feed8a0fad..4294713238 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -88,8 +88,7 @@ namespace controller { // No correlation to SDL enum StandardPoseChannel { - HIPS_ROOT = 0, - HIPS, + HIPS = 0, RIGHT_UP_LEG, RIGHT_LEG, RIGHT_FOOT, diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 4f52d8da98..152bce913d 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -17,6 +17,7 @@ #include #include #include +#include Q_DECLARE_LOGGING_CATEGORY(inputplugins) Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") @@ -30,8 +31,7 @@ const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; // This matches controller::StandardPoseChannel enum JointIndex { - HipsPosition = 0, - Hips, + Hips = 0, RightUpLeg, RightLeg, RightFoot, @@ -204,8 +204,9 @@ void NeuronPlugin::activate() { if (!_socketRef) { // error qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << "error = " << BRGetLastErrorMessage(); + } else { + qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort; } - qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort; } void NeuronPlugin::deactivate() { @@ -226,9 +227,9 @@ void NeuronPlugin::deactivate() { // convert between euler in degrees to quaternion static quat eulerToQuat(vec3 euler) { - return (glm::angleAxis(euler.y * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(euler.x * RADIANS_PER_DEGREE, Vectors::UNIT_X) * - glm::angleAxis(euler.z * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); + // euler.x and euler.y are swaped (thanks NOMICOM!) + glm::vec3 e = glm::vec3(euler.y, euler.x, euler.z) * RADIANS_PER_DEGREE; + return (glm::angleAxis(e.y, Vectors::UNIT_Y) * glm::angleAxis(e.x, Vectors::UNIT_X) * glm::angleAxis(e.z, Vectors::UNIT_Z)); } void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) { @@ -274,7 +275,6 @@ static controller::StandardPoseChannel neuronJointIndexToPoseIndex(JointIndex i) static const char* neuronJointName(JointIndex i) { switch (i) { - case HipsPosition: return "HipsPosition"; case Hips: return "Hips"; case RightUpLeg: return "RightUpLeg"; case RightLeg: return "RightLeg"; @@ -369,9 +369,17 @@ void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector Date: Thu, 24 Dec 2015 17:14:17 -0800 Subject: [PATCH 65/86] neruonAvatar.js: now sets rotations in correct frame. The rotations from the neuron are effectively in world space and are deltas from the default pose. There still is an issue with the thumb, due to the missing joint from the Neuron. The Neuron only has 3 thumb joints, not 4. --- examples/controllers/neuron/neuronAvatar.js | 95 ++++++++++++++++++++- plugins/hifiNeuron/src/NeuronPlugin.cpp | 52 +++++------ 2 files changed, 119 insertions(+), 28 deletions(-) diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js index 776cabd301..fae46330b1 100644 --- a/examples/controllers/neuron/neuronAvatar.js +++ b/examples/controllers/neuron/neuronAvatar.js @@ -61,6 +61,70 @@ var JOINT_NAME_MAP = { LeftHandPinky3: "LeftHandPinky4" }; +var JOINT_PARENT_MAP = { + Hips: "", + RightUpLeg: "Hips", + RightLeg: "RightUpLeg", + RightFoot: "RightLeg", + LeftUpLeg: "Hips", + LeftLeg: "LeftUpLeg", + LeftFoot: "LeftLeg", + Spine: "Hips", + Spine1: "Spine", + Spine2: "Spine1", + Spine3: "Spine2", + Neck: "Spine3", + Head: "Neck", + RightShoulder: "Spine3", + RightArm: "RightShoulder", + RightForeArm: "RightArm", + RightHand: "RightForeArm", + RightHandThumb1: "RightHand", + RightHandThumb2: "RightHandThumb1", + RightHandThumb3: "RightHandThumb2", + RightHandThumb4: "RightHandThumb3", + RightHandIndex1: "RightHand", + RightHandIndex2: "RightHandIndex1", + RightHandIndex3: "RightHandIndex2", + RightHandIndex4: "RightHandIndex3", + RightHandMiddle1: "RightHand", + RightHandMiddle2: "RightHandMiddle1", + RightHandMiddle3: "RightHandMiddle2", + RightHandMiddle4: "RightHandMiddle3", + RightHandRing1: "RightHand", + RightHandRing2: "RightHandRing1", + RightHandRing3: "RightHandRing2", + RightHandRing4: "RightHandRing3", + RightHandPinky1: "RightHand", + RightHandPinky2: "RightHandPinky1", + RightHandPinky3: "RightHandPinky2", + RightHandPinky4: "RightHandPinky3", + LeftShoulder: "Spine3", + LeftArm: "LeftShoulder", + LeftForeArm: "LeftArm", + LeftHand: "LeftForeArm", + LeftHandThumb1: "LeftHand", + LeftHandThumb2: "LeftHandThumb1", + LeftHandThumb3: "LeftHandThumb2", + LeftHandThumb4: "LeftHandThumb3", + LeftHandIndex1: "LeftHand", + LeftHandIndex2: "LeftHandIndex1", + LeftHandIndex3: "LeftHandIndex2", + LeftHandIndex4: "LeftHandIndex3", + LeftHandMiddle1: "LeftHand", + LeftHandMiddle2: "LeftHandMiddle1", + LeftHandMiddle3: "LeftHandMiddle2", + LeftHandMiddle4: "LeftHandMiddle3", + LeftHandRing1: "LeftHand", + LeftHandRing2: "LeftHandRing1", + LeftHandRing3: "LeftHandRing2", + LeftHandRing4: "LeftHandRing3", + LeftHandPinky1: "LeftHand", + LeftHandPinky2: "LeftHandPinky1", + LeftHandPinky3: "LeftHandPinky2", + LeftHandPinky: "LeftHandPinky3", +}; + function dumpHardwareMapping() { Object.keys(Controller.Hardware).forEach(function (deviceName) { Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { @@ -103,6 +167,21 @@ NeuronAvatar.prototype.activate = function () { Script.update.connect(updateCallback); } this._active = true; + + // build absDefaultPoseMap + this._absDefaultRotMap = {}; + var keys = Object.keys(JOINT_NAME_MAP); + var i, l = keys.length; + for (i = 0; i < l; i++) { + var jointName = JOINT_NAME_MAP[keys[i]]; + var j = MyAvatar.getJointIndex(jointName); + var parentJointName = JOINT_PARENT_MAP[jointName]; + if (parentJointName === "") { + this._absDefaultRotMap[jointName] = MyAvatar.getDefaultJointRotation(j); + } else { + this._absDefaultRotMap[jointName] = Quat.multiply(this._absDefaultRotMap[parentJointName], MyAvatar.getDefaultJointRotation(j)); + } + } }; NeuronAvatar.prototype.deactivate = function () { @@ -117,14 +196,22 @@ NeuronAvatar.prototype.deactivate = function () { NeuronAvatar.prototype.update = function (deltaTime) { var keys = Object.keys(JOINT_NAME_MAP); var i, l = keys.length; + var absDefaultRot = {}; for (i = 0; i < l; i++) { var channel = Controller.Hardware.Neuron[keys[i]]; if (channel) { var pose = Controller.getPoseValue(channel); - var j = MyAvatar.getJointIndex(JOINT_NAME_MAP[keys[i]]); - var defaultRot = MyAvatar.getDefaultJointRotation(j); - var rot = Quat.multiply(Quat.inverse(defaultRot), Quat.multiply(pose.rotation, defaultRot)); - MyAvatar.setJointRotation(j, Quat.multiply(defaultRot, rot)); + var jointName = JOINT_NAME_MAP[keys[i]]; + var parentJointName = JOINT_PARENT_MAP[jointName]; + var j = MyAvatar.getJointIndex(jointName); + var defaultAbsRot = this._absDefaultRotMap[jointName]; + var parentDefaultAbsRot; + if (parentJointName === "") { + parentDefaultAbsRot = {x: 0, y: 0, z: 0, w: 1}; + } else { + parentDefaultAbsRot = this._absDefaultRotMap[parentJointName]; + } + MyAvatar.setJointRotation(j, Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot))); //MyAvatar.setJointTranslation(j, Vec3.multiply(100.0, pose.translation)); } } diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 152bce913d..ae438c10e0 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -107,21 +107,37 @@ void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx // version 1.0 if (header->DataVersion.Major == 1 && header->DataVersion.Minor == 0) { - std::lock_guard guard(neuronPlugin->_jointsMutex); - - // Data is 6 floats: 3 position values, 3 rotation euler angles (degrees) - - // resize vector if necessary - const size_t NUM_FLOATS_PER_JOINT = 6; - const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT; - if (neuronPlugin->_joints.size() != NUM_JOINTS) { - neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + // skip reference joint if present + if (header->WithReference && header->WithDisp) { + data += 6; + } else if (header->WithReference && !header->WithDisp) { + data += 3; } - assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float))); + if (header->WithDisp) { + // enter mutex + std::lock_guard guard(neuronPlugin->_jointsMutex); - // copy the data - memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); + // + // Data is 6 floats per joint: 3 position values, 3 rotation euler angles (degrees) + // + + // resize vector if necessary + const size_t NUM_FLOATS_PER_JOINT = 6; + const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT; + if (neuronPlugin->_joints.size() != NUM_JOINTS) { + neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + } + + assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float))); + + // copy the data + memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); + } else { + + qCCritical(inputplugins) << "NeruonPlugin: format not supported"; + + } } else { static bool ONCE = false; @@ -368,18 +384,6 @@ void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector Date: Fri, 25 Dec 2015 09:57:50 -0800 Subject: [PATCH 66/86] Set up controller poses to match hifi standard skeleton Neuron plugin in fills in the gap (thumb1) with identity. Updated neuronAvatar script to work with new controller pose names. --- examples/controllers/neuron/neuronAvatar.js | 97 +----- .../src/controllers/StandardControls.h | 20 +- plugins/hifiNeuron/src/NeuronPlugin.cpp | 324 +++++++++++------- plugins/hifiNeuron/src/NeuronPlugin.h | 10 +- 4 files changed, 245 insertions(+), 206 deletions(-) diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js index fae46330b1..cc53987f05 100644 --- a/examples/controllers/neuron/neuronAvatar.js +++ b/examples/controllers/neuron/neuronAvatar.js @@ -1,66 +1,3 @@ -// maps controller joint names to avatar joint names -var JOINT_NAME_MAP = { - Hips: "Hips", - RightUpLeg: "RightUpLeg", - RightLeg: "RightLeg", - RightFoot: "RightFoot", - LeftUpLeg: "LeftUpLeg", - LeftLeg: "LeftLeg", - LeftFoot: "LeftFoot", - Spine: "Spine", - Spine1: "Spine1", - Spine2: "Spine2", - Spine3: "Spine3", - Neck: "Neck", - Head: "Head", - RightShoulder: "RightShoulder", - RightArm: "RightArm", - RightForeArm: "RightForeArm", - RightHand: "RightHand", - RightHandThumb1: "RightHandThumb2", - RightHandThumb2: "RightHandThumb3", - RightHandThumb3: "RightHandThumb4", - RightInHandIndex: "RightHandIndex1", - RightHandIndex1: "RightHandIndex2", - RightHandIndex2: "RightHandIndex3", - RightHandIndex3: "RightHandIndex4", - RightInHandMiddle: "RightHandMiddle1", - RightHandMiddle1: "RightHandMiddle2", - RightHandMiddle2: "RightHandMiddle3", - RightHandMiddle3: "RightHandMiddle4", - RightInHandRing: "RightHandRing1", - RightHandRing1: "RightHandRing2", - RightHandRing2: "RightHandRing3", - RightHandRing3: "RightHandRing4", - RightInHandPinky: "RightHandPinky1", - RightHandPinky1: "RightHandPinky2", - RightHandPinky2: "RightHandPinky3", - RightHandPinky3: "RightHandPinky4", - LeftShoulder: "LeftShoulder", - LeftArm: "LeftArm", - LeftForeArm: "LeftForeArm", - LeftHand: "LeftHand", - LeftHandThumb1: "LeftHandThumb2", - LeftHandThumb2: "LeftHandThumb3", - LeftHandThumb3: "LeftHandThumb4", - LeftInHandIndex: "LeftHandIndex1", - LeftHandIndex1: "LeftHandIndex2", - LeftHandIndex2: "LeftHandIndex3", - LeftHandIndex3: "LeftHandIndex4", - LeftInHandMiddle: "LeftHandMiddle1", - LeftHandMiddle1: "LeftHandMiddle2", - LeftHandMiddle2: "LeftHandMiddle3", - LeftHandMiddle3: "LeftHandMiddle4", - LeftInHandRing: "LeftHandRing1", - LeftHandRing1: "LeftHandRing2", - LeftHandRing2: "LeftHandRing3", - LeftHandRing3: "LeftHandRing4", - LeftInHandPinky: "LeftHandPinky1", - LeftHandPinky1: "LeftHandPinky2", - LeftHandPinky2: "LeftHandPinky3", - LeftHandPinky3: "LeftHandPinky4" -}; - var JOINT_PARENT_MAP = { Hips: "", RightUpLeg: "Hips", @@ -170,10 +107,11 @@ NeuronAvatar.prototype.activate = function () { // build absDefaultPoseMap this._absDefaultRotMap = {}; - var keys = Object.keys(JOINT_NAME_MAP); + this._absDefaultRotMap[""] = {x: 0, y: 0, z: 0, w: 1}; + var keys = Object.keys(JOINT_PARENT_MAP); var i, l = keys.length; for (i = 0; i < l; i++) { - var jointName = JOINT_NAME_MAP[keys[i]]; + var jointName = keys[i]; var j = MyAvatar.getJointIndex(jointName); var parentJointName = JOINT_PARENT_MAP[jointName]; if (parentJointName === "") { @@ -194,24 +132,27 @@ NeuronAvatar.prototype.deactivate = function () { }; NeuronAvatar.prototype.update = function (deltaTime) { - var keys = Object.keys(JOINT_NAME_MAP); + var keys = Object.keys(JOINT_PARENT_MAP); var i, l = keys.length; var absDefaultRot = {}; + var jointName, channel, pose, parentJointName, j, parentDefaultAbsRot; for (i = 0; i < l; i++) { - var channel = Controller.Hardware.Neuron[keys[i]]; + var jointName = keys[i]; + var channel = Controller.Hardware.Neuron[jointName]; if (channel) { - var pose = Controller.getPoseValue(channel); - var jointName = JOINT_NAME_MAP[keys[i]]; - var parentJointName = JOINT_PARENT_MAP[jointName]; - var j = MyAvatar.getJointIndex(jointName); - var defaultAbsRot = this._absDefaultRotMap[jointName]; - var parentDefaultAbsRot; - if (parentJointName === "") { - parentDefaultAbsRot = {x: 0, y: 0, z: 0, w: 1}; - } else { - parentDefaultAbsRot = this._absDefaultRotMap[parentJointName]; - } + pose = Controller.getPoseValue(channel); + parentJointName = JOINT_PARENT_MAP[jointName]; + j = MyAvatar.getJointIndex(jointName); + defaultAbsRot = this._absDefaultRotMap[jointName]; + parentDefaultAbsRot = this._absDefaultRotMap[parentJointName]; + + // Rotations from the neuron controller are in world orientation but are delta's from the default pose. + // So first we build the absolute rotation of the default pose (local into world). + // Then apply the rotation from the controller, in world space. + // Then we transform back into joint local by multiplying by the inverse of the parents absolute rotation. MyAvatar.setJointRotation(j, Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot))); + + // TODO: //MyAvatar.setJointTranslation(j, Vec3.multiply(100.0, pose.translation)); } } diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index 4294713238..2b0613321e 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -108,22 +108,23 @@ namespace controller { RIGHT_HAND_THUMB1, RIGHT_HAND_THUMB2, RIGHT_HAND_THUMB3, - RIGHT_IN_HAND_INDEX, + RIGHT_HAND_THUMB4, RIGHT_HAND_INDEX1, RIGHT_HAND_INDEX2, RIGHT_HAND_INDEX3, - RIGHT_IN_HAND_MIDDLE, + RIGHT_HAND_INDEX4, RIGHT_HAND_MIDDLE1, RIGHT_HAND_MIDDLE2, RIGHT_HAND_MIDDLE3, - RIGHT_IN_HANDRING, + RIGHT_HAND_MIDDLE4, RIGHT_HAND_RING1, RIGHT_HAND_RING2, RIGHT_HAND_RING3, - RIGHT_IN_HAND_PINKY, + RIGHT_HAND_RING4, RIGHT_HAND_PINKY1, RIGHT_HAND_PINKY2, RIGHT_HAND_PINKY3, + RIGHT_HAND_PINKY4, LEFT_SHOULDER, LEFT_ARM, LEFT_FORE_ARM, @@ -131,28 +132,29 @@ namespace controller { LEFT_HAND_THUMB1, LEFT_HAND_THUMB2, LEFT_HAND_THUMB3, - LEFT_IN_HAND_INDEX, + LEFT_HAND_THUMB4, LEFT_HAND_INDEX1, LEFT_HAND_INDEX2, LEFT_HAND_INDEX3, - LEFT_IN_HAND_MIDDLE, + LEFT_HAND_INDEX4, LEFT_HAND_MIDDLE1, LEFT_HAND_MIDDLE2, LEFT_HAND_MIDDLE3, - LEFT_IN_HAND_RING, + LEFT_HAND_MIDDLE4, LEFT_HAND_RING1, LEFT_HAND_RING2, LEFT_HAND_RING3, - LEFT_IN_HAND_PINKY, + LEFT_HAND_RING4, LEFT_HAND_PINKY1, LEFT_HAND_PINKY2, LEFT_HAND_PINKY3, + LEFT_HAND_PINKY4, NUM_STANDARD_POSES }; enum StandardCounts { TRIGGERS = 2, ANALOG_STICKS = 2, - POSES = 2, // FIXME 3? if we want to expose the head? + POSES = NUM_STANDARD_POSES }; } diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index ae438c10e0..4edda64c22 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -29,8 +29,10 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") const QString NeuronPlugin::NAME = "Neuron"; const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; -// This matches controller::StandardPoseChannel -enum JointIndex { +// indices of joints of the Neuron standard skeleton. +// This is 'almost' the same as the High Fidelity standard skeleton. +// It is missing a thumb joint. +enum NeuronJointIndex { Hips = 0, RightUpLeg, RightLeg, @@ -93,12 +95,160 @@ enum JointIndex { Size }; -bool NeuronPlugin::isSupported() const { - // Because it's a client/server network architecture, we can't tell - // if the neuron is actually connected until we connect to the server. - return true; +// Almost a direct mapping except for LEFT_HAND_THUMB1 and RIGHT_HAND_THUMB1, +// which are not present in the Neuron standard skeleton. +static controller::StandardPoseChannel neuronJointIndexToPoseIndexMap[NeuronJointIndex::Size] = { + controller::HIPS, + controller::RIGHT_UP_LEG, + controller::RIGHT_LEG, + controller::RIGHT_FOOT, + controller::LEFT_UP_LEG, + controller::LEFT_LEG, + controller::LEFT_FOOT, + controller::SPINE, + controller::SPINE1, + controller::SPINE2, + controller::SPINE3, + controller::NECK, + controller::HEAD, + controller::RIGHT_SHOULDER, + controller::RIGHT_ARM, + controller::RIGHT_FORE_ARM, + controller::RIGHT_HAND, + controller::RIGHT_HAND_THUMB2, + controller::RIGHT_HAND_THUMB3, + controller::RIGHT_HAND_THUMB4, + controller::RIGHT_HAND_INDEX1, + controller::RIGHT_HAND_INDEX2, + controller::RIGHT_HAND_INDEX3, + controller::RIGHT_HAND_INDEX4, + controller::RIGHT_HAND_MIDDLE1, + controller::RIGHT_HAND_MIDDLE2, + controller::RIGHT_HAND_MIDDLE3, + controller::RIGHT_HAND_MIDDLE4, + controller::RIGHT_HAND_RING1, + controller::RIGHT_HAND_RING2, + controller::RIGHT_HAND_RING3, + controller::RIGHT_HAND_RING4, + controller::RIGHT_HAND_PINKY1, + controller::RIGHT_HAND_PINKY2, + controller::RIGHT_HAND_PINKY3, + controller::RIGHT_HAND_PINKY4, + controller::LEFT_SHOULDER, + controller::LEFT_ARM, + controller::LEFT_FORE_ARM, + controller::LEFT_HAND, + controller::LEFT_HAND_THUMB2, + controller::LEFT_HAND_THUMB3, + controller::LEFT_HAND_THUMB4, + controller::LEFT_HAND_INDEX1, + controller::LEFT_HAND_INDEX2, + controller::LEFT_HAND_INDEX3, + controller::LEFT_HAND_INDEX4, + controller::LEFT_HAND_MIDDLE1, + controller::LEFT_HAND_MIDDLE2, + controller::LEFT_HAND_MIDDLE3, + controller::LEFT_HAND_MIDDLE4, + controller::LEFT_HAND_RING1, + controller::LEFT_HAND_RING2, + controller::LEFT_HAND_RING3, + controller::LEFT_HAND_RING4, + controller::LEFT_HAND_PINKY1, + controller::LEFT_HAND_PINKY2, + controller::LEFT_HAND_PINKY3, + controller::LEFT_HAND_PINKY4 +}; + +static controller::StandardPoseChannel neuronJointIndexToPoseIndex(NeuronJointIndex i) { + assert(i >= 0 && i < NeuronJointIndex::Size); + if (i >= 0 && i < NeuronJointIndex::Size) { + return neuronJointIndexToPoseIndexMap[i]; + } else { + return (controller::StandardPoseChannel)0; // not sure what to do here, but don't crash! + } } +static const char* controllerJointName(controller::StandardPoseChannel i) { + switch (i) { + case controller::HIPS: return "Hips"; + case controller::RIGHT_UP_LEG: return "RightUpLeg"; + case controller::RIGHT_LEG: return "RightLeg"; + case controller::RIGHT_FOOT: return "RightFoot"; + case controller::LEFT_UP_LEG: return "LeftUpLeg"; + case controller::LEFT_LEG: return "LeftLeg"; + case controller::LEFT_FOOT: return "LeftFoot"; + case controller::SPINE: return "Spine"; + case controller::SPINE1: return "Spine1"; + case controller::SPINE2: return "Spine2"; + case controller::SPINE3: return "Spine3"; + case controller::NECK: return "Neck"; + case controller::HEAD: return "Head"; + case controller::RIGHT_SHOULDER: return "RightShoulder"; + case controller::RIGHT_ARM: return "RightArm"; + case controller::RIGHT_FORE_ARM: return "RightForeArm"; + case controller::RIGHT_HAND: return "RightHand"; + case controller::RIGHT_HAND_THUMB1: return "RightHandThumb1"; + case controller::RIGHT_HAND_THUMB2: return "RightHandThumb2"; + case controller::RIGHT_HAND_THUMB3: return "RightHandThumb3"; + case controller::RIGHT_HAND_THUMB4: return "RightHandThumb4"; + case controller::RIGHT_HAND_INDEX1: return "RightHandIndex1"; + case controller::RIGHT_HAND_INDEX2: return "RightHandIndex2"; + case controller::RIGHT_HAND_INDEX3: return "RightHandIndex3"; + case controller::RIGHT_HAND_INDEX4: return "RightHandIndex4"; + case controller::RIGHT_HAND_MIDDLE1: return "RightHandMiddle1"; + case controller::RIGHT_HAND_MIDDLE2: return "RightHandMiddle2"; + case controller::RIGHT_HAND_MIDDLE3: return "RightHandMiddle3"; + case controller::RIGHT_HAND_MIDDLE4: return "RightHandMiddle4"; + case controller::RIGHT_HAND_RING1: return "RightHandRing1"; + case controller::RIGHT_HAND_RING2: return "RightHandRing2"; + case controller::RIGHT_HAND_RING3: return "RightHandRing3"; + case controller::RIGHT_HAND_RING4: return "RightHandRing4"; + case controller::RIGHT_HAND_PINKY1: return "RightHandPinky1"; + case controller::RIGHT_HAND_PINKY2: return "RightHandPinky2"; + case controller::RIGHT_HAND_PINKY3: return "RightHandPinky3"; + case controller::RIGHT_HAND_PINKY4: return "RightHandPinky4"; + case controller::LEFT_SHOULDER: return "LeftShoulder"; + case controller::LEFT_ARM: return "LeftArm"; + case controller::LEFT_FORE_ARM: return "LeftForeArm"; + case controller::LEFT_HAND: return "LeftHand"; + case controller::LEFT_HAND_THUMB1: return "LeftHandThumb1"; + case controller::LEFT_HAND_THUMB2: return "LeftHandThumb2"; + case controller::LEFT_HAND_THUMB3: return "LeftHandThumb3"; + case controller::LEFT_HAND_THUMB4: return "LeftHandThumb4"; + case controller::LEFT_HAND_INDEX1: return "LeftHandIndex1"; + case controller::LEFT_HAND_INDEX2: return "LeftHandIndex2"; + case controller::LEFT_HAND_INDEX3: return "LeftHandIndex3"; + case controller::LEFT_HAND_INDEX4: return "LeftHandIndex4"; + case controller::LEFT_HAND_MIDDLE1: return "LeftHandMiddle1"; + case controller::LEFT_HAND_MIDDLE2: return "LeftHandMiddle2"; + case controller::LEFT_HAND_MIDDLE3: return "LeftHandMiddle3"; + case controller::LEFT_HAND_MIDDLE4: return "LeftHandMiddle4"; + case controller::LEFT_HAND_RING1: return "LeftHandRing1"; + case controller::LEFT_HAND_RING2: return "LeftHandRing2"; + case controller::LEFT_HAND_RING3: return "LeftHandRing3"; + case controller::LEFT_HAND_RING4: return "LeftHandRing4"; + case controller::LEFT_HAND_PINKY1: return "LeftHandPinky1"; + case controller::LEFT_HAND_PINKY2: return "LeftHandPinky2"; + case controller::LEFT_HAND_PINKY3: return "LeftHandPinky3"; + case controller::LEFT_HAND_PINKY4: return "LeftHandPinky4"; + default: return "???"; + } +} + +// convert between YXZ neuron euler angles in degrees to quaternion +// this is the default setting in the Axis Neuron server. +static quat eulerToQuat(vec3 euler) { + // euler.x and euler.y are swaped, WTF. + glm::vec3 e = glm::vec3(euler.y, euler.x, euler.z) * RADIANS_PER_DEGREE; + return (glm::angleAxis(e.y, Vectors::UNIT_Y) * + glm::angleAxis(e.x, Vectors::UNIT_X) * + glm::angleAxis(e.z, Vectors::UNIT_Z)); +} + +// +// neuronDataReader SDK callback functions +// + // NOTE: must be thread-safe void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx* header, float* data) { @@ -133,7 +283,28 @@ void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx // copy the data memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); + } else { + // enter mutex + std::lock_guard guard(neuronPlugin->_jointsMutex); + + // + // Data is 3 floats per joint: 3 rotation euler angles (degrees) + // + + // resize vector if necessary + const size_t NUM_FLOATS_PER_JOINT = 3; + const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT; + if (neuronPlugin->_joints.size() != NUM_JOINTS) { + neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + } + + assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float))); + + for (int i = 0; i < NUM_JOINTS; i++) { + // TODO: should probably just copy over default positions?! + memcpy(&(neuronPlugin->_joints[i].euler), data, sizeof(float) * NUM_FLOATS_PER_JOINT); + } qCCritical(inputplugins) << "NeruonPlugin: format not supported"; @@ -148,6 +319,9 @@ void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx } } +// I can't even get the SDK to send me a callback. +// BRCommandFetchAvatarDataFromServer & BRRegisterAutoSyncParmeter [sic] don't seem to work. +// So this is totally untested. // NOTE: must be thread-safe static void CommandDataReceivedCallback(void* context, SOCKET_REF sender, CommandPack* pack, void* data) { @@ -196,9 +370,20 @@ static void CommandDataReceivedCallback(void* context, SOCKET_REF sender, Comman // NOTE: must be thread-safe static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, SocketStatus status, char* message) { + // just dump to log, later we might want to pop up a connection lost dialog or attempt to reconnect. qCDebug(inputplugins) << "NeuronPlugin: socket status = " << message; } +// +// NeuronPlugin +// + +bool NeuronPlugin::isSupported() const { + // Because it's a client/server network architecture, we can't tell + // if the neuron is actually connected until we connect to the server. + return true; +} + void NeuronPlugin::activate() { InputPlugin::activate(); @@ -206,28 +391,26 @@ void NeuronPlugin::activate() { auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); - qCDebug(inputplugins) << "NeuronPlugin::activate"; - // register c-style callbacks BRRegisterFrameDataCallback((void*)this, FrameDataReceivedCallback); BRRegisterCommandDataCallback((void*)this, CommandDataReceivedCallback); BRRegisterSocketStatusCallback((void*)this, SocketStatusChangedCallback); - // TODO: pull these from prefs! + // TODO: Pull these from prefs dialog? + // localhost is fine for now. _serverAddress = "localhost"; - _serverPort = 7001; + _serverPort = 7001; // default port for TCP Axis Neuron server. + _socketRef = BRConnectTo((char*)_serverAddress.c_str(), _serverPort); if (!_socketRef) { // error - qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << "error = " << BRGetLastErrorMessage(); + qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << ", error = " << BRGetLastErrorMessage(); } else { qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort; } } void NeuronPlugin::deactivate() { - qCDebug(inputplugins) << "NeuronPlugin::deactivate"; - // unregister from userInputMapper if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); @@ -241,126 +424,35 @@ void NeuronPlugin::deactivate() { InputPlugin::deactivate(); } -// convert between euler in degrees to quaternion -static quat eulerToQuat(vec3 euler) { - // euler.x and euler.y are swaped (thanks NOMICOM!) - glm::vec3 e = glm::vec3(euler.y, euler.x, euler.z) * RADIANS_PER_DEGREE; - return (glm::angleAxis(e.y, Vectors::UNIT_Y) * glm::angleAxis(e.x, Vectors::UNIT_X) * glm::angleAxis(e.z, Vectors::UNIT_Z)); -} - void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) { - std::vector joints; - // copy the shared data { + // lock and copy std::lock_guard guard(_jointsMutex); joints = _joints; } - - /* - DebugDraw::getInstance().addMyAvatarMarker("LEFT_FOOT", - eulerToQuat(joints[6].euler), - joints[6].pos / 100.0f, - glm::vec4(1)); - */ _inputDevice->update(deltaTime, joints, _prevJoints); _prevJoints = joints; } void NeuronPlugin::saveSettings() const { InputPlugin::saveSettings(); - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::saveSettings"; } void NeuronPlugin::loadSettings() { InputPlugin::loadSettings(); - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::loadSettings"; } // // InputDevice // -static controller::StandardPoseChannel neuronJointIndexToPoseIndex(JointIndex i) { - // Currently they are the same. - // but that won't always be the case... - return (controller::StandardPoseChannel)i; -} - -static const char* neuronJointName(JointIndex i) { - switch (i) { - case Hips: return "Hips"; - case RightUpLeg: return "RightUpLeg"; - case RightLeg: return "RightLeg"; - case RightFoot: return "RightFoot"; - case LeftUpLeg: return "LeftUpLeg"; - case LeftLeg: return "LeftLeg"; - case LeftFoot: return "LeftFoot"; - case Spine: return "Spine"; - case Spine1: return "Spine1"; - case Spine2: return "Spine2"; - case Spine3: return "Spine3"; - case Neck: return "Neck"; - case Head: return "Head"; - case RightShoulder: return "RightShoulder"; - case RightArm: return "RightArm"; - case RightForeArm: return "RightForeArm"; - case RightHand: return "RightHand"; - case RightHandThumb1: return "RightHandThumb1"; - case RightHandThumb2: return "RightHandThumb2"; - case RightHandThumb3: return "RightHandThumb3"; - case RightInHandIndex: return "RightInHandIndex"; - case RightHandIndex1: return "RightHandIndex1"; - case RightHandIndex2: return "RightHandIndex2"; - case RightHandIndex3: return "RightHandIndex3"; - case RightInHandMiddle: return "RightInHandMiddle"; - case RightHandMiddle1: return "RightHandMiddle1"; - case RightHandMiddle2: return "RightHandMiddle2"; - case RightHandMiddle3: return "RightHandMiddle3"; - case RightInHandRing: return "RightInHandRing"; - case RightHandRing1: return "RightHandRing1"; - case RightHandRing2: return "RightHandRing2"; - case RightHandRing3: return "RightHandRing3"; - case RightInHandPinky: return "RightInHandPinky"; - case RightHandPinky1: return "RightHandPinky1"; - case RightHandPinky2: return "RightHandPinky2"; - case RightHandPinky3: return "RightHandPinky3"; - case LeftShoulder: return "LeftShoulder"; - case LeftArm: return "LeftArm"; - case LeftForeArm: return "LeftForeArm"; - case LeftHand: return "LeftHand"; - case LeftHandThumb1: return "LeftHandThumb1"; - case LeftHandThumb2: return "LeftHandThumb2"; - case LeftHandThumb3: return "LeftHandThumb3"; - case LeftInHandIndex: return "LeftInHandIndex"; - case LeftHandIndex1: return "LeftHandIndex1"; - case LeftHandIndex2: return "LeftHandIndex2"; - case LeftHandIndex3: return "LeftHandIndex3"; - case LeftInHandMiddle: return "LeftInHandMiddle"; - case LeftHandMiddle1: return "LeftHandMiddle1"; - case LeftHandMiddle2: return "LeftHandMiddle2"; - case LeftHandMiddle3: return "LeftHandMiddle3"; - case LeftInHandRing: return "LeftInHandRing"; - case LeftHandRing1: return "LeftHandRing1"; - case LeftHandRing2: return "LeftHandRing2"; - case LeftHandRing3: return "LeftHandRing3"; - case LeftInHandPinky: return "LeftInHandPinky"; - case LeftHandPinky1: return "LeftHandPinky1"; - case LeftHandPinky2: return "LeftHandPinky2"; - case LeftHandPinky3: return "LeftHandPinky3"; - default: return "???"; - } -} - controller::Input::NamedVector NeuronPlugin::InputDevice::getAvailableInputs() const { - // TODO: static controller::Input::NamedVector availableInputs; - if (availableInputs.size() == 0) { - for (int i = 0; i < JointIndex::Size; i++) { - availableInputs.push_back(makePair(neuronJointIndexToPoseIndex((JointIndex)i), neuronJointName((JointIndex)i))); + for (int i = 0; i < controller::NUM_STANDARD_POSES; i++) { + auto channel = static_cast(i); + availableInputs.push_back(makePair(channel, controllerJointName(channel))); } }; return availableInputs; @@ -373,21 +465,21 @@ QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector& joints, const std::vector& prevJoints) { for (int i = 0; i < joints.size(); i++) { - int poseIndex = neuronJointIndexToPoseIndex((JointIndex)i); glm::vec3 linearVel, angularVel; glm::vec3 pos = (joints[i].pos * METERS_PER_CENTIMETER); glm::quat rot = eulerToQuat(joints[i].euler); if (i < prevJoints.size()) { - linearVel = (pos - (prevJoints[i].pos * METERS_PER_CENTIMETER)) / deltaTime; - // quat log imag part points along the axis of rotation, and it's length will be the half angle. + linearVel = (pos - (prevJoints[i].pos * METERS_PER_CENTIMETER)) / deltaTime; // m/s + // quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation. glm::quat d = glm::log(rot * glm::inverse(eulerToQuat(prevJoints[i].euler))); - angularVel = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); + angularVel = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s } + int poseIndex = neuronJointIndexToPoseIndex((NeuronJointIndex)i); _poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVel, angularVel); } -} -void NeuronPlugin::InputDevice::focusOutEvent() { - // TODO: - qCDebug(inputplugins) << "NeuronPlugin::InputDevice::focusOutEvent"; + // fill in missing thumb joints with identity. + // TODO: need a standard displacement + _poseStateMap[controller::RIGHT_HAND_THUMB1] = controller::Pose(glm::vec3(), glm::quat(), glm::vec3(), glm::vec3()); + _poseStateMap[controller::LEFT_HAND_THUMB1] = controller::Pose(glm::vec3(), glm::quat(), glm::vec3(), glm::vec3()); } diff --git a/plugins/hifiNeuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h index f787838ce2..c85a5dd383 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -57,7 +57,7 @@ protected: virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; virtual void update(float deltaTime, bool jointsCaptured) override {}; - virtual void focusOutEvent() override; + virtual void focusOutEvent() override {}; void update(float deltaTime, const std::vector& joints, const std::vector& prevJoints); }; @@ -71,9 +71,13 @@ protected: int _serverPort; void* _socketRef; - std::vector _joints; - std::mutex _jointsMutex; // used to guard access to _joints + // used to guard multi-threaded access to _joints + std::mutex _jointsMutex; + // copy of data directly from the NeuronDataReader SDK + std::vector _joints; + + // one frame old copy of _joints, used to caluclate angular and linear velocity. std::vector _prevJoints; }; From fd3eed3d4132e58ff5e58284ac57e1feb9ad9ae1 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Sat, 26 Dec 2015 11:50:54 -0800 Subject: [PATCH 67/86] NeruonPlugin: translation support Warn if translations are not present. --- examples/controllers/neuron/neuronAvatar.js | 10 +- plugins/hifiNeuron/src/NeuronPlugin.cpp | 100 +++++++++++++++----- 2 files changed, 85 insertions(+), 25 deletions(-) diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js index cc53987f05..a124316f24 100644 --- a/examples/controllers/neuron/neuronAvatar.js +++ b/examples/controllers/neuron/neuronAvatar.js @@ -62,6 +62,8 @@ var JOINT_PARENT_MAP = { LeftHandPinky: "LeftHandPinky3", }; +var USE_TRANSLATIONS = false; + function dumpHardwareMapping() { Object.keys(Controller.Hardware).forEach(function (deviceName) { Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { @@ -152,8 +154,12 @@ NeuronAvatar.prototype.update = function (deltaTime) { // Then we transform back into joint local by multiplying by the inverse of the parents absolute rotation. MyAvatar.setJointRotation(j, Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot))); - // TODO: - //MyAvatar.setJointTranslation(j, Vec3.multiply(100.0, pose.translation)); + // translation proportions might be different from the neuron avatar and the user avatar skeleton. + // so this is disabled by default + if (USE_TRANSLATIONS) { + var localTranslation = Vec3.multiplyQbyV(Quat.inverse(parentDefaultAbsRot), pose.translation); + MyAvatar.setJointTranslation(j, localTranslation); + } } } }; diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 4edda64c22..0e338c30f8 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -159,6 +159,73 @@ static controller::StandardPoseChannel neuronJointIndexToPoseIndexMap[NeuronJoin controller::LEFT_HAND_PINKY4 }; +// in rig frame +static glm::vec3 rightHandThumb1DefaultAbsTranslation(-2.155500650405884, -0.7610001564025879, 2.685631036758423); +static glm::vec3 leftHandThumb1DefaultAbsTranslation(2.1555817127227783, -0.7603635787963867, 2.6856393814086914); + +// default translations (cm) +static glm::vec3 neuronJointTranslations[NeuronJointIndex::Size] = { + {131.901, 95.6602, -27.9815}, + {-9.55907, -1.58772, 0.0760284}, + {0.0144232, -41.4683, -0.105322}, + {1.59348, -41.5875, -0.557237}, + {9.72077, -1.68926, -0.280643}, + {0.0886684, -43.1586, -0.0111596}, + {-2.98473, -44.0517, 0.0694456}, + {0.110967, 16.3959, 0.140463}, + {0.0500451, 10.0238, 0.0731921}, + {0.061568, 10.4352, 0.0583075}, + {0.0500606, 10.0217, 0.0711083}, + {0.0317731, 10.7176, 0.0779325}, + {-0.0204253, 9.71067, 0.131734}, + {-3.24245, 7.13584, 0.185638}, + {-13.0885, -0.0877601, 0.176065}, + {-27.2674, 0.0688724, 0.0272146}, + {-26.7673, 0.0301916, 0.0102847}, + {-2.56017, 0.195537, 3.20968}, + {-3.78796, 0, 0}, + {-2.63141, 0, 0}, + {-3.31579, 0.522947, 2.03495}, + {-5.36589, -0.0939789, 1.02771}, + {-3.72278, 0, 0}, + {-2.11074, 0, 0}, + {-3.47874, 0.532042, 0.778358}, + {-5.32194, -0.0864, 0.322863}, + {-4.06232, 0, 0}, + {-2.54653, 0, 0}, + {-3.46131, 0.553263, -0.132632}, + {-4.76716, -0.0227368, -0.492632}, + {-3.54073, 0, 0}, + {-2.45634, 0, 0}, + {-3.25137, 0.482779, -1.23613}, + {-4.25937, -0.0227368, -1.12168}, + {-2.83528, 0, 0}, + {-1.79166, 0, 0}, + {3.25624, 7.13148, -0.131575}, + {13.149, -0.052598, -0.125076}, + {27.2903, 0.00282644, -0.0181535}, + {26.6602, 0.000969969, -0.0487599}, + {2.56017, 0.195537, 3.20968}, + {3.78796, 0, 0}, + {2.63141, 0, 0}, + {3.31579, 0.522947, 2.03495}, + {5.36589, -0.0939789, 1.02771}, + {3.72278, 0, 0}, + {2.11074, 0, 0}, + {3.47874, 0.532042, 0.778358}, + {5.32194, -0.0864, 0.322863}, + {4.06232, 0, 0}, + {2.54653, 0, 0}, + {3.46131, 0.553263, -0.132632}, + {4.76716, -0.0227368, -0.492632}, + {3.54073, 0, 0}, + {2.45634, 0, 0}, + {3.25137, 0.482779, -1.23613}, + {4.25937, -0.0227368, -1.12168}, + {2.83528, 0, 0}, + {1.79166, 0, 0} +}; + static controller::StandardPoseChannel neuronJointIndexToPoseIndex(NeuronJointIndex i) { assert(i >= 0 && i < NeuronJointIndex::Size); if (i >= 0 && i < NeuronJointIndex::Size) { @@ -285,31 +352,20 @@ void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); } else { + qCWarning(inputplugins) << "NeuronPlugin: unsuported binary format, please enable displacements"; + // enter mutex std::lock_guard guard(neuronPlugin->_jointsMutex); - // - // Data is 3 floats per joint: 3 rotation euler angles (degrees) - // - - // resize vector if necessary - const size_t NUM_FLOATS_PER_JOINT = 3; - const size_t NUM_JOINTS = header->DataCount / NUM_FLOATS_PER_JOINT; - if (neuronPlugin->_joints.size() != NUM_JOINTS) { - neuronPlugin->_joints.resize(NUM_JOINTS, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + if (neuronPlugin->_joints.size() != NeuronJointIndex::Size) { + neuronPlugin->_joints.resize(NeuronJointIndex::Size, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); } - assert(sizeof(NeuronPlugin::NeuronJoint) == (NUM_FLOATS_PER_JOINT * sizeof(float))); - - for (int i = 0; i < NUM_JOINTS; i++) { - // TODO: should probably just copy over default positions?! - memcpy(&(neuronPlugin->_joints[i].euler), data, sizeof(float) * NUM_FLOATS_PER_JOINT); + for (int i = 0; i < NeuronJointIndex::Size; i++) { + neuronPlugin->_joints[i].euler = glm::vec3(); + neuronPlugin->_joints[i].pos = neuronJointTranslations[i]; } - - qCCritical(inputplugins) << "NeruonPlugin: format not supported"; - } - } else { static bool ONCE = false; if (!ONCE) { @@ -466,7 +522,7 @@ QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector& joints, const std::vector& prevJoints) { for (int i = 0; i < joints.size(); i++) { glm::vec3 linearVel, angularVel; - glm::vec3 pos = (joints[i].pos * METERS_PER_CENTIMETER); + glm::vec3 pos = joints[i].pos; glm::quat rot = eulerToQuat(joints[i].euler); if (i < prevJoints.size()) { linearVel = (pos - (prevJoints[i].pos * METERS_PER_CENTIMETER)) / deltaTime; // m/s @@ -478,8 +534,6 @@ void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector Date: Sat, 26 Dec 2015 12:13:29 -0800 Subject: [PATCH 68/86] NeuronPlugin: register for combination mode This will tell us, if user is using arms, upper-body or full body configurations. --- plugins/hifiNeuron/src/NeuronPlugin.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 0e338c30f8..97131a0a87 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -463,6 +463,8 @@ void NeuronPlugin::activate() { qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << ", error = " << BRGetLastErrorMessage(); } else { qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort; + + BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode); } } @@ -474,6 +476,7 @@ void NeuronPlugin::deactivate() { } if (_socketRef) { + BRUnregisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode); BRCloseSocket(_socketRef); } From 1f834a9c01f3343368cc89f1df87c4f1e7e6c957 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Sun, 27 Dec 2015 16:29:30 -0800 Subject: [PATCH 69/86] neruonAvatar.js: attempt to adjust hips for HMD mode. It attempts to adjust the hips so that the avatar's head is at the same location & orientation as the HMD in world space, however it's fighting with the internal c++ code that also attempts to adjust the hips as well. --- examples/controllers/neuron/neuronAvatar.js | 78 ++++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js index a124316f24..abd51f2990 100644 --- a/examples/controllers/neuron/neuronAvatar.js +++ b/examples/controllers/neuron/neuronAvatar.js @@ -64,6 +64,27 @@ var JOINT_PARENT_MAP = { var USE_TRANSLATIONS = false; +// ctor +function Xform(rot, pos) { + this.rot = rot; + this.pos = pos; +}; +Xform.mul = function (lhs, rhs) { + var rot = Quat.multiply(lhs.rot, rhs.rot); + var pos = Vec3.sum(lhs.pos, Vec3.multiplyQbyV(lhs.rot, rhs.pos)); + return new Xform(rot, pos); +}; +Xform.prototype.inv = function () { + var invRot = Quat.inverse(this.rot); + var invPos = Vec3.multiply(-1, this.pos); + return new Xform(invRot, Vec3.multiplyQbyV(invRot, invPos)); +}; +Xform.prototype.toString = function () { + var rot = this.rot; + var pos = this.pos; + return "Xform rot = (" + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "), pos = (" + pos.x + ", " + pos.y + ", " + pos.z + ")"; +}; + function dumpHardwareMapping() { Object.keys(Controller.Hardware).forEach(function (deviceName) { Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { @@ -108,19 +129,19 @@ NeuronAvatar.prototype.activate = function () { this._active = true; // build absDefaultPoseMap - this._absDefaultRotMap = {}; - this._absDefaultRotMap[""] = {x: 0, y: 0, z: 0, w: 1}; + this._defaultAbsRotMap = {}; + this._defaultAbsPosMap = {}; + this._defaultAbsRotMap[""] = {x: 0, y: 0, z: 0, w: 1}; + this._defaultAbsPosMap[""] = {x: 0, y: 0, z: 0}; var keys = Object.keys(JOINT_PARENT_MAP); var i, l = keys.length; for (i = 0; i < l; i++) { var jointName = keys[i]; var j = MyAvatar.getJointIndex(jointName); var parentJointName = JOINT_PARENT_MAP[jointName]; - if (parentJointName === "") { - this._absDefaultRotMap[jointName] = MyAvatar.getDefaultJointRotation(j); - } else { - this._absDefaultRotMap[jointName] = Quat.multiply(this._absDefaultRotMap[parentJointName], MyAvatar.getDefaultJointRotation(j)); - } + this._defaultAbsRotMap[jointName] = Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointRotation(j)); + this._defaultAbsPosMap[jointName] = Vec3.sum(this._defaultAbsPosMap[parentJointName], + Quat.multiply(this._defaultAbsRotMap[parentJointName], MyAvatar.getDefaultJointTranslation(j))); } }; @@ -134,10 +155,16 @@ NeuronAvatar.prototype.deactivate = function () { }; NeuronAvatar.prototype.update = function (deltaTime) { + + var hmdActive = HMD.active; + var hmdXform = new Xform(HMD.orientation, HMD.position); + var keys = Object.keys(JOINT_PARENT_MAP); var i, l = keys.length; var absDefaultRot = {}; var jointName, channel, pose, parentJointName, j, parentDefaultAbsRot; + var localRotations = {}; + var localTranslations = {}; for (i = 0; i < l; i++) { var jointName = keys[i]; var channel = Controller.Hardware.Neuron[jointName]; @@ -145,23 +172,54 @@ NeuronAvatar.prototype.update = function (deltaTime) { pose = Controller.getPoseValue(channel); parentJointName = JOINT_PARENT_MAP[jointName]; j = MyAvatar.getJointIndex(jointName); - defaultAbsRot = this._absDefaultRotMap[jointName]; - parentDefaultAbsRot = this._absDefaultRotMap[parentJointName]; + defaultAbsRot = this._defaultAbsRotMap[jointName]; + parentDefaultAbsRot = this._defaultAbsRotMap[parentJointName]; // Rotations from the neuron controller are in world orientation but are delta's from the default pose. // So first we build the absolute rotation of the default pose (local into world). // Then apply the rotation from the controller, in world space. // Then we transform back into joint local by multiplying by the inverse of the parents absolute rotation. - MyAvatar.setJointRotation(j, Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot))); + var localRotation = Quat.multiply(Quat.inverse(parentDefaultAbsRot), Quat.multiply(pose.rotation, defaultAbsRot)); + if (!hmdActive || jointName !== "Hips") { + MyAvatar.setJointRotation(j, localRotation); + } + localRotations[jointName] = localRotation; // translation proportions might be different from the neuron avatar and the user avatar skeleton. // so this is disabled by default if (USE_TRANSLATIONS) { var localTranslation = Vec3.multiplyQbyV(Quat.inverse(parentDefaultAbsRot), pose.translation); MyAvatar.setJointTranslation(j, localTranslation); + localTranslations[jointName] = localTranslation; + } else { + localTranslations[jointName] = MyAvatar.getJointTranslation(j); } } } + + // TODO: Currrently does not work. + // it attempts to adjust the hips so that the avatar's head is at the same location & oreintation as the HMD. + // however it's fighting with the internal c++ code that also attempts to adjust the hips. + if (hmdActive) { + + var y180Xform = new Xform({x: 0, y: 1, z: 0, w: 0}, {x: 0, y: 0, z: 0}); + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var headXform = new Xform(localRotations["Head"], localTranslations["Head"]); + + // transform eyes down the heirarchy chain into avatar space. + var hierarchy = ["Neck", "Spine3", "Spine2", "Spine1", "Spine"]; + var i, l = hierarchy.length; + for (i = 0; i < l; i++) { + var xform = new Xform(localRotations[hierarchy[i]], localTranslations[hierarchy[i]]); + headXform = Xform.mul(xform, headXform); + } + + // solve for the offset that will put the eyes at the hmd position & orientation. + var hipsXform = Xform.mul(y180Xform, Xform.mul(avatarXform.inv(), Xform.mul(hmdXform, Xform.mul(y180Xform, headXform.inv())))); + + MyAvatar.setJointRotation("Hips", hipsXform.rot); + MyAvatar.setJointTranslation("Hips", hipsXform.pos); + } }; var neuronAvatar = new NeuronAvatar(); From d4fd59edc0299101599959f7e7732535808f1b41 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 28 Dec 2015 13:49:00 -0800 Subject: [PATCH 70/86] send blacklist messages --- examples/light_modifier/lightModifier.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index dc9332405e..3ead989d6d 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -17,7 +17,7 @@ var VERTICAL_SLIDERS = false; var SHOW_OVERLAYS = true; var SHOW_LIGHT_VOLUME = true; var USE_PARENTED_PANEL = true; -var VISIBLE_PANEL = false; +var VISIBLE_PANEL = true; var USE_LABELS = true; var LEFT_LABELS = false; var RIGHT_LABELS = true; @@ -586,6 +586,8 @@ function createVisiblePanel() { } var panel = Entities.addEntity(panelProperties); + var data = {action:'add', id:panel}; + Messages.sendMessage ('Hifi-Hand-RayPick-Blacklist',JSON.stringify(data)) return panel } @@ -821,7 +823,12 @@ function cleanup(fromMessage) { Entities.deleteEntity(panel); Entities.deleteEntity(visiblePanel); - + var data = { + action: 'remove', + id: visiblePanel + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)) + selectionManager.clearSelections(); Script.update.disconnect(rotateCloseButtons); if (hasParent === false) { From 36beea17fabf6e0829e21ba580d84d03890df788 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 28 Dec 2015 14:59:43 -0800 Subject: [PATCH 71/86] Improved hand controller reticle movement --- .../controllers/reticleHandRotationTest.js | 192 +++--------------- 1 file changed, 24 insertions(+), 168 deletions(-) diff --git a/examples/controllers/reticleHandRotationTest.js b/examples/controllers/reticleHandRotationTest.js index ece9283deb..a303e5e7b4 100644 --- a/examples/controllers/reticleHandRotationTest.js +++ b/examples/controllers/reticleHandRotationTest.js @@ -22,33 +22,13 @@ function length(posA, posB) { return length; } -var EXPECTED_CHANGE = 50; -var lastPos = Controller.getReticlePosition(); function moveReticleAbsolute(x, y) { var globalPos = Controller.getReticlePosition(); - var dX = x - globalPos.x; - var dY = y - globalPos.y; - - // some debugging to see if position is jumping around on us... - var distanceSinceLastMove = length(lastPos, globalPos); - if (distanceSinceLastMove > EXPECTED_CHANGE) { - debugPrint("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); - } - - if (Math.abs(dX) > EXPECTED_CHANGE) { - debugPrint("surpressing unexpectedly large change dX:" + dX + "----------------------------"); - } - if (Math.abs(dY) > EXPECTED_CHANGE) { - debugPrint("surpressing unexpectedly large change dY:" + dY + "----------------------------"); - } - globalPos.x = x; globalPos.y = y; Controller.setReticlePosition(globalPos); - lastPos = globalPos; } - var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation"; var mapping = Controller.newMapping(MAPPING_NAME); mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick); @@ -56,8 +36,6 @@ mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.A mapping.enable(); -var lastRotatedLeft = Vec3.UNIT_NEG_Y; -var lastRotatedRight = Vec3.UNIT_NEG_Y; function debugPrint(message) { if (DEBUGGING) { @@ -65,24 +43,9 @@ function debugPrint(message) { } } -var MAX_WAKE_UP_DISTANCE = 0.005; -var MIN_WAKE_UP_DISTANCE = 0.001; -var INITIAL_WAKE_UP_DISTANCE = MIN_WAKE_UP_DISTANCE; -var INCREMENTAL_WAKE_UP_DISTANCE = 0.001; - -var MAX_SLEEP_DISTANCE = 0.0004; -var MIN_SLEEP_DISTANCE = 0.00001; //0.00002; -var INITIAL_SLEEP_DISTANCE = MIN_SLEEP_DISTANCE; -var INCREMENTAL_SLEEP_DISTANCE = 0.000002; // 0.00002; - -var leftAsleep = true; -var rightAsleep = true; - -var leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; -var rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; - -var leftSleepDistance = INITIAL_SLEEP_DISTANCE; -var rightSleepDistance = INITIAL_SLEEP_DISTANCE; +var leftRightBias = 0.0; +var filteredRotatedLeft = Vec3.UNIT_NEG_Y; +var filteredRotatedRight = Vec3.UNIT_NEG_Y; Script.update.connect(function(deltaTime) { @@ -96,153 +59,46 @@ Script.update.connect(function(deltaTime) { var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y); var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y); - var suppressRight = false; - var suppressLeft = false; - - // What I really want to do is to slowly increase the epsilon you have to move it - // to wake up, the longer you go without moving it - var leftDistance = Vec3.distance(rotatedLeft, lastRotatedLeft); - var rightDistance = Vec3.distance(rotatedRight, lastRotatedRight); - - // check to see if hand should wakeup or sleep - if (leftAsleep) { - if (leftDistance > leftWakeUpDistance) { - leftAsleep = false; - leftSleepDistance = INITIAL_SLEEP_DISTANCE; - leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; - } else { - // grow the wake up distance to make it harder to wake up - leftWakeUpDistance = Math.min(leftWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE); - } - } else { - // we are awake, determine if we should fall asleep, if we haven't moved - // at least as much as our sleep distance then we sleep - if (leftDistance < leftSleepDistance) { - leftAsleep = true; - leftSleepDistance = INITIAL_SLEEP_DISTANCE; - leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; - } else { - // if we moved more than the sleep amount, but we moved less than the max sleep - // amount, then increase our liklihood of sleep. - if (leftDistance < MAX_SLEEP_DISTANCE) { - print("growing sleep...."); - leftSleepDistance = Math.max(leftSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE); - } else { - // otherwise reset it to initial - leftSleepDistance = INITIAL_SLEEP_DISTANCE; - } - } - } - if (leftAsleep) { - suppressLeft = true; - debugPrint("suppressing left not moving enough"); - } - - // check to see if hand should wakeup or sleep - if (rightAsleep) { - if (rightDistance > rightWakeUpDistance) { - rightAsleep = false; - rightSleepDistance = INITIAL_SLEEP_DISTANCE; - rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; - } else { - // grow the wake up distance to make it harder to wake up - rightWakeUpDistance = Math.min(rightWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE); - } - } else { - // we are awake, determine if we should fall asleep, if we haven't moved - // at least as much as our sleep distance then we sleep - if (rightDistance < rightSleepDistance) { - rightAsleep = true; - rightSleepDistance = INITIAL_SLEEP_DISTANCE; - rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; - } else { - // if we moved more than the sleep amount, but we moved less than the max sleep - // amount, then increase our liklihood of sleep. - if (rightDistance < MAX_SLEEP_DISTANCE) { - print("growing sleep...."); - rightSleepDistance = Math.max(rightSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE); - } else { - // otherwise reset it to initial - rightSleepDistance = INITIAL_SLEEP_DISTANCE; - } - } - } - if (rightAsleep) { - suppressRight = true; - debugPrint("suppressing right not moving enough"); - } - - // check to see if hand is on base station - if (Vec3.equal(rotatedLeft, Vec3.UNIT_NEG_Y)) { - suppressLeft = true; - debugPrint("suppressing left on base station"); - } - if (Vec3.equal(rotatedRight, Vec3.UNIT_NEG_Y)) { - suppressRight = true; - debugPrint("suppressing right on base station"); - } - - // Keep track of last rotations, to detect resting (but not on base station hands) in the future - lastRotatedLeft = rotatedLeft; lastRotatedRight = rotatedRight; - if (suppressLeft && suppressRight) { - debugPrint("both hands suppressed bail out early"); - return; + + // Decide which hand should be controlling the pointer + // by comparing which one is moving more, and by + // tending to stay with the one moving more. + var BIAS_ADJUST_RATE = 0.5; + var BIAS_ADJUST_DEADZONE = 0.05; + leftRightBias += (Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity)) * BIAS_ADJUST_RATE; + if (leftRightBias < BIAS_ADJUST_DEADZONE) { + leftRightBias = 0.0; + } else if (leftRightBias > (1.0 - BIAS_ADJUST_DEADZONE)) { + leftRightBias = 1.0; } - if (suppressLeft) { - debugPrint("right only"); - rotatedLeft = rotatedRight; - } - if (suppressRight) { - debugPrint("left only"); - rotatedRight = rotatedLeft; - } - - // Average the two hand positions, if either hand is on base station, the - // other hand becomes the only used hand and the average is the hand in use - var rotated = Vec3.multiply(Vec3.sum(rotatedRight,rotatedLeft), 0.5); - - if (DEBUGGING) { - Vec3.print("rotatedRight:", rotatedRight); - Vec3.print("rotatedLeft:", rotatedLeft); - Vec3.print("rotated:", rotated); - } + // Velocity filter the hand rotation used to position reticle so that it is easier to target small things with the hand controllers + var VELOCITY_FILTER_GAIN = 1.0; + filteredRotatedLeft = Vec3.mix(filteredRotatedLeft, rotatedLeft, Math.clamp(Vec3.length(poseLeft.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0)); + filteredRotatedRight = Vec3.mix(filteredRotatedRight, rotatedRight, Math.clamp(Vec3.length(poseRight.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0)); + var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, leftRightBias); var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again... var absoluteYaw = -rotated.x; // from -1 left to 1 right - if (DEBUGGING) { - print("absolutePitch:" + absolutePitch); - print("absoluteYaw:" + absoluteYaw); - Vec3.print("rotated:", rotated); - } - var ROTATION_BOUND = 0.6; var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND); var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND); - if (DEBUGGING) { - print("clampYaw:" + clampYaw); - print("clampPitch:" + clampPitch); - } // using only from -ROTATION_BOUND to ROTATION_BOUND var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND); var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND); - if (DEBUGGING) { - print("xRatio:" + xRatio); - print("yRatio:" + yRatio); - } - var x = screenSizeX * xRatio; var y = screenSizeY * yRatio; - if (DEBUGGING) { - print("position x:" + x + " y:" + y); - } - if (!(xRatio == 0.5 && yRatio == 0)) { + // don't move the reticle with the hand controllers unless the controllers are actually being moved + var MINIMUM_CONTROLLER_ANGULAR_VELOCITY = 0.0001; + var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - leftRightBias) + Vec3.length(poseRight.angularVelocity) * leftRightBias; + + if (!(xRatio == 0.5 && yRatio == 0) && (angularVelocityMagnitude > MINIMUM_CONTROLLER_ANGULAR_VELOCITY)) { moveReticleAbsolute(x, y); } }); From 5786789a665f4e271084131f1a0124b47ada99f0 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 28 Dec 2015 15:12:29 -0800 Subject: [PATCH 72/86] cleanup cleanup --- examples/light_modifier/lightModifier.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 08271fa7a8..eff373d3d7 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -636,7 +636,7 @@ function createCloseButton(axisStart) { modelURL: CLOSE_BUTTON_MODEL_URL, dimensions: CLOSE_BUTTON_DIMENSIONS, position: Vec3.sum(position, VERTICAL_OFFFSET), - rotation: Quat.multiply(avatarRot,Quat.fromPitchYawRollDegrees(90, 0, 45)), + rotation: Quat.multiply(avatarRot, Quat.fromPitchYawRollDegrees(90, 0, 45)), //rotation: Quat.fromPitchYawRollDegrees(0, 0, 90), collisionsWillMove: false, ignoreForCollisions: true, @@ -652,8 +652,8 @@ function createCloseButton(axisStart) { closeButtons.push(button); - if(ROTATE_CLOSE_BUTTON===true){ - Script.update.connect(rotateCloseButtons); + if (ROTATE_CLOSE_BUTTON === true) { + Script.update.connect(rotateCloseButtons); } } @@ -742,7 +742,7 @@ function handleLightOverlayRayCheckMessages(channel, message, sender) { var lightID = doesIntersect.entityID; if (currentLight === lightID) { - // print('ALREADY HAVE A BLOCK, EXIT') + // print('ALREADY HAVE A BLOCK, EXIT') return; } @@ -833,17 +833,17 @@ function cleanup(fromMessage) { Entities.deleteEntity(panel); Entities.deleteEntity(visiblePanel); - var data = { - action: 'remove', - id: visiblePanel - }; - Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)) selectionManager.clearSelections(); - Script.update.disconnect(rotateCloseButtons); + + if (ROTATE_CLOSE_BUTTON === true) { + Script.update.disconnect(rotateCloseButtons); + } + if (hasParent === false) { Entities.deleteEntity(block); } + oldParent = null; hasParent = false; currentLight = null; From 9f342ba8ce14661a130ed2635a31e5130057d8e7 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 28 Dec 2015 15:14:59 -0800 Subject: [PATCH 73/86] readme --- examples/light_modifier/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md index 1358fbe917..f23f22148a 100644 --- a/examples/light_modifier/README.md +++ b/examples/light_modifier/README.md @@ -8,11 +8,12 @@ To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js To reset, I recommend stopping all scripts then re-loading lightLoader.js When you run the lightLoader.js script, several scripts will be loaded: -- handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end) +- handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end, will support blacklisting of entities for raypicking during search for objects) - lightModifier.js (listens for message to create sliders for a given light. will start with slider set to the light's initial properties) - lightModifierTestScene.js (creates a light) - slider.js (attached to each slider entity) - lightParent.js (attached to a 3d model of a light, to which a light is parented, so you can move it around. or keep the current parent if a light already has a parent) +- visiblePanel.js (the transparent panel) - closeButton.js (for closing the ui) - ../libraries/lightOverlayManager.js (shows 2d overlays for lights in the world) - ../libraries/entitySelectionTool.js (visualizes volume of the lights) From c266a093e026cf4d60cd3a98f64ffc76474e587b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 28 Dec 2015 15:17:00 -0800 Subject: [PATCH 74/86] clear sliders at cleanup --- examples/light_modifier/lightModifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index eff373d3d7..52d9342464 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -847,7 +847,7 @@ function cleanup(fromMessage) { oldParent = null; hasParent = false; currentLight = null; - + sliders = []; } From 2ec712a5db30b2c847fb8da5e02f8770f99ceece Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 28 Dec 2015 15:27:17 -0800 Subject: [PATCH 75/86] disable near grabbing on sliders --- examples/controllers/handControllerGrab.js | 35 +++++++++++++++++----- examples/light_modifier/lightModifier.js | 3 +- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 50fdf3353c..9652132c08 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -788,14 +788,19 @@ function MyController(hand) { } else { intersection = Entities.findRayIntersection(pickRayBacked, true); } - + if (intersection.intersects) { - + // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + var defaultDisableNearGrabData = { + disableNearGrab: false + }; + //sometimes we want things to stay right where they are when we let go. + var disableNearGrabData = getEntityCustomData('handControllerKey', intersection.entityID, defaultDisableNearGrabData); if (intersection.properties.name == "Grab Debug Entity") { continue; @@ -817,7 +822,11 @@ function MyController(hand) { } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; if (this.state == STATE_SEARCHING) { - this.setState(STATE_NEAR_GRABBING); + if (disableNearGrabData.disableNearGrab !== true) { + this.setState(STATE_NEAR_GRABBING); + } else { + //disable near grab on this thing + } } else { // equipping if (typeof grabbableData.spatialKey !== 'undefined') { // TODO @@ -940,7 +949,18 @@ function MyController(hand) { this.setState(STATE_NEAR_TRIGGER); return; } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + var defaultDisableNearGrabData = { + disableNearGrab: false + }; + //sometimes we want things to stay right where they are when we let go. + var disableNearGrabData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultDisableNearGrabData); + if (disableNearGrabData.disableNearGrab === true) { + //do nothing because near grab is disabled for this object + } else { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + + } + return; } } @@ -1753,15 +1773,14 @@ handleHandMessages = function(channel, message, sender) { } catch (e) {} - } - else if (channel === 'Hifi-Hand-RayPick-Blacklist') { + } else if (channel === 'Hifi-Hand-RayPick-Blacklist') { try { var data = JSON.parse(message); var action = data.action; var id = data.id; var index = blacklist.indexOf(id); - - if (action === 'add' && index ===-1) { + + if (action === 'add' && index === -1) { blacklist.push(id); } if (action === 'remove') { diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js index 52d9342464..b50bbe9478 100644 --- a/examples/light_modifier/lightModifier.js +++ b/examples/light_modifier/lightModifier.js @@ -374,7 +374,8 @@ entitySlider.prototype = { }, handControllerKey: { disableReleaseVelocity: true, - disableMoveWithHead: true + disableMoveWithHead: true, + disableNearGrab:true } }), }; From 4661152aa779c0e6dc1957e467f0fe237188816b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 28 Dec 2015 17:57:24 -0800 Subject: [PATCH 76/86] NeuronAvatar.js: Now aligns the head with the HMD if active --- examples/controllers/neuron/neuronAvatar.js | 23 ++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/examples/controllers/neuron/neuronAvatar.js b/examples/controllers/neuron/neuronAvatar.js index abd51f2990..7cebc13feb 100644 --- a/examples/controllers/neuron/neuronAvatar.js +++ b/examples/controllers/neuron/neuronAvatar.js @@ -157,8 +157,6 @@ NeuronAvatar.prototype.deactivate = function () { NeuronAvatar.prototype.update = function (deltaTime) { var hmdActive = HMD.active; - var hmdXform = new Xform(HMD.orientation, HMD.position); - var keys = Object.keys(JOINT_PARENT_MAP); var i, l = keys.length; var absDefaultRot = {}; @@ -192,18 +190,22 @@ NeuronAvatar.prototype.update = function (deltaTime) { MyAvatar.setJointTranslation(j, localTranslation); localTranslations[jointName] = localTranslation; } else { - localTranslations[jointName] = MyAvatar.getJointTranslation(j); + localTranslations[jointName] = MyAvatar.getDefaultJointTranslation(j); } } } - // TODO: Currrently does not work. // it attempts to adjust the hips so that the avatar's head is at the same location & oreintation as the HMD. // however it's fighting with the internal c++ code that also attempts to adjust the hips. if (hmdActive) { - + var UNIT_SCALE = 1 / 100; + var hmdXform = new Xform(HMD.orientation, Vec3.multiply(1 / UNIT_SCALE, HMD.position)); // convert to cm var y180Xform = new Xform({x: 0, y: 1, z: 0, w: 0}, {x: 0, y: 0, z: 0}); - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var avatarXform = new Xform(MyAvatar.orientation, Vec3.multiply(1 / UNIT_SCALE, MyAvatar.position)); // convert to cm + var hipsJointIndex = MyAvatar.getJointIndex("Hips"); + var modelOffsetInvXform = new Xform({x: 0, y: 0, z: 0, w: 1}, MyAvatar.getDefaultJointTranslation(hipsJointIndex)); + var defaultHipsXform = new Xform(MyAvatar.getDefaultJointRotation(hipsJointIndex), MyAvatar.getDefaultJointTranslation(hipsJointIndex)); + var headXform = new Xform(localRotations["Head"], localTranslations["Head"]); // transform eyes down the heirarchy chain into avatar space. @@ -213,9 +215,16 @@ NeuronAvatar.prototype.update = function (deltaTime) { var xform = new Xform(localRotations[hierarchy[i]], localTranslations[hierarchy[i]]); headXform = Xform.mul(xform, headXform); } + headXform = Xform.mul(defaultHipsXform, headXform); + + var preXform = Xform.mul(headXform, y180Xform); + var postXform = Xform.mul(avatarXform, Xform.mul(y180Xform, modelOffsetInvXform.inv())); // solve for the offset that will put the eyes at the hmd position & orientation. - var hipsXform = Xform.mul(y180Xform, Xform.mul(avatarXform.inv(), Xform.mul(hmdXform, Xform.mul(y180Xform, headXform.inv())))); + var hipsOffsetXform = Xform.mul(postXform.inv(), Xform.mul(hmdXform, preXform.inv())); + + // now combine it with the default hips transform + var hipsXform = Xform.mul(hipsOffsetXform, defaultHipsXform); MyAvatar.setJointRotation("Hips", hipsXform.rot); MyAvatar.setJointTranslation("Hips", hipsXform.pos); From 7e514d2f4dd477356d53ffff6c362549bea7de6d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 28 Dec 2015 18:42:03 -0800 Subject: [PATCH 77/86] Mac build fix --- cmake/externals/neuron/CMakeLists.txt | 9 ++++++++- plugins/hifiNeuron/src/NeuronPlugin.cpp | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cmake/externals/neuron/CMakeLists.txt b/cmake/externals/neuron/CMakeLists.txt index 324b3fb917..6936725571 100644 --- a/cmake/externals/neuron/CMakeLists.txt +++ b/cmake/externals/neuron/CMakeLists.txt @@ -41,8 +41,15 @@ if(WIN32) set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.lib" CACHE TYPE INTERNAL) add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}") + elseif(APPLE) - # TODO + + set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/NeuronDataReader_Mac/dylib") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/NeuronDataReader.dylib" CACHE TYPE INTERNAL) + + add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_LIB_PATH}") + else() # UNSUPPORTED endif() diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 97131a0a87..6132c16a43 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -523,7 +523,7 @@ QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { } void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector& joints, const std::vector& prevJoints) { - for (int i = 0; i < joints.size(); i++) { + for (size_t i = 0; i < joints.size(); i++) { glm::vec3 linearVel, angularVel; glm::vec3 pos = joints[i].pos; glm::quat rot = eulerToQuat(joints[i].euler); From c35995b8e3363dda1ec0f129db5e6423cfd4c408 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 29 Dec 2015 08:51:09 -0800 Subject: [PATCH 78/86] Build fix for linux? --- cmake/macros/TargetNeuron.cmake | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cmake/macros/TargetNeuron.cmake b/cmake/macros/TargetNeuron.cmake index 01891ef525..11a92e68ad 100644 --- a/cmake/macros/TargetNeuron.cmake +++ b/cmake/macros/TargetNeuron.cmake @@ -6,9 +6,12 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_NEURON) - add_dependency_external_projects(neuron) - find_package(Neuron REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${NEURON_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${NEURON_LIBRARIES}) - add_definitions(-DHAVE_NEURON) + # Neuron data reader is only available on these platforms + if (WIN32 OR APPLE) + add_dependency_external_projects(neuron) + find_package(Neuron REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${NEURON_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${NEURON_LIBRARIES}) + add_definitions(-DHAVE_NEURON) + endif(WIN32 OR APPLE) endmacro() From e10cecd3101400d2228db6b3f5a9d195f357193c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 29 Dec 2015 09:23:03 -0800 Subject: [PATCH 79/86] Build fix for linux #2? --- plugins/hifiNeuron/src/NeuronPlugin.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 6132c16a43..a175ce8e06 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -24,7 +24,10 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") #define __OS_XUN__ 1 #define BOOL int + +#ifdef HAVE_NEURON #include +#endif const QString NeuronPlugin::NAME = "Neuron"; const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; @@ -312,6 +315,8 @@ static quat eulerToQuat(vec3 euler) { glm::angleAxis(e.z, Vectors::UNIT_Z)); } +#ifdef HAVE_NEURON + // // neuronDataReader SDK callback functions // @@ -430,17 +435,24 @@ static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, Socket qCDebug(inputplugins) << "NeuronPlugin: socket status = " << message; } +#endif // #ifdef HAVE_NEURON + // // NeuronPlugin // bool NeuronPlugin::isSupported() const { +#ifdef HAVE_NEURON // Because it's a client/server network architecture, we can't tell // if the neuron is actually connected until we connect to the server. return true; +#else + return false; +#endif } void NeuronPlugin::activate() { +#ifdef HAVE_NEURON InputPlugin::activate(); // register with userInputMapper @@ -466,9 +478,11 @@ void NeuronPlugin::activate() { BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode); } +#endif } void NeuronPlugin::deactivate() { +#ifdef HAVE_NEURON // unregister from userInputMapper if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); @@ -481,6 +495,7 @@ void NeuronPlugin::deactivate() { } InputPlugin::deactivate(); +#endif } void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) { From 5d596bcbc449b26d3e5a8c89461b2af4e3ba8738 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 29 Dec 2015 10:02:46 -0800 Subject: [PATCH 80/86] Added tests for GLMHelpers::safeEulerAngles. To verify that converting to and from quats and eulers will use the same angle order. Also I fixed some naming inconsistencies in GeometryUtilTests. --- tests/shared/src/GLMHelpersTests.cpp | 57 ++++++++++++++++++++++++++ tests/shared/src/GLMHelpersTests.h | 27 ++++++++++++ tests/shared/src/GeometryUtilTests.cpp | 2 +- tests/shared/src/GeometryUtilTests.h | 8 ++-- 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 tests/shared/src/GLMHelpersTests.cpp create mode 100644 tests/shared/src/GLMHelpersTests.h diff --git a/tests/shared/src/GLMHelpersTests.cpp b/tests/shared/src/GLMHelpersTests.cpp new file mode 100644 index 0000000000..afb634ecbd --- /dev/null +++ b/tests/shared/src/GLMHelpersTests.cpp @@ -0,0 +1,57 @@ +// +// GLMHelpersTests.cpp +// tests/shared/src +// +// Created by Anthony Thibault on 2015.12.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 +// + +#include "GLMHelpersTests.h" + +#include +#include + +#include <../QTestExtensions.h> + + +QTEST_MAIN(GLMHelpersTests) + +void GLMHelpersTests::testEulerDecomposition() { + // quat to euler and back again.... + + const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); + const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f)); + const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f)); + + const float EPSILON = 0.00001f; + + std::vector quatVec = { + glm::quat(), + ROT_X_90, + ROT_Y_180, + ROT_Z_30, + ROT_X_90 * ROT_Y_180 * ROT_Z_30, + ROT_X_90 * ROT_Z_30 * ROT_Y_180, + ROT_Y_180 * ROT_Z_30 * ROT_X_90, + ROT_Y_180 * ROT_X_90 * ROT_Z_30, + ROT_Z_30 * ROT_X_90 * ROT_Y_180, + ROT_Z_30 * ROT_Y_180 * ROT_X_90, + }; + + for (auto& q : quatVec) { + glm::vec3 euler = safeEulerAngles(q); + glm::quat r(euler); + + // when the axis and angle are flipped. + if (glm::dot(q, r) < 0.0f) { + r = -r; + } + + QCOMPARE_WITH_ABS_ERROR(q, r, EPSILON); + } +} + + diff --git a/tests/shared/src/GLMHelpersTests.h b/tests/shared/src/GLMHelpersTests.h new file mode 100644 index 0000000000..5e880899e8 --- /dev/null +++ b/tests/shared/src/GLMHelpersTests.h @@ -0,0 +1,27 @@ +// +// GLMHelpersTests.h +// tests/shared/src +// +// Created by Anthony thibault on 2015.12.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 +// + +#ifndef hifi_GLMHelpersTests_h +#define hifi_GLMHelpersTests_h + +#include +#include + +class GLMHelpersTests : public QObject { + Q_OBJECT +private slots: + void testEulerDecomposition(); +}; + +float getErrorDifference(const float& a, const float& b); +float getErrorDifference(const glm::vec3& a, const glm::vec3& b); + +#endif // hifi_GLMHelpersTest_h diff --git a/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp index eaf1e7cd8a..7ba22ec13d 100644 --- a/tests/shared/src/GeometryUtilTests.cpp +++ b/tests/shared/src/GeometryUtilTests.cpp @@ -1,6 +1,6 @@ // // GeometryUtilTests.cpp -// tests/physics/src +// tests/shared/src // // Created by Andrew Meadows on 2015.07.27 // Copyright 2015 High Fidelity, Inc. diff --git a/tests/shared/src/GeometryUtilTests.h b/tests/shared/src/GeometryUtilTests.h index 6996c8bcea..daf740dcd3 100644 --- a/tests/shared/src/GeometryUtilTests.h +++ b/tests/shared/src/GeometryUtilTests.h @@ -1,6 +1,6 @@ // // GeometryUtilTests.h -// tests/physics/src +// tests/shared/src // // Created by Andrew Meadows on 2014.05.30 // Copyright 2014 High Fidelity, Inc. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_AngularConstraintTests_h -#define hifi_AngularConstraintTests_h +#ifndef hifi_GeometryUtilTests_h +#define hifi_GeometryUtilTests_h #include #include @@ -26,4 +26,4 @@ private slots: float getErrorDifference(const float& a, const float& b); float getErrorDifference(const glm::vec3& a, const glm::vec3& b); -#endif // hifi_AngularConstraintTests_h +#endif // hifi_GeometryUtilTests_h From 12fa22300491a5b3f26d22a60eb7cd10a9c8dfac Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 28 Dec 2015 16:23:19 -0800 Subject: [PATCH 81/86] Render SKY_DOME when SKY_MAP tex is loading --- interface/src/Application.cpp | 146 +++++++++--------- libraries/model/src/model/Skybox.cpp | 11 +- .../src/procedural/ProceduralSkybox.cpp | 9 +- 3 files changed, 87 insertions(+), 79 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d383ee3339..e81aa7ec52 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3486,78 +3486,86 @@ namespace render { // Background rendering decision auto skyStage = DependencyManager::get()->getSkyStage(); - if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) { + auto backgroundMode = skyStage->getBackgroundMode(); + + if (backgroundMode == model::SunSkyStage::NO_BACKGROUND) { // this line intentionally left blank - } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) { - if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceTimer perfTimer("stars"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::payloadRender() ... stars..."); - // should be the first rendering pass - w/o depth buffer / lighting - - // compute starfield alpha based on distance from atmosphere - float alpha = 1.0f; - bool hasStars = true; - - if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - // TODO: handle this correctly for zones - const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum - - if (closestData.getHasStars()) { - const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; - const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; - - glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation()) - / closestData.getAtmosphereOuterRadius(); - float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter()); - if (height < closestData.getAtmosphereInnerRadius()) { - // If we're inside the atmosphere, then determine if our keyLight is below the horizon - alpha = 0.0f; - - if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) - + APPROXIMATE_DISTANCE_FROM_HORIZON; - alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); - } - - - } else if (height < closestData.getAtmosphereOuterRadius()) { - alpha = (height - closestData.getAtmosphereInnerRadius()) / - (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); - - if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) - + APPROXIMATE_DISTANCE_FROM_HORIZON; - alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); - } - } - } else { - hasStars = false; - } + } else { + if (backgroundMode == model::SunSkyStage::SKY_BOX) { + auto skybox = skyStage->getSkybox(); + if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) { + PerformanceTimer perfTimer("skybox"); + skybox->render(batch, *(args->_viewFrustum)); + } else { + // If no skybox texture is available, render the SKY_DOME while it loads + backgroundMode = model::SunSkyStage::SKY_DOME; } - - // finally render the starfield - if (hasStars) { - background->_stars.render(args, alpha); - } - - // draw the sky dome - if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - PerformanceTimer perfTimer("atmosphere"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... atmosphere..."); - - background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); - } - } - } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) { - PerformanceTimer perfTimer("skybox"); - auto skybox = skyStage->getSkybox(); - if (skybox) { - skybox->render(batch, *(args->_viewFrustum)); + if (backgroundMode == model::SunSkyStage::SKY_DOME) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { + PerformanceTimer perfTimer("stars"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::payloadRender() ... stars..."); + // should be the first rendering pass - w/o depth buffer / lighting + + // compute starfield alpha based on distance from atmosphere + float alpha = 1.0f; + bool hasStars = true; + + if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { + // TODO: handle this correctly for zones + const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum + + if (closestData.getHasStars()) { + const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; + const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; + + glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation()) + / closestData.getAtmosphereOuterRadius(); + float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter()); + if (height < closestData.getAtmosphereInnerRadius()) { + // If we're inside the atmosphere, then determine if our keyLight is below the horizon + alpha = 0.0f; + + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } + + + } else if (height < closestData.getAtmosphereOuterRadius()) { + alpha = (height - closestData.getAtmosphereInnerRadius()) / + (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); + + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } + } + } else { + hasStars = false; + } + } + + // finally render the starfield + if (hasStars) { + background->_stars.render(args, alpha); + } + + // draw the sky dome + if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { + PerformanceTimer perfTimer("atmosphere"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::displaySide() ... atmosphere..."); + + background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); + } + + } } } } diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 8c37359638..476ac2fa08 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -96,7 +96,12 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky } }); + // Render + gpu::TexturePointer skymap = skybox.getCubemap(); + // FIXME: skymap->isDefined may not be threadsafe + assert(skymap && skymap->isDefined()); + glm::mat4 projMat; viewFrustum.evalProjectionMatrix(projMat); @@ -106,11 +111,6 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky batch.setViewTransform(viewTransform); batch.setModelTransform(Transform()); // only for Mac - gpu::TexturePointer skymap; - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { - skymap = skybox.getCubemap(); - } - batch.setPipeline(thePipeline); batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer); batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap); @@ -118,6 +118,5 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky batch.draw(gpu::TRIANGLE_STRIP, 4); batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr); - } diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index ce6f29c3d5..167d49cbaf 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -48,6 +48,10 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, } if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { + gpu::TexturePointer skymap = skybox.getCubemap(); + // FIXME: skymap->isDefined may not be threadsafe + assert(skymap && skymap->isDefined()); + glm::mat4 projMat; viewFrustum.evalProjectionMatrix(projMat); @@ -56,10 +60,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, batch.setProjectionTransform(projMat); batch.setViewTransform(viewTransform); batch.setModelTransform(Transform()); // only for Mac - - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { - batch.setResourceTexture(0, skybox.getCubemap()); - } + batch.setResourceTexture(0, skybox.getCubemap()); skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1)); batch.draw(gpu::TRIANGLE_STRIP, 4); From 1a84b5c0f0c68a2f4f729bf80026b901691d4823 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 29 Dec 2015 18:13:34 -0800 Subject: [PATCH 82/86] Fixing issue with muzzle flash and hit point being out of sync for pistol --- examples/toybox/pistol/pistol.js | 206 +++++++++++++++++-------------- 1 file changed, 115 insertions(+), 91 deletions(-) diff --git a/examples/toybox/pistol/pistol.js b/examples/toybox/pistol/pistol.js index 8ef26b94c1..df249a0aaf 100644 --- a/examples/toybox/pistol/pistol.js +++ b/examples/toybox/pistol/pistol.js @@ -44,6 +44,7 @@ this.showLaser = false; + }; Pistol.prototype = { @@ -58,20 +59,36 @@ if (!this.equipped) { return; } - this.toggleWithTriggerPressure(); + this.updateProps(); if (this.showLaser) { this.updateLaser(); } + this.toggleWithTriggerPressure(); + + + }, + + updateProps: function() { + var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); + this.position = gunProps.position; + this.rotation = gunProps.rotation; + this.firingDirection = Quat.getFront(this.rotation); + var upVec = Quat.getUp(this.rotation); + this.barrelPoint = Vec3.sum(this.position, Vec3.multiply(upVec, this.laserOffsets.y)); + this.laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength)); + this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z)) + var pickRay = { + origin: this.barrelPoint, + direction: this.firingDirection + }; }, toggleWithTriggerPressure: function() { this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]); if (this.triggerValue < RELOAD_THRESHOLD) { - // print('RELOAD'); this.canShoot = true; } if (this.canShoot === true && this.triggerValue === 1) { - // print('SHOOT'); this.fire(); this.canShoot = false; } @@ -91,17 +108,10 @@ }, updateLaser: function() { - var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); - var position = gunProps.position; - var rotation = gunProps.rotation; - this.firingDirection = Quat.getFront(rotation); - var upVec = Quat.getUp(rotation); - this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y)); - var laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength)); - this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z)) + Overlays.editOverlay(this.laser, { start: this.barrelPoint, - end: laserTip, + end: this.laserTip, alpha: 1 }); }, @@ -114,19 +124,6 @@ }); }, - preload: function(entityID) { - this.entityID = entityID; - // this.initControllerMapping(); - this.laser = Overlays.addOverlay("line3d", { - start: ZERO_VECTOR, - end: ZERO_VECTOR, - color: COLORS.RED, - alpha: 1, - visible: true, - lineWidth: 2 - }); - }, - triggerPress: function(hand, value) { if (this.hand === hand && value === 1) { //We are pulling trigger on the hand we have the gun in, so fire @@ -135,17 +132,18 @@ }, fire: function() { - var pickRay = { - origin: this.barrelPoint, - direction: this.firingDirection - }; + Audio.playSound(this.fireSound, { position: this.barrelPoint, volume: this.fireVolume }); + var pickRay = { + origin: this.barrelPoint, + direction: this.firingDirection + }; this.createGunFireEffect(this.barrelPoint) - var intersection = Entities.findRayIntersectionBlocking(pickRay, true); + var intersection = Entities.findRayIntersection(pickRay, true); if (intersection.intersects) { this.createEntityHitEffect(intersection.intersection); if (Math.random() < this.playRichochetSoundChance) { @@ -170,11 +168,11 @@ }, createEntityHitEffect: function(position) { - var flash = Entities.addEntity({ + var sparks = Entities.addEntity({ type: "ParticleEffect", position: position, lifetime: 4, - "name": "Flash Emitter", + "name": "Sparks Emitter", "color": { red: 228, green: 128, @@ -228,7 +226,7 @@ }); Script.setTimeout(function() { - Entities.editEntity(flash, { + Entities.editEntity(sparks, { isEmitting: false }); }, 100); @@ -282,70 +280,96 @@ }); }, 100); - var flash = Entities.addEntity({ - type: "ParticleEffect", - position: position, - lifetime: 4, - "name": "Muzzle Flash", - "color": { - red: 228, - green: 128, - blue: 12 - }, - "maxParticles": 1000, - "lifespan": 0.1, - "emitRate": 1000, - "emitSpeed": 0.5, - "speedSpread": 0, - "emitOrientation": { - "x": -0.4, - "y": 1, - "z": -0.2, - "w": 0.7071068286895752 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": Math.PI, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 2, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.05, - "radiusSpread": 0.01, - "radiusStart": 0.05, - "radiusFinish": 0.05, - "colorSpread": { - red: 100, - green: 100, - blue: 20 - }, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": 0, - "alphaFinish": 0, - "additiveBlending": true, - "textures": "http://ericrius1.github.io/PartiArt/assets/star.png" + Entities.editEntity(this.flash, { + isEmitting: true }); - Script.setTimeout(function() { - Entities.editEntity(flash, { + Entities.editEntity(_this.flash, { isEmitting: false }); }, 100) - } + }, + + preload: function(entityID) { + this.entityID = entityID; + this.laser = Overlays.addOverlay("line3d", { + start: ZERO_VECTOR, + end: ZERO_VECTOR, + color: COLORS.RED, + alpha: 1, + visible: true, + lineWidth: 2 + }); + + var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); + var position = gunProps.position; + var rotation = gunProps.rotation; + this.firingDirection = Quat.getFront(rotation); + var upVec = Quat.getUp(rotation); + this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y)); + this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z)) + + // this.flash = Entities.addEntity({ + // type: "ParticleEffect", + // parentID: this.entityID, + // position: this.barrelPoint, + // "name": "Muzzle Flash", + // // isEmitting: false, + // "color": { + // red: 228, + // green: 128, + // blue: 12 + // }, + // "maxParticles": 1000, + // "lifespan": 0.1, + // "emitRate": 1000, + // "emitSpeed": 0.5, + // "speedSpread": 0, + // "emitOrientation": { + // "x": -0.4, + // "y": 1, + // "z": -0.2, + // "w": 0.7071068286895752 + // }, + // "emitDimensions": { + // "x": 0, + // "y": 0, + // "z": 0 + // }, + // "polarStart": 0, + // "polarFinish": Math.PI, + // "azimuthStart": -3.1415927410125732, + // "azimuthFinish": 2, + // "emitAcceleration": { + // "x": 0, + // "y": 0, + // "z": 0 + // }, + // "accelerationSpread": { + // "x": 0, + // "y": 0, + // "z": 0 + // }, + // "particleRadius": 0.05, + // "radiusSpread": 0.01, + // "radiusStart": 0.05, + // "radiusFinish": 0.05, + // "colorSpread": { + // red: 100, + // green: 100, + // blue: 20 + // }, + // "alpha": 1, + // "alphaSpread": 0, + // "alphaStart": 0, + // "alphaFinish": 0, + // "additiveBlending": true, + // "textures": "http://ericrius1.github.io/PartiArt/assets/star.png" + // }); + + }, + }; From 4cc94b44ba0f8ed1320f6dcb35e1cc67ca362292 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 29 Dec 2015 18:30:07 -0800 Subject: [PATCH 83/86] muzzle is in sync --- examples/toybox/pistol/pistol.js | 140 +++++++++++++++---------------- 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/examples/toybox/pistol/pistol.js b/examples/toybox/pistol/pistol.js index df249a0aaf..372c704219 100644 --- a/examples/toybox/pistol/pistol.js +++ b/examples/toybox/pistol/pistol.js @@ -29,20 +29,12 @@ this.equipped = false; this.forceMultiplier = 1; this.laserLength = 100; - this.laserOffsets = { - y: .095 - }; - this.firingOffsets = { - z: 0.16 - } + this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw"); this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav"); this.playRichochetSoundChance = 0.1; this.fireVolume = 0.2; this.bulletForce = 10; - - - this.showLaser = false; }; @@ -143,7 +135,7 @@ direction: this.firingDirection }; this.createGunFireEffect(this.barrelPoint) - var intersection = Entities.findRayIntersection(pickRay, true); + var intersection = Entities.findRayIntersectionBlocking(pickRay, true); if (intersection.intersects) { this.createEntityHitEffect(intersection.intersection); if (Math.random() < this.playRichochetSoundChance) { @@ -301,76 +293,82 @@ visible: true, lineWidth: 2 }); - + this.laserOffsets = { + y: .095 + }; + this.firingOffsets = { + z: 0.16 + } var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); var position = gunProps.position; - var rotation = gunProps.rotation; + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); this.firingDirection = Quat.getFront(rotation); var upVec = Quat.getUp(rotation); this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y)); this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z)) - // this.flash = Entities.addEntity({ - // type: "ParticleEffect", - // parentID: this.entityID, - // position: this.barrelPoint, - // "name": "Muzzle Flash", - // // isEmitting: false, - // "color": { - // red: 228, - // green: 128, - // blue: 12 - // }, - // "maxParticles": 1000, - // "lifespan": 0.1, - // "emitRate": 1000, - // "emitSpeed": 0.5, - // "speedSpread": 0, - // "emitOrientation": { - // "x": -0.4, - // "y": 1, - // "z": -0.2, - // "w": 0.7071068286895752 - // }, - // "emitDimensions": { - // "x": 0, - // "y": 0, - // "z": 0 - // }, - // "polarStart": 0, - // "polarFinish": Math.PI, - // "azimuthStart": -3.1415927410125732, - // "azimuthFinish": 2, - // "emitAcceleration": { - // "x": 0, - // "y": 0, - // "z": 0 - // }, - // "accelerationSpread": { - // "x": 0, - // "y": 0, - // "z": 0 - // }, - // "particleRadius": 0.05, - // "radiusSpread": 0.01, - // "radiusStart": 0.05, - // "radiusFinish": 0.05, - // "colorSpread": { - // red: 100, - // green: 100, - // blue: 20 - // }, - // "alpha": 1, - // "alphaSpread": 0, - // "alphaStart": 0, - // "alphaFinish": 0, - // "additiveBlending": true, - // "textures": "http://ericrius1.github.io/PartiArt/assets/star.png" - // }); + this.flash = Entities.addEntity({ + type: "ParticleEffect", + position: this.barrelPoint, + "name": "Muzzle Flash", + isEmitting: false, + "color": { + red: 228, + green: 128, + blue: 12 + }, + "maxParticles": 1000, + "lifespan": 0.1, + "emitRate": 1000, + "emitSpeed": 0.5, + "speedSpread": 0, + "emitOrientation": { + "x": -0.4, + "y": 1, + "z": -0.2, + "w": 0.7071068286895752 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": Math.PI, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 2, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.05, + "radiusSpread": 0.01, + "radiusStart": 0.05, + "radiusFinish": 0.05, + "colorSpread": { + red: 100, + green: 100, + blue: 20 + }, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 0, + "alphaFinish": 0, + "additiveBlending": true, + "textures": "http://ericrius1.github.io/PartiArt/assets/star.png" + }); + + Script.setTimeout(function() { + Entities.editEntity(_this.flash, {parentID: _this.entityID}); + }, 500) }, - - }; // entity scripts always need to return a newly constructed object of our type From 5caa6cbdbf99b1531d757976b539c6606b497b9b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 29 Dec 2015 18:38:57 -0800 Subject: [PATCH 84/86] Update pistol.js leading zeroes --- examples/toybox/pistol/pistol.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/toybox/pistol/pistol.js b/examples/toybox/pistol/pistol.js index 372c704219..7fb05d992f 100644 --- a/examples/toybox/pistol/pistol.js +++ b/examples/toybox/pistol/pistol.js @@ -251,11 +251,11 @@ "z": 0 }, "accelerationSpread": { - "x": .2, + "x": 0.2, "y": 0, - "z": .2 + "z": 0.2 }, - "radiusSpread": .04, + "radiusSpread": 0.04, "particleRadius": 0.07, "radiusStart": 0.07, "radiusFinish": 0.07, @@ -294,7 +294,7 @@ lineWidth: 2 }); this.laserOffsets = { - y: .095 + y: 0.095 }; this.firingOffsets = { z: 0.16 @@ -373,4 +373,4 @@ // entity scripts always need to return a newly constructed object of our type return new Pistol(); -}); \ No newline at end of file +}); From 6d857296f9a4acc3e81d2df5c95d50f28bbfaa2c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 29 Dec 2015 23:37:10 -0800 Subject: [PATCH 85/86] show search sphere instead of beams, start at center of view --- examples/controllers/handControllerGrab.js | 96 ++++++++++++++++------ 1 file changed, 70 insertions(+), 26 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 3c8f1f0014..0dce72803c 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -96,7 +96,7 @@ var MSEC_PER_SEC = 1000.0; var LIFETIME = 10; var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; -var PICKS_PER_SECOND_PER_HAND = 5; +var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; var GRABBABLE_PROPERTIES = [ "position", @@ -123,8 +123,8 @@ var blacklist = []; //we've created various ways of visualizing looking for and moving distant objects var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_OVERLAY_LINES_FOR_SEARCHING = false; -var USE_PARTICLE_BEAM_FOR_SEARCHING = true; +var USE_OVERLAY_LINES_FOR_SEARCHING = true; +var USE_PARTICLE_BEAM_FOR_SEARCHING = false; var USE_ENTITY_LINES_FOR_MOVING = false; var USE_OVERLAY_LINES_FOR_MOVING = false; @@ -290,6 +290,11 @@ function MyController(hand) { this.spotlight = null; this.pointlight = null; this.overlayLine = null; + this.searchSphere = null; + + // how far from camera to search intersection? + this.intersectionDistance = 0.0; + this.searchSphereDistance = 0.0; this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; @@ -409,6 +414,23 @@ function MyController(hand) { } }; + var SEARCH_SPHERE_ALPHA = 0.5; + this.searchSphereOn = function(location, size, color) { + if (this.searchSphere === null) { + var sphereProperties = { + position: location, + size: size, + color: color, + alpha: SEARCH_SPHERE_ALPHA, + solid: true, + visible: true + } + this.searchSphere = Overlays.addOverlay("sphere", sphereProperties); + } else { + Overlays.editOverlay(this.searchSphere, { position: location, size: size, color: color, visible: true }); + } + } + this.overlayLineOn = function(closePoint, farPoint, color) { if (this.overlayLine === null) { var lineProperties = { @@ -654,6 +676,17 @@ function MyController(hand) { this.overlayLine = null; }; + this.searchSphereOff = function() { + if (this.searchSphere !== null) { + //Overlays.editOverlay(this.searchSphere, { visible: false }); + Overlays.deleteOverlay(this.searchSphere); + this.searchSphere = null; + this.searchSphereDistance = 0.0; + this.intersectionDistance = 0.0; + } + + }; + this.particleBeamOff = function() { if (this.particleBeam !== null) { Entities.editEntity(this.particleBeam, { @@ -687,6 +720,7 @@ function MyController(hand) { if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { this.particleBeamOff(); } + this.searchSphereOff(); }; this.triggerPress = function(value) { @@ -712,11 +746,6 @@ function MyController(hand) { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.triggerSqueezed = function() { - var triggerValue = this.rawTriggerValue; - return triggerValue > TRIGGER_ON_VALUE; - }; - this.bumperSqueezed = function() { return _this.rawBumperValue > BUMPER_ON_VALUE; }; @@ -726,15 +755,15 @@ function MyController(hand) { }; this.off = function() { - if (this.triggerSmoothedSqueezed()) { + if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) { this.lastPickTime = 0; - this.setState(STATE_SEARCHING); - return; - } - if (this.bumperSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_EQUIP_SEARCHING); - return; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation; + if (this.triggerSmoothedSqueezed()) { + this.setState(STATE_SEARCHING); + } else { + this.setState(STATE_EQUIP_SEARCHING); + } } }; @@ -748,9 +777,14 @@ function MyController(hand) { // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); + + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation; + var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation)); + var distantPickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()), + origin: Camera.position, + direction: Quat.getFront(Quat.multiply(Camera.orientation, handDeltaRotation)), length: PICK_MAX_DISTANCE }; @@ -789,7 +823,7 @@ function MyController(hand) { if (intersection.intersects) { // the ray is intersecting something we can move. - var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); @@ -800,11 +834,11 @@ function MyController(hand) { if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { continue; } - if (intersectionDistance > pickRay.length) { + if (this.intersectionDistance > pickRay.length) { // too far away for this ray. continue; } - if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + if (this.intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. if (grabbableData.wantsTrigger) { this.grabbedEntity = intersection.entityID; @@ -851,6 +885,7 @@ function MyController(hand) { } } + // forward ray test failed, try sphere test. if (WANT_DEBUG) { Entities.addEntity({ @@ -946,14 +981,23 @@ function MyController(hand) { this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); } - if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); - } - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); } - + if (this.intersectionDistance > 0) { + var SPHERE_INTERSECTION_SIZE = 0.011; + var SEARCH_SPHERE_FOLLOW_RATE = 0.50; + var SEARCH_SPHERE_CHASE_DROP = 0.2; + this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE + this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE); + var searchSphereLocation = Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, this.searchSphereDistance)); + searchSphereLocation.y -= ((this.intersectionDistance - this.searchSphereDistance) / this.intersectionDistance) * SEARCH_SPHERE_CHASE_DROP; + this.searchSphereOn(searchSphereLocation, SPHERE_INTERSECTION_SIZE * this.intersectionDistance, NO_INTERSECT_COLOR); + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + var OVERLAY_BEAM_SETBACK = 0.9; + var startBeam = Vec3.sum(handPosition, Vec3.multiply(Vec3.subtract(searchSphereLocation, handPosition), OVERLAY_BEAM_SETBACK)); + this.overlayLineOn(startBeam, searchSphereLocation, NO_INTERSECT_COLOR); + } + } }; this.distanceHolding = function() { From e995b29712dd184a4473f79e0ca8da1a6b72bcca Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 30 Dec 2015 00:36:11 -0800 Subject: [PATCH 86/86] can target without grabbing --- examples/controllers/handControllerGrab.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 0dce72803c..e362eb22e0 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -23,8 +23,9 @@ var WANT_DEBUG = false; // these tune time-averaging and "on" value for analog trigger // -var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value -var TRIGGER_ON_VALUE = 0.4; +var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing +var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab +var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab var TRIGGER_OFF_VALUE = 0.15; var BUMPER_ON_VALUE = 0.5; @@ -738,6 +739,10 @@ function MyController(hand) { (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); }; + this.triggerSmoothedGrab = function() { + return this.triggerValue > TRIGGER_GRAB_VALUE; + }; + this.triggerSmoothedSqueezed = function() { return this.triggerValue > TRIGGER_ON_VALUE; }; @@ -775,7 +780,7 @@ function MyController(hand) { return; } - // the trigger is being pressed, do a ray test + // the trigger is being pressed, so do a ray test to see what we are hitting var handPosition = this.getHandPosition(); var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; @@ -788,7 +793,7 @@ function MyController(hand) { length: PICK_MAX_DISTANCE }; - // don't pick 60x per second. + // Pick at some maximum rate, not always var pickRays = []; var now = Date.now(); if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { @@ -872,11 +877,11 @@ function MyController(hand) { // this.setState(STATE_EQUIP_SPRING); this.setState(STATE_EQUIP); return; - } else if (this.state == STATE_SEARCHING) { + } else if ((this.state == STATE_SEARCHING) && this.triggerSmoothedGrab()) { this.setState(STATE_DISTANCE_HOLDING); return; } - } else if (grabbableData.wantsTrigger) { + } else if (grabbableData.wantsTrigger && this.triggerSmoothedGrab()) { this.grabbedEntity = intersection.entityID; this.setState(STATE_FAR_TRIGGER); return; @@ -991,11 +996,11 @@ function MyController(hand) { this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE + this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE); var searchSphereLocation = Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, this.searchSphereDistance)); searchSphereLocation.y -= ((this.intersectionDistance - this.searchSphereDistance) / this.intersectionDistance) * SEARCH_SPHERE_CHASE_DROP; - this.searchSphereOn(searchSphereLocation, SPHERE_INTERSECTION_SIZE * this.intersectionDistance, NO_INTERSECT_COLOR); + this.searchSphereOn(searchSphereLocation, SPHERE_INTERSECTION_SIZE * this.intersectionDistance, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR); if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { var OVERLAY_BEAM_SETBACK = 0.9; var startBeam = Vec3.sum(handPosition, Vec3.multiply(Vec3.subtract(searchSphereLocation, handPosition), OVERLAY_BEAM_SETBACK)); - this.overlayLineOn(startBeam, searchSphereLocation, NO_INTERSECT_COLOR); + this.overlayLineOn(startBeam, searchSphereLocation, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR); } } };