From d3b37684cd0d14948529328004b9f00fdd84d33a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 2 Oct 2014 20:02:51 -0700 Subject: [PATCH 001/108] Add VR display auto-calibration logic --- interface/src/Application.cpp | 4 ++ interface/src/Application.h | 3 +- interface/src/devices/OculusManager.cpp | 79 +++++++++++++++++++++++++ interface/src/devices/OculusManager.h | 21 +++++++ 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d41b50a232..da7dd5bb25 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1082,6 +1082,9 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Equal: _myAvatar->resetSize(); break; + case Qt::Key_Escape: + OculusManager::abandonCalibration(); + break; default: event->ignore(); break; @@ -1474,6 +1477,7 @@ void Application::setEnableVRMode(bool enableVRMode) { OculusManager::disconnect(); OculusManager::connect(); } + OculusManager::recalibrate(); } resizeGL(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); diff --git a/interface/src/Application.h b/interface/src/Application.h index ca26cffab8..fa0733f5e3 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -365,6 +365,8 @@ public slots: void domainSettingsReceived(const QJsonObject& domainSettingsObject); + void resetSensors(); + private slots: void timer(); void idle(); @@ -381,7 +383,6 @@ private slots: void closeMirrorView(); void restoreMirrorView(); void shrinkMirrorView(); - void resetSensors(); void parseVersionXml(); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 65518b839c..99892e98be 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -55,6 +55,16 @@ bool OculusManager::_programInitialized = false; Camera* OculusManager::_camera = NULL; int OculusManager::_activeEyeIndex = -1; +float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f; +float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.f * RADIANS_PER_DEGREE; +float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f; +float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 0.5f * RADIANS_PER_DEGREE; +quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec +OculusManager::CalibrationState OculusManager::_calibrationState; +glm::vec3 OculusManager::_calibrationPosition; +glm::quat OculusManager::_calibrationOrientation; +quint64 OculusManager::_calibrationStartTime; + #endif glm::vec3 OculusManager::_leftEyePosition = glm::vec3(); @@ -62,6 +72,8 @@ glm::vec3 OculusManager::_rightEyePosition = glm::vec3(); void OculusManager::connect() { #ifdef HAVE_LIBOVR + _calibrationState = UNCALIBRATED; + ovr_Initialize(); _ovrHmd = ovrHmd_Create(0); @@ -172,6 +184,67 @@ void OculusManager::disconnect() { #endif } +void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { +#ifdef HAVE_LIBOVR + switch (_calibrationState) { + + case UNCALIBRATED: + if (position != glm::vec3() && orientation != glm::quat()) { // Handle zero values at start-up. + _calibrationPosition = position; + _calibrationOrientation = orientation; + _calibrationState = WAITING_FOR_DELTA; + } + break; + + case WAITING_FOR_DELTA: + if (glm::length(position - _calibrationPosition) > CALIBRATION_DELTA_MINIMUM_LENGTH + || glm::angle(orientation * glm::inverse(_calibrationOrientation)) > CALIBRATION_DELTA_MINIMUM_ANGLE) { + _calibrationPosition = position; + _calibrationOrientation = orientation; + _calibrationState = WAITING_FOR_ZERO; + } + break; + + case WAITING_FOR_ZERO: + if (glm::length(position - _calibrationPosition) < CALIBRATION_ZERO_MAXIMUM_LENGTH + && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) { + _calibrationStartTime = usecTimestampNow(); + _calibrationState = WAITING_FOR_ZERO_HELD; + } else { + _calibrationPosition = position; + _calibrationOrientation = orientation; + } + break; + + case WAITING_FOR_ZERO_HELD: + if (glm::length(position - _calibrationPosition) < CALIBRATION_ZERO_MAXIMUM_LENGTH + && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) { + if ((usecTimestampNow() - _calibrationStartTime) > CALIBRATION_ZERO_HOLD_TIME) { + _calibrationState = CALIBRATED; + Application::getInstance()->resetSensors(); + } + } else { + _calibrationPosition = position; + _calibrationOrientation = orientation; + _calibrationState = WAITING_FOR_ZERO; + } + break; + } +#endif +} + +void OculusManager::recalibrate() { +#ifdef HAVE_LIBOVR + _calibrationState = UNCALIBRATED; +#endif +} + +void OculusManager::abandonCalibration() { +#ifdef HAVE_LIBOVR + _calibrationState = CALIBRATED; +#endif +} + #ifdef HAVE_LIBOVR void OculusManager::generateDistortionMesh() { @@ -330,6 +403,12 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p trackerPosition = glm::vec3(ovrHeadPosition.x, ovrHeadPosition.y, ovrHeadPosition.z); trackerPosition = bodyOrientation * trackerPosition; + + if (_calibrationState != CALIBRATED) { + ovrQuatf ovrHeadOrientation = ts.HeadPose.ThePose.Orientation; + orientation = glm::quat(ovrHeadOrientation.w, ovrHeadOrientation.x, ovrHeadOrientation.y, ovrHeadOrientation.z); + calibrate(trackerPosition, orientation); + } #endif //Render each eye into an fbo diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 604580a24e..15d5c73b7b 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -30,6 +30,8 @@ public: static void connect(); static void disconnect(); static bool isConnected(); + static void recalibrate(); + static void abandonCalibration(); static void beginFrameTiming(); static void endFrameTiming(); static void configureCamera(Camera& camera, int screenWidth, int screenHeight); @@ -99,6 +101,25 @@ private: static bool _programInitialized; static Camera* _camera; static int _activeEyeIndex; + + static void calibrate(glm::vec3 position, glm::quat orientation); + enum CalibrationState { + UNCALIBRATED, + WAITING_FOR_DELTA, + WAITING_FOR_ZERO, + WAITING_FOR_ZERO_HELD, + CALIBRATED + }; + static float CALIBRATION_DELTA_MINIMUM_LENGTH; + static float CALIBRATION_DELTA_MINIMUM_ANGLE; + static float CALIBRATION_ZERO_MAXIMUM_LENGTH; + static float CALIBRATION_ZERO_MAXIMUM_ANGLE; + static quint64 CALIBRATION_ZERO_HOLD_TIME; + static CalibrationState _calibrationState; + static glm::vec3 _calibrationPosition; + static glm::quat _calibrationOrientation; + static quint64 _calibrationStartTime; + #endif static glm::vec3 _leftEyePosition; From 0a4bc2bc4883a92035c2abbf2d87b482fa1d99b2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 3 Oct 2014 13:14:39 -0700 Subject: [PATCH 002/108] Add VR display auto-calibration user message calculations --- interface/src/devices/OculusManager.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 99892e98be..59283dbc67 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -186,6 +186,8 @@ void OculusManager::disconnect() { void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { #ifdef HAVE_LIBOVR + static QString progressMessage; + switch (_calibrationState) { case UNCALIBRATED: @@ -210,6 +212,8 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) { _calibrationStartTime = usecTimestampNow(); _calibrationState = WAITING_FOR_ZERO_HELD; + qDebug() << "Progress box: Hold still to calibrate"; + progressMessage = ""; } else { _calibrationPosition = position; _calibrationOrientation = orientation; @@ -221,7 +225,19 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) { if ((usecTimestampNow() - _calibrationStartTime) > CALIBRATION_ZERO_HOLD_TIME) { _calibrationState = CALIBRATED; + qDebug() << "Delete progress box"; Application::getInstance()->resetSensors(); + } else { + // 3...2...1... + quint64 quarterSeconds = (usecTimestampNow() - _calibrationStartTime) / 250000; + if (quarterSeconds + 1 > progressMessage.length()) { + if (quarterSeconds == 4 * (quarterSeconds / 4)) { + progressMessage += QString::number(CALIBRATION_ZERO_HOLD_TIME / 1000000 - quarterSeconds / 4); + } else { + progressMessage += "."; + } + qDebug() << progressMessage; + } } } else { _calibrationPosition = position; From fc24d96947bcdf65608284b88defe596c1871b53 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 4 Oct 2014 19:31:52 -0700 Subject: [PATCH 003/108] Make overlays accessible from C++ code --- interface/src/ui/overlays/Overlays.cpp | 73 ++++++++------------------ interface/src/ui/overlays/Overlays.h | 3 ++ 2 files changed, 24 insertions(+), 52 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index fe9a9edca0..922c6a0408 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -124,90 +124,59 @@ void Overlays::render3D() { unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { unsigned int thisID = 0; - bool created = false; - bool is3D = false; Overlay* thisOverlay = NULL; + bool created = true; if (type == "image") { thisOverlay = new ImageOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; } else if (type == "text") { thisOverlay = new TextOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; } else if (type == "cube") { thisOverlay = new Cube3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "sphere") { thisOverlay = new Sphere3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "circle3d") { thisOverlay = new Circle3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "rectangle3d") { thisOverlay = new Rectangle3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "line3d") { thisOverlay = new Line3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "localvoxels") { thisOverlay = new LocalVoxelsOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "localmodels") { thisOverlay = new LocalModelsOverlay(Application::getInstance()->getEntityClipboardRenderer()); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "model") { thisOverlay = new ModelOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "billboard") { thisOverlay = new BillboardOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; + } else { + created = false; } if (created) { - QWriteLocker lock(&_lock); - thisID = _nextOverlayID; - _nextOverlayID++; - if (is3D) { - _overlays3D[thisID] = thisOverlay; - } else { - _overlays2D[thisID] = thisOverlay; - } + thisOverlay->setProperties(properties); + thisID = addOverlay(thisOverlay); } return thisID; } +unsigned int Overlays::addOverlay(Overlay* overlay) { + overlay->init(_parent); + + QWriteLocker lock(&_lock); + unsigned int thisID = _nextOverlayID; + _nextOverlayID++; + bool is3D = typeid(*overlay) != typeid(ImageOverlay) && typeid(*overlay) != typeid(TextOverlay); + if (is3D) { + _overlays3D[thisID] = overlay; + } else { + _overlays2D[thisID] = overlay; + } + + return thisID; +} + bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { Overlay* thisOverlay = NULL; QWriteLocker lock(&_lock); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 6676994eed..4851b1f9dc 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -44,6 +44,9 @@ public slots: /// adds an overlay with the specific properties unsigned int addOverlay(const QString& type, const QScriptValue& properties); + /// adds an overlay that's already been created + unsigned int addOverlay(Overlay* overlay); + /// edits an overlay updating only the included properties, will return the identified OverlayID in case of /// successful edit, if the input id is for an unknown overlay this function will have no effect bool editOverlay(unsigned int id, const QScriptValue& properties); From 614f5314f20e82c1eba50735d4fbf7a345116203 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 4 Oct 2014 19:32:20 -0700 Subject: [PATCH 004/108] Add some property setters to the billboard overlay --- interface/src/ui/overlays/BillboardOverlay.cpp | 4 ++++ interface/src/ui/overlays/BillboardOverlay.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 4f9e7c84f6..08d2652849 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -150,6 +150,10 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { } } +void BillboardOverlay::setURL(const QString url) { + setBillboardURL(url); +} + void BillboardOverlay::setBillboardURL(const QUrl url) { _isLoaded = false; QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(url)); diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index a0b76869b3..85af2a3c66 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -24,6 +24,12 @@ public: BillboardOverlay(); virtual void render(); + + // setters + void setURL(const QString url); + void setScale(float scale) { _scale = scale; } + void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } + virtual void setProperties(const QScriptValue& properties); void setClipFromSource(const QRect& bounds) { _fromImage = bounds; } From 817da8ca05061bd74e080154bb30327d5a35249e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 4 Oct 2014 19:33:24 -0700 Subject: [PATCH 005/108] Display a static billboard prompting user to hold still to calibrate HMD --- interface/src/devices/OculusManager.cpp | 48 ++++++++++++++++++++++--- interface/src/devices/OculusManager.h | 6 ++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 59283dbc67..123b02edf2 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -58,12 +58,16 @@ int OculusManager::_activeEyeIndex = -1; float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f; float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.f * RADIANS_PER_DEGREE; float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f; -float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 0.5f * RADIANS_PER_DEGREE; +float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE; quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec +float OculusManager::CALIBRATION_MESSAGE_DISTANCE = 2.5f; OculusManager::CalibrationState OculusManager::_calibrationState; glm::vec3 OculusManager::_calibrationPosition; glm::quat OculusManager::_calibrationOrientation; quint64 OculusManager::_calibrationStartTime; +int OculusManager::_calibrationMessage = 0; +QString OculusManager::CALIBRATION_BILLBOARD_URL = "http://ctrlaltstudio.com/hifi/hold-to-calibrate.svg"; // TODO: Update with final URL +float OculusManager::CALIBRATION_BILLBOARD_SCALE = 2.f; #endif @@ -184,9 +188,20 @@ void OculusManager::disconnect() { #endif } +void OculusManager::positionCalibrationBillboard(BillboardOverlay* billboard) { + glm::quat headOrientation = Application::getInstance()->getAvatar()->getHeadOrientation(); + headOrientation.x = 0; + headOrientation.z = 0; + glm::normalize(headOrientation); + billboard->setPosition(Application::getInstance()->getAvatar()->getHeadPosition() + + headOrientation * glm::vec3(0.f, 0.f, -CALIBRATION_MESSAGE_DISTANCE)); + billboard->setRotation(headOrientation); +} + void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { #ifdef HAVE_LIBOVR static QString progressMessage; + static BillboardOverlay* billboard; switch (_calibrationState) { @@ -212,7 +227,19 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) { _calibrationStartTime = usecTimestampNow(); _calibrationState = WAITING_FOR_ZERO_HELD; - qDebug() << "Progress box: Hold still to calibrate"; + + if (_calibrationMessage == 0) { + qDebug() << "Calibration message: Hold still to calibrate"; + + billboard = new BillboardOverlay(); + billboard->setURL(CALIBRATION_BILLBOARD_URL); + billboard->setScale(CALIBRATION_BILLBOARD_SCALE); + billboard->setIsFacingAvatar(false); + positionCalibrationBillboard(billboard); + + _calibrationMessage = Application::getInstance()->getOverlays().addOverlay(billboard); + } + progressMessage = ""; } else { _calibrationPosition = position; @@ -225,14 +252,22 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) { if ((usecTimestampNow() - _calibrationStartTime) > CALIBRATION_ZERO_HOLD_TIME) { _calibrationState = CALIBRATED; - qDebug() << "Delete progress box"; + qDebug() << "Delete calibration message"; + Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); + _calibrationMessage = 0; Application::getInstance()->resetSensors(); } else { - // 3...2...1... quint64 quarterSeconds = (usecTimestampNow() - _calibrationStartTime) / 250000; if (quarterSeconds + 1 > progressMessage.length()) { + // 3...2...1... if (quarterSeconds == 4 * (quarterSeconds / 4)) { - progressMessage += QString::number(CALIBRATION_ZERO_HOLD_TIME / 1000000 - quarterSeconds / 4); + quint64 wholeSeconds = CALIBRATION_ZERO_HOLD_TIME / 1000000 - quarterSeconds / 4; + + if (wholeSeconds == 2) { + positionCalibrationBillboard(billboard); + } + + progressMessage += QString::number(wholeSeconds); } else { progressMessage += "."; } @@ -258,6 +293,9 @@ void OculusManager::recalibrate() { void OculusManager::abandonCalibration() { #ifdef HAVE_LIBOVR _calibrationState = CALIBRATED; + qDebug() << "Delete calibration message"; + Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); + _calibrationMessage = 0; #endif } diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 15d5c73b7b..967c8b7139 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -18,6 +18,7 @@ #endif #include "renderer/ProgramObject.h" +#include "ui/overlays/BillboardOverlay.h" const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f; @@ -110,15 +111,20 @@ private: WAITING_FOR_ZERO_HELD, CALIBRATED }; + static void OculusManager::positionCalibrationBillboard(BillboardOverlay* billboard); static float CALIBRATION_DELTA_MINIMUM_LENGTH; static float CALIBRATION_DELTA_MINIMUM_ANGLE; static float CALIBRATION_ZERO_MAXIMUM_LENGTH; static float CALIBRATION_ZERO_MAXIMUM_ANGLE; static quint64 CALIBRATION_ZERO_HOLD_TIME; + static float CALIBRATION_MESSAGE_DISTANCE; static CalibrationState _calibrationState; static glm::vec3 _calibrationPosition; static glm::quat _calibrationOrientation; static quint64 _calibrationStartTime; + static int _calibrationMessage; + static QString CALIBRATION_BILLBOARD_URL; + static float CALIBRATION_BILLBOARD_SCALE; #endif From 6992b41c5c349525303703136fbba5a8f788859e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 4 Oct 2014 19:53:00 -0700 Subject: [PATCH 006/108] Fix avatar rotation erroneously initiating HMD calibration --- interface/src/devices/OculusManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 123b02edf2..cfeca9bb9a 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -456,13 +456,14 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position; trackerPosition = glm::vec3(ovrHeadPosition.x, ovrHeadPosition.y, ovrHeadPosition.z); - trackerPosition = bodyOrientation * trackerPosition; if (_calibrationState != CALIBRATED) { ovrQuatf ovrHeadOrientation = ts.HeadPose.ThePose.Orientation; orientation = glm::quat(ovrHeadOrientation.w, ovrHeadOrientation.x, ovrHeadOrientation.y, ovrHeadOrientation.z); calibrate(trackerPosition, orientation); } + + trackerPosition = bodyOrientation * trackerPosition; #endif //Render each eye into an fbo From 32399d8620253f382e6aef88d8f94384de415014 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 4 Oct 2014 20:07:37 -0700 Subject: [PATCH 007/108] Delete calibration message if exit VR display while calibrating --- interface/src/Application.cpp | 2 ++ interface/src/devices/OculusManager.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index da7dd5bb25..c135fd86f3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1478,6 +1478,8 @@ void Application::setEnableVRMode(bool enableVRMode) { OculusManager::connect(); } OculusManager::recalibrate(); + } else { + OculusManager::abandonCalibration(); } resizeGL(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index cfeca9bb9a..6dad7006d9 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -293,9 +293,11 @@ void OculusManager::recalibrate() { void OculusManager::abandonCalibration() { #ifdef HAVE_LIBOVR _calibrationState = CALIBRATED; - qDebug() << "Delete calibration message"; - Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); - _calibrationMessage = 0; + if (_calibrationMessage > 0) { + qDebug() << "Delete calibration message"; + Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); + _calibrationMessage = 0; + } #endif } From ebf615fc30b9f0a01a0141d7b057f8e7618e8e90 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 6 Oct 2014 17:25:40 -0700 Subject: [PATCH 008/108] Fix header declaration --- interface/src/devices/OculusManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 967c8b7139..e46f1673a0 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -111,7 +111,7 @@ private: WAITING_FOR_ZERO_HELD, CALIBRATED }; - static void OculusManager::positionCalibrationBillboard(BillboardOverlay* billboard); + static void positionCalibrationBillboard(BillboardOverlay* billboard); static float CALIBRATION_DELTA_MINIMUM_LENGTH; static float CALIBRATION_DELTA_MINIMUM_ANGLE; static float CALIBRATION_ZERO_MAXIMUM_LENGTH; From dda7c6699e02d2d5c7eecb1b2938a755a043c8c9 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 7 Oct 2014 23:52:00 -0700 Subject: [PATCH 009/108] backup current work on TextRenderer --- interface/src/Util.cpp | 5 + interface/src/Util.h | 2 + interface/src/ui/TextRenderer.cpp | 260 +++++++++++++++++++++++++++++- interface/src/ui/TextRenderer.h | 140 ++++++++++++++++ 4 files changed, 404 insertions(+), 3 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 9d7f5518d0..5399af8e75 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -197,6 +197,11 @@ static TextRenderer* textRenderer(int mono) { } } +void renderTextRenderer(int mono) { + textRenderer(mono)->executeDrawBatch(); + textRenderer(mono)->clearDrawBatch(); +} + int widthText(float scale, int mono, char const* string) { return textRenderer(mono)->computeWidth(string) * (scale / 0.10); } diff --git a/interface/src/Util.h b/interface/src/Util.h index 02cfd99f9a..acdbdb6f75 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -31,6 +31,8 @@ float widthChar(float scale, int mono, char ch); void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color); +void renderTextRenderer(int mono); + void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r=1.0, float g=1.0, float b=1.0); diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index c4f6b328ef..5f00d84b90 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -100,12 +100,25 @@ int TextRenderer::draw(int x, int y, const char* str) { glTexCoord2f(ls, tt); glVertex2f(left, top); glEnd(); - +/* + const int NUM_COORDS_PER_GLYPH = 16; + float vertexBuffer[NUM_COORDS_PER_GLYPH] = { ls, bt, left, bottom, rs, bt, right, bottom, rs, tt, right, top, ls, tt, left, top }; + gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { + _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + } else { + _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + } + _numGlyphsBatched++; +*/ x += glyph.width(); } glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); + // executeDrawBatch(); + // clearDrawBatch(); + return maxHeight; } @@ -131,8 +144,10 @@ TextRenderer::TextRenderer(const Properties& properties) : _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0), - _color(properties.color) { - + _color(properties.color), + _glyphsBuffer(), + _numGlyphsBatched(0) +{ _font.setKerning(false); } @@ -228,9 +243,248 @@ const Glyph& TextRenderer::getGlyph(char c) { return glyph; } +void TextRenderer::executeDrawBatch() { + if (_numGlyphsBatched<=0) { + return; + } + + glEnable(GL_TEXTURE_2D); + + GLuint textureID = 0; + glBindTexture(GL_TEXTURE_2D, textureID); + + gpu::backend::syncGPUObject(_glyphsBuffer); + GLuint vbo = _glyphsBuffer.getGLBufferObject(); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + const int NUM_POS_COORDS = 2; + const int NUM_TEX_COORDS = 2; + const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); + glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); + + glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + +} + +void TextRenderer::clearDrawBatch() { + _numGlyphsBatched = 0; +} + QHash TextRenderer::_instances; Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : _textureID(textureID), _location(location), _bounds(bounds), _width(width) { } +using namespace gpu; + +Buffer::Size Buffer::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { + if ( !dataAllocated ) { + qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; + return NOT_ALLOCATED; + } + + // Try to allocate if needed + Size newSize = 0; + if (size > 0) { + // Try allocating as much as the required size + one block of memory + newSize = size; + (*dataAllocated) = new Byte[newSize]; + // Failed? + if (!(*dataAllocated)) { + qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; + return NOT_ALLOCATED; + } + } + + // Return what's actually allocated + return newSize; +} + +void Buffer::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { + if (dataAllocated) { + delete[] dataAllocated; + } +} + +Buffer::Sysmem::Sysmem() : + _data(NULL), + _size(0), + _stamp(0) +{ +} + +Buffer::Sysmem::Sysmem(Size size, const Byte* bytes) : + _data(NULL), + _size(0), + _stamp(0) +{ + if (size > 0) { + _size = allocateMemory(&_data, size); + if (_size >= size) { + if (bytes) { + memcpy(_data, bytes, size); + } + } + } +} + +Buffer::Sysmem::~Sysmem() { + deallocateMemory( _data, _size ); + _data = NULL; + _size = 0; +} + +Buffer::Size Buffer::Sysmem::allocate(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return 0; + } + newSize = allocated; + } + // Allocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Buffer::Size Buffer::Sysmem::resize(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return _size; + } + newSize = allocated; + // Restore back data from old buffer in the new one + if (_data) { + Size copySize = ((newSize < _size)? newSize: _size); + memcpy( newData, _data, copySize); + } + } + // Reallocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Buffer::Size Buffer::Sysmem::setData( Size size, const Byte* bytes ) { + if (allocate(size) == size) { + if (bytes) { + memcpy( _data, bytes, _size ); + _stamp++; + } + } + return _size; +} + +Buffer::Size Buffer::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { + if (((offset + size) <= getSize()) && bytes) { + memcpy( _data + offset, bytes, size ); + _stamp++; + return size; + } + return 0; +} + +Buffer::Size Buffer::Sysmem::append(Size size, const Byte* bytes) { + if (size > 0) { + Size oldSize = getSize(); + Size totalSize = oldSize + size; + if (resize(totalSize) == totalSize) { + return setSubData(oldSize, size, bytes); + } + } + return 0; +} + +Buffer::Buffer() : + _sysmem(NULL), + _gpuObject(NULL) { + _sysmem = new Sysmem(); +} + +Buffer::~Buffer() { + if (_sysmem) { + delete _sysmem; + _sysmem = 0; + } + if (_gpuObject) { + delete _gpuObject; + _gpuObject = 0; + } +} + +Buffer::Size Buffer::resize(Size size) { + return editSysmem().resize(size); +} + +Buffer::Size Buffer::setData(Size size, const Byte* data) { + return editSysmem().setData(size, data); +} + +Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { + return editSysmem().setSubData( offset, size, data); +} + +Buffer::Size Buffer::append(Size size, const Byte* data) { + return editSysmem().append( size, data); +} + +namespace gpu { +namespace backend { + +void syncGPUObject(const Buffer& buffer) { + BufferObject* object = buffer.getGPUObject(); + + if (object && (object->_stamp == buffer.getSysmem().getStamp())) { + return; + } + + // need to have a gpu object? + if (!object) { + object = new BufferObject(); + glGenBuffers(1, &object->_buffer); + buffer.setGPUObject(object); + } + + // Now let's update the content of the bo with the sysmem version + //if (object->_size < buffer.getSize()) { + glBindBuffer(GL_COPY_WRITE_BUFFER, object->_buffer); + glBufferData(GL_COPY_WRITE_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().read(), GL_STATIC_DRAW); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + object->_stamp = buffer.getSysmem().getStamp(); + object->_size = buffer.getSysmem().getSize(); + //} +} + +}; +}; \ No newline at end of file diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index d3340462d5..ec1c9a088e 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -19,6 +19,8 @@ #include #include +#include + #include "InterfaceConfig.h" // a special "character" that renders as a solid block @@ -35,6 +37,138 @@ const char SOLID_BLOCK_CHAR = 127; class Glyph; +namespace gpu { +class Buffer; +typedef int Stamp; + +namespace backend { + + class BufferObject { + public: + Stamp _stamp; + GLuint _buffer; + GLuint _size; + + BufferObject() : + _stamp(0), + _buffer(0), + _size(0) + {} + }; + void syncGPUObject(const Buffer& buffer); +}; + +class Buffer { +public: + + typedef unsigned char Byte; + typedef unsigned int Size; + + static const Size MIN_ALLOCATION_BLOCK_SIZE = 256; + static const Size NOT_ALLOCATED = -1; + + Buffer(); + Buffer(const Buffer& buf ); + ~Buffer(); + + // The size in bytes of data stored in the buffer + inline Size getSize() const { return getSysmem().getSize(); } + inline const Byte* getData() const { return getSysmem().read(); } + + // Resize the buffer + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData(Size size, const Byte* data); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setSubData(Size offset, Size size, const Byte* data); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object + // TODO: remove asap, when the backend is doing more of the gl features + inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } + +protected: + + // Sysmem is the underneath cache for the data in ram. + class Sysmem { + public: + + Sysmem(); + Sysmem(Size size , const Byte* bytes); + ~Sysmem(); + + Size getSize() const { return _size; } + + // Allocate the byte array + // \param pSize The nb of bytes to allocate, if already exist, content is lost. + // \return The nb of bytes allocated, nothing if allready the appropriate size. + Size allocate(Size pSize); + + // Resize the byte array + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData( Size size, const Byte* bytes ); + + // Update Sub data, + // doesn't allocate and only copy size * bytes at the offset location + // only if all fits in the existing allocated buffer + Size setSubData( Size offset, Size size, const Byte* bytes); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // Access the byte array. + // The edit version allow to map data. + inline const Byte* read() const { return _data; } + inline Byte* edit() { _stamp++; return _data; } + + template< typename T > + const T* read() const { return reinterpret_cast< T* > ( _data ); } + template< typename T > + T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } + + // Access the current version of the sysmem, used to compare if copies are in sync + inline Stamp getStamp() const { return _stamp; } + + static Size allocateMemory(Byte** memAllocated, Size size); + static void deallocateMemory(Byte* memDeallocated, Size size); + + private: + Sysmem(const Sysmem& sysmem) {} + Sysmem &operator=(const Sysmem &other) {return *this;} + + Byte* _data; + Size _size; + Stamp _stamp; + }; + + Sysmem* _sysmem; + + typedef backend::BufferObject GPUObject; + mutable backend::BufferObject* _gpuObject; + + inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } + inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } + + inline GPUObject* getGPUObject() const { return _gpuObject; } + inline void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + + friend void backend::syncGPUObject(const Buffer& buffer); +}; + +}; + class TextRenderer { public: @@ -64,6 +198,8 @@ public: int computeWidth(char ch); int computeWidth(const char* str); + void executeDrawBatch(); + void clearDrawBatch(); private: TextRenderer(const Properties& properties); @@ -100,6 +236,10 @@ private: // text color QColor _color; + // Graphics Buffer containing the current accumulated glyphs to render + gpu::Buffer _glyphsBuffer; + int _numGlyphsBatched; + static QHash _instances; }; From 05915ae134b15e0dfdd0409f4fdd73c5ad13ef10 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 8 Oct 2014 11:53:07 -0700 Subject: [PATCH 010/108] Make calibration message update its location more often --- interface/src/devices/OculusManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 6dad7006d9..8078c2666b 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -263,7 +263,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { if (quarterSeconds == 4 * (quarterSeconds / 4)) { quint64 wholeSeconds = CALIBRATION_ZERO_HOLD_TIME / 1000000 - quarterSeconds / 4; - if (wholeSeconds == 2) { + if (wholeSeconds == 3) { positionCalibrationBillboard(billboard); } From 1990419eebb74aa332c951fdebbd7b243081e5e5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 15:15:49 -0700 Subject: [PATCH 011/108] first cut at correct handle hide/show behavior and rotate overlay hide/show behavior --- examples/libraries/entitySelectionTool.js | 372 +++++++++++++++++++--- 1 file changed, 323 insertions(+), 49 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d9cf2c54fd..a1dd1d2032 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -31,6 +31,13 @@ SelectionDisplay = (function () { var handleHoverColor = { red: 224, green: 67, blue: 36 }; var handleHoverAlpha = 1.0; + var yawHandleRotation; + var pitchHandleRotation; + var rollHandleRotation; + var yawCenter; + var pitchCenter; + var rollCenter; + var rotateHandleColor = { red: 0, green: 0, blue: 0 }; var rotateHandleAlpha = 0.7; @@ -412,10 +419,6 @@ SelectionDisplay = (function () { var pitchCorner; var rollCorner; - var yawHandleRotation; - var pitchHandleRotation; - var rollHandleRotation; - // determine which bottom corner we are closest to /*------------------------------ example: @@ -448,6 +451,18 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: near - rotateHandleOffset}; + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: center.x, + y: center.y, + z: far }; + + rollCenter = { x: left, + y: center.y, + z: center.z}; + } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 270, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); @@ -465,6 +480,19 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: near - rotateHandleOffset}; + + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: left, + y: center.y, + z: center.z }; + + rollCenter = { x: center.x, + y: center.y, + z: near}; + } } else { // must be BLF or BLN @@ -485,6 +513,19 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: far + rotateHandleOffset}; + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: right, + y: center.y, + z: center.z }; + + rollCenter = { x: center.x, + y: center.y, + z: far}; + + } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 180, z: 0 }); @@ -502,8 +543,40 @@ SelectionDisplay = (function () { rollCorner = { x: right + rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset}; + + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: center.x, + y: center.y, + z: near }; + + rollCenter = { x: right, + y: center.y, + z: center.z}; + + } } + + + print("select() with mode:" + mode); + var rotateHandlesVisible = true; + var translateHandlesVisible = true; + var stretchHandlesVisible = true; + if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_XZ") { + rotateHandlesVisible = false; + translateHandlesVisible = false; + stretchHandlesVisible = false; + } else if (mode == "TRANSLATE_UP_DOWN") { + rotateHandlesVisible = false; + stretchHandlesVisible = false; + } else if (mode != "UNKNOWN") { + // every other mode is a stretch mode... + rotateHandlesVisible = false; + translateHandlesVisible = false; + } Overlays.editOverlay(highlightBox, { visible: false }); @@ -516,37 +589,37 @@ SelectionDisplay = (function () { }); - Overlays.editOverlay(grabberMoveUp, { visible: true, position: { x: center.x, y: top + grabberMoveUpOffset, z: center.z } }); + Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: center.x, y: top + grabberMoveUpOffset, z: center.z } }); - Overlays.editOverlay(grabberLBN, { visible: true, position: { x: left, y: bottom, z: near } }); - Overlays.editOverlay(grabberRBN, { visible: true, position: { x: right, y: bottom, z: near } }); - Overlays.editOverlay(grabberLBF, { visible: true, position: { x: left, y: bottom, z: far } }); - Overlays.editOverlay(grabberRBF, { visible: true, position: { x: right, y: bottom, z: far } }); - Overlays.editOverlay(grabberLTN, { visible: true, position: { x: left, y: top, z: near } }); - Overlays.editOverlay(grabberRTN, { visible: true, position: { x: right, y: top, z: near } }); - Overlays.editOverlay(grabberLTF, { visible: true, position: { x: left, y: top, z: far } }); - Overlays.editOverlay(grabberRTF, { visible: true, position: { x: right, y: top, z: far } }); + Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: near } }); + Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: near } }); + Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: far } }); + Overlays.editOverlay(grabberRBF, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: far } }); + Overlays.editOverlay(grabberLTN, { visible: stretchHandlesVisible, position: { x: left, y: top, z: near } }); + Overlays.editOverlay(grabberRTN, { visible: stretchHandlesVisible, position: { x: right, y: top, z: near } }); + Overlays.editOverlay(grabberLTF, { visible: stretchHandlesVisible, position: { x: left, y: top, z: far } }); + Overlays.editOverlay(grabberRTF, { visible: stretchHandlesVisible, position: { x: right, y: top, z: far } }); - Overlays.editOverlay(grabberTOP, { visible: true, position: { x: center.x, y: top, z: center.z } }); - Overlays.editOverlay(grabberBOTTOM, { visible: true, position: { x: center.x, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberLEFT, { visible: true, position: { x: left, y: center.y, z: center.z } }); - Overlays.editOverlay(grabberRIGHT, { visible: true, position: { x: right, y: center.y, z: center.z } }); - Overlays.editOverlay(grabberNEAR, { visible: true, position: { x: center.x, y: center.y, z: near } }); - Overlays.editOverlay(grabberFAR, { visible: true, position: { x: center.x, y: center.y, z: far } }); + Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: center.z } }); + Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: center.z } }); + Overlays.editOverlay(grabberLEFT, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: center.z } }); + Overlays.editOverlay(grabberRIGHT, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: center.z } }); + Overlays.editOverlay(grabberNEAR, { visible: stretchHandlesVisible, position: { x: center.x, y: center.y, z: near } }); + Overlays.editOverlay(grabberFAR, { visible: stretchHandlesVisible, position: { x: center.x, y: center.y, z: far } }); - Overlays.editOverlay(grabberEdgeTR, { visible: true, position: { x: right, y: top, z: center.z } }); - Overlays.editOverlay(grabberEdgeTL, { visible: true, position: { x: left, y: top, z: center.z } }); - Overlays.editOverlay(grabberEdgeTF, { visible: true, position: { x: center.x, y: top, z: far } }); - Overlays.editOverlay(grabberEdgeTN, { visible: true, position: { x: center.x, y: top, z: near } }); - Overlays.editOverlay(grabberEdgeBR, { visible: true, position: { x: right, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberEdgeBL, { visible: true, position: { x: left, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberEdgeBF, { visible: true, position: { x: center.x, y: bottom, z: far } }); - Overlays.editOverlay(grabberEdgeBN, { visible: true, position: { x: center.x, y: bottom, z: near } }); - Overlays.editOverlay(grabberEdgeNR, { visible: true, position: { x: right, y: center.y, z: near } }); - Overlays.editOverlay(grabberEdgeNL, { visible: true, position: { x: left, y: center.y, z: near } }); - Overlays.editOverlay(grabberEdgeFR, { visible: true, position: { x: right, y: center.y, z: far } }); - Overlays.editOverlay(grabberEdgeFL, { visible: true, position: { x: left, y: center.y, z: far } }); + Overlays.editOverlay(grabberEdgeTR, { visible: stretchHandlesVisible, position: { x: right, y: top, z: center.z } }); + Overlays.editOverlay(grabberEdgeTL, { visible: stretchHandlesVisible, position: { x: left, y: top, z: center.z } }); + Overlays.editOverlay(grabberEdgeTF, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: far } }); + Overlays.editOverlay(grabberEdgeTN, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: near } }); + Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: center.z } }); + Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: center.z } }); + Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: far } }); + Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: near } }); + Overlays.editOverlay(grabberEdgeNR, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: near } }); + Overlays.editOverlay(grabberEdgeNL, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: near } }); + Overlays.editOverlay(grabberEdgeFR, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: far } }); + Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: far } }); Overlays.editOverlay(baseOfEntityProjectionOverlay, @@ -561,15 +634,10 @@ SelectionDisplay = (function () { dimensions: { x: properties.dimensions.x, y: properties.dimensions.z }, rotation: properties.rotation, }); - - + Overlays.editOverlay(rotateOverlayInner, { visible: false, - position: { x: properties.position.x, - y: properties.position.y - (properties.dimensions.y / 2), - z: properties.position.z}, - size: innerRadius, innerRadius: 0.9, alpha: innerAlpha @@ -578,10 +646,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { visible: false, - position: { x: properties.position.x, - y: properties.position.y - (properties.dimensions.y / 2), - z: properties.position.z}, - size: outerRadius, innerRadius: 0.9, startAt: 0, @@ -592,10 +656,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { visible: false, - position: { x: properties.position.x, - y: properties.position.y - (properties.dimensions.y / 2), - z: properties.position.z}, - size: outerRadius, startAt: 0, endAt: 0, @@ -603,9 +663,9 @@ SelectionDisplay = (function () { }); // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden - Overlays.editOverlay(yawHandle, { visible: false, position: yawCorner, rotation: yawHandleRotation}); - Overlays.editOverlay(pitchHandle, { visible: false, position: pitchCorner, rotation: pitchHandleRotation}); - Overlays.editOverlay(rollHandle, { visible: false, 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}); Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); }; @@ -1440,7 +1500,25 @@ SelectionDisplay = (function () { Entities.editEntity(currentSelection, selectedEntityProperties); tooltip.updateText(selectedEntityProperties); that.select(currentSelection, false); // TODO: this should be more than highlighted - }; + }; + + that.rotateYaw = function(event) { + if (!entitySelected || mode !== "ROTATE_YAW") { + return; // not allowed + } + }; + + that.rotatePitch = function(event) { + if (!entitySelected || mode !== "ROTATE_PITCH") { + return; // not allowed + } + }; + + that.rotateRoll = function(event) { + if (!entitySelected || mode !== "ROTATE_ROLL") { + return; // not allowed + } + }; that.checkMove = function() { if (currentSelection.isKnownID && @@ -1477,6 +1555,36 @@ SelectionDisplay = (function () { case grabberMoveUp: mode = "TRANSLATE_UP_DOWN"; somethingClicked = true; + + // in translate mode, we hide our stretch handles... + 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(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 }); break; case grabberRBN: @@ -1559,20 +1667,128 @@ 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 }); + + if (mode != "TRANSLATE_UP_DOWN") { + 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 }); var result = Overlays.findRayIntersection(pickRay); + + var overlayOrientation; + var overlayCenter; + if (result.intersects) { switch(result.overlayID) { + case yawHandle: + mode = "ROTATE_YAW"; + somethingClicked = true; + overlayOrientation = yawHandleRotation; + overlayCenter = yawCenter; + break; + + case pitchHandle: + mode = "ROTATE_PITCH"; + somethingClicked = true; + overlayOrientation = pitchHandleRotation; + overlayCenter = pitchCenter; + break; + + case rollHandle: + mode = "ROTATE_ROLL"; + somethingClicked = true; + overlayOrientation = rollHandleRotation; + overlayCenter = rollCenter; + break; + default: print("mousePressEvent()...... " + overlayNames[result.overlayID]); mode = "UNKNOWN"; break; } } + + print(" somethingClicked:" + somethingClicked); + print(" mode:" + mode); + + if (somethingClicked) { + + // TODO: need to place correctly.... + print(" attempting to show overlays:" + somethingClicked); + + Overlays.editOverlay(rotateOverlayInner, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + + Overlays.editOverlay(rotateOverlayOuter, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + + Overlays.editOverlay(rotateOverlayCurrent, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + + 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(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 }); + } } if (!somethingClicked) { @@ -1614,6 +1830,15 @@ SelectionDisplay = (function () { that.mouseMoveEvent = function(event) { //print("mouseMoveEvent()... mode:" + mode); switch (mode) { + case "ROTATE_YAW": + that.rotateYaw(event); + break; + case "ROTATE_PITCH": + that.rotatePitch(event); + break; + case "ROTATE_ROLL": + that.rotateRoll(event); + break; case "TRANSLATE_UP_DOWN": that.translateUpDown(event); break; @@ -1671,6 +1896,54 @@ SelectionDisplay = (function () { }; that.mouseReleaseEvent = function(event) { + var showHandles = false; + // hide our rotation overlays..., and show our handles + if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL") { + Overlays.editOverlay(rotateOverlayInner, { visible: false }); + Overlays.editOverlay(rotateOverlayOuter, { visible: false }); + Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + showHandles = true; + } + + if (mode != "UNKNOWN") { + showHandles = true; + } + + if (showHandles) { + Overlays.editOverlay(yawHandle, { visible: true }); + Overlays.editOverlay(pitchHandle, { visible: true }); + Overlays.editOverlay(rollHandle, { visible: true }); + Overlays.editOverlay(grabberMoveUp, { visible: true }); + Overlays.editOverlay(grabberLBN, { visible: true }); + Overlays.editOverlay(grabberLBF, { visible: true }); + Overlays.editOverlay(grabberRBN, { visible: true }); + Overlays.editOverlay(grabberRBF, { visible: true }); + Overlays.editOverlay(grabberLTN, { visible: true }); + Overlays.editOverlay(grabberLTF, { visible: true }); + Overlays.editOverlay(grabberRTN, { visible: true }); + Overlays.editOverlay(grabberRTF, { visible: true }); + + Overlays.editOverlay(grabberTOP, { visible: true }); + Overlays.editOverlay(grabberBOTTOM, { visible: true }); + Overlays.editOverlay(grabberLEFT, { visible: true }); + Overlays.editOverlay(grabberRIGHT, { visible: true }); + Overlays.editOverlay(grabberNEAR, { visible: true }); + Overlays.editOverlay(grabberFAR, { visible: true }); + + Overlays.editOverlay(grabberEdgeTR, { visible: true }); + Overlays.editOverlay(grabberEdgeTL, { visible: true }); + Overlays.editOverlay(grabberEdgeTF, { visible: true }); + Overlays.editOverlay(grabberEdgeTN, { visible: true }); + Overlays.editOverlay(grabberEdgeBR, { visible: true }); + Overlays.editOverlay(grabberEdgeBL, { visible: true }); + Overlays.editOverlay(grabberEdgeBF, { visible: true }); + Overlays.editOverlay(grabberEdgeBN, { visible: true }); + Overlays.editOverlay(grabberEdgeNR, { visible: true }); + Overlays.editOverlay(grabberEdgeNL, { visible: true }); + Overlays.editOverlay(grabberEdgeFR, { visible: true }); + Overlays.editOverlay(grabberEdgeFL, { visible: true }); + } + mode = "UNKNOWN"; // if something is selected, then reset the "original" properties for any potential next click+move operation @@ -1679,6 +1952,7 @@ SelectionDisplay = (function () { selectedEntityPropertiesOriginalPosition = properties.position; selectedEntityPropertiesOriginalDimensions = properties.dimensions; } + }; Controller.mousePressEvent.connect(that.mousePressEvent); From e6dee30a6ac4b5cb1a93b1c6f0396f1795a78f4c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Oct 2014 16:01:43 -0700 Subject: [PATCH 012/108] handle drawing a table for array values --- .../resources/describe-settings.json | 11 ++++--- domain-server/resources/web/js/settings.js | 32 +++++++++++++++---- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 30d843c7eb..a2df9e23be 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -68,10 +68,13 @@ "numbered": false, "can_add": true, "can_delete": true, - "key": { - "name": "username", - "label": "Username" - } + "columns": [ + { + "name": "username", + "label": "Username", + "can_set": true + } + ] } ] }, diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index c2b2babbb2..5318e50d7f 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -285,13 +285,19 @@ function makeTable(setting, setting_name, setting_value) { // Column names html += "" - if (setting.number === true) { + + if (setting.numbered === true) { html += "#" // Row number } - html += "" + setting.key.label + "" // Key + + if (setting.key) { + html += "" + setting.key.label + "" // Key + } + _.each(setting.columns, function(col) { html += "" + col.label + "" // Data }) + if (setting.can_delete === true || setting.can_add === true) { html += "+/-" // Buttons } @@ -301,10 +307,15 @@ function makeTable(setting, setting_name, setting_value) { var row_num = 1 _.each(setting_value, function(row, name) { html += "" + if (setting.numbered === true) { html += "" + row_num + "" } - html += "" + name + "" + + if (setting.key) { + html += "" + name + "" + } + _.each(setting.columns, function(col) { html += "" if (row.hasOwnProperty(col.name)) { @@ -312,6 +323,7 @@ function makeTable(setting, setting_name, setting_value) { } html += "" }) + if (setting.can_delete === true) { html += "" } else if (setting.can_add === true) { @@ -333,17 +345,23 @@ function makeTable(setting, setting_name, setting_value) { function makeTableInputs(setting) { var html = "" + if (setting.numbered === true) { html += "" } - html += "\ - \ - " + + if (setting.key) { + html += "\ + \ + " + } + _.each(setting.columns, function(col) { html += "\ - \ + \ " }) + html += "" html += "" From 01e6acda39fe3f5842ce4ab3715c8687e5c6ad2d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Oct 2014 16:52:04 -0700 Subject: [PATCH 013/108] changes to allow an array setting via table --- domain-server/resources/web/css/style.css | 1 + domain-server/resources/web/js/settings.js | 192 ++++++++++++--------- 2 files changed, 111 insertions(+), 82 deletions(-) diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 4c365ed110..2da27ed900 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -82,4 +82,5 @@ td.buttons { td.buttons .glyphicon { display: block; text-align: center; + font-size: 16px; } diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 5318e50d7f..4f51312068 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -88,80 +88,7 @@ $(document).ready(function(){ }) $('#settings-form').on('click', '.add-row', function(){ - var row = $(this).parents("tr") - var data = row.parent().children(".row-data") - - // Check key spaces - var name = row.children(".key").children("input").val() - if (name.indexOf(' ') !== -1) { - showErrorMessage("Error", "Key contains spaces") - return - } - // Check keys with the same name - var equals = false; - _.each(data.children(".key"), function(element) { - if ($(element).text() === name) { - equals = true - return - } - }) - if (equals) { - showErrorMessage("Error", "Two keys cannot be identical") - return - } - - // Check empty fields - var empty = false; - _.each(row.children(".row-data").children("input"), function(element) { - if ($(element).val().length === 0) { - empty = true - return - } - }) - if (empty) { - showErrorMessage("Error", "Empty field(s)") - return - } - - var input_clone = row.clone() - // Change input row to data row - var full_name = row.parents("table").attr("name") + "." + name - row.attr("class", "row-data") - - _.each(row.children(), function(element) { - if ($(element).hasClass("number")) { // Index row - var numbers = data.children(".number") - if (numbers.length > 0) { - $(element).html(parseInt(numbers.last().text()) + 1) - } else { - $(element).html(1) - } - } else if ($(element).hasClass("buttons")) { // Change buttons - var prevSpan = $(element).parent().prev().children(".buttons").children("span") - var span = $(element).children("span") - if (prevSpan.hasClass("del-row")) { - span.removeClass("glyphicon-ok add-row") - span.addClass("glyphicon-remove del-row") - } else { - span.remove() - } - } else if ($(element).hasClass("key")) { - var input = $(element).children("input") - $(element).html(input.val()) - input.remove() - } else if($(element).hasClass("row-data")) { // Hide inputs - var input = $(element).children("input") - input.attr("type", "hidden") - input.attr("name", full_name + "." + $(element).attr("name")) - input.attr("value", input.val()) - input.attr("data-changed", "true") - - $(element).html($(element).html() + input.val()) - } else { - console.log("Unknown table element") - } - }) - row.parent().append(input_clone) + addTableRow(this); }) $('#settings-form').on('click', '.del-row', function(){ @@ -279,9 +206,12 @@ $('body').on('click', '.save-button', function(e){ }); function makeTable(setting, setting_name, setting_value) { + var isArray = !_.has(setting, 'key') + var html = "" html += "" + setting.help + "" - html += "" + html += "
" // Column names html += "" @@ -303,24 +233,31 @@ function makeTable(setting, setting_name, setting_value) { } html += "" - // Rows + // populate rows in the table from existing values var row_num = 1 - _.each(setting_value, function(row, name) { - html += "" + + _.each(setting_value, function(row, indexOrName) { + html += "" if (setting.numbered === true) { html += "" } if (setting.key) { - html += "" + html += "" } _.each(setting.columns, function(col) { html += "" }) @@ -333,7 +270,7 @@ function makeTable(setting, setting_name, setting_value) { row_num++ }) - // Inputs + // populate inputs in the table for new values, if we can add if (setting.can_add === true) { html += makeTableInputs(setting) } @@ -392,6 +329,97 @@ function badgeSidebarForDifferences(changedInput) { $("a[href='#" + panelParentID + "'] .badge").html(badgeValue); } +function addTableRow(add_glyphicon) { + var row = $(add_glyphicon).closest('tr') + + var table = row.parents('table') + var isArray = table.data('setting-type') === 'array' + + var data = row.parent().children(".row-data") + + if (!isArray) { + // Check key spaces + var name = row.children(".key").children("input").val() + if (name.indexOf(' ') !== -1) { + showErrorMessage("Error", "Key contains spaces") + return + } + // Check keys with the same name + var equals = false; + _.each(data.children(".key"), function(element) { + if ($(element).text() === name) { + equals = true + return + } + }) + if (equals) { + showErrorMessage("Error", "Two keys cannot be identical") + return + } + } + + // Check empty fields + var empty = false; + _.each(row.children(".row-data").children("input"), function(element) { + if ($(element).val().length === 0) { + empty = true + return + } + }) + if (empty) { + showErrorMessage("Error", "Empty field(s)") + return + } + + var input_clone = row.clone() + + // Change input row to data row + var setting_name = row.parents("table").attr("name") + var full_name = setting_name + "." + name + row.attr("class", "row-data") + + _.each(row.children(), function(element) { + if ($(element).hasClass("numbered")) { // Index row + var numbers = data.children(".numbered") + if (numbers.length > 0) { + $(element).html(parseInt(numbers.last().text()) + 1) + } else { + $(element).html(1) + } + } else if ($(element).hasClass("buttons")) { // Change buttons + var prevSpan = $(element).parent().prev().children(".buttons").children("span") + var span = $(element).children("span") + if (prevSpan.hasClass("del-row")) { + span.removeClass("glyphicon-ok add-row") + span.addClass("glyphicon-remove del-row") + } else { + span.remove() + } + } else if ($(element).hasClass("key")) { + var input = $(element).children("input") + $(element).html(input.val()) + input.remove() + } else if ($(element).hasClass("row-data")) { // Hide inputs + var input = $(element).children("input") + input.attr("type", "hidden") + + if (isArray) { + input.attr("name", setting_name) + } else { + input.attr("name", full_name + "." + $(element).attr("name")) + } + + input.attr("value", input.val()) + input.attr("data-changed", "true") + + $(element).html($(element).html() + input.val()) + } else { + console.log("Unknown table element") + } + }) + row.parent().append(input_clone) +} + function showRestartModal() { $('#restart-modal').modal({ backdrop: 'static', From 1fa0d8df72d516612f376b14d31565c9140fef38 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Oct 2014 16:56:41 -0700 Subject: [PATCH 014/108] change the add and remove style for glyphs in settings --- domain-server/resources/web/css/style.css | 2 +- domain-server/resources/web/js/settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 2da27ed900..2766da0830 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -82,5 +82,5 @@ td.buttons { td.buttons .glyphicon { display: block; text-align: center; - font-size: 16px; + font-size: 12px; } diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 4f51312068..c7ebc96a65 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -299,7 +299,7 @@ function makeTableInputs(setting) { " }) - html += "" + html += "" html += "" return html From ed6b62cea428fe1a915d7c7487e787b76013aff8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Oct 2014 17:07:31 -0700 Subject: [PATCH 015/108] clear a row value after it is added to the table --- domain-server/resources/web/js/settings.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index c7ebc96a65..e29f30601b 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -92,9 +92,20 @@ $(document).ready(function(){ }) $('#settings-form').on('click', '.del-row', function(){ - var row = $(this).parents("tr") - row.empty() - row.html(""); + var row = $(this).parents('tr') + + var table = row.parents('table') + var isArray = table.data('setting-type') === 'array' + + if (!isArray) { + // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save + row.empty() + row.html(""); + } else { + // just remove this row completely - the removal of the hidden input will remove it from the array on post + row.remove() + } + }) $('#settings-form').on('change', 'input', function(){ @@ -417,6 +428,11 @@ function addTableRow(add_glyphicon) { console.log("Unknown table element") } }) + + input_clone.find('input').each(function(){ + $(this).val('') + }); + row.parent().append(input_clone) } From e544c06b92e1bacfce57cef3dec7d65be3451ee5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 19:28:40 -0700 Subject: [PATCH 016/108] correctly reselect entity after property edits to make sure handles move if dimensions or position changes --- examples/libraries/entityPropertyDialogBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index fbac30e796..becc8c86b2 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -244,7 +244,7 @@ EntityPropertyDialogBox = (function () { properties.color.blue = array[index++].value; } Entities.editEntity(editModelID, properties); - selectionDisplay.highlightSelectable(editModelID, propeties); + selectionDisplay.select(editModelID, false); } modelSelected = false; }); From 18f3a208f7c7657aef816a21eaef2c32dd217969 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 19:29:07 -0700 Subject: [PATCH 017/108] fix property edit dialog box --- examples/newEditEntities.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index a223813347..b753537631 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -570,6 +570,19 @@ function handeMenuEvent(menuItem) { } } else if (menuItem == "Edit Properties...") { // good place to put the properties dialog + + editModelID = -1; + if (entitySelected) { + print(" Edit Properties.... selectedEntityID="+ selectedEntityID); + editModelID = selectedEntityID; + } else { + print(" Edit Properties.... not holding..."); + } + if (editModelID != -1) { + print(" Edit Properties.... about to edit properties..."); + entityPropertyDialogBox.openDialog(editModelID); + } + } else if (menuItem == "Paste Models") { modelImporter.paste(); } else if (menuItem == "Export Models") { From 4558caf4315e7e791a653ca824e4e05bc3fd31b0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 19:30:43 -0700 Subject: [PATCH 018/108] display current pitch/yaw/roll in rotation overlays --- examples/libraries/entitySelectionTool.js | 33 ++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index a1dd1d2032..69f4e4ae45 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -561,7 +561,6 @@ SelectionDisplay = (function () { } - print("select() with mode:" + mode); var rotateHandlesVisible = true; var translateHandlesVisible = true; var stretchHandlesVisible = true; @@ -1506,6 +1505,14 @@ SelectionDisplay = (function () { if (!entitySelected || mode !== "ROTATE_YAW") { return; // not allowed } + + var pickRay = Camera.computePickRay(event.x, event.y); + var result = Overlays.findRayIntersection(pickRay); + print("result.intersects:" + result.intersects); + print("result.overlayID:" + overlayNames[result.overlayID]); + print("result.distance:" + result.distance); + print("result.face:" + result.face); + Vec3.print("result.intersection:", result.intersection); }; that.rotatePitch = function(event) { @@ -1691,6 +1698,13 @@ SelectionDisplay = (function () { var overlayOrientation; var overlayCenter; + + var properties = Entities.getEntityProperties(currentSelection); + var angles = Quat.safeEulerAngles(properties.rotation); + var pitch = angles.x; + var yaw = angles.y; + var roll = angles.z; + var currentRotation; if (result.intersects) { switch(result.overlayID) { @@ -1699,6 +1713,7 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = yawHandleRotation; overlayCenter = yawCenter; + currentRotation = yaw; break; case pitchHandle: @@ -1706,6 +1721,7 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; + currentRotation = pitch; break; case rollHandle: @@ -1713,6 +1729,7 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = rollHandleRotation; overlayCenter = rollCenter; + currentRotation = roll; break; default: @@ -1725,10 +1742,16 @@ SelectionDisplay = (function () { print(" somethingClicked:" + somethingClicked); print(" mode:" + mode); + if (somethingClicked) { + if (currentRotation < 0) { + currentRotation = currentRotation + 360; + } + // TODO: need to place correctly.... print(" attempting to show overlays:" + somethingClicked); + print(" currentRotation:" + currentRotation); Overlays.editOverlay(rotateOverlayInner, { @@ -1741,14 +1764,18 @@ SelectionDisplay = (function () { { visible: true, rotation: overlayOrientation, - position: overlayCenter + position: overlayCenter, + startAt: currentRotation, + endAt: 360 }); Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, - position: overlayCenter + position: overlayCenter, + startAt: 0, + endAt: currentRotation }); Overlays.editOverlay(yawHandle, { visible: false }); From 2004159778394fd0b691afd29020217ded5c5841 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 20:29:53 -0700 Subject: [PATCH 019/108] implement basic support for planar overlay type intersections --- interface/src/ui/overlays/Circle3DOverlay.cpp | 20 +++++++++++++ interface/src/ui/overlays/Circle3DOverlay.h | 2 ++ interface/src/ui/overlays/Planar3DOverlay.cpp | 28 +++++++++++++++++++ interface/src/ui/overlays/Planar3DOverlay.h | 2 ++ 4 files changed, 52 insertions(+) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 6ff256d48e..7cd385f963 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "Circle3DOverlay.h" #include "renderer/GlowEffect.h" @@ -291,6 +292,25 @@ void Circle3DOverlay::setProperties(const QScriptValue &properties) { } } +bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, + const glm::vec3& direction, float& distance, BoxFace& face) const { + + bool intersects = Planar3DOverlay::findRayIntersection(origin, direction, distance, face); + if (intersects) { + glm::vec3 hitAt = origin + (direction * distance); + float distanceToHit = glm::distance(hitAt, _position); + + float maxDimension = glm::max(_dimensions.x, _dimensions.y); + float innerRadius = maxDimension * getInnerRadius(); + float outerRadius = maxDimension * getOuterRadius(); + + // TODO: this really only works for circles, we should be handling the ellipse case as well... + if (distanceToHit < innerRadius || distanceToHit > outerRadius) { + intersects = false; + } + } + return intersects; +} diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index 791d951105..289781cdd7 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -45,6 +45,8 @@ public: void setMinorTickMarksLength(float value) { _minorTickMarksLength = value; } void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; } void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; } + + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; protected: float _startAt; diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 42e059c3ca..91a3a023f7 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -12,6 +12,8 @@ #include "InterfaceConfig.h" #include +#include +#include #include #include @@ -74,3 +76,29 @@ void Planar3DOverlay::setProperties(const QScriptValue& properties) { } } } + +bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face) const { + + RayIntersectionInfo rayInfo; + rayInfo._rayStart = origin; + rayInfo._rayDirection = direction; + rayInfo._rayLength = std::numeric_limits::max(); + + PlaneShape plane; + + const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); + glm::vec3 normal = _rotation * UNROTATED_NORMAL; + plane.setNormal(normal); + plane.setPoint(_position); // the position is definitely a point on our plane + + bool intersects = plane.findRayIntersection(rayInfo); + + if (intersects) { + distance = rayInfo._hitDistance; + // TODO: if it intersects, we want to check to see if the intersection point is within our dimensions + // glm::vec3 hitAt = origin + direction * distance; + // _dimensions + } + return intersects; +} diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index c1733bc0fb..ee4bb3e05a 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -37,6 +37,8 @@ public: virtual void setProperties(const QScriptValue& properties); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; + protected: glm::vec2 _dimensions; }; From 458475ee64b34b729679c67b69eb57c6503c8081 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 20:31:46 -0700 Subject: [PATCH 020/108] work on intersection with rotation overlays --- examples/libraries/entitySelectionTool.js | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 69f4e4ae45..d88a8a52eb 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1507,6 +1507,12 @@ SelectionDisplay = (function () { } var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + var result = Overlays.findRayIntersection(pickRay); print("result.intersects:" + result.intersects); print("result.overlayID:" + overlayNames[result.overlayID]); @@ -1519,12 +1525,36 @@ SelectionDisplay = (function () { if (!entitySelected || mode !== "ROTATE_PITCH") { return; // not allowed } + var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + var result = Overlays.findRayIntersection(pickRay); + print("result.intersects:" + result.intersects); + print("result.overlayID:" + overlayNames[result.overlayID]); + print("result.distance:" + result.distance); + print("result.face:" + result.face); + Vec3.print("result.intersection:", result.intersection); }; that.rotateRoll = function(event) { if (!entitySelected || mode !== "ROTATE_ROLL") { return; // not allowed } + var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + var result = Overlays.findRayIntersection(pickRay); + print("result.intersects:" + result.intersects); + print("result.overlayID:" + overlayNames[result.overlayID]); + print("result.distance:" + result.distance); + print("result.face:" + result.face); + Vec3.print("result.intersection:", result.intersection); }; that.checkMove = function() { From 188320a7b724a567e22466ed1bf87b1ce2b7e6f6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 09:37:24 -0700 Subject: [PATCH 021/108] badge sidebar for table removal --- domain-server/resources/web/js/settings.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index e29f30601b..ce8dc7ef44 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -94,18 +94,20 @@ $(document).ready(function(){ $('#settings-form').on('click', '.del-row', function(){ var row = $(this).parents('tr') - var table = row.parents('table') + var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' if (!isArray) { // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save row.empty() row.html(""); - } else { + } else { // just remove this row completely - the removal of the hidden input will remove it from the array on post row.remove() + + // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated + badgeSidebarForDifferences($(table)) } - }) $('#settings-form').on('change', 'input', function(){ @@ -264,9 +266,9 @@ function makeTable(setting, setting_name, setting_value) { if (isArray) { html += row // for arrays we add a hidden input to this td so that values can be posted appropriately - html += "" + html += "" } else if (row.hasOwnProperty(col.name)) { - html += row[col.name] + html += row[col.name] } html += "" @@ -326,7 +328,7 @@ function badgeSidebarForDifferences(changedInput) { var badgeValue = 0 - for (var setting in panelJSON) { + for (var setting in Settings.initialValues[panelParentID]) { if (panelJSON[setting] != Settings.initialValues[panelParentID][setting]) { badgeValue += 1 } From 90c73dc9e1f21d429d2228ae3fc4d7b538e81ac1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 09:47:53 -0700 Subject: [PATCH 022/108] post an empty array to remove an array from settings --- domain-server/resources/web/js/settings.js | 11 +++++++++-- domain-server/src/DomainServerSettingsManager.cpp | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index ce8dc7ef44..bc36a73b28 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -102,8 +102,15 @@ $(document).ready(function(){ row.empty() row.html(""); } else { - // just remove this row completely - the removal of the hidden input will remove it from the array on post - row.remove() + if (table.children('tr.row-data').length) { + // this isn't the last row - we can just remove it + row.remove() + } else { + // this is the last row, we can't remove it completely since we need to post an empty array + row.empty() + row.html(""); + } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated badgeSidebarForDifferences($(table)) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 6a63168579..0feaeefe37 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -295,6 +295,8 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant, const QJsonArray& descriptionArray) { + + qDebug() << postedObject; // Iterate on the setting groups foreach(const QString& groupKey, postedObject.keys()) { QJsonValue groupValue = postedObject[groupKey]; From 9f0936de532e3a3474c4fb26b94adc8297343dce Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 9 Oct 2014 10:17:50 -0700 Subject: [PATCH 023/108] Improve the TextRenderer::draw(), first step - Introduce a managed Buffer for data to be used on the GPU. THis is the first type of resource (second will be texture) that we plan to use in the graphics engine in the long term. this is an api agnostic replacement to QGLBuggerObject It's in the new file gpu/Resource.h(.cpp) - Add two gpu::Buffers in the TextRenderer that collect all the glyph vertices (coords + texcoords + color) during the for loop on the string characters of the TextRenderer::draw(). Right now the text glyphs are then drawn in one draw call (and not one per character) at the end of the function. THe step 2 plan is to keep on collecting all the glyphs from all the TextRenderer::Draw() issued during one frame and to draw all of them in a single drawcall. We decided to split the task in 2 so it's easier to review. --- interface/CMakeLists.txt | 2 +- interface/src/Util.cpp | 5 - interface/src/Util.h | 2 - interface/src/gpu/Resource.cpp | 219 ++++++++ interface/src/gpu/Resource.h | 164 ++++++ interface/src/ui/Stats.cpp | 1 - interface/src/ui/TextRenderer.cpp | 813 ++++++++++++------------------ interface/src/ui/TextRenderer.h | 139 +---- 8 files changed, 721 insertions(+), 624 deletions(-) create mode 100644 interface/src/gpu/Resource.cpp create mode 100644 interface/src/gpu/Resource.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 19f8ef6d5f..1202b36a9f 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -41,7 +41,7 @@ configure_file(InterfaceVersion.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceVe # grab the implementation and header files from src dirs file(GLOB INTERFACE_SRCS src/*.cpp src/*.h) -foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels particles entities) +foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels particles entities gpu) file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h) set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}") endforeach(SUBDIR) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 5399af8e75..9d7f5518d0 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -197,11 +197,6 @@ static TextRenderer* textRenderer(int mono) { } } -void renderTextRenderer(int mono) { - textRenderer(mono)->executeDrawBatch(); - textRenderer(mono)->clearDrawBatch(); -} - int widthText(float scale, int mono, char const* string) { return textRenderer(mono)->computeWidth(string) * (scale / 0.10); } diff --git a/interface/src/Util.h b/interface/src/Util.h index acdbdb6f75..02cfd99f9a 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -31,8 +31,6 @@ float widthChar(float scale, int mono, char ch); void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color); -void renderTextRenderer(int mono); - void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r=1.0, float g=1.0, float b=1.0); diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp new file mode 100644 index 0000000000..890039e429 --- /dev/null +++ b/interface/src/gpu/Resource.cpp @@ -0,0 +1,219 @@ +// +// Resource.cpp +// interface/src/gpu +// +// Created by Sam Gateau on 10/8/2014. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "Resource.h" + +#include + +using namespace gpu; + +Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { + if ( !dataAllocated ) { + qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; + return NOT_ALLOCATED; + } + + // Try to allocate if needed + Size newSize = 0; + if (size > 0) { + // Try allocating as much as the required size + one block of memory + newSize = size; + (*dataAllocated) = new Byte[newSize]; + // Failed? + if (!(*dataAllocated)) { + qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; + return NOT_ALLOCATED; + } + } + + // Return what's actually allocated + return newSize; +} + +void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { + if (dataAllocated) { + delete[] dataAllocated; + } +} + +Resource::Sysmem::Sysmem() : + _data(NULL), + _size(0), + _stamp(0) +{ +} + +Resource::Sysmem::Sysmem(Size size, const Byte* bytes) : + _data(NULL), + _size(0), + _stamp(0) +{ + if (size > 0) { + _size = allocateMemory(&_data, size); + if (_size >= size) { + if (bytes) { + memcpy(_data, bytes, size); + } + } + } +} + +Resource::Sysmem::~Sysmem() { + deallocateMemory( _data, _size ); + _data = NULL; + _size = 0; +} + +Resource::Size Resource::Sysmem::allocate(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return 0; + } + newSize = allocated; + } + // Allocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Resource::Size Resource::Sysmem::resize(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return _size; + } + newSize = allocated; + // Restore back data from old buffer in the new one + if (_data) { + Size copySize = ((newSize < _size)? newSize: _size); + memcpy( newData, _data, copySize); + } + } + // Reallocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) { + if (allocate(size) == size) { + if (bytes) { + memcpy( _data, bytes, _size ); + _stamp++; + } + } + return _size; +} + +Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { + if (((offset + size) <= getSize()) && bytes) { + memcpy( _data + offset, bytes, size ); + _stamp++; + return size; + } + return 0; +} + +Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) { + if (size > 0) { + Size oldSize = getSize(); + Size totalSize = oldSize + size; + if (resize(totalSize) == totalSize) { + return setSubData(oldSize, size, bytes); + } + } + return 0; +} + +Buffer::Buffer() : + Resource(), + _sysmem(NULL), + _gpuObject(NULL) { + _sysmem = new Sysmem(); +} + +Buffer::~Buffer() { + if (_sysmem) { + delete _sysmem; + _sysmem = 0; + } + if (_gpuObject) { + delete _gpuObject; + _gpuObject = 0; + } +} + +Buffer::Size Buffer::resize(Size size) { + return editSysmem().resize(size); +} + +Buffer::Size Buffer::setData(Size size, const Byte* data) { + return editSysmem().setData(size, data); +} + +Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { + return editSysmem().setSubData( offset, size, data); +} + +Buffer::Size Buffer::append(Size size, const Byte* data) { + return editSysmem().append( size, data); +} + +namespace gpu { +namespace backend { + +BufferObject::~BufferObject() { + if (_buffer!=0) { + glDeleteBuffers(1, &_buffer); + } +} + +void syncGPUObject(const Buffer& buffer) { + BufferObject* object = buffer.getGPUObject(); + + if (object && (object->_stamp == buffer.getSysmem().getStamp())) { + return; + } + + // need to have a gpu object? + if (!object) { + object = new BufferObject(); + glGenBuffers(1, &object->_buffer); + buffer.setGPUObject(object); + } + + // Now let's update the content of the bo with the sysmem version + //if (object->_size < buffer.getSize()) { + glBindBuffer(GL_ARRAY_BUFFER, object->_buffer); + glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + object->_stamp = buffer.getSysmem().getStamp(); + object->_size = buffer.getSysmem().getSize(); + //} +} + +}; +}; \ No newline at end of file diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h new file mode 100644 index 0000000000..32e8e454e8 --- /dev/null +++ b/interface/src/gpu/Resource.h @@ -0,0 +1,164 @@ +// +// Resource.h +// interface/src/gpu +// +// Created by Sam Gateau on 10/8/2014. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Resource_h +#define hifi_gpu_Resource_h + +#include +#include "interfaceconfig.h" + +namespace gpu { + +class Buffer; +typedef int Stamp; + +// TODO: move the backend namespace into dedicated files, for now we keep it close to the gpu objects definition for convenience +namespace backend { + + class BufferObject { + public: + Stamp _stamp; + GLuint _buffer; + GLuint _size; + + BufferObject() : + _stamp(0), + _buffer(0), + _size(0) + {} + + ~BufferObject(); + }; + void syncGPUObject(const Buffer& buffer); +}; + +class Resource { +public: + typedef unsigned char Byte; + typedef unsigned int Size; + + static const Size NOT_ALLOCATED = -1; + + // The size in bytes of data stored in the resource + virtual Size getSize() const = 0; + +protected: + + Resource() {} + virtual ~Resource() {} + + // Sysmem is the underneath cache for the data in ram of a resource. + class Sysmem { + public: + + Sysmem(); + Sysmem(Size size , const Byte* bytes); + ~Sysmem(); + + Size getSize() const { return _size; } + + // Allocate the byte array + // \param pSize The nb of bytes to allocate, if already exist, content is lost. + // \return The nb of bytes allocated, nothing if allready the appropriate size. + Size allocate(Size pSize); + + // Resize the byte array + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData( Size size, const Byte* bytes ); + + // Update Sub data, + // doesn't allocate and only copy size * bytes at the offset location + // only if all fits in the existing allocated buffer + Size setSubData( Size offset, Size size, const Byte* bytes); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // Access the byte array. + // The edit version allow to map data. + inline const Byte* readData() const { return _data; } + inline Byte* editData() { _stamp++; return _data; } + + template< typename T > + const T* read() const { return reinterpret_cast< T* > ( _data ); } + template< typename T > + T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } + + // Access the current version of the sysmem, used to compare if copies are in sync + inline Stamp getStamp() const { return _stamp; } + + static Size allocateMemory(Byte** memAllocated, Size size); + static void deallocateMemory(Byte* memDeallocated, Size size); + + private: + Sysmem(const Sysmem& sysmem) {} + Sysmem &operator=(const Sysmem &other) {return *this;} + + Stamp _stamp; + Size _size; + Byte* _data; + }; + +}; + +class Buffer : public Resource { +public: + + Buffer(); + Buffer(const Buffer& buf ); + ~Buffer(); + + // The size in bytes of data stored in the buffer + inline Size getSize() const { return getSysmem().getSize(); } + inline const Byte* getData() const { return getSysmem().readData(); } + + // Resize the buffer + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData(Size size, const Byte* data); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setSubData(Size offset, Size size, const Byte* data); + + // Append new data at the end of the current buffer + // do a resize( size + getSize) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object + // TODO: remove asap, when the backend is doing more of the gl features + inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } + +protected: + + Sysmem* _sysmem; + + typedef backend::BufferObject GPUObject; + mutable backend::BufferObject* _gpuObject; + + inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } + inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } + + inline GPUObject* getGPUObject() const { return _gpuObject; } + inline void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + + friend void backend::syncGPUObject(const Buffer& buffer); +}; + +}; + +#endif \ No newline at end of file diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 6b7d499bf9..dd9ac67837 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -690,7 +690,6 @@ void Stats::display( drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color); } - } void Stats::setMetavoxelStats(int internal, int leaves, int sendProgress, diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 5f00d84b90..d8d1b17da7 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -1,490 +1,343 @@ -// -// TextRenderer.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 4/24/13. -// Copyright 2013 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 -#include - -#include "InterfaceConfig.h" -#include "TextRenderer.h" - -// the width/height of the cached glyph textures -const int IMAGE_SIZE = 256; - -static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { - // can be switched to qHash(key.font, seed) when we require Qt 5.3+ - return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); -} - -static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { - return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; -} - -TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, - EffectType effect, int effectThickness, const QColor& color) { - Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; - TextRenderer*& instance = _instances[properties]; - if (!instance) { - instance = new TextRenderer(properties); - } - return instance; -} - -TextRenderer::~TextRenderer() { - glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); -} - -int TextRenderer::calculateHeight(const char* str) { - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - } - return maxHeight; -} - -int TextRenderer::draw(int x, int y, const char* str) { - glEnable(GL_TEXTURE_2D); - - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - x += glyph.width(); - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - - glBindTexture(GL_TEXTURE_2D, glyph.textureID()); - - int left = x + glyph.bounds().x(); - int right = x + glyph.bounds().x() + glyph.bounds().width(); - int bottom = y + glyph.bounds().y(); - int top = y + glyph.bounds().y() + glyph.bounds().height(); - - float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; - float ls = glyph.location().x() * scale; - float rs = (glyph.location().x() + glyph.bounds().width()) * scale; - float bt = glyph.location().y() * scale; - float tt = (glyph.location().y() + glyph.bounds().height()) * scale; - - glBegin(GL_QUADS); - glTexCoord2f(ls, bt); - glVertex2f(left, bottom); - glTexCoord2f(rs, bt); - glVertex2f(right, bottom); - glTexCoord2f(rs, tt); - glVertex2f(right, top); - glTexCoord2f(ls, tt); - glVertex2f(left, top); - glEnd(); -/* - const int NUM_COORDS_PER_GLYPH = 16; - float vertexBuffer[NUM_COORDS_PER_GLYPH] = { ls, bt, left, bottom, rs, bt, right, bottom, rs, tt, right, top, ls, tt, left, top }; - gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; - if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { - _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - } else { - _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - } - _numGlyphsBatched++; -*/ - x += glyph.width(); - } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - // executeDrawBatch(); - // clearDrawBatch(); - - return maxHeight; -} - -int TextRenderer::computeWidth(char ch) -{ - return getGlyph(ch).width(); -} - -int TextRenderer::computeWidth(const char* str) -{ - int width = 0; - for (const char* ch = str; *ch != 0; ch++) { - width += computeWidth(*ch); - } - return width; -} - -TextRenderer::TextRenderer(const Properties& properties) : - _font(properties.font), - _metrics(_font), - _effectType(properties.effect), - _effectThickness(properties.effectThickness), - _x(IMAGE_SIZE), - _y(IMAGE_SIZE), - _rowHeight(0), - _color(properties.color), - _glyphsBuffer(), - _numGlyphsBatched(0) -{ - _font.setKerning(false); -} - -const Glyph& TextRenderer::getGlyph(char c) { - Glyph& glyph = _glyphs[c]; - if (glyph.isValid()) { - return glyph; - } - // we use 'J' as a representative size for the solid block character - QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); - QRect baseBounds = _metrics.boundingRect(ch); - if (baseBounds.isEmpty()) { - glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); - return glyph; - } - // grow the bounds to account for effect, if any - if (_effectType == SHADOW_EFFECT) { - baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); - - } else if (_effectType == OUTLINE_EFFECT) { - baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); - } - - // grow the bounds to account for antialiasing - baseBounds.adjust(-1, -1, 1, 1); - - // adjust bounds for device pixel scaling - float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); - QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); - - if (_x + bounds.width() > IMAGE_SIZE) { - // we can't fit it on the current row; move to next - _y += _rowHeight; - _x = _rowHeight = 0; - } - if (_y + bounds.height() > IMAGE_SIZE) { - // can't fit it on current texture; make a new one - glGenTextures(1, &_currentTextureID); - _x = _y = _rowHeight = 0; - - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _allTextureIDs.append(_currentTextureID); - - } else { - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - } - // render the glyph into an image and copy it into the texture - QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); - if (c == SOLID_BLOCK_CHAR) { - image.fill(_color); - - } else { - image.fill(0); - QPainter painter(&image); - QFont font = _font; - if (ratio == 1.0f) { - painter.setFont(_font); - } else { - QFont enlargedFont = _font; - enlargedFont.setPointSize(_font.pointSize() * ratio); - painter.setFont(enlargedFont); - } - if (_effectType == SHADOW_EFFECT) { - for (int i = 0; i < _effectThickness * ratio; i++) { - painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); - } - } else if (_effectType == OUTLINE_EFFECT) { - QPainterPath path; - QFont font = _font; - font.setStyleStrategy(QFont::ForceOutline); - path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); - QPen pen; - pen.setWidth(_effectThickness * ratio); - pen.setJoinStyle(Qt::RoundJoin); - pen.setCapStyle(Qt::RoundCap); - painter.setPen(pen); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawPath(path); - } - painter.setPen(_color); - painter.drawText(-bounds.x(), -bounds.y(), ch); - } - glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); - - glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); - _x += bounds.width(); - _rowHeight = qMax(_rowHeight, bounds.height()); - - glBindTexture(GL_TEXTURE_2D, 0); - return glyph; -} - -void TextRenderer::executeDrawBatch() { - if (_numGlyphsBatched<=0) { - return; - } - - glEnable(GL_TEXTURE_2D); - - GLuint textureID = 0; - glBindTexture(GL_TEXTURE_2D, textureID); - - gpu::backend::syncGPUObject(_glyphsBuffer); - GLuint vbo = _glyphsBuffer.getGLBufferObject(); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - const int NUM_POS_COORDS = 2; - const int NUM_TEX_COORDS = 2; - const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); - glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); - glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); - - glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - -} - -void TextRenderer::clearDrawBatch() { - _numGlyphsBatched = 0; -} - -QHash TextRenderer::_instances; - -Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : - _textureID(textureID), _location(location), _bounds(bounds), _width(width) { -} - -using namespace gpu; - -Buffer::Size Buffer::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { - if ( !dataAllocated ) { - qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; - return NOT_ALLOCATED; - } - - // Try to allocate if needed - Size newSize = 0; - if (size > 0) { - // Try allocating as much as the required size + one block of memory - newSize = size; - (*dataAllocated) = new Byte[newSize]; - // Failed? - if (!(*dataAllocated)) { - qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; - return NOT_ALLOCATED; - } - } - - // Return what's actually allocated - return newSize; -} - -void Buffer::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { - if (dataAllocated) { - delete[] dataAllocated; - } -} - -Buffer::Sysmem::Sysmem() : - _data(NULL), - _size(0), - _stamp(0) -{ -} - -Buffer::Sysmem::Sysmem(Size size, const Byte* bytes) : - _data(NULL), - _size(0), - _stamp(0) -{ - if (size > 0) { - _size = allocateMemory(&_data, size); - if (_size >= size) { - if (bytes) { - memcpy(_data, bytes, size); - } - } - } -} - -Buffer::Sysmem::~Sysmem() { - deallocateMemory( _data, _size ); - _data = NULL; - _size = 0; +// +// TextRenderer.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/24/13. +// Copyright 2013 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 +#include + +#include "InterfaceConfig.h" +#include "TextRenderer.h" + +#include "glm/glm.hpp" +#include + + +// the width/height of the cached glyph textures +const int IMAGE_SIZE = 256; + +static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { + // can be switched to qHash(key.font, seed) when we require Qt 5.3+ + return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); } -Buffer::Size Buffer::Sysmem::allocate(Size size) { - if (size != _size) { - Byte* newData = 0; - Size newSize = 0; - if (size > 0) { - Size allocated = allocateMemory(&newData, size); - if (allocated == NOT_ALLOCATED) { - // early exit because allocation failed - return 0; - } - newSize = allocated; - } - // Allocation was successful, can delete previous data - deallocateMemory(_data, _size); - _data = newData; - _size = newSize; - _stamp++; - } - return _size; +static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { + return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; } -Buffer::Size Buffer::Sysmem::resize(Size size) { - if (size != _size) { - Byte* newData = 0; - Size newSize = 0; - if (size > 0) { - Size allocated = allocateMemory(&newData, size); - if (allocated == NOT_ALLOCATED) { - // early exit because allocation failed - return _size; - } - newSize = allocated; - // Restore back data from old buffer in the new one - if (_data) { - Size copySize = ((newSize < _size)? newSize: _size); - memcpy( newData, _data, copySize); - } - } - // Reallocation was successful, can delete previous data - deallocateMemory(_data, _size); - _data = newData; - _size = newSize; - _stamp++; +TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, + EffectType effect, int effectThickness, const QColor& color) { + Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; + TextRenderer*& instance = _instances[properties]; + if (!instance) { + instance = new TextRenderer(properties); } - return _size; + return instance; } -Buffer::Size Buffer::Sysmem::setData( Size size, const Byte* bytes ) { - if (allocate(size) == size) { - if (bytes) { - memcpy( _data, bytes, _size ); - _stamp++; - } - } - return _size; +TextRenderer::~TextRenderer() { + glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); } -Buffer::Size Buffer::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { - if (((offset + size) <= getSize()) && bytes) { - memcpy( _data + offset, bytes, size ); - _stamp++; - return size; +int TextRenderer::calculateHeight(const char* str) { + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } } - return 0; -} - -Buffer::Size Buffer::Sysmem::append(Size size, const Byte* bytes) { - if (size > 0) { - Size oldSize = getSize(); - Size totalSize = oldSize + size; - if (resize(totalSize) == totalSize) { - return setSubData(oldSize, size, bytes); - } - } - return 0; -} - -Buffer::Buffer() : - _sysmem(NULL), - _gpuObject(NULL) { - _sysmem = new Sysmem(); -} - -Buffer::~Buffer() { - if (_sysmem) { - delete _sysmem; - _sysmem = 0; - } - if (_gpuObject) { - delete _gpuObject; - _gpuObject = 0; - } -} - -Buffer::Size Buffer::resize(Size size) { - return editSysmem().resize(size); -} - -Buffer::Size Buffer::setData(Size size, const Byte* data) { - return editSysmem().setData(size, data); -} - -Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { - return editSysmem().setSubData( offset, size, data); -} - -Buffer::Size Buffer::append(Size size, const Byte* data) { - return editSysmem().append( size, data); -} - -namespace gpu { -namespace backend { - -void syncGPUObject(const Buffer& buffer) { - BufferObject* object = buffer.getGPUObject(); - - if (object && (object->_stamp == buffer.getSysmem().getStamp())) { - return; - } - - // need to have a gpu object? - if (!object) { - object = new BufferObject(); - glGenBuffers(1, &object->_buffer); - buffer.setGPUObject(object); - } - - // Now let's update the content of the bo with the sysmem version - //if (object->_size < buffer.getSize()) { - glBindBuffer(GL_COPY_WRITE_BUFFER, object->_buffer); - glBufferData(GL_COPY_WRITE_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().read(), GL_STATIC_DRAW); - glBindBuffer(GL_COPY_WRITE_BUFFER, 0); - object->_stamp = buffer.getSysmem().getStamp(); - object->_size = buffer.getSysmem().getSize(); - //} -} - -}; -}; \ No newline at end of file + return maxHeight; +} + +int TextRenderer::draw(int x, int y, const char* str) { + // Grab the current color + float currentColor[4]; + glGetFloatv(GL_CURRENT_COLOR, currentColor); + int compactColor = ((int( currentColor[0] * 255.f) & 0xFF)) | + ((int( currentColor[1] * 255.f) & 0xFF) << 8) | + ((int( currentColor[2] * 255.f) & 0xFF) << 16) | + ((int( currentColor[3] * 255.f) & 0xFF) << 24); + + //glEnable(GL_TEXTURE_2D); + + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + x += glyph.width(); + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + //glBindTexture(GL_TEXTURE_2D, glyph.textureID()); + + int left = x + glyph.bounds().x(); + int right = x + glyph.bounds().x() + glyph.bounds().width(); + int bottom = y + glyph.bounds().y(); + int top = y + glyph.bounds().y() + glyph.bounds().height(); + + glm::vec2 leftBottom = glm::vec2(float(left), float(bottom)); + glm::vec2 rightTop = glm::vec2(float(right), float(top)); + + float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; + float ls = glyph.location().x() * scale; + float rs = (glyph.location().x() + glyph.bounds().width()) * scale; + float bt = glyph.location().y() * scale; + float tt = (glyph.location().y() + glyph.bounds().height()) * scale; +/* + glBegin(GL_QUADS); + glTexCoord2f(ls, bt); + glVertex2f(left, bottom); + glTexCoord2f(rs, bt); + glVertex2f(right, bottom); + glTexCoord2f(rs, tt); + glVertex2f(right, top); + glTexCoord2f(ls, tt); + glVertex2f(left, top); + glEnd(); +*/ + + const int NUM_COORDS_SCALARS_PER_GLYPH = 16; + float vertexBuffer[NUM_COORDS_SCALARS_PER_GLYPH] = { leftBottom.x, leftBottom.y, ls, bt, + rightTop.x, leftBottom.y, rs, bt, + rightTop.x, rightTop.y, rs, tt, + leftBottom.x, rightTop.y, ls, tt, }; + + const int NUM_COLOR_SCALARS_PER_GLYPH = 4; + unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; + + gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; + gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { + _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } else { + _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } + _numGlyphsBatched++; + + x += glyph.width(); + } + + // TODO: remove these calls once we move to a full batched rendering of the text, for now, one draw call per draw() function call + drawBatch(); + clearBatch(); + + // glBindTexture(GL_TEXTURE_2D, 0); + // glDisable(GL_TEXTURE_2D); + + return maxHeight; +} + +int TextRenderer::computeWidth(char ch) +{ + return getGlyph(ch).width(); +} + +int TextRenderer::computeWidth(const char* str) +{ + int width = 0; + for (const char* ch = str; *ch != 0; ch++) { + width += computeWidth(*ch); + } + return width; +} + +TextRenderer::TextRenderer(const Properties& properties) : + _font(properties.font), + _metrics(_font), + _effectType(properties.effect), + _effectThickness(properties.effectThickness), + _x(IMAGE_SIZE), + _y(IMAGE_SIZE), + _rowHeight(0), + _color(properties.color), + _glyphsBuffer(), + _numGlyphsBatched(0) +{ + _font.setKerning(false); +} + +const Glyph& TextRenderer::getGlyph(char c) { + Glyph& glyph = _glyphs[c]; + if (glyph.isValid()) { + return glyph; + } + // we use 'J' as a representative size for the solid block character + QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); + QRect baseBounds = _metrics.boundingRect(ch); + if (baseBounds.isEmpty()) { + glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); + return glyph; + } + // grow the bounds to account for effect, if any + if (_effectType == SHADOW_EFFECT) { + baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); + + } else if (_effectType == OUTLINE_EFFECT) { + baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); + } + + // grow the bounds to account for antialiasing + baseBounds.adjust(-1, -1, 1, 1); + + // adjust bounds for device pixel scaling + float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); + QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); + + if (_x + bounds.width() > IMAGE_SIZE) { + // we can't fit it on the current row; move to next + _y += _rowHeight; + _x = _rowHeight = 0; + } + if (_y + bounds.height() > IMAGE_SIZE) { + // can't fit it on current texture; make a new one + glGenTextures(1, &_currentTextureID); + _x = _y = _rowHeight = 0; + + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _allTextureIDs.append(_currentTextureID); + + } else { + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + } + // render the glyph into an image and copy it into the texture + QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); + if (c == SOLID_BLOCK_CHAR) { + image.fill(_color); + + } else { + image.fill(0); + QPainter painter(&image); + QFont font = _font; + if (ratio == 1.0f) { + painter.setFont(_font); + } else { + QFont enlargedFont = _font; + enlargedFont.setPointSize(_font.pointSize() * ratio); + painter.setFont(enlargedFont); + } + if (_effectType == SHADOW_EFFECT) { + for (int i = 0; i < _effectThickness * ratio; i++) { + painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); + } + } else if (_effectType == OUTLINE_EFFECT) { + QPainterPath path; + QFont font = _font; + font.setStyleStrategy(QFont::ForceOutline); + path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); + QPen pen; + pen.setWidth(_effectThickness * ratio); + pen.setJoinStyle(Qt::RoundJoin); + pen.setCapStyle(Qt::RoundCap); + painter.setPen(pen); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawPath(path); + } + painter.setPen(_color); + painter.drawText(-bounds.x(), -bounds.y(), ch); + } + glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); + + glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); + _x += bounds.width(); + _rowHeight = qMax(_rowHeight, bounds.height()); + + glBindTexture(GL_TEXTURE_2D, 0); + return glyph; +} + +void TextRenderer::drawBatch() { + if (_numGlyphsBatched<=0) { + return; + } + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + GLint matrixMode; + glGetIntegerv(GL_MATRIX_MODE, &matrixMode); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + */ + + glEnable(GL_TEXTURE_2D); + // TODO: Apply the correct font atlas texture, for now only one texture per TextRenderer so it should be good + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + + GLuint vbo = _glyphsBuffer.getGLBufferObject(); + GLuint colorvbo = _glyphsColorBuffer.getGLBufferObject(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + const int NUM_POS_COORDS = 2; + const int NUM_TEX_COORDS = 2; + const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); + glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); + + glBindBuffer(GL_ARRAY_BUFFER, colorvbo); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*) 0 ); + + glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(matrixMode); + */ +} + +void TextRenderer::clearBatch() { + _numGlyphsBatched = 0; +} + +QHash TextRenderer::_instances; + +Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : + _textureID(textureID), _location(location), _bounds(bounds), _width(width) { +} diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index ec1c9a088e..70283eedd8 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -19,7 +19,7 @@ #include #include -#include +#include "gpu/Resource.h" #include "InterfaceConfig.h" @@ -37,138 +37,6 @@ const char SOLID_BLOCK_CHAR = 127; class Glyph; -namespace gpu { -class Buffer; -typedef int Stamp; - -namespace backend { - - class BufferObject { - public: - Stamp _stamp; - GLuint _buffer; - GLuint _size; - - BufferObject() : - _stamp(0), - _buffer(0), - _size(0) - {} - }; - void syncGPUObject(const Buffer& buffer); -}; - -class Buffer { -public: - - typedef unsigned char Byte; - typedef unsigned int Size; - - static const Size MIN_ALLOCATION_BLOCK_SIZE = 256; - static const Size NOT_ALLOCATED = -1; - - Buffer(); - Buffer(const Buffer& buf ); - ~Buffer(); - - // The size in bytes of data stored in the buffer - inline Size getSize() const { return getSysmem().getSize(); } - inline const Byte* getData() const { return getSysmem().read(); } - - // Resize the buffer - // Keep previous data [0 to min(pSize, mSize)] - Size resize(Size pSize); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData(Size size, const Byte* data); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setSubData(Size offset, Size size, const Byte* data); - - // Append new data at the end of the current buffer - // do a resize( size + getSIze) and copy the new data - // \return the number of bytes copied - Size append(Size size, const Byte* data); - - // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object - // TODO: remove asap, when the backend is doing more of the gl features - inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } - -protected: - - // Sysmem is the underneath cache for the data in ram. - class Sysmem { - public: - - Sysmem(); - Sysmem(Size size , const Byte* bytes); - ~Sysmem(); - - Size getSize() const { return _size; } - - // Allocate the byte array - // \param pSize The nb of bytes to allocate, if already exist, content is lost. - // \return The nb of bytes allocated, nothing if allready the appropriate size. - Size allocate(Size pSize); - - // Resize the byte array - // Keep previous data [0 to min(pSize, mSize)] - Size resize(Size pSize); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData( Size size, const Byte* bytes ); - - // Update Sub data, - // doesn't allocate and only copy size * bytes at the offset location - // only if all fits in the existing allocated buffer - Size setSubData( Size offset, Size size, const Byte* bytes); - - // Append new data at the end of the current buffer - // do a resize( size + getSIze) and copy the new data - // \return the number of bytes copied - Size append(Size size, const Byte* data); - - // Access the byte array. - // The edit version allow to map data. - inline const Byte* read() const { return _data; } - inline Byte* edit() { _stamp++; return _data; } - - template< typename T > - const T* read() const { return reinterpret_cast< T* > ( _data ); } - template< typename T > - T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } - - // Access the current version of the sysmem, used to compare if copies are in sync - inline Stamp getStamp() const { return _stamp; } - - static Size allocateMemory(Byte** memAllocated, Size size); - static void deallocateMemory(Byte* memDeallocated, Size size); - - private: - Sysmem(const Sysmem& sysmem) {} - Sysmem &operator=(const Sysmem &other) {return *this;} - - Byte* _data; - Size _size; - Stamp _stamp; - }; - - Sysmem* _sysmem; - - typedef backend::BufferObject GPUObject; - mutable backend::BufferObject* _gpuObject; - - inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } - inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } - - inline GPUObject* getGPUObject() const { return _gpuObject; } - inline void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } - - friend void backend::syncGPUObject(const Buffer& buffer); -}; - -}; - class TextRenderer { public: @@ -198,8 +66,8 @@ public: int computeWidth(char ch); int computeWidth(const char* str); - void executeDrawBatch(); - void clearDrawBatch(); + void drawBatch(); + void clearBatch(); private: TextRenderer(const Properties& properties); @@ -238,6 +106,7 @@ private: // Graphics Buffer containing the current accumulated glyphs to render gpu::Buffer _glyphsBuffer; + gpu::Buffer _glyphsColorBuffer; int _numGlyphsBatched; static QHash _instances; From a416f6775178b89dc346e05c52f8a89b7f92e001 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 10:33:20 -0700 Subject: [PATCH 024/108] handle add and delete for badged sidebar --- domain-server/resources/web/js/settings.js | 29 +++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index bc36a73b28..a8caf4f418 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -25,7 +25,7 @@ var viewHelpers = { form_group += "" form_group += "
" form_group += ""; form_group += "
" @@ -46,14 +46,14 @@ var viewHelpers = { form_group += "" - form_group += "" + form_group += "" } else { if (input_type == 'integer') { input_type = "text" } - form_group += "" } @@ -117,7 +117,7 @@ $(document).ready(function(){ } }) - $('#settings-form').on('change', 'input', function(){ + $('#settings-form').on('change', 'input.trigger-change', function(){ // this input was changed, add the changed data attribute to it $(this).attr('data-changed', true) @@ -335,12 +335,20 @@ function badgeSidebarForDifferences(changedInput) { var badgeValue = 0 - for (var setting in Settings.initialValues[panelParentID]) { + // badge for any settings we have that are not the same or are not present in initialValues + for (var setting in panelJSON) { if (panelJSON[setting] != Settings.initialValues[panelParentID][setting]) { badgeValue += 1 } } + // badge for any settings we remove that are in the initialValues + for (var setting in Settings.initialValues[panelParentID]) { + if (!_.has(panelJSON, setting)) { + badgeValue += 1 + } + } + // update the list-group-item badge to have the new value if (badgeValue == 0) { badgeValue = "" @@ -394,7 +402,8 @@ function addTableRow(add_glyphicon) { var input_clone = row.clone() // Change input row to data row - var setting_name = row.parents("table").attr("name") + var table = row.parents("table") + var setting_name = table.attr("name") var full_name = setting_name + "." + name row.attr("class", "row-data") @@ -419,7 +428,8 @@ function addTableRow(add_glyphicon) { var input = $(element).children("input") $(element).html(input.val()) input.remove() - } else if ($(element).hasClass("row-data")) { // Hide inputs + } else if ($(element).hasClass("row-data")) { + // Hide inputs var input = $(element).children("input") input.attr("type", "hidden") @@ -429,10 +439,9 @@ function addTableRow(add_glyphicon) { input.attr("name", full_name + "." + $(element).attr("name")) } - input.attr("value", input.val()) input.attr("data-changed", "true") - $(element).html($(element).html() + input.val()) + $(element).append(input.val()) } else { console.log("Unknown table element") } @@ -442,6 +451,8 @@ function addTableRow(add_glyphicon) { $(this).val('') }); + badgeSidebarForDifferences($(table)) + row.parent().append(input_clone) } From adc4e8c5134b58012f4e433769e1a05e52ecc4b5 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 10:39:28 -0700 Subject: [PATCH 025/108] Fixing unix file format issues with msdev2010 --- interface/src/gpu/Resource.cpp | 2 +- interface/src/gpu/Resource.h | 2 +- interface/src/ui/TextRenderer.cpp | 686 +++++++++++++++--------------- 3 files changed, 345 insertions(+), 345 deletions(-) diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp index 890039e429..5e5f8d8ae4 100644 --- a/interface/src/gpu/Resource.cpp +++ b/interface/src/gpu/Resource.cpp @@ -216,4 +216,4 @@ void syncGPUObject(const Buffer& buffer) { } }; -}; \ No newline at end of file +}; diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index 32e8e454e8..e74f2d7697 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -161,4 +161,4 @@ protected: }; -#endif \ No newline at end of file +#endif diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index d8d1b17da7..9cb88f1f03 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -1,343 +1,343 @@ -// -// TextRenderer.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 4/24/13. -// Copyright 2013 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 -#include - -#include "InterfaceConfig.h" -#include "TextRenderer.h" - -#include "glm/glm.hpp" -#include - - -// the width/height of the cached glyph textures -const int IMAGE_SIZE = 256; - -static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { - // can be switched to qHash(key.font, seed) when we require Qt 5.3+ - return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); -} - -static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { - return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; -} - -TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, - EffectType effect, int effectThickness, const QColor& color) { - Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; - TextRenderer*& instance = _instances[properties]; - if (!instance) { - instance = new TextRenderer(properties); - } - return instance; -} - -TextRenderer::~TextRenderer() { - glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); -} - -int TextRenderer::calculateHeight(const char* str) { - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - } - return maxHeight; -} - -int TextRenderer::draw(int x, int y, const char* str) { - // Grab the current color - float currentColor[4]; - glGetFloatv(GL_CURRENT_COLOR, currentColor); - int compactColor = ((int( currentColor[0] * 255.f) & 0xFF)) | - ((int( currentColor[1] * 255.f) & 0xFF) << 8) | - ((int( currentColor[2] * 255.f) & 0xFF) << 16) | - ((int( currentColor[3] * 255.f) & 0xFF) << 24); - - //glEnable(GL_TEXTURE_2D); - - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - x += glyph.width(); - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - //glBindTexture(GL_TEXTURE_2D, glyph.textureID()); - - int left = x + glyph.bounds().x(); - int right = x + glyph.bounds().x() + glyph.bounds().width(); - int bottom = y + glyph.bounds().y(); - int top = y + glyph.bounds().y() + glyph.bounds().height(); - - glm::vec2 leftBottom = glm::vec2(float(left), float(bottom)); - glm::vec2 rightTop = glm::vec2(float(right), float(top)); - - float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; - float ls = glyph.location().x() * scale; - float rs = (glyph.location().x() + glyph.bounds().width()) * scale; - float bt = glyph.location().y() * scale; - float tt = (glyph.location().y() + glyph.bounds().height()) * scale; -/* - glBegin(GL_QUADS); - glTexCoord2f(ls, bt); - glVertex2f(left, bottom); - glTexCoord2f(rs, bt); - glVertex2f(right, bottom); - glTexCoord2f(rs, tt); - glVertex2f(right, top); - glTexCoord2f(ls, tt); - glVertex2f(left, top); - glEnd(); -*/ - - const int NUM_COORDS_SCALARS_PER_GLYPH = 16; - float vertexBuffer[NUM_COORDS_SCALARS_PER_GLYPH] = { leftBottom.x, leftBottom.y, ls, bt, - rightTop.x, leftBottom.y, rs, bt, - rightTop.x, rightTop.y, rs, tt, - leftBottom.x, rightTop.y, ls, tt, }; - - const int NUM_COLOR_SCALARS_PER_GLYPH = 4; - unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; - - gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; - gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; - if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { - _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); - } else { - _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - _glyphsColorBuffer.setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); - } - _numGlyphsBatched++; - - x += glyph.width(); - } - - // TODO: remove these calls once we move to a full batched rendering of the text, for now, one draw call per draw() function call - drawBatch(); - clearBatch(); - - // glBindTexture(GL_TEXTURE_2D, 0); - // glDisable(GL_TEXTURE_2D); - - return maxHeight; -} - -int TextRenderer::computeWidth(char ch) -{ - return getGlyph(ch).width(); -} - -int TextRenderer::computeWidth(const char* str) -{ - int width = 0; - for (const char* ch = str; *ch != 0; ch++) { - width += computeWidth(*ch); - } - return width; -} - -TextRenderer::TextRenderer(const Properties& properties) : - _font(properties.font), - _metrics(_font), - _effectType(properties.effect), - _effectThickness(properties.effectThickness), - _x(IMAGE_SIZE), - _y(IMAGE_SIZE), - _rowHeight(0), - _color(properties.color), - _glyphsBuffer(), - _numGlyphsBatched(0) -{ - _font.setKerning(false); -} - -const Glyph& TextRenderer::getGlyph(char c) { - Glyph& glyph = _glyphs[c]; - if (glyph.isValid()) { - return glyph; - } - // we use 'J' as a representative size for the solid block character - QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); - QRect baseBounds = _metrics.boundingRect(ch); - if (baseBounds.isEmpty()) { - glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); - return glyph; - } - // grow the bounds to account for effect, if any - if (_effectType == SHADOW_EFFECT) { - baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); - - } else if (_effectType == OUTLINE_EFFECT) { - baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); - } - - // grow the bounds to account for antialiasing - baseBounds.adjust(-1, -1, 1, 1); - - // adjust bounds for device pixel scaling - float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); - QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); - - if (_x + bounds.width() > IMAGE_SIZE) { - // we can't fit it on the current row; move to next - _y += _rowHeight; - _x = _rowHeight = 0; - } - if (_y + bounds.height() > IMAGE_SIZE) { - // can't fit it on current texture; make a new one - glGenTextures(1, &_currentTextureID); - _x = _y = _rowHeight = 0; - - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _allTextureIDs.append(_currentTextureID); - - } else { - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - } - // render the glyph into an image and copy it into the texture - QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); - if (c == SOLID_BLOCK_CHAR) { - image.fill(_color); - - } else { - image.fill(0); - QPainter painter(&image); - QFont font = _font; - if (ratio == 1.0f) { - painter.setFont(_font); - } else { - QFont enlargedFont = _font; - enlargedFont.setPointSize(_font.pointSize() * ratio); - painter.setFont(enlargedFont); - } - if (_effectType == SHADOW_EFFECT) { - for (int i = 0; i < _effectThickness * ratio; i++) { - painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); - } - } else if (_effectType == OUTLINE_EFFECT) { - QPainterPath path; - QFont font = _font; - font.setStyleStrategy(QFont::ForceOutline); - path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); - QPen pen; - pen.setWidth(_effectThickness * ratio); - pen.setJoinStyle(Qt::RoundJoin); - pen.setCapStyle(Qt::RoundCap); - painter.setPen(pen); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawPath(path); - } - painter.setPen(_color); - painter.drawText(-bounds.x(), -bounds.y(), ch); - } - glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); - - glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); - _x += bounds.width(); - _rowHeight = qMax(_rowHeight, bounds.height()); - - glBindTexture(GL_TEXTURE_2D, 0); - return glyph; -} - -void TextRenderer::drawBatch() { - if (_numGlyphsBatched<=0) { - return; - } - - // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack - /* - GLint matrixMode; - glGetIntegerv(GL_MATRIX_MODE, &matrixMode); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - */ - - glEnable(GL_TEXTURE_2D); - // TODO: Apply the correct font atlas texture, for now only one texture per TextRenderer so it should be good - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - - GLuint vbo = _glyphsBuffer.getGLBufferObject(); - GLuint colorvbo = _glyphsColorBuffer.getGLBufferObject(); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - const int NUM_POS_COORDS = 2; - const int NUM_TEX_COORDS = 2; - const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); - glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); - - glBindBuffer(GL_ARRAY_BUFFER, colorvbo); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*) 0 ); - - glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack - /* - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(matrixMode); - */ -} - -void TextRenderer::clearBatch() { - _numGlyphsBatched = 0; -} - -QHash TextRenderer::_instances; - -Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : - _textureID(textureID), _location(location), _bounds(bounds), _width(width) { -} +// +// TextRenderer.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/24/13. +// Copyright 2013 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 +#include + +#include "InterfaceConfig.h" +#include "TextRenderer.h" + +#include "glm/glm.hpp" +#include + + +// the width/height of the cached glyph textures +const int IMAGE_SIZE = 256; + +static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { + // can be switched to qHash(key.font, seed) when we require Qt 5.3+ + return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); +} + +static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { + return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; +} + +TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, + EffectType effect, int effectThickness, const QColor& color) { + Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; + TextRenderer*& instance = _instances[properties]; + if (!instance) { + instance = new TextRenderer(properties); + } + return instance; +} + +TextRenderer::~TextRenderer() { + glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); +} + +int TextRenderer::calculateHeight(const char* str) { + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + } + return maxHeight; +} + +int TextRenderer::draw(int x, int y, const char* str) { + // Grab the current color + float currentColor[4]; + glGetFloatv(GL_CURRENT_COLOR, currentColor); + int compactColor = ((int( currentColor[0] * 255.f) & 0xFF)) | + ((int( currentColor[1] * 255.f) & 0xFF) << 8) | + ((int( currentColor[2] * 255.f) & 0xFF) << 16) | + ((int( currentColor[3] * 255.f) & 0xFF) << 24); + + //glEnable(GL_TEXTURE_2D); + + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + x += glyph.width(); + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + //glBindTexture(GL_TEXTURE_2D, glyph.textureID()); + + int left = x + glyph.bounds().x(); + int right = x + glyph.bounds().x() + glyph.bounds().width(); + int bottom = y + glyph.bounds().y(); + int top = y + glyph.bounds().y() + glyph.bounds().height(); + + glm::vec2 leftBottom = glm::vec2(float(left), float(bottom)); + glm::vec2 rightTop = glm::vec2(float(right), float(top)); + + float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; + float ls = glyph.location().x() * scale; + float rs = (glyph.location().x() + glyph.bounds().width()) * scale; + float bt = glyph.location().y() * scale; + float tt = (glyph.location().y() + glyph.bounds().height()) * scale; +/* + glBegin(GL_QUADS); + glTexCoord2f(ls, bt); + glVertex2f(left, bottom); + glTexCoord2f(rs, bt); + glVertex2f(right, bottom); + glTexCoord2f(rs, tt); + glVertex2f(right, top); + glTexCoord2f(ls, tt); + glVertex2f(left, top); + glEnd(); +*/ + + const int NUM_COORDS_SCALARS_PER_GLYPH = 16; + float vertexBuffer[NUM_COORDS_SCALARS_PER_GLYPH] = { leftBottom.x, leftBottom.y, ls, bt, + rightTop.x, leftBottom.y, rs, bt, + rightTop.x, rightTop.y, rs, tt, + leftBottom.x, rightTop.y, ls, tt, }; + + const int NUM_COLOR_SCALARS_PER_GLYPH = 4; + unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; + + gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; + gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { + _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } else { + _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } + _numGlyphsBatched++; + + x += glyph.width(); + } + + // TODO: remove these calls once we move to a full batched rendering of the text, for now, one draw call per draw() function call + drawBatch(); + clearBatch(); + + // glBindTexture(GL_TEXTURE_2D, 0); + // glDisable(GL_TEXTURE_2D); + + return maxHeight; +} + +int TextRenderer::computeWidth(char ch) +{ + return getGlyph(ch).width(); +} + +int TextRenderer::computeWidth(const char* str) +{ + int width = 0; + for (const char* ch = str; *ch != 0; ch++) { + width += computeWidth(*ch); + } + return width; +} + +TextRenderer::TextRenderer(const Properties& properties) : + _font(properties.font), + _metrics(_font), + _effectType(properties.effect), + _effectThickness(properties.effectThickness), + _x(IMAGE_SIZE), + _y(IMAGE_SIZE), + _rowHeight(0), + _color(properties.color), + _glyphsBuffer(), + _numGlyphsBatched(0) +{ + _font.setKerning(false); +} + +const Glyph& TextRenderer::getGlyph(char c) { + Glyph& glyph = _glyphs[c]; + if (glyph.isValid()) { + return glyph; + } + // we use 'J' as a representative size for the solid block character + QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); + QRect baseBounds = _metrics.boundingRect(ch); + if (baseBounds.isEmpty()) { + glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); + return glyph; + } + // grow the bounds to account for effect, if any + if (_effectType == SHADOW_EFFECT) { + baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); + + } else if (_effectType == OUTLINE_EFFECT) { + baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); + } + + // grow the bounds to account for antialiasing + baseBounds.adjust(-1, -1, 1, 1); + + // adjust bounds for device pixel scaling + float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); + QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); + + if (_x + bounds.width() > IMAGE_SIZE) { + // we can't fit it on the current row; move to next + _y += _rowHeight; + _x = _rowHeight = 0; + } + if (_y + bounds.height() > IMAGE_SIZE) { + // can't fit it on current texture; make a new one + glGenTextures(1, &_currentTextureID); + _x = _y = _rowHeight = 0; + + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _allTextureIDs.append(_currentTextureID); + + } else { + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + } + // render the glyph into an image and copy it into the texture + QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); + if (c == SOLID_BLOCK_CHAR) { + image.fill(_color); + + } else { + image.fill(0); + QPainter painter(&image); + QFont font = _font; + if (ratio == 1.0f) { + painter.setFont(_font); + } else { + QFont enlargedFont = _font; + enlargedFont.setPointSize(_font.pointSize() * ratio); + painter.setFont(enlargedFont); + } + if (_effectType == SHADOW_EFFECT) { + for (int i = 0; i < _effectThickness * ratio; i++) { + painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); + } + } else if (_effectType == OUTLINE_EFFECT) { + QPainterPath path; + QFont font = _font; + font.setStyleStrategy(QFont::ForceOutline); + path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); + QPen pen; + pen.setWidth(_effectThickness * ratio); + pen.setJoinStyle(Qt::RoundJoin); + pen.setCapStyle(Qt::RoundCap); + painter.setPen(pen); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawPath(path); + } + painter.setPen(_color); + painter.drawText(-bounds.x(), -bounds.y(), ch); + } + glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); + + glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); + _x += bounds.width(); + _rowHeight = qMax(_rowHeight, bounds.height()); + + glBindTexture(GL_TEXTURE_2D, 0); + return glyph; +} + +void TextRenderer::drawBatch() { + if (_numGlyphsBatched<=0) { + return; + } + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + GLint matrixMode; + glGetIntegerv(GL_MATRIX_MODE, &matrixMode); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + */ + + glEnable(GL_TEXTURE_2D); + // TODO: Apply the correct font atlas texture, for now only one texture per TextRenderer so it should be good + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + + GLuint vbo = _glyphsBuffer.getGLBufferObject(); + GLuint colorvbo = _glyphsColorBuffer.getGLBufferObject(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + const int NUM_POS_COORDS = 2; + const int NUM_TEX_COORDS = 2; + const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); + glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); + + glBindBuffer(GL_ARRAY_BUFFER, colorvbo); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*) 0 ); + + glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(matrixMode); + */ +} + +void TextRenderer::clearBatch() { + _numGlyphsBatched = 0; +} + +QHash TextRenderer::_instances; + +Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : + _textureID(textureID), _location(location), _bounds(bounds), _width(width) { +} From f39120519de52efcb3e59c73e95c8ce96bba6020 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 10:50:05 -0700 Subject: [PATCH 026/108] fix removal icons, handle row removal for hash --- .../resources/describe-settings.json | 4 -- domain-server/resources/web/js/settings.js | 47 +++++++------------ 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a2df9e23be..77b3fcb5fc 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -66,8 +66,6 @@ "label": "Allowed Users", "help": "A list of usernames for the High Fidelity users you want to allow into your domain. Users not found in this list will not be allowed to connect.", "numbered": false, - "can_add": true, - "can_delete": true, "columns": [ { "name": "username", @@ -89,8 +87,6 @@ "label": "Zones", "help": "In this table you can define a set of zones in which you can specify various audio properties.", "numbered": false, - "can_add": true, - "can_delete": true, "key": { "name": "name", "label": "Name", diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index a8caf4f418..9bf3c498c1 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -100,7 +100,7 @@ $(document).ready(function(){ if (!isArray) { // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save row.empty() - row.html(""); + row.html(""); } else { if (table.children('tr.row-data').length) { // this isn't the last row - we can just remove it @@ -111,10 +111,10 @@ $(document).ready(function(){ row.html(""); } - - // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated - badgeSidebarForDifferences($(table)) } + + // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated + badgeSidebarForDifferences($(table)) }) $('#settings-form').on('change', 'input.trigger-change', function(){ @@ -248,10 +248,7 @@ function makeTable(setting, setting_name, setting_value) { html += "
" // Data }) - if (setting.can_delete === true || setting.can_add === true) { - html += "" // Buttons - } - html += "" + html += "" // populate rows in the table from existing values var row_num = 1 @@ -281,20 +278,13 @@ function makeTable(setting, setting_name, setting_value) { html += "" }) - if (setting.can_delete === true) { - html += "" - } else if (setting.can_add === true) { - html += "" - } + html += "" html += "" row_num++ }) - // populate inputs in the table for new values, if we can add - if (setting.can_add === true) { - html += makeTableInputs(setting) - } - + // populate inputs in the table for new values + html += makeTableInputs(setting) html += "
" + row_num + "" + name + "" + indexOrName + "" - if (row.hasOwnProperty(col.name)) { - html += row[col.name] + + if (isArray) { + html += row + // for arrays we add a hidden input to this td so that values can be posted appropriately + html += "" + } else if (row.hasOwnProperty(col.name)) { + html += row[col.name] } + html += "
" + col.label + "+/-
+/-
" return html; @@ -330,8 +320,10 @@ function badgeSidebarForDifferences(changedInput) { var panelParentID = changedInput.closest('.panel').attr('id') // get a JSON representation of that section - var rootJSON = form2js(panelParentID, ".", false, cleanupFormValues, true); - var panelJSON = rootJSON[panelParentID] + var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID] + + console.log(panelJSON) + console.log(Settings.initialValues[panelParentID]) var badgeValue = 0 @@ -408,22 +400,19 @@ function addTableRow(add_glyphicon) { row.attr("class", "row-data") _.each(row.children(), function(element) { - if ($(element).hasClass("numbered")) { // Index row + if ($(element).hasClass("numbered")) { + // Index row var numbers = data.children(".numbered") if (numbers.length > 0) { $(element).html(parseInt(numbers.last().text()) + 1) } else { $(element).html(1) } - } else if ($(element).hasClass("buttons")) { // Change buttons - var prevSpan = $(element).parent().prev().children(".buttons").children("span") + } else if ($(element).hasClass("buttons")) { + // Change buttons var span = $(element).children("span") - if (prevSpan.hasClass("del-row")) { - span.removeClass("glyphicon-ok add-row") - span.addClass("glyphicon-remove del-row") - } else { - span.remove() - } + span.removeClass("glyphicon-ok add-row") + span.addClass("glyphicon-remove del-row") } else if ($(element).hasClass("key")) { var input = $(element).children("input") $(element).html(input.val()) From b0c02311c09ae29931d8bb68a9dc3780cccd92b0 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 10:56:41 -0700 Subject: [PATCH 027/108] Fixing include filename case --- interface/src/gpu/Resource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index e74f2d7697..56defeb554 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -12,7 +12,7 @@ #define hifi_gpu_Resource_h #include -#include "interfaceconfig.h" +#include "InterfaceConfig.h" namespace gpu { From a175c22724a9e0c5cf51be23019f35b04c098b86 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 10:56:54 -0700 Subject: [PATCH 028/108] handle addition and removal on same page session --- domain-server/resources/web/js/settings.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 9bf3c498c1..e2f043bbc9 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -321,6 +321,7 @@ function badgeSidebarForDifferences(changedInput) { // get a JSON representation of that section var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID] + var initialPanelJSON = Settings.initialValues[panelParentID] console.log(panelJSON) console.log(Settings.initialValues[panelParentID]) @@ -329,14 +330,8 @@ function badgeSidebarForDifferences(changedInput) { // badge for any settings we have that are not the same or are not present in initialValues for (var setting in panelJSON) { - if (panelJSON[setting] != Settings.initialValues[panelParentID][setting]) { - badgeValue += 1 - } - } - - // badge for any settings we remove that are in the initialValues - for (var setting in Settings.initialValues[panelParentID]) { - if (!_.has(panelJSON, setting)) { + if (panelJSON[setting] !== initialPanelJSON[setting] + && (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting))) { badgeValue += 1 } } From 51d908008c252476a89bf619335aec2f6da8df90 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 11:00:58 -0700 Subject: [PATCH 029/108] handle array posting in domain server settings manager --- domain-server/src/DomainServerSettingsManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 0feaeefe37..3beccaa3f1 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -289,6 +289,9 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV // we've cleared all of the settings below this value, so remove this one too settingMap.remove(key); } + } else if (newValue.isArray()) { + // we just assume array is replacement + settingMap[key] = newValue.toArray().toVariantList(); } } From d080d6347b86942cb8bd5085e054e89a9d776f68 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 11:10:04 -0700 Subject: [PATCH 030/108] Fixing const usage on a template function --- interface/src/gpu/Resource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index 56defeb554..a7b87881c5 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -94,7 +94,7 @@ protected: template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); } template< typename T > - T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } + T* edit() { _stamp++; return reinterpret_cast< T* > ( _data ); } // Access the current version of the sysmem, used to compare if copies are in sync inline Stamp getStamp() const { return _stamp; } From 68dffabd5ee4d3018efe4e6fa23db6328ad02454 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 11:31:00 -0700 Subject: [PATCH 031/108] handle data-changed setting for all array values --- domain-server/resources/web/js/settings.js | 78 ++++++++++++++-------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index e2f043bbc9..cd8f995870 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -92,29 +92,7 @@ $(document).ready(function(){ }) $('#settings-form').on('click', '.del-row', function(){ - var row = $(this).parents('tr') - - var table = $(row).closest('table') - var isArray = table.data('setting-type') === 'array' - - if (!isArray) { - // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save - row.empty() - row.html(""); - } else { - if (table.children('tr.row-data').length) { - // this isn't the last row - we can just remove it - row.remove() - } else { - // this is the last row, we can't remove it completely since we need to post an empty array - row.empty() - row.html(""); - } - } - - // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated - badgeSidebarForDifferences($(table)) + deleteTableRow(this); }) $('#settings-form').on('change', 'input.trigger-change', function(){ @@ -254,7 +232,7 @@ function makeTable(setting, setting_name, setting_value) { var row_num = 1 _.each(setting_value, function(row, indexOrName) { - html += "" + html += "" if (setting.numbered === true) { html += "" + row_num + "" @@ -323,14 +301,11 @@ function badgeSidebarForDifferences(changedInput) { var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID] var initialPanelJSON = Settings.initialValues[panelParentID] - console.log(panelJSON) - console.log(Settings.initialValues[panelParentID]) - var badgeValue = 0 // badge for any settings we have that are not the same or are not present in initialValues for (var setting in panelJSON) { - if (panelJSON[setting] !== initialPanelJSON[setting] + if (!_.isEqual(panelJSON[setting], initialPanelJSON[setting]) && (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting))) { badgeValue += 1 } @@ -392,7 +367,8 @@ function addTableRow(add_glyphicon) { var table = row.parents("table") var setting_name = table.attr("name") var full_name = setting_name + "." + name - row.attr("class", "row-data") + row.addClass("row-data new-row") + row.removeClass("inputs") _.each(row.children(), function(element) { if ($(element).hasClass("numbered")) { @@ -435,11 +411,55 @@ function addTableRow(add_glyphicon) { $(this).val('') }); + if (isArray) { + updateDataChangedForSiblingRows(row, true) + } + badgeSidebarForDifferences($(table)) row.parent().append(input_clone) } +function deleteTableRow(delete_glyphicon) { + var row = $(delete_glyphicon).closest('tr') + + var table = $(row).closest('table') + var isArray = table.data('setting-type') === 'array' + + if (!isArray) { + // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save + row.empty() + row.html(""); + } else if (table.find('tr.row-data').length > 1) { + updateDataChangedForSiblingRows(row, true) + + // this isn't the last row - we can just remove it + row.remove() + } else { + // this is the last row, we can't remove it completely since we need to post an empty array + row.empty() + + row.html(""); + } + + // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated + badgeSidebarForDifferences($(table)) +} + +function updateDataChangedForSiblingRows(row, isTrue) { + // anytime a new row is added to an array we need to set data-changed for all sibling row-data inputs to true + row.siblings('.row-data').each(function(){ + var hiddenInput = $(this).find('td.row-data input') + if (isTrue) { + hiddenInput.attr('data-changed', isTrue) + } else { + hiddenInput.removeAttr('data-changed') + } + + }) +} + function showRestartModal() { $('#restart-modal').modal({ backdrop: 'static', From bfaf96dbf518348df2bb434a0ec6451e8e6a559d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 11:31:30 -0700 Subject: [PATCH 032/108] remove the allowed_users setting that isn't ready for primetime --- domain-server/resources/describe-settings.json | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 77b3fcb5fc..64441446a8 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -59,20 +59,6 @@ "type": "password", "help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.", "value-hidden": true - }, - { - "name": "allowed_users", - "type": "table", - "label": "Allowed Users", - "help": "A list of usernames for the High Fidelity users you want to allow into your domain. Users not found in this list will not be allowed to connect.", - "numbered": false, - "columns": [ - { - "name": "username", - "label": "Username", - "can_set": true - } - ] } ] }, From 5bb0c4e8fb2b1c6c0ce1d0191a10553017e66fbd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 11:32:09 -0700 Subject: [PATCH 033/108] remove an extra debug of posted settings in DSSM --- domain-server/src/DomainServerSettingsManager.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 3beccaa3f1..e7a9eef1ee 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -298,8 +298,6 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant, const QJsonArray& descriptionArray) { - - qDebug() << postedObject; // Iterate on the setting groups foreach(const QString& groupKey, postedObject.keys()) { QJsonValue groupValue = postedObject[groupKey]; From 806e73d3210c9d550da96477fe982fabbf96c470 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 11:54:37 -0700 Subject: [PATCH 034/108] don't set data-changed when a row is added and deleted in same session --- domain-server/resources/web/js/settings.js | 37 +++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index cd8f995870..aa224fac70 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -21,12 +21,14 @@ var viewHelpers = { label_class += ' locked' } + common_attrs = " class='" + (setting.type !== 'checkbox' ? 'form-control' : '') + + " trigger-change' data-short-name='" + setting.name + "' name='" + setting_name + "' " + if (setting.type === 'checkbox') { form_group += "" form_group += "
" form_group += ""; form_group += "
" } else if (setting.type === 'table') { @@ -46,15 +48,15 @@ var viewHelpers = { form_group += "" - form_group += "" + form_group += "" } else { if (input_type == 'integer') { input_type = "text" } - form_group += "" } @@ -208,7 +210,7 @@ function makeTable(setting, setting_name, setting_value) { var html = "" html += "" + setting.help + "" - html += "" // Column names @@ -431,7 +433,7 @@ function deleteTableRow(delete_glyphicon) { row.empty() row.html(""); } else if (table.find('tr.row-data').length > 1) { - updateDataChangedForSiblingRows(row, true) + updateDataChangedForSiblingRows(row) // this isn't the last row - we can just remove it row.remove() @@ -447,8 +449,27 @@ function deleteTableRow(delete_glyphicon) { badgeSidebarForDifferences($(table)) } -function updateDataChangedForSiblingRows(row, isTrue) { +function updateDataChangedForSiblingRows(row, forceTrue) { // anytime a new row is added to an array we need to set data-changed for all sibling row-data inputs to true + // unless it matches the inital set of values + + if (!forceTrue) { + // figure out which group this row is in + var panelParentID = row.closest('.panel').attr('id') + // get the short name for the setting from the table + var tableShortName = row.closest('table').data('short-name') + + // get a JSON representation of that section + var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName] + var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName] + + // if they are equal, we don't need data-changed + isTrue = _.isEqual(panelSettingJSON, initialPanelSettingJSON) + + } else { + isTrue = true + } + row.siblings('.row-data').each(function(){ var hiddenInput = $(this).find('td.row-data input') if (isTrue) { From 84fa1c7f5fc493ddfa31939e6dec3155ecb34f6a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 11:54:53 -0700 Subject: [PATCH 035/108] remove an extra space --- domain-server/resources/web/js/settings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index aa224fac70..44bb081106 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -465,7 +465,6 @@ function updateDataChangedForSiblingRows(row, forceTrue) { // if they are equal, we don't need data-changed isTrue = _.isEqual(panelSettingJSON, initialPanelSettingJSON) - } else { isTrue = true } From 6d017245685c209162e4cef297d49385620b0dce Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 11:57:11 -0700 Subject: [PATCH 036/108] rename in changedInput in badge sidebar in settings.js --- domain-server/resources/web/js/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 44bb081106..130000fc9c 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -295,9 +295,9 @@ function makeTableInputs(setting) { return html } -function badgeSidebarForDifferences(changedInput) { +function badgeSidebarForDifferences(changedElement) { // figure out which group this input is in - var panelParentID = changedInput.closest('.panel').attr('id') + var panelParentID = changedElement.closest('.panel').attr('id') // get a JSON representation of that section var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID] From 86c20b26ebb8f3b1ab81581dfb15e84259b83be3 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 12:20:46 -0700 Subject: [PATCH 037/108] Adding proper TODO and comments --- interface/src/gpu/Resource.cpp | 3 ++- interface/src/gpu/Resource.h | 6 ++++-- interface/src/ui/TextRenderer.cpp | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp index 5e5f8d8ae4..23ea12e6e8 100644 --- a/interface/src/gpu/Resource.cpp +++ b/interface/src/gpu/Resource.cpp @@ -206,7 +206,8 @@ void syncGPUObject(const Buffer& buffer) { } // Now let's update the content of the bo with the sysmem version - //if (object->_size < buffer.getSize()) { + // TODO: in the future, be smarter about when to actually upload the glBO version based on the data that did change + //if () { glBindBuffer(GL_ARRAY_BUFFER, object->_buffer); glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index a7b87881c5..5c5b7f6367 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -129,9 +129,11 @@ public: Size resize(Size pSize); // Assign data bytes and size (allocate for size, then copy bytes if exists) + // \return the size of the buffer Size setData(Size size, const Byte* data); // Assign data bytes and size (allocate for size, then copy bytes if exists) + // \return the number of bytes copied Size setSubData(Size offset, Size size, const Byte* data); // Append new data at the end of the current buffer @@ -145,10 +147,10 @@ public: protected: - Sysmem* _sysmem; + Sysmem* _sysmem; typedef backend::BufferObject GPUObject; - mutable backend::BufferObject* _gpuObject; + mutable backend::BufferObject* _gpuObject; inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 9cb88f1f03..138c10b26c 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -75,6 +75,7 @@ int TextRenderer::draw(int x, int y, const char* str) { ((int( currentColor[2] * 255.f) & 0xFF) << 16) | ((int( currentColor[3] * 255.f) & 0xFF) << 24); +// TODO: Remove that code once we test for performance improvments //glEnable(GL_TEXTURE_2D); int maxHeight = 0; @@ -103,6 +104,8 @@ int TextRenderer::draw(int x, int y, const char* str) { float rs = (glyph.location().x() + glyph.bounds().width()) * scale; float bt = glyph.location().y() * scale; float tt = (glyph.location().y() + glyph.bounds().height()) * scale; + +// TODO: Remove that code once we test for performance improvments /* glBegin(GL_QUADS); glTexCoord2f(ls, bt); @@ -143,6 +146,7 @@ int TextRenderer::draw(int x, int y, const char* str) { drawBatch(); clearBatch(); +// TODO: Remove that code once we test for performance improvments // glBindTexture(GL_TEXTURE_2D, 0); // glDisable(GL_TEXTURE_2D); From 2da4345b217ed3d8707dd2e0a398c6c0528001e8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 9 Oct 2014 13:32:35 -0700 Subject: [PATCH 038/108] First person and mirror HMD view almost fixed --- interface/src/Application.cpp | 7 +++++-- interface/src/Camera.cpp | 25 ++++--------------------- interface/src/Camera.h | 22 +++++++++++++++------- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/devices/OculusManager.cpp | 17 ++++++++++++----- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718360864a..55fe438cfe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -605,10 +605,14 @@ void Application::paintGL() { if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { if (!OculusManager::isConnected()) { + // If there isn't an HMD, match exactly to avatar's head _myCamera.setPosition(_myAvatar->getHead()->getEyePosition()); _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); + } else { + // For an HMD, set the base position and orientation to that of the avatar body + _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); } - // OculusManager::display() updates camera position and rotation a bit further on. } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { static const float THIRD_PERSON_CAMERA_DISTANCE = 1.5f; @@ -664,7 +668,6 @@ void Application::paintGL() { _viewFrustumOffsetCamera.setRotation(_myCamera.getRotation() * frustumRotation); - _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = &_viewFrustumOffsetCamera; } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 79d66568bf..a8138363fa 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -22,13 +22,16 @@ Camera::Camera() : - _needsToInitialize(true), _mode(CAMERA_MODE_THIRD_PERSON), _position(0.0f, 0.0f, 0.0f), _fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES), _aspectRatio(16.0f/9.0f), _nearClip(DEFAULT_NEAR_CLIP), // default _farClip(DEFAULT_FAR_CLIP), // default + _hmdPosition(), + _hmdRotation(), + _targetPosition(), + _targetRotation(), _scale(1.0f) { } @@ -64,26 +67,6 @@ void Camera::setFarClip(float f) { _farClip = f; } -void Camera::setEyeOffsetPosition(const glm::vec3& p) { - _eyeOffsetPosition = p; -} - -void Camera::setEyeOffsetOrientation(const glm::quat& o) { - _eyeOffsetOrientation = o; - -} - -void Camera::setScale(float s) { - _scale = s; - _needsToInitialize = true; - -} - -void Camera::initialize() { - _needsToInitialize = true; -} - - CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum) : _camera(camera), _viewFrustum(viewFrustum) { diff --git a/interface/src/Camera.h b/interface/src/Camera.h index ee4930272d..80454a969e 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -38,17 +38,23 @@ public: void setPosition(const glm::vec3& p) { _position = p; } void setRotation(const glm::quat& rotation) { _rotation = rotation; }; + void setHmdPosition(const glm::vec3& hmdPosition) { _hmdPosition = hmdPosition; } + void setHmdRotation(const glm::quat& hmdRotation) { _hmdRotation = hmdRotation; }; + void setMode(CameraMode m); void setFieldOfView(float f); void setAspectRatio(float a); void setNearClip(float n); void setFarClip(float f); - void setEyeOffsetPosition(const glm::vec3& p); - void setEyeOffsetOrientation(const glm::quat& o); - void setScale(const float s); + void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; } + void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; } + void setScale(const float s) { _scale = s; } + + glm::vec3 getPosition() const { return _position + _hmdPosition; } + glm::quat getRotation() const { return _rotation * _hmdRotation; } + const glm::vec3& getHmdPosition() const { return _hmdPosition; } + const glm::quat& getHmdRotation() const { return _hmdRotation; } - const glm::vec3& getPosition() const { return _position; } - const glm::quat& getRotation() const { return _rotation; } CameraMode getMode() const { return _mode; } float getFieldOfView() const { return _fieldOfView; } float getAspectRatio() const { return _aspectRatio; } @@ -60,7 +66,6 @@ public: private: - bool _needsToInitialize; CameraMode _mode; glm::vec3 _position; float _fieldOfView; // degrees @@ -70,7 +75,10 @@ private: glm::vec3 _eyeOffsetPosition; glm::quat _eyeOffsetOrientation; glm::quat _rotation; - + glm::vec3 _hmdPosition; + glm::quat _hmdRotation; + glm::vec3 _targetPosition; + glm::quat _targetRotation; float _scale; }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c0ce474d16..36035880fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1151,7 +1151,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const { const Head* head = getHead(); - return (renderMode != NORMAL_RENDER_MODE) || + return (renderMode != NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); } diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index eb48e3d463..c5aa73582e 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -337,15 +337,22 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p #else ovrEyeType eye = _ovrHmdDesc.EyeRenderOrder[eyeIndex]; #endif - //Set the camera rotation for this eye + // Set the camera rotation for this eye eyeRenderPose[eye] = ovrHmd_GetEyePose(_ovrHmd, eye); orientation.x = eyeRenderPose[eye].Orientation.x; orientation.y = eyeRenderPose[eye].Orientation.y; orientation.z = eyeRenderPose[eye].Orientation.z; orientation.w = eyeRenderPose[eye].Orientation.w; - _camera->setRotation(bodyOrientation * orientation); - _camera->setPosition(position + trackerPosition); + // Update the application camera with the latest HMD position + whichCamera.setHmdPosition(trackerPosition); + whichCamera.setHmdRotation(orientation); + + //_camera->setRotation(bodyOrientation * orientation); + //_camera->setPosition(position + trackerPosition); + // Update our camera to what the application camera is doing + _camera->setRotation(whichCamera.getRotation()); + _camera->setPosition(whichCamera.getPosition()); // Store the latest left and right eye render locations for things that need to know glm::vec3 thisEyePosition = position + trackerPosition + @@ -409,8 +416,8 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p glBindTexture(GL_TEXTURE_2D, 0); // Update camera for use by rest of Interface. - whichCamera.setPosition((_leftEyePosition + _rightEyePosition) / 2.f); - whichCamera.setRotation(_camera->getRotation()); + //whichCamera.setPosition((_leftEyePosition + _rightEyePosition) / 2.f); + //whichCamera.setRotation(_camera->getRotation()); #endif } From df49bf8dda57ae72a82663a6460f49fc945cfbd8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 9 Oct 2014 14:04:51 -0700 Subject: [PATCH 039/108] Substantial betterification of Hermite data averaging. --- .../metavoxels/src/AttributeRegistry.cpp | 40 +++++++------------ 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 44c333a03f..397e5b55e4 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -2112,53 +2112,41 @@ bool VoxelHermiteAttribute::merge(void*& parent, void* children[], bool postRead QRgb* dest = contents.data() + ((zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize)) * VoxelHermiteData::EDGE_COUNT; const QRgb* src = childContents.data(); + int offsets[VoxelHermiteData::EDGE_COUNT]; if (childSize == size) { // simple case: one destination value for four child values for (int z = 0; z < halfSizeComplement; z++) { - int offset4 = (z == halfSize) ? 0 : (childArea * VoxelHermiteData::EDGE_COUNT); + offsets[2] = (z == halfSize) ? 0 : (childArea * VoxelHermiteData::EDGE_COUNT); for (int y = 0; y < halfSizeComplement; y++) { - int offset2 = (y == halfSize) ? 0 : (childSize * VoxelHermiteData::EDGE_COUNT); - int offset6 = offset4 + offset2; + offsets[1] = (y == halfSize) ? 0 : (childSize * VoxelHermiteData::EDGE_COUNT); for (QRgb* end = dest + halfSizeComplement * VoxelHermiteData::EDGE_COUNT; dest != end; dest += VoxelHermiteData::EDGE_COUNT) { - int offset1 = (dest == end - VoxelHermiteData::EDGE_COUNT) ? 0 : VoxelHermiteData::EDGE_COUNT; + offsets[0] = (dest == end - VoxelHermiteData::EDGE_COUNT) ? 0 : VoxelHermiteData::EDGE_COUNT; for (int i = 0; i < VoxelHermiteData::EDGE_COUNT; i++) { - QRgb v[] = { src[i], src[offset1 + i], src[offset2 + i], src[offset2 + offset1 + i], - src[offset4 + i], src[offset4 + offset1 + i], src[offset6 + i], src[offset6 + offset1 + i] }; - glm::vec3 n[] = { unpackNormal(v[0]), unpackNormal(v[1]), unpackNormal(v[2]), unpackNormal(v[3]), - unpackNormal(v[4]), unpackNormal(v[5]), unpackNormal(v[6]), unpackNormal(v[7]) }; - float l[] = { glm::length(n[0]), glm::length(n[1]), glm::length(n[2]), glm::length(n[3]), - glm::length(n[4]), glm::length(n[5]), glm::length(n[6]), glm::length(n[7]) }; - float lengthTotal = l[0] + l[1] + l[2] + l[3] + l[4] + l[5] + l[6] + l[7]; + QRgb v0 = src[i], v1 = src[i + offsets[i]]; + glm::vec3 n0 = unpackNormal(v0), n1 = unpackNormal(v1); + float l0 = glm::length(n0), l1 = glm::length(n1); + float lengthTotal = l0 + l1; if (lengthTotal == 0.0f) { dest[i] = qRgba(0, 0, 0, 0); continue; } - glm::vec3 combinedNormal = n[0] * l[0] + n[1] * l[1] + n[2] * l[2] + n[3] * l[3] + n[4] * l[4] + - n[5] * l[5] + n[6] * l[6] + n[7] * l[7]; + glm::vec3 combinedNormal = n0 + n1; float combinedLength = glm::length(combinedNormal); if (combinedLength > 0.0f) { combinedNormal /= combinedLength; } - float combinedOffset = 0.0f; - int mask = 1 << i; - for (int j = 0; j < MERGE_COUNT; j++) { - float offset = qAlpha(v[j]) * (0.5f / EIGHT_BIT_MAXIMUM); - if (j & mask) { - offset += 0.5f; - } - combinedOffset += offset * l[j]; - } - dest[i] = packNormal(combinedNormal, EIGHT_BIT_MAXIMUM * combinedOffset / lengthTotal); + float combinedOffset = qAlpha(v0) * 0.5f * l0 + (qAlpha(v1) + EIGHT_BIT_MAXIMUM) * 0.5f * l1; + dest[i] = packNormal(combinedNormal, combinedOffset / lengthTotal); } - src += (VoxelHermiteData::EDGE_COUNT + offset1); + src += (VoxelHermiteData::EDGE_COUNT + offsets[0]); } dest += (halfSize * VoxelHermiteData::EDGE_COUNT); - src += offset2; + src += offsets[1]; } dest += (halfSize * size * VoxelHermiteData::EDGE_COUNT); - src += offset4; + src += offsets[2]; } } else { // more complex: N destination values for four child values From f69184d8cb65f5222d064530d3069a7dc4300f55 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 9 Oct 2014 14:22:22 -0700 Subject: [PATCH 040/108] Adjustment of threshold multiplier for metavoxels. --- libraries/metavoxels/src/AttributeRegistry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 397e5b55e4..a625c71d6a 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -70,7 +70,7 @@ AttributeRegistry::AttributeRegistry() : _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 32.0f; + const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f; _voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); _voxelColorAttribute->setUserFacing(true); _voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); From e1b64275c9ea1a08d48b3a64faff22d019193803 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 14:26:39 -0700 Subject: [PATCH 041/108] fix population of multi-column array from values in settings --- .../resources/describe-settings.json | 41 +++++++++++++++++++ domain-server/resources/web/js/settings.js | 22 +++++++--- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 64441446a8..e9df7f264d 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -59,6 +59,20 @@ "type": "password", "help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.", "value-hidden": true + }, + { + "name": "allowed_users", + "type": "table", + "label": "Allowed Users", + "help": "A list of usernames for the High Fidelity users you want to allow into your domain. Users not found in this list will not be allowed to connect.", + "numbered": false, + "columns": [ + { + "name": "username", + "label": "Username", + "can_set": true + } + ] } ] }, @@ -99,6 +113,33 @@ } ] }, + { + "name": "attenuation_coefficients", + "type": "table", + "label": "Attenuation Coefficients", + "help": "In this table you can set custom attenuation coefficients between audio zones", + "numbered": false, + "columns": [ + { + "name": "source", + "label": "Source", + "can_set": true, + "placeholder": "Zone_A" + }, + { + "name": "listener", + "label": "Listener", + "can_set": true, + "placeholder": "Zone_B" + }, + { + "name": "coefficient", + "label": "Attenuation coefficient", + "can_set": true, + "placeholder": "0.18" + } + ] + }, { "name": "enable_filter", "type": "checkbox", diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 130000fc9c..ce8352e35c 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -210,7 +210,7 @@ function makeTable(setting, setting_name, setting_value) { var html = "" html += "" + setting.help + "" - html += "
" // Column names @@ -248,9 +248,15 @@ function makeTable(setting, setting_name, setting_value) { html += "" html += "" + row_num++ }) @@ -358,11 +365,12 @@ function addTableRow(add_glyphicon) { return } }) + if (empty) { showErrorMessage("Error", "Empty field(s)") return } - + var input_clone = row.clone() // Change input row to data row @@ -384,7 +392,7 @@ function addTableRow(add_glyphicon) { } else if ($(element).hasClass("buttons")) { // Change buttons var span = $(element).children("span") - span.removeClass("glyphicon-ok add-row") + span.removeClass("glyphicon-plus add-row") span.addClass("glyphicon-remove del-row") } else if ($(element).hasClass("key")) { var input = $(element).children("input") @@ -396,7 +404,9 @@ function addTableRow(add_glyphicon) { input.attr("type", "hidden") if (isArray) { - input.attr("name", setting_name) + var row_index = row.siblings('tr.row-data').length + + input.attr("name", setting_name + "[" + row_index + "]") } else { input.attr("name", full_name + "." + $(element).attr("name")) } From 66b83d0ff9110e34d0f17679ea80325d8ccaade8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 9 Oct 2014 14:31:41 -0700 Subject: [PATCH 042/108] Increment packet version to avoid crashes. --- libraries/networking/src/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 14bcd7c3ce..069b3f000d 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -85,7 +85,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 4; + return 5; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: From 98925e4adbbc44dc1554ed2e5bfbd83dd7946651 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 14:34:05 -0700 Subject: [PATCH 043/108] handle removal of last row and removal of hidden input for empty array --- domain-server/resources/web/js/settings.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index ce8352e35c..385759b79c 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -425,6 +425,9 @@ function addTableRow(add_glyphicon) { if (isArray) { updateDataChangedForSiblingRows(row, true) + + // the addition of any table row should remove the empty-array-row + row.siblings('.empty-array-row').remove() } badgeSidebarForDifferences($(table)) @@ -451,6 +454,9 @@ function deleteTableRow(delete_glyphicon) { // this is the last row, we can't remove it completely since we need to post an empty array row.empty() + row.removeClass('new-row row-data') + row.addClass('empty-array-row') + row.html(""); } From 323eaa0ac9696807973d84345563e56fb66e8403 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 14:36:33 -0700 Subject: [PATCH 044/108] remove an extra space --- domain-server/resources/web/js/settings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 385759b79c..4fa7e2bd08 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -492,7 +492,6 @@ function updateDataChangedForSiblingRows(row, forceTrue) { } else { hiddenInput.removeAttr('data-changed') } - }) } From 2472dc0c113297226308fa6dcf2060e9a76186b6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 14:42:21 -0700 Subject: [PATCH 045/108] handle transition from input to row for array of objects --- domain-server/resources/web/js/settings.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 4fa7e2bd08..f2326d3f11 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -338,15 +338,15 @@ function addTableRow(add_glyphicon) { if (!isArray) { // Check key spaces - var name = row.children(".key").children("input").val() - if (name.indexOf(' ') !== -1) { + var key = row.children(".key").children("input").val() + if (key.indexOf(' ') !== -1) { showErrorMessage("Error", "Key contains spaces") return } // Check keys with the same name var equals = false; _.each(data.children(".key"), function(element) { - if ($(element).text() === name) { + if ($(element).text() === key) { equals = true return } @@ -376,7 +376,7 @@ function addTableRow(add_glyphicon) { // Change input row to data row var table = row.parents("table") var setting_name = table.attr("name") - var full_name = setting_name + "." + name + var full_name = setting_name + "." + key row.addClass("row-data new-row") row.removeClass("inputs") @@ -405,8 +405,12 @@ function addTableRow(add_glyphicon) { if (isArray) { var row_index = row.siblings('tr.row-data').length + var key = $(element).attr('name') - input.attr("name", setting_name + "[" + row_index + "]") + // are there multiple columns or just one? + // with multiple we have an array of Objects, with one we have an array of whatever the value type is + var num_columns = row.children('td.row-data').length + input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) } else { input.attr("name", full_name + "." + $(element).attr("name")) } From 9dc0bf52b8c6b866843880a28b5e97174e8dd37e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 14:44:24 -0700 Subject: [PATCH 046/108] constantize the advanced settings class --- domain-server/resources/web/js/settings.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index f2326d3f11..7b666be5ec 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -1,12 +1,13 @@ var Settings = { - showAdvanced: false + showAdvanced: false, + ADVANCED_CLASS: 'advanced-setting' }; var viewHelpers = { getFormGroup: function(groupName, setting, values, isAdvanced, isLocked) { setting_name = groupName + "." + setting.name - form_group = "
" + form_group = "
" if (_.has(values, groupName) && _.has(values[groupName], setting.name)) { setting_value = values[groupName][setting.name] @@ -106,7 +107,7 @@ $(document).ready(function(){ $('#advanced-toggle-button').click(function(){ Settings.showAdvanced = !Settings.showAdvanced - var advancedSelector = $('.advanced-setting') + var advancedSelector = $('.' + Settings.ADVANCED_CLASS) if (Settings.showAdvanced) { advancedSelector.show() From ad48592a6976cd40539d17c5cc7110c5032d13fb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:02:14 -0700 Subject: [PATCH 047/108] start to constantize some classes in settings.js --- domain-server/resources/web/js/settings.js | 36 ++++++++++++---------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 7b666be5ec..d3faa94b66 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -1,6 +1,8 @@ var Settings = { showAdvanced: false, - ADVANCED_CLASS: 'advanced-setting' + ADVANCED_CLASS: 'advanced-setting', + DATA_ROW_CLASS: 'value-row', + DATA_COL_CLASS: 'value-col' }; var viewHelpers = { @@ -235,7 +237,7 @@ function makeTable(setting, setting_name, setting_value) { var row_num = 1 _.each(setting_value, function(row, indexOrName) { - html += "
" + html += "" if (setting.numbered === true) { html += "" @@ -246,7 +248,7 @@ function makeTable(setting, setting_name, setting_value) { } _.each(setting.columns, function(col) { - html += "" }) @@ -335,7 +337,7 @@ function addTableRow(add_glyphicon) { var table = row.parents('table') var isArray = table.data('setting-type') === 'array' - var data = row.parent().children(".row-data") + var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS) if (!isArray) { // Check key spaces @@ -346,7 +348,7 @@ function addTableRow(add_glyphicon) { } // Check keys with the same name var equals = false; - _.each(data.children(".key"), function(element) { + _.each(columns.children(".key"), function(element) { if ($(element).text() === key) { equals = true return @@ -360,7 +362,7 @@ function addTableRow(add_glyphicon) { // Check empty fields var empty = false; - _.each(row.children(".row-data").children("input"), function(element) { + _.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) { if ($(element).val().length === 0) { empty = true return @@ -378,13 +380,13 @@ function addTableRow(add_glyphicon) { var table = row.parents("table") var setting_name = table.attr("name") var full_name = setting_name + "." + key - row.addClass("row-data new-row") + row.addClass(Settings.DATA_ROW_CLASS + " new-row") row.removeClass("inputs") _.each(row.children(), function(element) { if ($(element).hasClass("numbered")) { // Index row - var numbers = data.children(".numbered") + var numbers = columns.children(".numbered") if (numbers.length > 0) { $(element).html(parseInt(numbers.last().text()) + 1) } else { @@ -399,18 +401,18 @@ function addTableRow(add_glyphicon) { var input = $(element).children("input") $(element).html(input.val()) input.remove() - } else if ($(element).hasClass("row-data")) { + } else if ($(element).hasClass(Settings.DATA_COL_CLASS)) { // Hide inputs var input = $(element).children("input") input.attr("type", "hidden") if (isArray) { - var row_index = row.siblings('tr.row-data').length + var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length var key = $(element).attr('name') // are there multiple columns or just one? // with multiple we have an array of Objects, with one we have an array of whatever the value type is - var num_columns = row.children('td.row-data').length + var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) } else { input.attr("name", full_name + "." + $(element).attr("name")) @@ -450,7 +452,7 @@ function deleteTableRow(delete_glyphicon) { // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save row.empty() row.html(""); - } else if (table.find('tr.row-data').length > 1) { + } else if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { updateDataChangedForSiblingRows(row) // this isn't the last row - we can just remove it @@ -459,7 +461,7 @@ function deleteTableRow(delete_glyphicon) { // this is the last row, we can't remove it completely since we need to post an empty array row.empty() - row.removeClass('new-row row-data') + row.removeClass(Settings.DATA_ROW_CLASS).removeClass('new-row') row.addClass('empty-array-row') row.html(" Date: Thu, 9 Oct 2014 15:10:17 -0700 Subject: [PATCH 048/108] handle the enter button in domain server forms --- domain-server/resources/web/js/settings.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index d3faa94b66..b657fc4a7a 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -2,7 +2,9 @@ var Settings = { showAdvanced: false, ADVANCED_CLASS: 'advanced-setting', DATA_ROW_CLASS: 'value-row', - DATA_COL_CLASS: 'value-col' + DATA_COL_CLASS: 'value-col', + ADD_ROW_BUTTON_CLASS: 'add-row', + TABLE_BUTTONS_CLASS: 'buttons' }; var viewHelpers = { @@ -99,6 +101,22 @@ $(document).ready(function(){ $('#settings-form').on('click', '.del-row', function(){ deleteTableRow(this); }) + + $('#settings-form').on('keypress', 'table input', function(e){ + if (e.keyCode == 13) { + // capture enter in table input + // if we have a sibling next to us that has an input, jump to it, otherwise check if we have a glyphicon for add to click + sibling = $(this).parent('td').next(); + + if (sibling.hasClass(Settings.DATA_COL_CLASS)) { + // jump to that input + sibling.find('input').focus() + } else if (sibling.hasClass(Settings.TABLE_BUTTONS_CLASS)) { + sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click() + $(this).blur() + } + } + }); $('#settings-form').on('change', 'input.trigger-change', function(){ // this input was changed, add the changed data attribute to it From ee36b6b91ad63df1b09bd7a57b375df71702b8d2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:12:42 -0700 Subject: [PATCH 049/108] jump to input for next row after enter on previous row --- domain-server/resources/web/js/settings.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index b657fc4a7a..63ac13ae4a 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -109,11 +109,13 @@ $(document).ready(function(){ sibling = $(this).parent('td').next(); if (sibling.hasClass(Settings.DATA_COL_CLASS)) { - // jump to that input + // set focus to next input sibling.find('input').focus() } else if (sibling.hasClass(Settings.TABLE_BUTTONS_CLASS)) { sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click() - $(this).blur() + + // set focus to the first input in the new row + $(this).closest('table').find('tr.inputs input:first').focus() } } }); From 712a81c95087d6ee3ed8ec337382e323e5e8c713 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:17:45 -0700 Subject: [PATCH 050/108] more constantization and removal of extra line --- domain-server/resources/web/js/settings.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 63ac13ae4a..d6eba5bf23 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -4,6 +4,7 @@ var Settings = { DATA_ROW_CLASS: 'value-row', DATA_COL_CLASS: 'value-col', ADD_ROW_BUTTON_CLASS: 'add-row', + ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', TABLE_BUTTONS_CLASS: 'buttons' }; @@ -94,7 +95,7 @@ $(document).ready(function(){ $(window).resize(resizeFn); }) - $('#settings-form').on('click', '.add-row', function(){ + $('#settings-form').on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){ addTableRow(this); }) @@ -278,8 +279,6 @@ function makeTable(setting, setting_name, setting_value) { // for arrays we add a hidden input to this td so that values can be posted appropriately html += "" - - } else if (row.hasOwnProperty(col.name)) { html += row[col.name] } @@ -319,7 +318,7 @@ function makeTableInputs(setting) { " }) - html += "" + html += "" html += "" return html @@ -415,8 +414,8 @@ function addTableRow(add_glyphicon) { } else if ($(element).hasClass("buttons")) { // Change buttons var span = $(element).children("span") - span.removeClass("glyphicon-plus add-row") - span.addClass("glyphicon-remove del-row") + span.removeClass(Settings.ADD_ROW_SPAN_CLASSES) + span.addClass("glyphicon glyphicon-remove del-row") } else if ($(element).hasClass("key")) { var input = $(element).children("input") $(element).html(input.val()) From 4d13718d8722bf69bed8cddd35bbee4223c887d1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:19:56 -0700 Subject: [PATCH 051/108] constantize the trigger change class for input changes --- domain-server/resources/web/js/settings.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index d6eba5bf23..1f86ec728e 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -1,6 +1,7 @@ var Settings = { showAdvanced: false, ADVANCED_CLASS: 'advanced-setting', + TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', DATA_COL_CLASS: 'value-col', ADD_ROW_BUTTON_CLASS: 'add-row', @@ -28,7 +29,7 @@ var viewHelpers = { } common_attrs = " class='" + (setting.type !== 'checkbox' ? 'form-control' : '') - + " trigger-change' data-short-name='" + setting.name + "' name='" + setting_name + "' " + + " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + setting_name + "' " if (setting.type === 'checkbox') { form_group += "" @@ -121,7 +122,7 @@ $(document).ready(function(){ } }); - $('#settings-form').on('change', 'input.trigger-change', function(){ + $('#settings-form').on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){ // this input was changed, add the changed data attribute to it $(this).attr('data-changed', true) From e80cf6fc76996e7ac4be6ba7bcbbb8fdb228d5dc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:21:38 -0700 Subject: [PATCH 052/108] highlight new rows by making them green --- domain-server/resources/web/css/style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 2766da0830..ad889274d4 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -84,3 +84,8 @@ td.buttons .glyphicon { text-align: center; font-size: 12px; } + +tr.new-row { + color: #3c763d; + background-color: #dff0d8; +} From c748ecb96798a1914b4ee5de2bf8e73ede11475e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:22:20 -0700 Subject: [PATCH 053/108] constantize the new row class --- domain-server/resources/web/js/settings.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 1f86ec728e..69c52ceb6c 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -6,7 +6,8 @@ var Settings = { DATA_COL_CLASS: 'value-col', ADD_ROW_BUTTON_CLASS: 'add-row', ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', - TABLE_BUTTONS_CLASS: 'buttons' + TABLE_BUTTONS_CLASS: 'buttons', + NEW_ROW_CLASS: 'new-row' }; var viewHelpers = { @@ -400,7 +401,7 @@ function addTableRow(add_glyphicon) { var table = row.parents("table") var setting_name = table.attr("name") var full_name = setting_name + "." + key - row.addClass(Settings.DATA_ROW_CLASS + " new-row") + row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS) row.removeClass("inputs") _.each(row.children(), function(element) { @@ -481,7 +482,7 @@ function deleteTableRow(delete_glyphicon) { // this is the last row, we can't remove it completely since we need to post an empty array row.empty() - row.removeClass(Settings.DATA_ROW_CLASS).removeClass('new-row') + row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) row.addClass('empty-array-row') row.html(" Date: Thu, 9 Oct 2014 15:24:12 -0700 Subject: [PATCH 054/108] constantize the delete row button classes --- domain-server/resources/web/js/settings.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 69c52ceb6c..923a01a8a9 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -6,6 +6,8 @@ var Settings = { DATA_COL_CLASS: 'value-col', ADD_ROW_BUTTON_CLASS: 'add-row', ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', + DEL_ROW_BUTTON_CLASS: 'del-row', + DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', TABLE_BUTTONS_CLASS: 'buttons', NEW_ROW_CLASS: 'new-row' }; @@ -101,7 +103,7 @@ $(document).ready(function(){ addTableRow(this); }) - $('#settings-form').on('click', '.del-row', function(){ + $('#settings-form').on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){ deleteTableRow(this); }) @@ -288,7 +290,7 @@ function makeTable(setting, setting_name, setting_value) { html += "" }) - html += "" + html += "" html += "" row_num++ @@ -417,7 +419,7 @@ function addTableRow(add_glyphicon) { // Change buttons var span = $(element).children("span") span.removeClass(Settings.ADD_ROW_SPAN_CLASSES) - span.addClass("glyphicon glyphicon-remove del-row") + span.addClass(Settings.DEL_ROW_SPAN_CLASSES) } else if ($(element).hasClass("key")) { var input = $(element).children("input") $(element).html(input.val()) From 90a79fc6f5c7ef5e685805c6c347808b278bf976 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:27:51 -0700 Subject: [PATCH 055/108] remove allowed users from describe until ready for primetime --- domain-server/resources/describe-settings.json | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e9df7f264d..5619508844 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -59,20 +59,6 @@ "type": "password", "help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.", "value-hidden": true - }, - { - "name": "allowed_users", - "type": "table", - "label": "Allowed Users", - "help": "A list of usernames for the High Fidelity users you want to allow into your domain. Users not found in this list will not be allowed to connect.", - "numbered": false, - "columns": [ - { - "name": "username", - "label": "Username", - "can_set": true - } - ] } ] }, @@ -139,7 +125,7 @@ "placeholder": "0.18" } ] - }, + } { "name": "enable_filter", "type": "checkbox", From 95bca3f33ec6838779322890ec6c04eee3477262 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:29:15 -0700 Subject: [PATCH 056/108] fix an error in describe json --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 5619508844..ee2c9ae796 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -125,7 +125,7 @@ "placeholder": "0.18" } ] - } + }, { "name": "enable_filter", "type": "checkbox", From 0fce89a62796724021a1e5bc51ad32baef4c0ee9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 9 Oct 2014 16:22:16 -0700 Subject: [PATCH 057/108] Add gamepad.js --- examples/gamepad.js | 266 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 examples/gamepad.js diff --git a/examples/gamepad.js b/examples/gamepad.js new file mode 100644 index 0000000000..1911e71a25 --- /dev/null +++ b/examples/gamepad.js @@ -0,0 +1,266 @@ +// +// controller.js +// examples +// +// Created by Ryan Huffman on 10/9/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); + +if (!gamepad) { + print("No gamepad found."); +} + +// Controller axis/button mappings +var GAMEPAD = { + AXES: { + LEFT_JOYSTICK_X: 0, + LEFT_JOYSTICK_Y: 1, + + RIGHT_JOYSTICK_X: 2, + RIGHT_JOYSTICK_Y: 3, + + LEFT_TRIGGER: 4, + RIGHT_TRIGGER: 5, + }, + BUTTONS: { + DPAD_UP: 0, + DPAD_DOWN: 1, + DPAD_LEFT: 2, + DPAD_RIGHT: 3, + + LEFT_JOYSTICK: 6, + RIGHT_JOYSTICK: 7, + + LEFT_BUMPER: 8, + RIGHT_BUMPER: 9, + + // Face buttons, ABXY on an XBOX controller + FACE_BOTTOM: 11, + FACE_RIGHT: 12, + FACE_LEFT: 13, + FACE_TOP: 14, + } +} + +// Button/axis mappings +var AXIS_STRAFE = GAMEPAD.AXES.LEFT_JOYSTICK_X; +var AXIS_FORWARD = GAMEPAD.AXES.LEFT_JOYSTICK_Y; +var AXIS_ROTATE = GAMEPAD.AXES.RIGHT_JOYSTICK_X; + +var BUTTON_TURN_AROUND = GAMEPAD.BUTTONS.RIGHT_JOYSTICK; + +var BUTTON_FLY_UP = GAMEPAD.BUTTONS.RIGHT_BUMPER; +var BUTTON_FLY_DOWN = GAMEPAD.BUTTONS.LEFT_BUMPER +var BUTTON_WARP = GAMEPAD.BUTTONS.FACE_BOTTOM; + +var BUTTON_WARP_FORWARD = GAMEPAD.BUTTONS.DPAD_UP; +var BUTTON_WARP_BACKWARD = GAMEPAD.BUTTONS.DPAD_DOWN; +var BUTTON_WARP_LEFT = GAMEPAD.BUTTONS.DPAD_LEFT; +var BUTTON_WARP_RIGHT = GAMEPAD.BUTTONS.DPAD_RIGHT; + +// Distance in meters to warp via BUTTON_WARP_* +var WARP_DISTANCE = 1; + +// Walk speed in m/s +var MOVE_SPEED = 2; + +// Amount to rotate in radians +var ROTATE_INCREMENT = Math.PI / 8; + +// Pick from above where we want to warp +var WARP_PICK_OFFSET = { x: 0, y: 10, z: 0 }; + +// When warping, the warp position will snap to a target below the current warp position. +// This is the max distance it will snap to. +var WARP_PICK_MAX_DISTANCE = 100; + +var flyDownButtonState = false; +var flyUpButtonState = false; + +// Current move direction, axis aligned - that is, looking down and moving forward +// will not move you into the ground, but instead will keep you on the horizontal plane. +var moveDirection = { x: 0, y: 0, z: 0 }; + +var warpActive = false; +var warpPosition = { x: 0, y: 0, z: 0 }; + +var WARP_SPHERE_SIZE = 1; +var warpSphere = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + size: WARP_SPHERE_SIZE, + color: { red: 0, green: 255, blue: 0 }, + alpha: 1.0, + solid: true, + visible: false, +}); + +var WARP_LINE_HEIGHT = 10; +var warpLine = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z:0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 255, blue: 255}, + alpha: 1, + lineWidth: 5, + visible: false, +}); + +function copyVec3(vec) { + return { x: vec.x, y: vec.y, z: vec.z }; +} + +function activateWarp() { + if (warpActive) return; + warpActive = true; + + updateWarp(); +} + +function updateWarp() { + if (!warpActive) return; + + var look = Quat.getFront(Camera.getOrientation()); + var pitch = Math.asin(look.y); + + // Get relative to looking straight down + pitch += Math.PI / 2; + + // Scale up + pitch *= 2; + var distance = pitch * pitch * pitch; + + var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z }); + warpPosition = Vec3.multiply(warpDirection, distance); + warpPosition = Vec3.sum(MyAvatar.position, warpPosition); + + var pickRay = { + origin: Vec3.sum(warpPosition, WARP_PICK_OFFSET), + direction: { x: 0, y: -1, z: 0 } + }; + + var intersection = Voxels.findRayIntersection(pickRay); + + if (intersection.intersects && intersection.distance < WARP_PICK_MAX_DISTANCE) { + // Warp 1 meter above the object - this is an approximation + // TODO Get the actual offset to the Avatar's feet and plant them to + // the object. + warpPosition = Vec3.sum(intersection.intersection, { x: 0, y: 1, z:0 }); + } + + // Adjust overlays to match warp position + Overlays.editOverlay(warpSphere, { + position: warpPosition, + visible: true, + }); + Overlays.editOverlay(warpLine, { + position: warpPosition, + end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }), + visible: true, + }); +} + +function finishWarp() { + if (!warpActive) return; + warpActive = false; + Overlays.editOverlay(warpSphere, { + visible: false, + }); + Overlays.editOverlay(warpLine, { + visible: false, + }); + MyAvatar.position = warpPosition; +} + +function reportAxisValue(axis, newValue, oldValue) { + if (Math.abs(oldValue) < 0.2) oldValue = 0; + if (Math.abs(newValue) < 0.2) newValue = 0; + + if (axis == AXIS_FORWARD) { + moveDirection.z = newValue; + } else if (axis == AXIS_STRAFE) { + moveDirection.x = newValue; + } else if (axis == AXIS_ROTATE) { + if (oldValue == 0 && newValue != 0) { + var rotateRadians = newValue > 0 ? -ROTATE_INCREMENT : ROTATE_INCREMENT; + var orientation = MyAvatar.orientation; + orientation = Quat.multiply(Quat.fromPitchYawRollRadians(0, rotateRadians, 0), orientation) ; + MyAvatar.orientation = orientation; + } + } +} + +function reportButtonValue(button, newValue, oldValue) { + print("The value for button " + button + " has changed from " + oldValue + " to " + newValue); + if (button == BUTTON_FLY_DOWN) { + flyDownButtonState = newValue; + } else if (button == BUTTON_FLY_UP) { + flyUpButtonState = newValue; + } else if (button == BUTTON_WARP) { + if (newValue) { + activateWarp(); + } else { + finishWarp(); + } + } else if (button == BUTTON_TURN_AROUND) { + if (newValue) { + MyAvatar.orientation = Quat.multiply( + Quat.fromPitchYawRollRadians(0, Math.PI, 0), MyAvatar.orientation); + } + } else if (newValue) { + var direction = null; + + if (button == BUTTON_WARP_FORWARD) { + direction = Quat.getFront(Camera.getOrientation()); + } else if (button == BUTTON_WARP_BACKWARD) { + direction = Quat.getFront(Camera.getOrientation()); + direction = Vec3.multiply(-1, direction); + } else if (button == BUTTON_WARP_LEFT) { + direction = Quat.getRight(Camera.getOrientation()); + direction = Vec3.multiply(-1, direction); + } else if (button == BUTTON_WARP_RIGHT) { + direction = Quat.getRight(Camera.getOrientation()); + } + + if (direction) { + direction.y = 0; + direction = Vec3.multiply(Vec3.normalize(direction), WARP_DISTANCE); + MyAvatar.position = Vec3.sum(MyAvatar.position, direction); + } + } + + if (flyUpButtonState && !flyDownButtonState) { + moveDirection.y = 1; + } else if (!flyUpButtonState && flyDownButtonState) { + moveDirection.y = -1; + } else { + moveDirection.y = 0; + } +} + +function update(dt) { + var velocity = { x: 0, y: 0, z: 0 }; + var move = copyVec3(moveDirection); + move.y = 0; + if (Vec3.length(move) > 0) { + velocity = Vec3.multiplyQbyV(Camera.getOrientation(), move); + velocity.y = 0; + velocity = Vec3.multiply(Vec3.normalize(velocity), MOVE_SPEED); + } + + if (moveDirection.y != 0) { + velocity.y = moveDirection.y * MOVE_SPEED; + } + + MyAvatar.setVelocity(velocity); + + updateWarp(); +} + +gamepad.axisValueChanged.connect(reportAxisValue); +gamepad.buttonStateChanged.connect(reportButtonValue); + +Script.update.connect(update); From f18faf9e3e7505e25b6043d26f48f1ef6b7a8aef Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 9 Oct 2014 16:46:06 -0700 Subject: [PATCH 058/108] removed commented code. --- interface/src/devices/OculusManager.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index c5aa73582e..ad6a914112 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -348,8 +348,6 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p whichCamera.setHmdPosition(trackerPosition); whichCamera.setHmdRotation(orientation); - //_camera->setRotation(bodyOrientation * orientation); - //_camera->setPosition(position + trackerPosition); // Update our camera to what the application camera is doing _camera->setRotation(whichCamera.getRotation()); _camera->setPosition(whichCamera.getPosition()); @@ -414,10 +412,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p renderDistortionMesh(eyeRenderPose); glBindTexture(GL_TEXTURE_2D, 0); - - // Update camera for use by rest of Interface. - //whichCamera.setPosition((_leftEyePosition + _rightEyePosition) / 2.f); - //whichCamera.setRotation(_camera->getRotation()); + #endif } From 1fc78e42779889aec55d2a3b3b4332a93b0538b8 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 17:18:51 -0700 Subject: [PATCH 059/108] Fixing Andrzej's comments --- interface/src/gpu/Resource.h | 10 +++++----- interface/src/ui/TextRenderer.cpp | 6 +++--- interface/src/ui/TextRenderer.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index 5c5b7f6367..d4daae8690 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -59,7 +59,7 @@ protected: public: Sysmem(); - Sysmem(Size size , const Byte* bytes); + Sysmem(Size size, const Byte* bytes); ~Sysmem(); Size getSize() const { return _size; } @@ -74,12 +74,12 @@ protected: Size resize(Size pSize); // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData( Size size, const Byte* bytes ); + Size setData(Size size, const Byte* bytes ); // Update Sub data, // doesn't allocate and only copy size * bytes at the offset location // only if all fits in the existing allocated buffer - Size setSubData( Size offset, Size size, const Byte* bytes); + Size setSubData(Size offset, Size size, const Byte* bytes); // Append new data at the end of the current buffer // do a resize( size + getSIze) and copy the new data @@ -117,7 +117,7 @@ class Buffer : public Resource { public: Buffer(); - Buffer(const Buffer& buf ); + Buffer(const Buffer& buf); ~Buffer(); // The size in bytes of data stored in the buffer @@ -143,7 +143,7 @@ public: // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object // TODO: remove asap, when the backend is doing more of the gl features - inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } + inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } protected: diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 138c10b26c..8c78a06d55 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -128,8 +128,8 @@ int TextRenderer::draw(int x, int y, const char* str) { const int NUM_COLOR_SCALARS_PER_GLYPH = 4; unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; - gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; - gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; + gpu::Buffer::Size offset = sizeof(vertexBuffer) * _numGlyphsBatched; + gpu::Buffer::Size colorOffset = sizeof(colorBuffer) * _numGlyphsBatched; if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); @@ -275,7 +275,7 @@ const Glyph& TextRenderer::getGlyph(char c) { } void TextRenderer::drawBatch() { - if (_numGlyphsBatched<=0) { + if (_numGlyphsBatched <= 0) { return; } diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index 70283eedd8..1953dda422 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -107,7 +107,7 @@ private: // Graphics Buffer containing the current accumulated glyphs to render gpu::Buffer _glyphsBuffer; gpu::Buffer _glyphsColorBuffer; - int _numGlyphsBatched; + int _numGlyphsBatched; static QHash _instances; }; From 6687660d2ff5caf780fe2c12743a44cd12f6bdcf Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 17:29:06 -0700 Subject: [PATCH 060/108] Fixing Andrzej's comments bis --- interface/src/gpu/Resource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index d4daae8690..fbbbe7f7fd 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -104,7 +104,7 @@ protected: private: Sysmem(const Sysmem& sysmem) {} - Sysmem &operator=(const Sysmem &other) {return *this;} + Sysmem &operator=(const Sysmem& other) {return *this;} Stamp _stamp; Size _size; From b50e996ec8cc1bd66835a788b1b9cc09097280d6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 17:35:58 -0700 Subject: [PATCH 061/108] added Vec3.orientedAngle() --- libraries/script-engine/src/Vec3.cpp | 8 +++++++- libraries/script-engine/src/Vec3.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 689ce2c747..f88df3b7c0 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include "Vec3.h" @@ -37,7 +39,7 @@ glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } glm::vec3 Vec3::subtract(const glm::vec3& v1, const glm::vec3& v2) { - return v1 - v2; + return v1 - v2; } float Vec3::length(const glm::vec3& v) { @@ -48,6 +50,10 @@ float Vec3::distance(const glm::vec3& v1, const glm::vec3& v2) { return glm::distance(v1, v2); } +float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { + return glm::degrees(glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3))); +} + glm::vec3 Vec3::normalize(const glm::vec3& v) { return glm::normalize(v); } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 2af1350e4a..693fd604f7 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -34,6 +34,7 @@ public slots: glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2); float length(const glm::vec3& v); float distance(const glm::vec3& v1, const glm::vec3& v2); + float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3); glm::vec3 normalize(const glm::vec3& v); void print(const QString& lable, const glm::vec3& v); bool equal(const glm::vec3& v1, const glm::vec3& v2); From f6c07a5ada8dfc69c35ca74225a06a99f95ae87f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 17:37:56 -0700 Subject: [PATCH 062/108] first cut at making the rotate handles actually rotate --- examples/libraries/entitySelectionTool.js | 221 +++++++++++++++------- 1 file changed, 154 insertions(+), 67 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d88a8a52eb..281ac07261 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -37,11 +37,22 @@ SelectionDisplay = (function () { var yawCenter; var pitchCenter; var rollCenter; + var yawZero; + var pitchZero; + var rollZero; + var yawNormal; + var pitchNormal; + var rollNormal; + var rotationNormal; + + var originalPitch; + var originalYaw; + var originalRoll; + var rotateHandleColor = { red: 0, green: 0, blue: 0 }; var rotateHandleAlpha = 0.7; - var grabberSizeCorner = 0.025; var grabberSizeEdge = 0.015; var grabberSizeFace = 0.025; @@ -168,6 +179,21 @@ SelectionDisplay = (function () { var rollOverlayAngles = { x: 0, y: 180, z: 0 }; var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); + 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 }, + }); + + 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 }, + }); var rotateOverlayInner = Overlays.addOverlay("circle3d", { position: { x:0, y: 0, z: 0}, @@ -290,7 +316,9 @@ SelectionDisplay = (function () { overlayNames[rotateOverlayOuter] = "rotateOverlayOuter"; overlayNames[rotateOverlayCurrent] = "rotateOverlayCurrent"; - + overlayNames[rotateZeroOverlay] = "rotateZeroOverlay"; + overlayNames[rotateCurrentOverlay] = "rotateCurrentOverlay"; + that.cleanup = function () { Overlays.deleteOverlay(highlightBox); Overlays.deleteOverlay(selectionBox); @@ -333,6 +361,10 @@ SelectionDisplay = (function () { Overlays.deleteOverlay(rotateOverlayOuter); Overlays.deleteOverlay(rotateOverlayCurrent); + Overlays.deleteOverlay(rotateZeroOverlay); + Overlays.deleteOverlay(rotateCurrentOverlay); + + }; that.highlightSelectable = function(entityID) { @@ -439,6 +471,10 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 180, z: 180 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 180 }); + yawNormal = { x: 0, y: 1, z: 0 }; + pitchNormal = { x: 0, y: 0, z: -1 }; + rollNormal = { x: 1, y: 0, z: 0 }; + yawCorner = { x: right + rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; @@ -451,23 +487,19 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: near - rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; - - pitchCenter = { x: center.x, - y: center.y, - z: far }; - - rollCenter = { x: left, - y: center.y, - z: center.z}; - + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: center.x, y: center.y, z: far }; + rollCenter = { x: left, y: center.y, z: center.z}; } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 270, 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: right + rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; @@ -481,18 +513,9 @@ SelectionDisplay = (function () { z: near - rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; - - pitchCenter = { x: left, - y: center.y, - z: center.z }; - - rollCenter = { x: center.x, - y: center.y, - z: near}; - + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: left, y: center.y, z: center.z }; + rollCenter = { x: center.x, y: center.y, z: near}; } } else { // must be BLF or BLN @@ -501,6 +524,10 @@ SelectionDisplay = (function () { 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 }; + yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; @@ -513,25 +540,20 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: far + rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: right, y: center.y, z: center.z }; + rollCenter = { x: center.x, y: center.y, z: far}; - pitchCenter = { x: right, - y: center.y, - z: center.z }; - - rollCenter = { x: center.x, - y: center.y, - z: far}; - - - } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 180, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); + // TODO: fix these + yawNormal = { x: 0, y: 1, z: 0 }; + pitchNormal = { x: 0, y: 0, z: 1 }; + rollNormal = { x: -1, y: 0, z: 0 }; + yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; @@ -544,19 +566,9 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: far + rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; - - pitchCenter = { x: center.x, - y: center.y, - z: near }; - - rollCenter = { x: right, - y: center.y, - z: center.z}; - - + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: center.x, y: center.y, z: near }; + rollCenter = { x: right, y: center.y, z: center.z}; } } @@ -660,6 +672,9 @@ SelectionDisplay = (function () { endAt: 0, innerRadius: 0.9, }); + + Overlays.editOverlay(rotateZeroOverlay, { visible: false }); + Overlays.editOverlay(rotateCurrentOverlay, { visible: false }); // 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}); @@ -718,6 +733,9 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotateZeroOverlay, { visible: false }); + Overlays.editOverlay(rotateCurrentOverlay, { visible: false }); + Entities.editEntity(entityID, { localRenderAlpha: 1.0 }); currentSelection = { id: -1, isKnownID: false }; @@ -1514,11 +1532,26 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); var result = Overlays.findRayIntersection(pickRay); - print("result.intersects:" + result.intersects); - print("result.overlayID:" + overlayNames[result.overlayID]); - print("result.distance:" + result.distance); - print("result.face:" + result.face); - Vec3.print("result.intersection:", result.intersection); + if (result.intersects) { + print("ROTATE_YAW"); + var properties = Entities.getEntityProperties(currentSelection); + var center = yawCenter; + var zero = yawZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: center, + end: result.intersection + }); + + + var newYaw = originalYaw + angleFromZero; + var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: newYaw, z: originalRoll }); + Entities.editEntity(currentSelection, { rotation: newRotation }); + } }; that.rotatePitch = function(event) { @@ -1532,11 +1565,27 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); var result = Overlays.findRayIntersection(pickRay); - print("result.intersects:" + result.intersects); - print("result.overlayID:" + overlayNames[result.overlayID]); - print("result.distance:" + result.distance); - print("result.face:" + result.face); - Vec3.print("result.intersection:", result.intersection); + + if (result.intersects) { + print("ROTATE_PITCH"); + + var properties = Entities.getEntityProperties(currentSelection); + var center = pitchCenter; + var zero = pitchZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: center, + end: result.intersection + }); + + var newPitch = originalPitch + angleFromZero; + var newRotation = Quat.fromVec3Degrees({ x: newPitch, y: originalYaw, z: originalRoll }); + Entities.editEntity(currentSelection, { rotation: newRotation }); + } }; that.rotateRoll = function(event) { @@ -1550,11 +1599,25 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); var result = Overlays.findRayIntersection(pickRay); - print("result.intersects:" + result.intersects); - print("result.overlayID:" + overlayNames[result.overlayID]); - print("result.distance:" + result.distance); - print("result.face:" + result.face); - Vec3.print("result.intersection:", result.intersection); + if (result.intersects) { + print("ROTATE_ROLL"); + var properties = Entities.getEntityProperties(currentSelection); + var center = rollCenter; + var zero = rollZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: center, + end: result.intersection + }); + + var newRoll = originalRoll + angleFromZero; + var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: originalYaw, z: newRoll }); + Entities.editEntity(currentSelection, { rotation: newRotation }); + } }; that.checkMove = function() { @@ -1736,6 +1799,10 @@ SelectionDisplay = (function () { var roll = angles.z; var currentRotation; + originalPitch = pitch; + originalYaw = yaw; + originalRoll = roll; + if (result.intersects) { switch(result.overlayID) { case yawHandle: @@ -1744,6 +1811,8 @@ SelectionDisplay = (function () { overlayOrientation = yawHandleRotation; overlayCenter = yawCenter; currentRotation = yaw; + yawZero = result.intersection; + rotationNormal = yawNormal; break; case pitchHandle: @@ -1752,6 +1821,8 @@ SelectionDisplay = (function () { overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; currentRotation = pitch; + pitchZero = result.intersection; + rotationNormal = pitchNormal; break; case rollHandle: @@ -1760,6 +1831,8 @@ SelectionDisplay = (function () { overlayOrientation = rollHandleRotation; overlayCenter = rollCenter; currentRotation = roll; + rollZero = result.intersection; + rotationNormal = rollNormal; break; default: @@ -1807,6 +1880,20 @@ SelectionDisplay = (function () { startAt: 0, endAt: currentRotation }); + + Overlays.editOverlay(rotateZeroOverlay, + { + visible: true, + start: overlayCenter, + end: result.intersection + }); + + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: overlayCenter, + end: result.intersection + }); Overlays.editOverlay(yawHandle, { visible: false }); Overlays.editOverlay(pitchHandle, { visible: false }); From f1bc6d729cdbf57a956a620864ee48b67f5a3a1d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 18:01:48 -0700 Subject: [PATCH 063/108] make rotate handles work as expected --- examples/libraries/entitySelectionTool.js | 77 ++++++++--------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 281ac07261..b5df2df9b8 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -45,6 +45,7 @@ SelectionDisplay = (function () { var rollNormal; var rotationNormal; + var originalRotation; var originalPitch; var originalYaw; var originalRoll; @@ -576,10 +577,12 @@ SelectionDisplay = (function () { var rotateHandlesVisible = true; var translateHandlesVisible = true; var stretchHandlesVisible = true; + var selectionBoxVisible = true; if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_XZ") { rotateHandlesVisible = false; translateHandlesVisible = false; stretchHandlesVisible = false; + selectionBoxVisible = false; } else if (mode == "TRANSLATE_UP_DOWN") { rotateHandlesVisible = false; stretchHandlesVisible = false; @@ -591,9 +594,10 @@ SelectionDisplay = (function () { Overlays.editOverlay(highlightBox, { visible: false }); + print("selectionBoxVisible:" + selectionBoxVisible); Overlays.editOverlay(selectionBox, { - visible: true, + visible: selectionBoxVisible, position: center, dimensions: properties.dimensions, rotation: properties.rotation, @@ -1525,8 +1529,8 @@ SelectionDisplay = (function () { } var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); @@ -1546,10 +1550,10 @@ SelectionDisplay = (function () { start: center, end: result.intersection }); - - - var newYaw = originalYaw + angleFromZero; - var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: newYaw, z: originalRoll }); + + var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); + var newRotation = Quat.multiply(yawChange, originalRotation); + Entities.editEntity(currentSelection, { rotation: newRotation }); } }; @@ -1559,8 +1563,8 @@ SelectionDisplay = (function () { return; // not allowed } var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); @@ -1582,8 +1586,9 @@ SelectionDisplay = (function () { end: result.intersection }); - var newPitch = originalPitch + angleFromZero; - var newRotation = Quat.fromVec3Degrees({ x: newPitch, y: originalYaw, z: originalRoll }); + var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); + var newRotation = Quat.multiply(pitchChange, originalRotation); + Entities.editEntity(currentSelection, { rotation: newRotation }); } }; @@ -1593,8 +1598,8 @@ SelectionDisplay = (function () { return; // not allowed } var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); @@ -1614,8 +1619,9 @@ SelectionDisplay = (function () { end: result.intersection }); - var newRoll = originalRoll + angleFromZero; - var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: originalYaw, z: newRoll }); + var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); + var newRotation = Quat.multiply(rollChange, originalRotation); + Entities.editEntity(currentSelection, { rotation: newRotation }); } }; @@ -1799,6 +1805,7 @@ SelectionDisplay = (function () { var roll = angles.z; var currentRotation; + originalRotation = properties.rotation; originalPitch = pitch; originalYaw = yaw; originalRoll = roll; @@ -2053,45 +2060,15 @@ SelectionDisplay = (function () { showHandles = true; } - if (showHandles) { - Overlays.editOverlay(yawHandle, { visible: true }); - Overlays.editOverlay(pitchHandle, { visible: true }); - Overlays.editOverlay(rollHandle, { visible: true }); - Overlays.editOverlay(grabberMoveUp, { visible: true }); - Overlays.editOverlay(grabberLBN, { visible: true }); - Overlays.editOverlay(grabberLBF, { visible: true }); - Overlays.editOverlay(grabberRBN, { visible: true }); - Overlays.editOverlay(grabberRBF, { visible: true }); - Overlays.editOverlay(grabberLTN, { visible: true }); - Overlays.editOverlay(grabberLTF, { visible: true }); - Overlays.editOverlay(grabberRTN, { visible: true }); - Overlays.editOverlay(grabberRTF, { visible: true }); - - Overlays.editOverlay(grabberTOP, { visible: true }); - Overlays.editOverlay(grabberBOTTOM, { visible: true }); - Overlays.editOverlay(grabberLEFT, { visible: true }); - Overlays.editOverlay(grabberRIGHT, { visible: true }); - Overlays.editOverlay(grabberNEAR, { visible: true }); - Overlays.editOverlay(grabberFAR, { visible: true }); - - Overlays.editOverlay(grabberEdgeTR, { visible: true }); - Overlays.editOverlay(grabberEdgeTL, { visible: true }); - Overlays.editOverlay(grabberEdgeTF, { visible: true }); - Overlays.editOverlay(grabberEdgeTN, { visible: true }); - Overlays.editOverlay(grabberEdgeBR, { visible: true }); - Overlays.editOverlay(grabberEdgeBL, { visible: true }); - Overlays.editOverlay(grabberEdgeBF, { visible: true }); - Overlays.editOverlay(grabberEdgeBN, { visible: true }); - Overlays.editOverlay(grabberEdgeNR, { visible: true }); - Overlays.editOverlay(grabberEdgeNL, { visible: true }); - Overlays.editOverlay(grabberEdgeFR, { visible: true }); - Overlays.editOverlay(grabberEdgeFL, { visible: true }); - } - mode = "UNKNOWN"; // if something is selected, then reset the "original" properties for any potential next click+move operation if (entitySelected) { + + if (showHandles) { + that.select(currentSelection, event); + } + selectedEntityProperties = Entities.getEntityProperties(currentSelection); selectedEntityPropertiesOriginalPosition = properties.position; selectedEntityPropertiesOriginalDimensions = properties.dimensions; From e5572e9deb69a27567537bf4e85d796f1d6fa6d3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 19:20:33 -0700 Subject: [PATCH 064/108] don't render if alpha is 0 --- interface/src/ui/overlays/Circle3DOverlay.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 7cd385f963..62ddee4cf2 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -40,13 +40,18 @@ void Circle3DOverlay::render() { if (!_visible) { return; // do nothing if we're not visible } + + float alpha = getAlpha(); + + if (alpha == 0.0) { + return; // do nothing if our alpha is 0, we're not visible + } const float FULL_CIRCLE = 360.0f; const float SLICES = 180.0f; // The amount of segment to create the circle const float SLICE_ANGLE = FULL_CIRCLE / SLICES; //const int slices = 15; - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); From 89f423255ef887255e7cfbb7dc21bfbc03462823 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 19:21:15 -0700 Subject: [PATCH 065/108] use large target for rotate overlays so we are guarenteed to hit the plane --- examples/libraries/entitySelectionTool.js | 47 ++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index b5df2df9b8..111e7aabee 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -31,6 +31,7 @@ SelectionDisplay = (function () { var handleHoverColor = { red: 224, green: 67, blue: 36 }; var handleHoverAlpha = 1.0; + var rotateOverlayTargetSize = 10000; // really big target var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -196,6 +197,17 @@ SelectionDisplay = (function () { color: { red: 0, green: 0, blue: 255 }, }); + + 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, + }); + var rotateOverlayInner = Overlays.addOverlay("circle3d", { position: { x:0, y: 0, z: 0}, size: 1, @@ -313,6 +325,7 @@ SelectionDisplay = (function () { overlayNames[pitchHandle] = "pitchHandle"; overlayNames[rollHandle] = "rollHandle"; + overlayNames[rotateOverlayTarget] = "rotateOverlayTarget"; overlayNames[rotateOverlayInner] = "rotateOverlayInner"; overlayNames[rotateOverlayOuter] = "rotateOverlayOuter"; overlayNames[rotateOverlayCurrent] = "rotateOverlayCurrent"; @@ -358,6 +371,7 @@ SelectionDisplay = (function () { Overlays.deleteOverlay(pitchHandle); Overlays.deleteOverlay(rollHandle); + Overlays.deleteOverlay(rotateOverlayTarget); Overlays.deleteOverlay(rotateOverlayInner); Overlays.deleteOverlay(rotateOverlayOuter); Overlays.deleteOverlay(rotateOverlayCurrent); @@ -649,7 +663,10 @@ SelectionDisplay = (function () { dimensions: { x: properties.dimensions.x, y: properties.dimensions.z }, rotation: properties.rotation, }); + + Overlays.editOverlay(rotateOverlayTarget, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { visible: false, @@ -733,6 +750,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(pitchHandle, { visible: false }); Overlays.editOverlay(rollHandle, { visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { visible: false }); Overlays.editOverlay(rotateOverlayInner, { visible: false }); Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); @@ -1531,9 +1549,10 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -1565,9 +1584,10 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -1600,9 +1620,10 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { print("ROTATE_ROLL"); @@ -1862,6 +1883,13 @@ SelectionDisplay = (function () { // TODO: need to place correctly.... print(" attempting to show overlays:" + somethingClicked); print(" currentRotation:" + currentRotation); + + Overlays.editOverlay(rotateOverlayTarget, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); Overlays.editOverlay(rotateOverlayInner, { @@ -2050,6 +2078,7 @@ SelectionDisplay = (function () { var showHandles = false; // 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 }); From b1da04f0f0e78b71a4e219676eac9719b6ce5386 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 19:41:51 -0700 Subject: [PATCH 066/108] support for snap to rotation --- examples/libraries/entitySelectionTool.js | 24 +++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 111e7aabee..ab176da559 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -32,6 +32,8 @@ SelectionDisplay = (function () { var handleHoverAlpha = 1.0; var rotateOverlayTargetSize = 10000; // really big target + var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool + var innerRadius; var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -217,7 +219,7 @@ SelectionDisplay = (function () { visible: false, rotation: yawOverlayRotation, hasTickMarks: true, - majorTickMarksAngle: 12.5, + majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, majorTickMarksLength: -0.25, minorTickMarksLength: 0, @@ -431,7 +433,7 @@ SelectionDisplay = (function () { var diagonal = (Vec3.length(properties.dimensions) / 2) * 1.1; var halfDimensions = Vec3.multiply(properties.dimensions, 0.5); - var innerRadius = diagonal; + innerRadius = diagonal; var outerRadius = diagonal * 1.15; var innerActive = false; var innerAlpha = 0.2; @@ -1563,6 +1565,12 @@ SelectionDisplay = (function () { var centerToZero = Vec3.subtract(center, zero); var centerToIntersect = Vec3.subtract(center, result.intersection); var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + } + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, @@ -1599,6 +1607,12 @@ SelectionDisplay = (function () { var centerToZero = Vec3.subtract(center, zero); var centerToIntersect = Vec3.subtract(center, result.intersection); var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + } + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, @@ -1633,6 +1647,12 @@ SelectionDisplay = (function () { var centerToZero = Vec3.subtract(center, zero); var centerToIntersect = Vec3.subtract(center, result.intersection); var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + } + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, From 7b0d00235520aa8f5a06c8cdbec4dcfb3622ac59 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 9 Oct 2014 19:51:02 -0700 Subject: [PATCH 067/108] Working on ray/voxel intersection testing. --- interface/src/MetavoxelSystem.cpp | 115 ++++++++++++++++++++++++++- interface/src/MetavoxelSystem.h | 9 ++- interface/src/ui/MetavoxelEditor.cpp | 44 ++++++++++ interface/src/ui/MetavoxelEditor.h | 28 +++++++ 4 files changed, 193 insertions(+), 3 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 10a211f61b..9732036def 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -328,6 +328,50 @@ bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origi return true; } +class RayVoxelIntersectionVisitor : public RayIntersectionVisitor { +public: + + float intersectionDistance; + + RayVoxelIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); + + virtual int visit(MetavoxelInfo& info, float distance); +}; + +RayVoxelIntersectionVisitor::RayVoxelIntersectionVisitor(const glm::vec3& origin, + const glm::vec3& direction, const MetavoxelLOD& lod) : + RayIntersectionVisitor(origin, direction, QVector() << + Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), QVector(), lod), + intersectionDistance(FLT_MAX) { +} + +int RayVoxelIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { + if (!info.isLeaf) { + return _order; + } + const VoxelBuffer* buffer = static_cast( + info.inputValues.at(0).getInlineValue().data()); + if (!buffer) { + return STOP_RECURSION; + } + glm::vec3 origin = ((_origin + distance * _direction) - info.minimum) / info.size; + if (buffer->findFirstRayIntersection(origin, _direction, intersectionDistance)) { + intersectionDistance = intersectionDistance * info.size + distance; + return SHORT_CIRCUIT; + } + return STOP_RECURSION; +} + +bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) { + RayVoxelIntersectionVisitor visitor(origin, direction, getLOD()); + guideToAugmented(visitor); + if (visitor.intersectionDistance == FLT_MAX) { + return false; + } + distance = visitor.intersectionDistance; + return true; +} + class HeightfieldHeightVisitor : public MetavoxelVisitor { public: @@ -991,10 +1035,12 @@ void VoxelPoint::setNormal(const glm::vec3& normal) { } VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QVector& materials) : + const QHash& points, int size, const QVector& materials) : _vertices(vertices), _indices(indices), _hermite(hermite), + _points(points), + _size(size), _vertexCount(vertices.size()), _indexCount(indices.size()), _hermiteCount(hermite.size()), @@ -1002,6 +1048,68 @@ VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector _materials(materials) { } +bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + float highest = _size - 1.0f; + glm::vec3 position = origin * highest; + glm::vec3 floors = glm::floor(position); + int max = _size - 2; + int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); + float combinedDistance = 0.0f; + forever { + if (_points.contains(qRgb(x + 1, y + 1, z + 1))) { + distance = combinedDistance / highest; + return true; + } + float xDistance = FLT_MAX, yDistance = FLT_MAX, zDistance = FLT_MAX; + if (direction.x > 0.0f) { + xDistance = (x + 1.0f - position.x) / direction.x; + } else if (direction.x < 0.0f) { + xDistance = (x - position.x) / direction.x; + } + if (direction.y > 0.0f) { + yDistance = (y + 1.0f - position.y) / direction.y; + } else if (direction.y < 0.0f) { + yDistance = (y - position.y) / direction.y; + } + if (direction.z > 0.0f) { + zDistance = (z + 1.0f - position.z) / direction.z; + } else if (direction.z < 0.0f) { + zDistance = (z - position.z) / direction.z; + } + float minimumDistance = qMin(xDistance, qMin(yDistance, zDistance)); + if (minimumDistance == xDistance) { + if (direction.x > 0.0f) { + if (x++ == max) { + return false; + } + } else if (x-- == 0) { + return false; + } + } + if (minimumDistance == yDistance) { + if (direction.y > 0.0f) { + if (y++ == max) { + return false; + } + } else if (y-- == 0) { + return false; + } + } + if (minimumDistance == zDistance) { + if (direction.z > 0.0f) { + if (z++ == max) { + return false; + } + } else if (z-- == 0) { + return false; + } + } + position += direction * minimumDistance; + combinedDistance += minimumDistance; + } + return false; +} + void VoxelBuffer::render(bool cursor) { if (!_vertexBuffer.isCreated()) { _vertexBuffer.create(); @@ -1627,6 +1735,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { QVector vertices; QVector indices; QVector hermiteSegments; + QHash points; // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges @@ -2033,6 +2142,8 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; + points.insert(qRgb(x + 1, y + 1, z + 1), (glm::vec3(clampedX, clampedY, clampedZ) + center) / highest); + // determine whether we must "crease" by generating directional normals const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); AxisIndex index(vertices.size(), vertices.size(), vertices.size()); @@ -2158,7 +2269,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { colorZ += area; } } - buffer = new VoxelBuffer(vertices, indices, hermiteSegments, + buffer = new VoxelBuffer(vertices, indices, hermiteSegments, points, size, material ? material->getMaterials() : QVector()); } BufferDataPointer pointer(buffer); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 52706aab56..e256716fc0 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -48,6 +48,8 @@ public: bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); + bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); + Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID); @@ -241,7 +243,10 @@ class VoxelBuffer : public BufferData { public: VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QVector& materials = QVector()); + const QHash& points, int size, const QVector& materials = + QVector()); + + bool findFirstRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; virtual void render(bool cursor = false); @@ -250,6 +255,8 @@ private: QVector _vertices; QVector _indices; QVector _hermite; + QHash _points; + int _size; int _vertexCount; int _indexCount; int _hermiteCount; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 45911d9626..6dfe3d77ed 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -129,6 +129,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new EraseHeightfieldTool(this)); addTool(new VoxelMaterialBoxTool(this)); addTool(new VoxelMaterialSpannerTool(this)); + addTool(new VoxelMaterialBrushTool(this)); updateAttributes(); @@ -1525,3 +1526,46 @@ void VoxelMaterialSpannerTool::updateTexture() { void VoxelMaterialSpannerTool::textureLoaded() { _color->setColor(_texture->getAverageColor()); } + +VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) : + MetavoxelTool(editor, name, false, true) { +} + +bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("VoxelColorAttribute"); +} + +void VoxelBrushTool::render() { + if (Application::getInstance()->isMouseHidden()) { + return; + } + + // find the intersection with the voxels + glm::vec3 origin = Application::getInstance()->getMouseRayOrigin(); + glm::vec3 direction = Application::getInstance()->getMouseRayDirection(); + + float distance; + if (!Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, distance)) { + return; + } + _position = origin + distance * direction; + + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + + glPushMatrix(); + glTranslatef(_position.x, _position.y, _position.z); + + Application::getInstance()->getGeometryCache()->renderSphere(0.1f, 16, 16); + + glPopMatrix(); +} + +bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) { + return false; +} + +VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) : + VoxelBrushTool(editor, "Material Brush") { +} + + diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index ec92d9de28..725eac87f7 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -469,4 +469,32 @@ private: QSharedPointer _texture; }; +/// Base class for voxel brush tools. +class VoxelBrushTool : public MetavoxelTool { + Q_OBJECT + +public: + + VoxelBrushTool(MetavoxelEditor* editor, const QString& name); + + virtual bool appliesTo(const AttributePointer& attribute) const; + + virtual void render(); + + virtual bool eventFilter(QObject* watched, QEvent* event); + +protected: + + glm::vec3 _position; +}; + +/// Allows texturing parts of the voxel field. +class VoxelMaterialBrushTool : public VoxelBrushTool { + Q_OBJECT + +public: + + VoxelMaterialBrushTool(MetavoxelEditor* editor); +}; + #endif // hifi_MetavoxelEditor_h From 8105ded4ebe07e8a46b3be4f9e788d8b788324cd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:12:05 -0700 Subject: [PATCH 068/108] handle reloading a new url in billboard overlays --- .../src/ui/overlays/BillboardOverlay.cpp | 25 ++++++++++++++----- interface/src/ui/overlays/BillboardOverlay.h | 5 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 4f9e7c84f6..c7511ca466 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -18,7 +18,8 @@ BillboardOverlay::BillboardOverlay() : _fromImage(-1,-1,-1,-1), _scale(1.0f), - _isFacingAvatar(true) { + _isFacingAvatar(true), + _newTextureNeeded(true) { _isLoaded = false; } @@ -28,6 +29,9 @@ void BillboardOverlay::render() { } if (!_billboard.isEmpty()) { + if (_newTextureNeeded && _billboardTexture) { + _billboardTexture.reset(); + } if (!_billboardTexture) { QImage image = QImage::fromData(_billboard); if (image.format() != QImage::Format_ARGB32) { @@ -38,6 +42,7 @@ void BillboardOverlay::render() { _fromImage.setRect(0, 0, _size.width(), _size.height()); } _billboardTexture.reset(new Texture()); + _newTextureNeeded = false; glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _size.width(), _size.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); @@ -107,9 +112,10 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { QScriptValue urlValue = properties.property("url"); if (urlValue.isValid()) { - _url = urlValue.toVariant().toString(); - - setBillboardURL(_url); + QString newURL = urlValue.toVariant().toString(); + if (newURL != _url) { + setBillboardURL(newURL); + } } QScriptValue subImageBounds = properties.property("subImage"); @@ -150,9 +156,16 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { } } -void BillboardOverlay::setBillboardURL(const QUrl url) { +void BillboardOverlay::setBillboardURL(const QString& url) { + _url = url; + QUrl actualURL = url; _isLoaded = false; - QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(url)); + + // clear the billboard if previously set + _billboard.clear(); + _newTextureNeeded = true; + + QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(actualURL)); connect(reply, &QNetworkReply::finished, this, &BillboardOverlay::replyFinished); } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index a0b76869b3..fd594abe67 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -33,12 +33,13 @@ private slots: void replyFinished(); private: - void setBillboardURL(const QUrl url); + void setBillboardURL(const QString& url); - QUrl _url; + QString _url; QByteArray _billboard; QSize _size; QScopedPointer _billboardTexture; + bool _newTextureNeeded; QRect _fromImage; // where from in the image to sample From 3b01b652f3cb1fa2ca7efa1b86352a53d97b393a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:12:23 -0700 Subject: [PATCH 069/108] clean up of rotation from all directions --- examples/libraries/entitySelectionTool.js | 234 +++++++++++++--------- 1 file changed, 137 insertions(+), 97 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index ab176da559..c4ca72ad76 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -34,6 +34,7 @@ SelectionDisplay = (function () { var rotateOverlayTargetSize = 10000; // really big target var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool var innerRadius; + var outerRadius; var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -173,7 +174,8 @@ SelectionDisplay = (function () { alpha: 0.5, solid: true, visible: false, - rotation: baseOverlayRotation + rotation: baseOverlayRotation, + ignoreRayIntersection: true, // always ignore this }); var yawOverlayAngles = { x: 90, y: 0, z: 0 }; @@ -189,6 +191,7 @@ SelectionDisplay = (function () { 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", { @@ -197,6 +200,7 @@ SelectionDisplay = (function () { 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 }); @@ -225,6 +229,7 @@ SelectionDisplay = (function () { 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", { @@ -243,6 +248,7 @@ SelectionDisplay = (function () { 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", { @@ -253,10 +259,11 @@ SelectionDisplay = (function () { solid: true, visible: false, rotation: yawOverlayRotation, + ignoreRayIntersection: true, // always ignore this }); var yawHandle = Overlays.addOverlay("billboard", { - url: "https://s3.amazonaws.com/uploads.hipchat.com/33953/231323/HRRhkMk8ueLk8ku/rotate-arrow.png", + url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png", position: { x:0, y: 0, z: 0}, color: rotateHandleColor, alpha: rotateHandleAlpha, @@ -268,7 +275,7 @@ SelectionDisplay = (function () { var pitchHandle = Overlays.addOverlay("billboard", { - url: "https://s3.amazonaws.com/uploads.hipchat.com/33953/231323/HRRhkMk8ueLk8ku/rotate-arrow.png", + url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png", position: { x:0, y: 0, z: 0}, color: rotateHandleColor, alpha: rotateHandleAlpha, @@ -280,7 +287,7 @@ SelectionDisplay = (function () { var rollHandle = Overlays.addOverlay("billboard", { - url: "https://s3.amazonaws.com/uploads.hipchat.com/33953/231323/HRRhkMk8ueLk8ku/rotate-arrow.png", + url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png", position: { x:0, y: 0, z: 0}, color: rotateHandleColor, alpha: rotateHandleAlpha, @@ -434,7 +441,7 @@ SelectionDisplay = (function () { var diagonal = (Vec3.length(properties.dimensions) / 2) * 1.1; var halfDimensions = Vec3.multiply(properties.dimensions, 0.5); innerRadius = diagonal; - var outerRadius = diagonal * 1.15; + outerRadius = diagonal * 1.15; var innerActive = false; var innerAlpha = 0.2; var outerAlpha = 0.2; @@ -484,31 +491,37 @@ SelectionDisplay = (function () { if (MyAvatar.position.x > center.x) { // must be BRF or BRN if (MyAvatar.position.z < center.z) { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 180, z: 180 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 180 }); + yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); + pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); // 0, 90, 0?? + rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 0, y: 0, z: -1 }; - rollNormal = { x: 1, y: 0, z: 0 }; + pitchNormal = { x: 1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: 1 }; yawCorner = { x: right + rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; - pitchCorner = { x: right + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; - - rollCorner = { x: left - rotateHandleOffset, + pitchCorner = { x: left - rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset}; + rollCorner = { x: right + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset }; + yawCenter = { x: center.x, y: bottom, z: center.z }; - pitchCenter = { x: center.x, y: center.y, z: far }; - rollCenter = { x: left, y: center.y, z: center.z}; + pitchCenter = { x: left, y: center.y, z: center.z}; + rollCenter = { x: center.x, y: center.y, z: far }; + + + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-south.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-south.png" }); + + } else { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 270, z: 0 }); + 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 }); @@ -533,17 +546,20 @@ SelectionDisplay = (function () { yawCenter = { x: center.x, y: bottom, z: center.z }; pitchCenter = { x: left, y: center.y, z: center.z }; rollCenter = { x: center.x, y: center.y, z: near}; + + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); } } else { // must be BLF or BLN if (MyAvatar.position.z < center.z) { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 90, z: 0 }); + 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 }; + pitchNormal = { x: 1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: 1 }; yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, @@ -561,31 +577,38 @@ SelectionDisplay = (function () { pitchCenter = { x: right, y: center.y, z: center.z }; rollCenter = { x: center.x, y: center.y, z: far}; + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + } else { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 180, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); + yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 270, z: 0 }); + rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); + pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); // TODO: fix these yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 0, y: 0, z: 1 }; - rollNormal = { x: -1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: 1 }; + pitchNormal = { x: 1, y: 0, z: 0 }; yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; - pitchCorner = { x: left - rotateHandleOffset, + rollCorner = { x: left - rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset }; - rollCorner = { x: right + rotateHandleOffset, + pitchCorner = { x: right + rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset}; yawCenter = { x: center.x, y: bottom, z: center.z }; - pitchCenter = { x: center.x, y: center.y, z: near }; - rollCenter = { x: right, y: center.y, z: center.z}; + rollCenter = { x: center.x, y: center.y, z: near }; + pitchCenter = { x: right, y: center.y, z: center.z}; + + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + } } @@ -1558,7 +1581,6 @@ SelectionDisplay = (function () { var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - print("ROTATE_YAW"); var properties = Entities.getEntityProperties(currentSelection); var center = yawCenter; var zero = yawZero; @@ -1567,21 +1589,41 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; if (distanceFromCenter < innerRadius) { angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; } - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: center, - end: result.intersection - }); + // for debugging + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); var newRotation = Quat.multiply(yawChange, originalRotation); Entities.editEntity(currentSelection, { rotation: newRotation }); + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + 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 }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius }); + } + } }; @@ -1599,8 +1641,6 @@ SelectionDisplay = (function () { var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - print("ROTATE_PITCH"); - var properties = Entities.getEntityProperties(currentSelection); var center = pitchCenter; var zero = pitchZero; @@ -1609,21 +1649,40 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; if (distanceFromCenter < innerRadius) { angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; } - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: center, - end: result.intersection - }); + // for debugging + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); var newRotation = Quat.multiply(pitchChange, originalRotation); Entities.editEntity(currentSelection, { rotation: newRotation }); + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + 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 }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius }); + } } }; @@ -1640,7 +1699,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - print("ROTATE_ROLL"); var properties = Entities.getEntityProperties(currentSelection); var center = rollCenter; var zero = rollZero; @@ -1649,21 +1707,40 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; if (distanceFromCenter < innerRadius) { angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; } - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: center, - end: result.intersection - }); + // for debugging + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); var newRotation = Quat.multiply(rollChange, originalRotation); Entities.editEntity(currentSelection, { rotation: newRotation }); + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + 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 }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius }); + } } }; @@ -1904,51 +1981,14 @@ SelectionDisplay = (function () { print(" attempting to show overlays:" + somethingClicked); print(" currentRotation:" + currentRotation); - 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: currentRotation, - endAt: 360 - }); - - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - rotation: overlayOrientation, - position: overlayCenter, - startAt: 0, - endAt: currentRotation - }); - - Overlays.editOverlay(rotateZeroOverlay, - { - visible: true, - start: overlayCenter, - end: result.intersection - }); - - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: overlayCenter, - end: result.intersection - }); + 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 }); + + // for debugging + //Overlays.editOverlay(rotateZeroOverlay, { visible: true, start: overlayCenter, end: result.intersection }); + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: overlayCenter, end: result.intersection }); Overlays.editOverlay(yawHandle, { visible: false }); Overlays.editOverlay(pitchHandle, { visible: false }); From df16bd2b3c77424ba377abd1a45dce68504f592f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:24:09 -0700 Subject: [PATCH 070/108] cleanup comment --- examples/libraries/entitySelectionTool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index c4ca72ad76..d39234a90f 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -492,7 +492,7 @@ SelectionDisplay = (function () { // must be BRF or BRN if (MyAvatar.position.z < center.z) { yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); // 0, 90, 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 }; From ec5ba59f0e2962a8c893fe42b46d1b2071a28c78 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:24:34 -0700 Subject: [PATCH 071/108] cleanup comment --- examples/libraries/entitySelectionTool.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d39234a90f..bd02182af3 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -585,7 +585,6 @@ SelectionDisplay = (function () { rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - // TODO: fix these yawNormal = { x: 0, y: 1, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; pitchNormal = { x: 1, y: 0, z: 0 }; From eae1e45adaecb3852de458b1d808c76c95410de2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:29:02 -0700 Subject: [PATCH 072/108] cleanup comments and dead code --- examples/libraries/entitySelectionTool.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index bd02182af3..0f68ef4442 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1920,7 +1920,6 @@ SelectionDisplay = (function () { var pitch = angles.x; var yaw = angles.y; var roll = angles.z; - var currentRotation; originalRotation = properties.rotation; originalPitch = pitch; @@ -1934,7 +1933,6 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = yawHandleRotation; overlayCenter = yawCenter; - currentRotation = yaw; yawZero = result.intersection; rotationNormal = yawNormal; break; @@ -1944,7 +1942,6 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; - currentRotation = pitch; pitchZero = result.intersection; rotationNormal = pitchNormal; break; @@ -1954,7 +1951,6 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = rollHandleRotation; overlayCenter = rollCenter; - currentRotation = roll; rollZero = result.intersection; rotationNormal = rollNormal; break; @@ -1972,14 +1968,6 @@ SelectionDisplay = (function () { if (somethingClicked) { - if (currentRotation < 0) { - currentRotation = currentRotation + 360; - } - - // TODO: need to place correctly.... - print(" attempting to show overlays:" + somethingClicked); - print(" currentRotation:" + currentRotation); - 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 }); From d2c7bf4334a552319e6c0cabfc6b235c6d67c6c1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:58:26 -0700 Subject: [PATCH 073/108] fix for delete of object table row --- domain-server/resources/web/js/settings.js | 27 ++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 923a01a8a9..38baf223b3 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -474,21 +474,24 @@ function deleteTableRow(delete_glyphicon) { if (!isArray) { // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save row.empty() - row.html(""); - } else if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { - updateDataChangedForSiblingRows(row) - - // this isn't the last row - we can just remove it - row.remove() + row.html(""); } else { - // this is the last row, we can't remove it completely since we need to post an empty array - row.empty() + if (table.find('.' + Settings.DATA_ROW_CLASS).length) { + updateDataChangedForSiblingRows(row) - row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) - row.addClass('empty-array-row') + // this isn't the last row - we can just remove it + row.remove() + } else { + // this is the last row, we can't remove it completely since we need to post an empty array + row.empty() - row.html(""); + row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) + row.addClass('empty-array-row') + + row.html(""); + } } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated From a9eaf3b740537d9994b1ef11c980743ea86a1a1b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 09:27:07 -0700 Subject: [PATCH 074/108] Update gamepad.js to work with wired 360 controller --- examples/gamepad.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/gamepad.js b/examples/gamepad.js index 1911e71a25..e4ca03451a 100644 --- a/examples/gamepad.js +++ b/examples/gamepad.js @@ -9,7 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); +var CONTROLLER_NAMES = [ + "Wireless 360 Controller", + "Controller", // Wired 360 controller +] + +for (var i = 0; i < CONTROLLER_NAMES.length; i++) { + gamepad = Joysticks.joystickWithName(CONTROLLER_NAMES[i]); + if (gamepad) { + print("Found controller: " + CONTROLLER_NAMES[i]); + break; + } +} if (!gamepad) { print("No gamepad found."); @@ -194,7 +205,6 @@ function reportAxisValue(axis, newValue, oldValue) { } function reportButtonValue(button, newValue, oldValue) { - print("The value for button " + button + " has changed from " + oldValue + " to " + newValue); if (button == BUTTON_FLY_DOWN) { flyDownButtonState = newValue; } else if (button == BUTTON_FLY_UP) { @@ -260,7 +270,9 @@ function update(dt) { updateWarp(); } -gamepad.axisValueChanged.connect(reportAxisValue); -gamepad.buttonStateChanged.connect(reportButtonValue); +if (gamepad) { + gamepad.axisValueChanged.connect(reportAxisValue); + gamepad.buttonStateChanged.connect(reportButtonValue); -Script.update.connect(update); + Script.update.connect(update); +} From 3aca910e17a9e6694d3e4beed9bc98c3be2715f7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Oct 2014 09:27:15 -0700 Subject: [PATCH 075/108] remove some extra console logs --- domain-server/resources/web/js/settings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 38baf223b3..9d586c8436 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -152,7 +152,6 @@ $(document).ready(function(){ }) $('#settings-form').on('change', 'select', function(){ - console.log("Changed" + $(this)) $("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change() }) From c69ad78d3b67d1ee736e04920962fb5556a6d303 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 09:30:51 -0700 Subject: [PATCH 076/108] Add wired 360 controller on Windows support --- examples/gamepad.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/gamepad.js b/examples/gamepad.js index e4ca03451a..4ec0309511 100644 --- a/examples/gamepad.js +++ b/examples/gamepad.js @@ -9,8 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// TODO Update to work with any controller that is plugged in. var CONTROLLER_NAMES = [ "Wireless 360 Controller", + "Controller (XBOX 360 For Windows)", "Controller", // Wired 360 controller ] From f5ee55bef4052c0f63bf9b971a735d4d9836fcb8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Oct 2014 09:44:38 -0700 Subject: [PATCH 077/108] remove xbox.js now that we have gamepad.js --- examples/xbox.js | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 examples/xbox.js diff --git a/examples/xbox.js b/examples/xbox.js deleted file mode 100644 index 603e0dbf56..0000000000 --- a/examples/xbox.js +++ /dev/null @@ -1,19 +0,0 @@ -// -// xbox.js -// examples -// -// Created by Stephen Birarda on September 23, 2014 -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); - -function reportAxisValue(axis, newValue, oldValue) { - print("The value for axis " + axis + " has changed to " + newValue + ". It was " + oldValue); -} - -gamepad.axisValueChanged.connect(reportAxisValue); \ No newline at end of file From db881c0dc8368ac15f428d4b5646b351c205cc56 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 10:32:18 -0700 Subject: [PATCH 078/108] add boundBox gettable property to EntityItemProperties --- .../entities/src/EntityItemProperties.cpp | 27 +++++++++++++++++++ libraries/entities/src/EntityItemProperties.h | 1 + 2 files changed, 28 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 1080a866f5..2690bcd45b 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -174,6 +174,16 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons sittingPoints.setProperty("length", _sittingPoints.size()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable + AABox aaBox = getAABoxInMeters(); + QScriptValue boundingBox = engine->newObject(); + QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); + boundingBox.setProperty("brn", bottomRightNear); + boundingBox.setProperty("tfl", topFarLeft); + boundingBox.setProperty("center", center); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(boundingBox, boundingBox); // gettable, but not settable + return properties; } @@ -643,3 +653,20 @@ AACube EntityItemProperties::getMaximumAACubeInMeters() const { return AACube(minimumCorner, diameter); } + +// The minimum bounding box for the entity. +AABox EntityItemProperties::getAABoxInMeters() const { + + // _position represents the position of the registration point. + glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint; + + glm::vec3 unrotatedMinRelativeToEntity = glm::vec3(0.0f, 0.0f, 0.0f) - (_dimensions * _registrationPoint); + glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; + Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; + Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation()); + + // shift the extents to be relative to the position/registration point + rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position); + + return AABox(rotatedExtentsRelativeToRegistrationPoint); +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 3c77b63ab6..6e1594fb9b 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -101,6 +101,7 @@ public: AACube getMaximumAACubeInTreeUnits() const; AACube getMaximumAACubeInMeters() const; + AABox getAABoxInMeters() const; void debugDump() const; From ca66319aa219ab64b6fee51336ad51a15fd08148 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 10:37:32 -0700 Subject: [PATCH 079/108] draw selection handles for bounding box correctly when rotated --- examples/libraries/entitySelectionTool.js | 144 +++++++++++----------- 1 file changed, 74 insertions(+), 70 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 0f68ef4442..b00876c969 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -454,22 +454,28 @@ SelectionDisplay = (function () { var rotateHandleOffset = 0.05; var grabberMoveUpOffset = 0.1; - var left = properties.position.x - halfDimensions.x; - var right = properties.position.x + halfDimensions.x; - var bottom = properties.position.y - halfDimensions.y; - var top = properties.position.y + halfDimensions.y; - var near = properties.position.z - halfDimensions.z; - var far = properties.position.z + halfDimensions.z; - var center = { x: properties.position.x, y: properties.position.y, z: properties.position.z }; + var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; - var BLN = { x: left, y: bottom, z: near }; - var BRN = { x: right, y: bottom, z: near }; - var BLF = { x: left, y: bottom, z: far }; - var BRF = { x: right, y: bottom, z: far }; - var TLN = { x: left, y: top, z: near }; - var TRN = { x: right, y: top, z: near }; - var TLF = { x: left, y: top, z: far }; - var TRF = { x: right, y: top, z: far }; + objectCenter = { x: properties.position.x, y: properties.position.y, z: properties.position.z }; + + top = properties.boundingBox.tfl.y; + far = properties.boundingBox.tfl.z; + left = properties.boundingBox.tfl.x; + + bottom = properties.boundingBox.brn.y; + right = properties.boundingBox.brn.x; + near = properties.boundingBox.brn.z; + + boundsCenter = { x: properties.boundingBox.center.x, y: properties.boundingBox.center.y, z: properties.boundingBox.center.z }; + + BLN = { x: left, y: bottom, z: near }; + BRN = { x: right, y: bottom, z: near }; + BLF = { x: left, y: bottom, z: far }; + BRF = { x: right, y: bottom, z: far }; + TLN = { x: left, y: top, z: near }; + TRN = { x: right, y: top, z: near }; + TLF = { x: left, y: top, z: far }; + TRF = { x: right, y: top, z: far }; var yawCorner; var pitchCorner; @@ -488,9 +494,10 @@ SelectionDisplay = (function () { ------------------------------*/ - if (MyAvatar.position.x > center.x) { + if (MyAvatar.position.x > objectCenter.x) { // must be BRF or BRN - if (MyAvatar.position.z < center.z) { + if (MyAvatar.position.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 }); @@ -499,21 +506,21 @@ SelectionDisplay = (function () { pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; - yawCorner = { x: right + rotateHandleOffset, + yawCorner = { x: left + rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; - pitchCorner = { x: left - rotateHandleOffset, + pitchCorner = { x: right - rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset}; - rollCorner = { x: right + rotateHandleOffset, + rollCorner = { x: left + rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset }; - yawCenter = { x: center.x, y: bottom, z: center.z }; - pitchCenter = { x: left, y: center.y, z: center.z}; - rollCenter = { x: center.x, y: center.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: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-south.png" }); @@ -521,6 +528,7 @@ SelectionDisplay = (function () { } 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 }); @@ -530,29 +538,31 @@ SelectionDisplay = (function () { rollNormal = { x: 0, y: 0, z: 1 }; - yawCorner = { x: right + rotateHandleOffset, + yawCorner = { x: left + rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; - pitchCorner = { x: left - rotateHandleOffset, + pitchCorner = { x: right - rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset }; - rollCorner = { x: right + rotateHandleOffset, + rollCorner = { x: left + rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset}; - yawCenter = { x: center.x, y: bottom, z: center.z }; - pitchCenter = { x: left, y: center.y, z: center.z }; - rollCenter = { x: center.x, y: center.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: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); } } else { + // must be BLF or BLN - if (MyAvatar.position.z < center.z) { + if (MyAvatar.position.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 }); @@ -561,26 +571,27 @@ SelectionDisplay = (function () { pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; - yawCorner = { x: left - rotateHandleOffset, + yawCorner = { x: right - rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; - pitchCorner = { x: right + rotateHandleOffset, + pitchCorner = { x: left + rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset }; - rollCorner = { x: left - rotateHandleOffset, + rollCorner = { x: right - rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset}; - yawCenter = { x: center.x, y: bottom, z: center.z }; - pitchCenter = { x: right, y: center.y, z: center.z }; - rollCenter = { x: center.x, y: center.y, z: far}; + 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: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); } else { + yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 270, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); @@ -589,21 +600,21 @@ SelectionDisplay = (function () { rollNormal = { x: 0, y: 0, z: 1 }; pitchNormal = { x: 1, y: 0, z: 0 }; - yawCorner = { x: left - rotateHandleOffset, + yawCorner = { x: right - rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; - rollCorner = { x: left - rotateHandleOffset, + rollCorner = { x: right - rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset }; - pitchCorner = { x: right + rotateHandleOffset, + pitchCorner = { x: left + rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset}; - yawCenter = { x: center.x, y: bottom, z: center.z }; - rollCenter = { x: center.x, y: center.y, z: near }; - pitchCenter = { x: right, y: center.y, z: center.z}; + 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: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); @@ -611,7 +622,6 @@ SelectionDisplay = (function () { } } - var rotateHandlesVisible = true; var translateHandlesVisible = true; var stretchHandlesVisible = true; @@ -632,17 +642,11 @@ SelectionDisplay = (function () { Overlays.editOverlay(highlightBox, { visible: false }); - print("selectionBoxVisible:" + selectionBoxVisible); - Overlays.editOverlay(selectionBox, - { - visible: selectionBoxVisible, - position: center, - dimensions: properties.dimensions, - rotation: properties.rotation, - }); + Overlays.editOverlay(selectionBox, { visible: selectionBoxVisible, position: objectCenter, dimensions: properties.dimensions, + rotation: properties.rotation,}); - Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: center.x, y: top + grabberMoveUpOffset, z: center.z } }); + Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: boundsCenter.x, y: top + grabberMoveUpOffset, z: boundsCenter.z } }); Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: near } }); Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: near } }); @@ -654,25 +658,25 @@ SelectionDisplay = (function () { Overlays.editOverlay(grabberRTF, { visible: stretchHandlesVisible, position: { x: right, y: top, z: far } }); - Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: center.z } }); - Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberLEFT, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: center.z } }); - Overlays.editOverlay(grabberRIGHT, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: center.z } }); - Overlays.editOverlay(grabberNEAR, { visible: stretchHandlesVisible, position: { x: center.x, y: center.y, z: near } }); - Overlays.editOverlay(grabberFAR, { visible: stretchHandlesVisible, position: { x: center.x, y: center.y, z: far } }); + Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: top, z: boundsCenter.z } }); + Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: bottom, z: boundsCenter.z } }); + Overlays.editOverlay(grabberLEFT, { visible: stretchHandlesVisible, position: { x: left, y: boundsCenter.y, z: boundsCenter.z } }); + Overlays.editOverlay(grabberRIGHT, { visible: stretchHandlesVisible, position: { x: right, y: boundsCenter.y, z: boundsCenter.z } }); + Overlays.editOverlay(grabberNEAR, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: boundsCenter.y, z: near } }); + Overlays.editOverlay(grabberFAR, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: boundsCenter.y, z: far } }); - Overlays.editOverlay(grabberEdgeTR, { visible: stretchHandlesVisible, position: { x: right, y: top, z: center.z } }); - Overlays.editOverlay(grabberEdgeTL, { visible: stretchHandlesVisible, position: { x: left, y: top, z: center.z } }); - Overlays.editOverlay(grabberEdgeTF, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: far } }); - Overlays.editOverlay(grabberEdgeTN, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: near } }); - Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: far } }); - Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: near } }); - Overlays.editOverlay(grabberEdgeNR, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: near } }); - Overlays.editOverlay(grabberEdgeNL, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: near } }); - Overlays.editOverlay(grabberEdgeFR, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: far } }); - Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: far } }); + Overlays.editOverlay(grabberEdgeTR, { visible: stretchHandlesVisible, position: { x: right, y: top, z: boundsCenter.z } }); + Overlays.editOverlay(grabberEdgeTL, { visible: stretchHandlesVisible, position: { x: left, y: top, z: boundsCenter.z } }); + Overlays.editOverlay(grabberEdgeTF, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: top, z: far } }); + Overlays.editOverlay(grabberEdgeTN, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: top, z: near } }); + Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: boundsCenter.z } }); + Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: boundsCenter.z } }); + Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: bottom, z: far } }); + Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: bottom, z: near } }); + Overlays.editOverlay(grabberEdgeNR, { visible: stretchHandlesVisible, position: { x: right, y: boundsCenter.y, z: near } }); + Overlays.editOverlay(grabberEdgeNL, { visible: stretchHandlesVisible, position: { x: left, y: boundsCenter.y, z: near } }); + Overlays.editOverlay(grabberEdgeFR, { visible: stretchHandlesVisible, position: { x: right, y: boundsCenter.y, z: far } }); + Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, position: { x: left, y: boundsCenter.y, z: far } }); Overlays.editOverlay(baseOfEntityProjectionOverlay, From 64d9a96a2ba53c8231769917bc5459bba57d825b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Oct 2014 10:42:13 -0700 Subject: [PATCH 080/108] Update SVG file location --- interface/src/devices/OculusManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 8078c2666b..010258a56d 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -66,7 +66,7 @@ glm::vec3 OculusManager::_calibrationPosition; glm::quat OculusManager::_calibrationOrientation; quint64 OculusManager::_calibrationStartTime; int OculusManager::_calibrationMessage = 0; -QString OculusManager::CALIBRATION_BILLBOARD_URL = "http://ctrlaltstudio.com/hifi/hold-to-calibrate.svg"; // TODO: Update with final URL +QString OculusManager::CALIBRATION_BILLBOARD_URL = "http://hifi-public.s3.amazonaws.com/images/hold-to-calibrate.svg"; float OculusManager::CALIBRATION_BILLBOARD_SCALE = 2.f; #endif From aa37ba5dfdfeb0cecd17bc349dda44403f9e5c21 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Oct 2014 10:57:17 -0700 Subject: [PATCH 081/108] Use const parameters --- interface/src/devices/OculusManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index e46f1673a0..dfe4a212b6 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -103,7 +103,7 @@ private: static Camera* _camera; static int _activeEyeIndex; - static void calibrate(glm::vec3 position, glm::quat orientation); + static void calibrate(const glm::vec3 position, const glm::quat orientation); enum CalibrationState { UNCALIBRATED, WAITING_FOR_DELTA, From 0a5fdee1f2d4e97c8ff5a8d730ea50c4eb49d9a5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Oct 2014 11:00:41 -0700 Subject: [PATCH 082/108] Use NULL rather than 0 for "no overlay" --- interface/src/devices/OculusManager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 010258a56d..158ace0a37 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -65,7 +65,7 @@ OculusManager::CalibrationState OculusManager::_calibrationState; glm::vec3 OculusManager::_calibrationPosition; glm::quat OculusManager::_calibrationOrientation; quint64 OculusManager::_calibrationStartTime; -int OculusManager::_calibrationMessage = 0; +int OculusManager::_calibrationMessage = NULL; QString OculusManager::CALIBRATION_BILLBOARD_URL = "http://hifi-public.s3.amazonaws.com/images/hold-to-calibrate.svg"; float OculusManager::CALIBRATION_BILLBOARD_SCALE = 2.f; @@ -228,7 +228,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { _calibrationStartTime = usecTimestampNow(); _calibrationState = WAITING_FOR_ZERO_HELD; - if (_calibrationMessage == 0) { + if (!_calibrationMessage) { qDebug() << "Calibration message: Hold still to calibrate"; billboard = new BillboardOverlay(); @@ -254,7 +254,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { _calibrationState = CALIBRATED; qDebug() << "Delete calibration message"; Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); - _calibrationMessage = 0; + _calibrationMessage = NULL; Application::getInstance()->resetSensors(); } else { quint64 quarterSeconds = (usecTimestampNow() - _calibrationStartTime) / 250000; @@ -293,10 +293,10 @@ void OculusManager::recalibrate() { void OculusManager::abandonCalibration() { #ifdef HAVE_LIBOVR _calibrationState = CALIBRATED; - if (_calibrationMessage > 0) { + if (_calibrationMessage) { qDebug() << "Delete calibration message"; Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); - _calibrationMessage = 0; + _calibrationMessage = NULL; } #endif } From b0f4bc0d1ccbbb2c092dd35c721ea9740eb9f0a7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Oct 2014 11:08:15 -0700 Subject: [PATCH 083/108] Tidy up log messages --- interface/src/devices/OculusManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 158ace0a37..7ddd83458f 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -229,7 +229,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { _calibrationState = WAITING_FOR_ZERO_HELD; if (!_calibrationMessage) { - qDebug() << "Calibration message: Hold still to calibrate"; + qDebug() << "Hold still to calibrate HMD"; billboard = new BillboardOverlay(); billboard->setURL(CALIBRATION_BILLBOARD_URL); @@ -252,7 +252,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) { if ((usecTimestampNow() - _calibrationStartTime) > CALIBRATION_ZERO_HOLD_TIME) { _calibrationState = CALIBRATED; - qDebug() << "Delete calibration message"; + qDebug() << "HMD calibrated"; Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); _calibrationMessage = NULL; Application::getInstance()->resetSensors(); @@ -271,7 +271,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { } else { progressMessage += "."; } - qDebug() << progressMessage; + //qDebug() << progressMessage; // Progress message ready for 3D text overlays. } } } else { @@ -294,7 +294,7 @@ void OculusManager::abandonCalibration() { #ifdef HAVE_LIBOVR _calibrationState = CALIBRATED; if (_calibrationMessage) { - qDebug() << "Delete calibration message"; + qDebug() << "Abandoned HMD calibration"; Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage); _calibrationMessage = NULL; } From 357d49d442381cdbcf2280d3f0242529230c9a0a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Oct 2014 11:36:47 -0700 Subject: [PATCH 084/108] Fix building without Oculus library --- interface/src/devices/OculusManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 7ddd83458f..53715aabe9 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -188,6 +188,7 @@ void OculusManager::disconnect() { #endif } +#ifdef HAVE_LIBOVR void OculusManager::positionCalibrationBillboard(BillboardOverlay* billboard) { glm::quat headOrientation = Application::getInstance()->getAvatar()->getHeadOrientation(); headOrientation.x = 0; @@ -197,9 +198,10 @@ void OculusManager::positionCalibrationBillboard(BillboardOverlay* billboard) { + headOrientation * glm::vec3(0.f, 0.f, -CALIBRATION_MESSAGE_DISTANCE)); billboard->setRotation(headOrientation); } +#endif -void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { #ifdef HAVE_LIBOVR +void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { static QString progressMessage; static BillboardOverlay* billboard; @@ -281,8 +283,8 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { } break; } -#endif } +#endif void OculusManager::recalibrate() { #ifdef HAVE_LIBOVR From edf8cb671d13b510ff939d479d4a8ceb2aa8d45a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 3 Oct 2014 09:37:56 -0700 Subject: [PATCH 085/108] Add "reset sensors" menu item corresponding to the apostrophe key --- interface/src/Menu.cpp | 14 ++++++++++---- interface/src/Menu.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 77009da18b..08fc5067fb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -246,10 +246,16 @@ Menu::Menu() : #endif addActionToQMenuAndActionHash(toolsMenu, - MenuOption::Console, - Qt::CTRL | Qt::ALT | Qt::Key_J, - this, - SLOT(toggleConsole())); + MenuOption::Console, + Qt::CTRL | Qt::ALT | Qt::Key_J, + this, + SLOT(toggleConsole())); + + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::ResetSensors, + Qt::Key_Apostrophe, + appInstance, + SLOT(resetSensors())); QMenu* avatarMenu = addMenu("Avatar"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 67a4163c67..8fcce3dbf3 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -442,6 +442,7 @@ namespace MenuOption { const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; const QString ResetAvatarSize = "Reset Avatar Size"; + const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts"; const QString RunTimingTests = "Run Timing Tests"; const QString ScriptEditor = "Script Editor..."; From df6f4d4c95be5ab069f253cd4813baf383732e21 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Oct 2014 14:17:52 -0700 Subject: [PATCH 086/108] remove cara face tracking, cleanup warnings --- interface/src/Application.cpp | 17 +- interface/src/Application.h | 4 - interface/src/devices/CaraFaceTracker.cpp | 455 ------------------ interface/src/devices/CaraFaceTracker.h | 124 ----- interface/src/devices/OculusManager.cpp | 3 + interface/src/gpu/Resource.cpp | 8 +- .../src/ui/overlays/BillboardOverlay.cpp | 11 +- 7 files changed, 14 insertions(+), 608 deletions(-) delete mode 100644 interface/src/devices/CaraFaceTracker.cpp delete mode 100644 interface/src/devices/CaraFaceTracker.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0c4cc71190..d3bed0c3ea 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1551,10 +1551,9 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox FaceTracker* Application::getActiveFaceTracker() { return (_dde.isActive() ? static_cast(&_dde) : - (_cara.isActive() ? static_cast(&_cara) : (_faceshift.isActive() ? static_cast(&_faceshift) : (_faceplus.isActive() ? static_cast(&_faceplus) : - (_visage.isActive() ? static_cast(&_visage) : NULL))))); + (_visage.isActive() ? static_cast(&_visage) : NULL)))); } struct SendVoxelsOperationArgs { @@ -2013,19 +2012,6 @@ void Application::updateDDE() { _dde.update(); } -void Application::updateCara() { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateCara()"); - - // Update Cara - _cara.update(); - - // Copy angular velocity if measured by cara, to the head - if (_cara.isActive()) { - _myAvatar->getHead()->setAngularVelocity(_cara.getHeadAngularVelocity()); - } -} - void Application::updateMyAvatarLookAtPosition() { PerformanceTimer perfTimer("lookAt"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); @@ -2125,7 +2111,6 @@ void Application::updateMetavoxels(float deltaTime) { } void Application::cameraMenuChanged() { - float modeShiftPeriod = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? 0.0f : 1.0f; if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); diff --git a/interface/src/Application.h b/interface/src/Application.h index 7277c87b2e..ce9ed0c1a6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -63,7 +63,6 @@ #include "devices/PrioVR.h" #include "devices/SixenseManager.h" #include "devices/Visage.h" -#include "devices/CaraFaceTracker.h" #include "devices/DdeFaceTracker.h" #include "entities/EntityTreeRenderer.h" #include "particles/ParticleTreeRenderer.h" @@ -219,7 +218,6 @@ public: Faceshift* getFaceshift() { return &_faceshift; } Visage* getVisage() { return &_visage; } DdeFaceTracker* getDDE() { return &_dde; } - CaraFaceTracker* getCara() { return &_cara; } FaceTracker* getActiveFaceTracker(); PrioVR* getPrioVR() { return &_prioVR; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } @@ -408,7 +406,6 @@ private: void updateFaceshift(); void updateVisage(); void updateDDE(); - void updateCara(); void updateMyAvatarLookAtPosition(); void updateThreads(float deltaTime); void updateMetavoxels(float deltaTime); @@ -509,7 +506,6 @@ private: Faceplus _faceplus; Faceshift _faceshift; Visage _visage; - CaraFaceTracker _cara; DdeFaceTracker _dde; PrioVR _prioVR; diff --git a/interface/src/devices/CaraFaceTracker.cpp b/interface/src/devices/CaraFaceTracker.cpp deleted file mode 100644 index bc2c4bb2d1..0000000000 --- a/interface/src/devices/CaraFaceTracker.cpp +++ /dev/null @@ -1,455 +0,0 @@ -// -// CaraFaceTracker.cpp -// interface/src/devices -// -// Created by Li Zuwei on 7/22/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "CaraFaceTracker.h" -#include - -//qt -#include -#include -#include -#include - -#define PI M_PI -#define RADTODEG(x) ( (x) * 180.0 / PI ) -#define DEGTORAD(x) ( (x) * PI / 180.0 ) - -static const QHostAddress CARA_FEATURE_POINT_SERVER_ADDR("127.0.0.1"); -static const quint16 CARA_FEATURE_POINT_SERVER_PORT = 36555; -static QString sampleJson = "[{\"id\":1, \ - \"face\":{\"x\":248,\"y\":64,\"width\":278,\"height\":341}, \ - \"pose\":{\"roll\":2.62934,\"pitch\":-12.2318,\"yaw\":0.936743}, \ - \"feature_points\":[314,194,326,187,340,187,354,189,367,193,409,190,421,187,435,184,448,183,459,188, \ - 388,207,389,223,390,240,391,257,377,266,384,267,392,268,399,266,407,264,331,209, \ - 341,204,354,204,364,209,353,214,341,214,410,208,420,201,433,200,443,205,434,211, \ - 421,211,362,294,372,290,383,287,393,289,404,286,415,289,426,291,418,300,407,306, \ - 394,308,382,307,371,302,383,295,394,295,404,294,404,295,393,297,383,296], \ - \"classifiers\":{\"emotion\":{\"smi\":-0.368829,\"sur\":-1.33334,\"neg\":0.00235828,\"att\":1},\"blink\":1}}]"; - -static const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.0f); -static const float TRANSLATION_SCALE = 1.0f; -static const int NUM_BLENDSHAPE_COEFF = 30; -static const int NUM_SMOOTHING_SAMPLES = 3; - -struct CaraPerson { - struct CaraPose { - float roll, pitch, yaw; - CaraPose() : - roll(0.0f), - pitch(0.0f), - yaw(0.0f) - { - } - }; - - struct CaraEmotion { - float smile, surprise, negative, attention; - CaraEmotion(): - smile(0.0f), - surprise(0.0f), - negative(0.0f), - attention(0.0f) - { - } - }; - - enum CaraBlink { - BLINK_NOT_AVAILABLE, - NO_BLINK, - BLINK - }; - - CaraPerson() : - id(-1), - blink(BLINK_NOT_AVAILABLE) - { - } - - int id; - CaraPose pose; - CaraEmotion emotion; - CaraBlink blink; - - QString toString() { - QString s = QString("id: %1, roll: %2, pitch: %3, yaw: %4, smi: %5, sur: %6, neg: %7, att: %8, blink: %9"). - arg(id). - arg(pose.roll). - arg(pose.pitch). - arg(pose.yaw). - arg(emotion.smile). - arg(emotion.surprise). - arg(emotion.negative). - arg(emotion.attention). - arg(blink); - return s; - } -}; - -class CaraPacketDecoder { -public: - static CaraPerson extractOne(const QByteArray& buffer, QJsonParseError* jsonError) { - CaraPerson person; - QJsonDocument dom = QJsonDocument::fromJson(buffer, jsonError); - - //check for errors - if(jsonError->error == QJsonParseError::NoError) { - //read the dom structure and populate the blend shapes and head poses - //qDebug() << "[Info] Cara Face Tracker Packet Parsing Successful!"; - - //begin extracting the packet - if(dom.isArray()) { - QJsonArray people = dom.array(); - //extract the first person in the array - if(people.size() > 0) { - QJsonValue val = people.at(0); - if(val.isObject()) { - QJsonObject personDOM = val.toObject(); - person.id = extractId(personDOM); - person.pose = extractPose(personDOM); - - //extract the classifier outputs - QJsonObject::const_iterator it = personDOM.constFind("classifiers"); - if(it != personDOM.constEnd()) { - QJsonObject classifierDOM = (*it).toObject(); - person.emotion = extractEmotion(classifierDOM); - person.blink = extractBlink(classifierDOM); - } - } - } - } - } - - return person; - } - -private: - static int extractId(const QJsonObject& person) { - int id = -1; - QJsonObject::const_iterator it = person.constFind("id"); - if(it != person.constEnd()) { - id = (*it).toInt(-1); - } - return id; - } - - static CaraPerson::CaraPose extractPose(const QJsonObject& person) { - CaraPerson::CaraPose pose; - QJsonObject::const_iterator it = person.constFind("pose"); - if(it != person.constEnd()) { - QJsonObject poseDOM = (*it).toObject(); - - //look for the roll, pitch, yaw; - QJsonObject::const_iterator poseIt = poseDOM.constFind("roll"); - QJsonObject::const_iterator poseEnd = poseDOM.constEnd(); - if(poseIt != poseEnd) { - pose.roll = (float)(*poseIt).toDouble(0.0); - } - poseIt = poseDOM.constFind("pitch"); - if(poseIt != poseEnd) { - pose.pitch = (float)(*poseIt).toDouble(0.0); - } - poseIt = poseDOM.constFind("yaw"); - if(poseIt != poseEnd) { - pose.yaw = (float)(*poseIt).toDouble(0.0); - } - } - return pose; - } - - static CaraPerson::CaraEmotion extractEmotion(const QJsonObject& classifiers) { - CaraPerson::CaraEmotion emotion; - QJsonObject::const_iterator it = classifiers.constFind("emotion"); - if(it != classifiers.constEnd()) { - QJsonObject emotionDOM = (*it).toObject(); - - //look for smile, surprise, negative, attention responses - QJsonObject::const_iterator emoEnd = emotionDOM.constEnd(); - QJsonObject::const_iterator emoIt = emotionDOM.constFind("smi"); - if(emoIt != emoEnd) { - emotion.smile = (float)(*emoIt).toDouble(0.0); - } - emoIt = emotionDOM.constFind("sur"); - if(emoIt != emoEnd) { - emotion.surprise = (float)(*emoIt).toDouble(0.0); - } - emoIt = emotionDOM.constFind("neg"); - if(emoIt != emoEnd) { - emotion.negative = (float)(*emoIt).toDouble(0.0); - } - emoIt = emotionDOM.constFind("att"); - if(emoIt != emoEnd) { - emotion.attention = (float)(*emoIt).toDouble(0.0); - } - } - return emotion; - } - - static CaraPerson::CaraBlink extractBlink(const QJsonObject& classifiers) { - CaraPerson::CaraBlink blink = CaraPerson::BLINK_NOT_AVAILABLE; - QJsonObject::const_iterator it = classifiers.constFind("blink"); - if(it != classifiers.constEnd()) { - int b = (*it).toInt(CaraPerson::BLINK_NOT_AVAILABLE); - switch(b) { - case CaraPerson::BLINK_NOT_AVAILABLE: - blink = CaraPerson::BLINK_NOT_AVAILABLE; - break; - case CaraPerson::NO_BLINK: - blink = CaraPerson::NO_BLINK; - break; - case CaraPerson::BLINK: - blink = CaraPerson::BLINK; - break; - default: - blink = CaraPerson::BLINK_NOT_AVAILABLE; - break; - } - } - return blink; - } -}; - -CaraFaceTracker::CaraFaceTracker() : - _lastReceiveTimestamp(0), - _pitchAverage(NUM_SMOOTHING_SAMPLES), - _yawAverage(NUM_SMOOTHING_SAMPLES), - _rollAverage(NUM_SMOOTHING_SAMPLES), - _eyeGazeLeftPitch(0.0f), - _eyeGazeLeftYaw(0.0f), - _eyeGazeRightPitch(0.0f), - _eyeGazeRightYaw(0), - _leftBlinkIndex(0), - _rightBlinkIndex(1), - _leftEyeOpenIndex(8), - _rightEyeOpenIndex(9), - _browDownLeftIndex(14), - _browDownRightIndex(15), - _browUpCenterIndex(16), - _browUpLeftIndex(17), - _browUpRightIndex(18), - _mouthSmileLeftIndex(28), - _mouthSmileRightIndex(29), - _jawOpenIndex(21) -{ - connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); - connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); - connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState))); - - bindTo(CARA_FEATURE_POINT_SERVER_PORT); - - _headTranslation = DEFAULT_HEAD_ORIGIN; - _blendshapeCoefficients.resize(NUM_BLENDSHAPE_COEFF); - _blendshapeCoefficients.fill(0.0f); - - //qDebug() << sampleJson; -} - -CaraFaceTracker::CaraFaceTracker(const QHostAddress& host, quint16 port) : - _lastReceiveTimestamp(0), - _pitchAverage(NUM_SMOOTHING_SAMPLES), - _yawAverage(NUM_SMOOTHING_SAMPLES), - _rollAverage(NUM_SMOOTHING_SAMPLES), - _eyeGazeLeftPitch(0.0f), - _eyeGazeLeftYaw(0.0f), - _eyeGazeRightPitch(0.0f), - _eyeGazeRightYaw(0.0f), - _leftBlinkIndex(0), - _rightBlinkIndex(1), - _leftEyeOpenIndex(8), - _rightEyeOpenIndex(9), - _browDownLeftIndex(14), - _browDownRightIndex(15), - _browUpCenterIndex(16), - _browUpLeftIndex(17), - _browUpRightIndex(18), - _mouthSmileLeftIndex(28), - _mouthSmileRightIndex(29), - _jawOpenIndex(21) -{ - connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); - connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); - connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(socketStateChanged(QAbstractSocket::SocketState))); - - bindTo(host, port); - - _headTranslation = DEFAULT_HEAD_ORIGIN * TRANSLATION_SCALE; - _blendshapeCoefficients.resize(NUM_BLENDSHAPE_COEFF); //set the size of the blendshape coefficients - _blendshapeCoefficients.fill(0.0f); -} - -CaraFaceTracker::~CaraFaceTracker() { - if(_udpSocket.isOpen()) - _udpSocket.close(); -} - -void CaraFaceTracker::init() { - -} - -void CaraFaceTracker::reset() { - -} - -void CaraFaceTracker::bindTo(quint16 port) { - bindTo(QHostAddress::Any, port); -} - -void CaraFaceTracker::bindTo(const QHostAddress& host, quint16 port) { - if(_udpSocket.isOpen()) { - _udpSocket.close(); - } - _udpSocket.bind(host, port); -} - -bool CaraFaceTracker::isActive() const { - static const quint64 ACTIVE_TIMEOUT_USECS = 3000000; //3 secs - return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS); -} - -void CaraFaceTracker::update() { - // get the euler angles relative to the window - glm::vec3 eulers = glm::degrees(safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3( - (_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))))); - - //TODO: integrate when cara has eye gaze estimation - - _estimatedEyePitch = eulers.x; - _estimatedEyeYaw = eulers.y; -} - -//private slots and methods -void CaraFaceTracker::socketErrorOccurred(QAbstractSocket::SocketError socketError) { - qDebug() << "[Error] Cara Face Tracker Socket Error: " << _udpSocket.errorString(); -} - -void CaraFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState) { - QString state; - switch(socketState) { - case QAbstractSocket::BoundState: - state = "Bounded"; - break; - case QAbstractSocket::ClosingState: - state = "Closing"; - break; - case QAbstractSocket::ConnectedState: - state = "Connected"; - break; - case QAbstractSocket::ConnectingState: - state = "Connecting"; - break; - case QAbstractSocket::HostLookupState: - state = "Host Lookup"; - break; - case QAbstractSocket::ListeningState: - state = "Listening"; - break; - case QAbstractSocket::UnconnectedState: - state = "Unconnected"; - break; - } - qDebug() << "[Info] Cara Face Tracker Socket: " << socketState; -} - -void CaraFaceTracker::readPendingDatagrams() { - QByteArray buffer; - while (_udpSocket.hasPendingDatagrams()) { - buffer.resize(_udpSocket.pendingDatagramSize()); - _udpSocket.readDatagram(buffer.data(), buffer.size()); - decodePacket(buffer); - } -} - -void CaraFaceTracker::decodePacket(const QByteArray& buffer) { - //decode the incoming udp packet - QJsonParseError jsonError; - CaraPerson person = CaraPacketDecoder::extractOne(buffer, &jsonError); - - if(jsonError.error == QJsonParseError::NoError) { - - //do some noise filtering to the head poses - //reduce the noise first by truncating to 1 dp - person.pose.roll = glm::floor(person.pose.roll * 10) / 10; - person.pose.pitch = glm::floor(person.pose.pitch * 10) / 10; - person.pose.yaw = glm::floor(person.pose.yaw * 10) / 10; - - //qDebug() << person.toString(); - - glm::quat newRotation(glm::vec3(DEGTORAD(person.pose.pitch), DEGTORAD(person.pose.yaw), DEGTORAD(person.pose.roll))); - - // Compute angular velocity of the head - glm::quat r = newRotation * glm::inverse(_headRotation); - float theta = 2 * acos(r.w); - if (theta > EPSILON) { - float rMag = glm::length(glm::vec3(r.x, r.y, r.z)); - const float AVERAGE_CARA_FRAME_TIME = 0.04f; - const float YAW_STANDARD_DEV_DEG = 2.5f; - - _headAngularVelocity = theta / AVERAGE_CARA_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag; - _pitchAverage.updateAverage(person.pose.pitch); - _rollAverage.updateAverage(person.pose.roll); - - //could use the angular velocity to detemine whether to update pitch and roll to further remove the noise. - //use the angular velocity for roll and pitch, update if > THRESHOLD - //if(glm::abs(_headAngularVelocity.x) > ANGULAR_VELOCITY_MIN) { - // _pitchAverage.updateAverage(person.pose.pitch); - //} - - //if(glm::abs(_headAngularVelocity.z) > ANGULAR_VELOCITY_MIN) { - // _rollAverage.updateAverage(person.pose.roll);; - //} - - //for yaw, the jitter is great, you can't use angular velocity because it swings too much - //use the previous and current yaw, calculate the - //abs difference and move it the difference is above the standard deviation which is around 2.5 - // > the standard deviation 2.5 deg, update the yaw smoothing average - if(glm::abs(person.pose.yaw - _yawAverage.getAverage()) > YAW_STANDARD_DEV_DEG) { - //qDebug() << "Yaw Diff: " << glm::abs(person.pose.yaw - _previousYaw); - _yawAverage.updateAverage(person.pose.yaw); - } - - //set the new rotation - newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage()))); - } - else { - //no change in position, use previous averages - newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage()))); - _headAngularVelocity = glm::vec3(0,0,0); - } - - //update to new rotation angles - _headRotation = newRotation; - - //TODO: head translation, right now is 0 - - //Do Blendshapes, clip between 0.0f to 1.0f, neg should be ignored - _blendshapeCoefficients[_leftBlinkIndex] = person.blink == CaraPerson::BLINK ? 1.0f : 0.0f; - _blendshapeCoefficients[_rightBlinkIndex] = person.blink == CaraPerson::BLINK ? 1.0f : 0.0f; - - //anger and surprised are mutually exclusive so we could try use this fact to determine - //whether to down the brows or up the brows - _blendshapeCoefficients[_browDownLeftIndex] = person.emotion.negative < 0.0f ? 0.0f : person.emotion.negative; - _blendshapeCoefficients[_browDownRightIndex] = person.emotion.negative < 0.0f ? 0.0f : person.emotion.negative; - _blendshapeCoefficients[_browUpCenterIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise; - _blendshapeCoefficients[_browUpLeftIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise; - _blendshapeCoefficients[_browUpRightIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise; - _blendshapeCoefficients[_jawOpenIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise; - _blendshapeCoefficients[_mouthSmileLeftIndex] = person.emotion.smile < 0.0f ? 0.0f : person.emotion.smile; - _blendshapeCoefficients[_mouthSmileRightIndex] = person.emotion.smile < 0.0f ? 0.0f : person.emotion.smile; - } - else { - qDebug() << "[Error] Cara Face Tracker Decode Error: " << jsonError.errorString(); - } - - _lastReceiveTimestamp = usecTimestampNow(); -} - -float CaraFaceTracker::getBlendshapeCoefficient(int index) const { - return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f; -} diff --git a/interface/src/devices/CaraFaceTracker.h b/interface/src/devices/CaraFaceTracker.h deleted file mode 100644 index 0c715afda9..0000000000 --- a/interface/src/devices/CaraFaceTracker.h +++ /dev/null @@ -1,124 +0,0 @@ -// -// CaraFaceTracker.h -// interface/src/devices -// -// Created by Li Zuwei on 7/22/14. -// Copyright 2013 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_CaraFaceTracker_h -#define hifi_CaraFaceTracker_h - -#include - -#include -#include "FaceTracker.h" - -/*! - * \class CaraFaceTracker - * - * \brief Handles interaction with the Cara software, - * which provides head position/orientation and facial features. - * \details By default, opens a udp socket with IPV4_ANY_ADDR with port 36555. - * User needs to run the Cara Face Detection UDP Client with the destination - * host address (eg: 127.0.0.1 for localhost) and destination port 36555. -**/ - -class CaraFaceTracker : public FaceTracker { - Q_OBJECT - -public: - CaraFaceTracker(); - CaraFaceTracker(const QHostAddress& host, quint16 port); - ~CaraFaceTracker(); - - //initialization - void init(); - void reset(); - - //sockets - void bindTo(quint16 port); - void bindTo(const QHostAddress& host, quint16 port); - bool isActive() const; - - //tracking - void update(); - - //head angular velocity - const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; } - - //eye gaze - float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; } - float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; } - - float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; } - float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; } - - //blend shapes - float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } - float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } - float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); } - float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); } - - float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); } - float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); } - float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); } - float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); } - float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); } - - float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); } - float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); } - float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); } - -private slots: - - //sockets - void socketErrorOccurred(QAbstractSocket::SocketError socketError); - void readPendingDatagrams(); - void socketStateChanged(QAbstractSocket::SocketState socketState); - -private: - void decodePacket(const QByteArray& buffer); - float getBlendshapeCoefficient(int index) const; - - // sockets - QUdpSocket _udpSocket; - quint64 _lastReceiveTimestamp; - - //head tracking - glm::vec3 _headAngularVelocity; - - //pose average - SimpleMovingAverage _pitchAverage; - SimpleMovingAverage _yawAverage; - SimpleMovingAverage _rollAverage; - - // eye gaze degrees - float _eyeGazeLeftPitch; - float _eyeGazeLeftYaw; - float _eyeGazeRightPitch; - float _eyeGazeRightYaw; - - //blend shapes - int _leftBlinkIndex; - int _rightBlinkIndex; - int _leftEyeOpenIndex; - int _rightEyeOpenIndex; - - // Brows - int _browDownLeftIndex; - int _browDownRightIndex; - int _browUpCenterIndex; - int _browUpLeftIndex; - int _browUpRightIndex; - - int _mouthSmileLeftIndex; - int _mouthSmileRightIndex; - - int _jawOpenIndex; -}; - -#endif //endif hifi_CaraFaceTracker_h diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 1f43979c03..fcbfd32350 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -282,6 +282,9 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { _calibrationState = WAITING_FOR_ZERO; } break; + default: + break; + } } #endif diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp index 23ea12e6e8..31f53c8201 100644 --- a/interface/src/gpu/Resource.cpp +++ b/interface/src/gpu/Resource.cpp @@ -44,16 +44,16 @@ void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { } Resource::Sysmem::Sysmem() : - _data(NULL), + _stamp(0), _size(0), - _stamp(0) + _data(NULL) { } Resource::Sysmem::Sysmem(Size size, const Byte* bytes) : - _data(NULL), + _stamp(0), _size(0), - _stamp(0) + _data(NULL) { if (size > 0) { _size = allocateMemory(&_data, size); diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index e00011bf6b..c52b07bcfa 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -15,11 +15,12 @@ #include "BillboardOverlay.h" -BillboardOverlay::BillboardOverlay() -: _fromImage(-1,-1,-1,-1), - _scale(1.0f), - _isFacingAvatar(true), - _newTextureNeeded(true) { +BillboardOverlay::BillboardOverlay() : + _newTextureNeeded(true), + _fromImage(-1,-1,-1,-1), + _scale(1.0f), + _isFacingAvatar(true) +{ _isLoaded = false; } From 1911cfd0be9313b16afa5588e8da97e90f9484a9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Oct 2014 15:11:37 -0700 Subject: [PATCH 087/108] Basic ray/voxel intersection testing up and running. --- interface/src/MetavoxelSystem.cpp | 76 +++++++++++++++++++++++-------- interface/src/MetavoxelSystem.h | 9 ++-- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 9732036def..19a9a7ce89 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -354,9 +354,8 @@ int RayVoxelIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { if (!buffer) { return STOP_RECURSION; } - glm::vec3 origin = ((_origin + distance * _direction) - info.minimum) / info.size; - if (buffer->findFirstRayIntersection(origin, _direction, intersectionDistance)) { - intersectionDistance = intersectionDistance * info.size + distance; + glm::vec3 entry = ((_origin + distance * _direction) - info.minimum) / info.size; + if (buffer->findFirstRayIntersection(entry, _origin, _direction, intersectionDistance)) { return SHORT_CIRCUIT; } return STOP_RECURSION; @@ -1035,11 +1034,11 @@ void VoxelPoint::setNormal(const glm::vec3& normal) { } VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QHash& points, int size, const QVector& materials) : + const QMultiHash& quadIndices, int size, const QVector& materials) : _vertices(vertices), _indices(indices), _hermite(hermite), - _points(points), + _quadIndices(quadIndices), _size(size), _vertexCount(vertices.size()), _indexCount(indices.size()), @@ -1048,17 +1047,47 @@ VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector _materials(materials) { } -bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +static bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction, + const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance) { + glm::vec3 firstSide = v0 - v1; + glm::vec3 secondSide = v2 - v1; + glm::vec3 normal = glm::cross(secondSide, firstSide); + float dividend = glm::dot(normal, v1) - glm::dot(origin, normal); + if (dividend > 0.0f) { + return false; // origin below plane + } + float divisor = glm::dot(normal, direction); + if (divisor > -EPSILON) { + return false; + } + float t = dividend / divisor; + glm::vec3 point = origin + direction * t; + if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f && + glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f && + glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) { + distance = t; + return true; + } + return false; +} + +bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, + const glm::vec3& direction, float& distance) const { float highest = _size - 1.0f; - glm::vec3 position = origin * highest; + glm::vec3 position = entry * highest; glm::vec3 floors = glm::floor(position); int max = _size - 2; int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); - float combinedDistance = 0.0f; forever { - if (_points.contains(qRgb(x + 1, y + 1, z + 1))) { - distance = combinedDistance / highest; - return true; + for (QMultiHash::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); + it != _quadIndices.constEnd(); it++) { + const int* indices = _indices.constData() + *it; + if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, + _vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) || + findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, + _vertices.at(indices[2]).vertex, _vertices.at(indices[3]).vertex, distance)) { + return true; + } } float xDistance = FLT_MAX, yDistance = FLT_MAX, zDistance = FLT_MAX; if (direction.x > 0.0f) { @@ -1105,7 +1134,6 @@ bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& origin, const glm::v } } position += direction * minimumDistance; - combinedDistance += minimumDistance; } return false; } @@ -1115,13 +1143,11 @@ void VoxelBuffer::render(bool cursor) { _vertexBuffer.create(); _vertexBuffer.bind(); _vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint)); - _vertices.clear(); - + _indexBuffer.create(); _indexBuffer.bind(); _indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int)); - _indices.clear(); - + if (!_materials.isEmpty()) { _networkTextures.resize(_materials.size()); for (int i = 0; i < _materials.size(); i++) { @@ -1735,7 +1761,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { QVector vertices; QVector indices; QVector hermiteSegments; - QHash points; + QMultiHash quadIndices; // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges @@ -2142,8 +2168,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; - points.insert(qRgb(x + 1, y + 1, z + 1), (glm::vec3(clampedX, clampedY, clampedZ) + center) / highest); - // determine whether we must "crease" by generating directional normals const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); AxisIndex index(vertices.size(), vertices.size(), vertices.size()); @@ -2202,6 +2226,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { // quads for each edge that includes a transition, using indices of previously generated vertices if (x != 0 && y != 0 && z != 0) { if (alpha0 != alpha1) { + quadIndices.insert(qRgb(x, y, z), indices.size()); + quadIndices.insert(qRgb(x, y - 1, z), indices.size()); + quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size()); + quadIndices.insert(qRgb(x, y, z - 1), indices.size()); indices.append(index.x); int index1 = lastLineIndices.at(x).x; int index2 = lastPlaneIndices.at((y - 1) * expanded + x).x; @@ -2218,6 +2246,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } if (alpha0 != alpha2) { + quadIndices.insert(qRgb(x, y, z), indices.size()); + quadIndices.insert(qRgb(x - 1, y, z), indices.size()); + quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size()); + quadIndices.insert(qRgb(x, y, z - 1), indices.size()); indices.append(index.y); int index1 = lastIndex.y; int index2 = lastPlaneIndices.at(y * expanded + x - 1).y; @@ -2234,6 +2266,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } if (alpha0 != alpha4) { + quadIndices.insert(qRgb(x, y, z), indices.size()); + quadIndices.insert(qRgb(x - 1, y, z), indices.size()); + quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size()); + quadIndices.insert(qRgb(x, y - 1, z), indices.size()); indices.append(index.z); int index1 = lastIndex.z; int index2 = lastLineIndices.at(x - 1).z; @@ -2269,7 +2305,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { colorZ += area; } } - buffer = new VoxelBuffer(vertices, indices, hermiteSegments, points, size, + buffer = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, size, material ? material->getMaterials() : QVector()); } BufferDataPointer pointer(buffer); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index e256716fc0..daa1bd6ca2 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -243,10 +243,13 @@ class VoxelBuffer : public BufferData { public: VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QHash& points, int size, const QVector& materials = + const QMultiHash& quadIndices, int size, const QVector& materials = QVector()); - bool findFirstRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + /// Finds the first intersection between the described ray and the voxel data. + /// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1) + bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, + const glm::vec3& direction, float& distance) const; virtual void render(bool cursor = false); @@ -255,7 +258,7 @@ private: QVector _vertices; QVector _indices; QVector _hermite; - QHash _points; + QMultiHash _quadIndices; int _size; int _vertexCount; int _indexCount; From 762cc71d31b0f8a747eea4c3557d9e8ca636d8c7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 17:03:10 -0700 Subject: [PATCH 088/108] Update to SDL2 --- cmake/modules/FindSDL2.cmake | 249 ++++++++++++++++++ interface/CMakeLists.txt | 2 +- interface/src/devices/Joystick.cpp | 52 ++-- interface/src/devices/Joystick.h | 34 ++- .../scripting/JoystickScriptingInterface.cpp | 130 ++++----- .../scripting/JoystickScriptingInterface.h | 83 +++++- 6 files changed, 436 insertions(+), 114 deletions(-) create mode 100644 cmake/modules/FindSDL2.cmake diff --git a/cmake/modules/FindSDL2.cmake b/cmake/modules/FindSDL2.cmake new file mode 100644 index 0000000000..f24af555a2 --- /dev/null +++ b/cmake/modules/FindSDL2.cmake @@ -0,0 +1,249 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2_main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDL2main.h and SDL2main.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2 is an environment variable that would +# correspond to the ./configure --prefix=$SDL2 +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL2 guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL2 convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). +# +# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake +# module with the minor edit of changing "SDL" to "SDL2" where necessary. This +# was not created for redistribution, and exists temporarily pending official +# SDL2 CMake modules. +# +# Note that on windows this will only search for the 32bit libraries, to search +# for 64bit change x86/i686-w64 to x64/x86_64-w64 + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# CMake - Cross Platform Makefile Generator +# Copyright 2000-2014 Kitware, Inc. +# Copyright 2000-2011 Insight Software Consortium +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2} + PATH_SUFFIXES include/SDL2 include SDL2 + i686-w64-mingw32/include/SDL2 + x86_64-w64-mingw32/include/SDL2 + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL2 + /usr/include/SDL2 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +# Lookup the 64 bit libs on x64 +IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 + HINTS + $ENV{SDL2} + PATH_SUFFIXES lib64 lib + lib/x64 + x86_64-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) +# On 32bit build find the 32bit libs +ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 + HINTS + $ENV{SDL2} + PATH_SUFFIXES lib + lib/x86 + i686-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) +ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + # Lookup the 64 bit libs on x64 + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2} + PATH_SUFFIXES lib64 lib + lib/x64 + x86_64-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + # On 32bit build find the 32bit libs + ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2} + PATH_SUFFIXES lib + lib/x86 + i686-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL2_FOUND "NO") + IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL2_FOUND "YES") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 1202b36a9f..16ca977bae 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL") +set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL2") foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index fe67eaceaa..b63d49eea2 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -15,13 +15,17 @@ #include "Joystick.h" -#ifdef HAVE_SDL +const float MAX_AXIS = 32767.0f; -Joystick::Joystick(const QString& name, SDL_Joystick* sdlJoystick) : +#ifdef HAVE_SDL2 + +Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : + _instanceId(instanceId), _name(name), - _axes(QVector(SDL_JoystickNumAxes(sdlJoystick))), - _buttons(QVector(SDL_JoystickNumButtons(sdlJoystick))), - _sdlJoystick(sdlJoystick) + _sdlGameController(sdlGameController), + _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), + _axes(QVector(SDL_JoystickNumAxes(_sdlJoystick))), + _buttons(QVector(SDL_JoystickNumButtons(_sdlJoystick))) { } @@ -33,30 +37,24 @@ Joystick::~Joystick() { } void Joystick::closeJoystick() { -#ifdef HAVE_SDL - SDL_JoystickClose(_sdlJoystick); +#ifdef HAVE_SDL2 + SDL_GameControllerClose(_sdlGameController); #endif } -void Joystick::update() { -#ifdef HAVE_SDL - // update our current values, emit a signal when there is a change - for (int j = 0; j < getNumAxes(); j++) { - float newValue = glm::round(SDL_JoystickGetAxis(_sdlJoystick, j) + 0.5f) / std::numeric_limits::max(); - if (_axes[j] != newValue) { - float oldValue = _axes[j]; - _axes[j] = newValue; - emit axisValueChanged(j, newValue, oldValue); - } - } - for (int j = 0; j < getNumButtons(); j++) { - bool newValue = SDL_JoystickGetButton(_sdlJoystick, j); - if (_buttons[j] != newValue) { - bool oldValue = _buttons[j]; - _buttons[j] = newValue; - emit buttonStateChanged(j, newValue, oldValue); - } - } +#ifdef HAVE_SDL2 +void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { + float oldValue = _axes[event.axis]; + float newValue = event.value / MAX_AXIS; + _axes[event.axis] = newValue; + emit axisValueChanged(event.axis, newValue, oldValue); +} + +void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { + bool oldValue = _buttons[event.button]; + bool newValue = event.state == SDL_PRESSED; + _buttons[event.button] = newValue; + emit buttonStateChanged(event.button, newValue, oldValue); +} #endif -} \ No newline at end of file diff --git a/interface/src/devices/Joystick.h b/interface/src/devices/Joystick.h index 8343c20a04..eeeeb03759 100644 --- a/interface/src/devices/Joystick.h +++ b/interface/src/devices/Joystick.h @@ -15,7 +15,7 @@ #include #include -#ifdef HAVE_SDL +#ifdef HAVE_SDL2 #include #undef main #endif @@ -24,6 +24,10 @@ class Joystick : public QObject { Q_OBJECT Q_PROPERTY(QString name READ getName) + +#ifdef HAVE_SDL2 + Q_PROPERTY(int instanceId READ getInstanceId) +#endif Q_PROPERTY(int numAxes READ getNumAxes) Q_PROPERTY(int numButtons READ getNumButtons) @@ -31,19 +35,21 @@ public: Joystick(); ~Joystick(); -#ifdef HAVE_SDL - Joystick(const QString& name, SDL_Joystick* sdlJoystick); +#ifdef HAVE_SDL2 + Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); #endif - void update(); - void closeJoystick(); - -#ifdef HAVE_SDL - void setSDLJoystick(SDL_Joystick* sdlJoystick) { _sdlJoystick = sdlJoystick; } + +#ifdef HAVE_SDL2 + void handleAxisEvent(const SDL_ControllerAxisEvent& event); + void handleButtonEvent(const SDL_ControllerButtonEvent& event); #endif const QString& getName() const { return _name; } +#ifdef HAVE_SDL2 + int getInstanceId() const { return _instanceId; } +#endif const QVector& getAxes() const { return _axes; } const QVector& getButtons() const { return _buttons; } @@ -55,13 +61,15 @@ signals: void axisValueChanged(int axis, float newValue, float oldValue); void buttonStateChanged(int button, float newValue, float oldValue); private: +#ifdef HAVE_SDL2 + SDL_GameController* _sdlGameController; + SDL_Joystick* _sdlJoystick; + SDL_JoystickID _instanceId; +#endif + QString _name; QVector _axes; QVector _buttons; - -#ifdef HAVE_SDL - SDL_Joystick* _sdlJoystick; -#endif }; -#endif // hifi_JoystickTracker_h \ No newline at end of file +#endif // hifi_JoystickTracker_h diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/scripting/JoystickScriptingInterface.cpp index 87f841c494..3f0371e1cd 100644 --- a/interface/src/scripting/JoystickScriptingInterface.cpp +++ b/interface/src/scripting/JoystickScriptingInterface.cpp @@ -10,8 +10,9 @@ // #include +#include -#ifdef HAVE_SDL +#ifdef HAVE_SDL2 #include #undef main #endif @@ -20,104 +21,103 @@ #include "JoystickScriptingInterface.h" +#ifdef HAVE_SDL2 +SDL_JoystickID getInstanceId(SDL_GameController* controller) { + SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); + return SDL_JoystickInstanceID(joystick); +} +#endif + JoystickScriptingInterface& JoystickScriptingInterface::getInstance() { static JoystickScriptingInterface sharedInstance; return sharedInstance; } JoystickScriptingInterface::JoystickScriptingInterface() : +#ifdef HAVE_SDL2 _openJoysticks(), - _availableDeviceNames(), +#endif _isInitialized(false) { - reset(); +#ifdef HAVE_SDL2 + bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0); + + if (initSuccess) { + int joystickCount = SDL_NumJoysticks(); + + for (int i = 0; i < joystickCount; i++) { + SDL_GameController* controller = SDL_GameControllerOpen(i); + SDL_JoystickID id = getInstanceId(controller); + Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + _openJoysticks[id] = joystick; + } + + _isInitialized = true; + } else { + qDebug() << "Error initializing SDL"; + } +#endif } JoystickScriptingInterface::~JoystickScriptingInterface() { +#ifdef HAVE_SDL2 qDeleteAll(_openJoysticks); -#ifdef HAVE_SDL SDL_Quit(); - _isInitialized = false; #endif } -void JoystickScriptingInterface::reset() { -#ifdef HAVE_SDL - - if (_isInitialized) { - _isInitialized = false; - - // close all the open joysticks before we quit - foreach(Joystick* openJoystick, _openJoysticks) { - openJoystick->closeJoystick(); - } - - SDL_Quit(); - } - - bool initSuccess = (SDL_Init(SDL_INIT_JOYSTICK) == 0); - - if (initSuccess) { - - int joystickCount = SDL_NumJoysticks(); - - for (int i = 0; i < joystickCount; i++) { - _availableDeviceNames << SDL_JoystickName(i); - } - - foreach(const QString& joystickName, _openJoysticks.keys()) { - _openJoysticks[joystickName]->setSDLJoystick(openSDLJoystickWithName(joystickName)); - } - - _isInitialized = true; +const QObjectList JoystickScriptingInterface::getAllJoysticks() const { + QObjectList objectList; +#ifdef HAVE_SDL2 + const QList joystickList = _openJoysticks.values(); + for (int i = 0; i < joystickList.length(); i++) { + objectList << joystickList[i]; } #endif + return objectList; } void JoystickScriptingInterface::update() { -#ifdef HAVE_SDL +#ifdef HAVE_SDL2 if (_isInitialized) { PerformanceTimer perfTimer("JoystickScriptingInterface::update"); - SDL_JoystickUpdate(); - - foreach(Joystick* joystick, _openJoysticks) { - joystick->update(); + SDL_GameControllerUpdate(); + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_CONTROLLERAXISMOTION) { + Joystick* joystick = _openJoysticks[event.caxis.which]; + if (joystick) { + joystick->handleAxisEvent(event.caxis); + } + } else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) { + Joystick* joystick = _openJoysticks[event.cbutton.which]; + if (joystick) { + joystick->handleButtonEvent(event.cbutton); + } + } else if (event.type == SDL_CONTROLLERDEVICEADDED) { + SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); + + SDL_JoystickID id = getInstanceId(controller); + Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + _openJoysticks[id] = joystick; + emit joystickAdded(joystick); + } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { + Joystick* joystick = _openJoysticks[event.cdevice.which]; + _openJoysticks.remove(event.cdevice.which); + emit joystickRemoved(joystick); + } } } #endif } -Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) { - Joystick* matchingJoystick = _openJoysticks.value(name); -#ifdef HAVE_SDL - if (!matchingJoystick) { - SDL_Joystick* openSDLJoystick = openSDLJoystickWithName(name); - - if (openSDLJoystick) { - matchingJoystick = _openJoysticks.insert(name, new Joystick(name, openSDLJoystick)).value(); - } else { - qDebug() << "No matching joystick found with name" << name << "- returning NULL pointer."; - } - } -#endif - - return matchingJoystick; -} +#ifdef HAVE_SDL2 - -#ifdef HAVE_SDL - -SDL_Joystick* JoystickScriptingInterface::openSDLJoystickWithName(const QString &name) { +SDL_Joystick* JoystickScriptingInterface::openSDLJoystickWithName(const QString& name) { // we haven't opened a joystick with this name yet - enumerate our SDL devices and see if it exists int joystickCount = SDL_NumJoysticks(); - for (int i = 0; i < joystickCount; i++) { - if (SDL_JoystickName(i) == name) { - return SDL_JoystickOpen(i); - break; - } - } return NULL; } diff --git a/interface/src/scripting/JoystickScriptingInterface.h b/interface/src/scripting/JoystickScriptingInterface.h index 02624c70d5..37546a5a85 100644 --- a/interface/src/scripting/JoystickScriptingInterface.h +++ b/interface/src/scripting/JoystickScriptingInterface.h @@ -15,34 +15,101 @@ #include #include +#ifdef HAVE_SDL2 +#include +#endif + #include "devices/Joystick.h" /// Handles joystick input through SDL. class JoystickScriptingInterface : public QObject { Q_OBJECT - Q_PROPERTY(QStringList availableJoystickNames READ getAvailableJoystickNames) +#ifdef HAVE_SDL2 + Q_PROPERTY(int AXIS_INVALID READ axisInvalid) + Q_PROPERTY(int AXIS_LEFT_X READ axisLeftX) + Q_PROPERTY(int AXIS_LEFT_Y READ axisLeftY) + Q_PROPERTY(int AXIS_RIGHT_X READ axisRightX) + Q_PROPERTY(int AXIS_RIGHT_Y READ axisRightY) + Q_PROPERTY(int AXIS_TRIGGER_LEFT READ axisTriggerLeft) + Q_PROPERTY(int AXIS_TRIGGER_RIGHT READ axisTriggerRight) + Q_PROPERTY(int AXIS_MAX READ axisMax) + + Q_PROPERTY(int BUTTON_INVALID READ buttonInvalid) + Q_PROPERTY(int BUTTON_FACE_BOTTOM READ buttonFaceBottom) + Q_PROPERTY(int BUTTON_FACE_RIGHT READ buttonFaceRight) + Q_PROPERTY(int BUTTON_FACE_LEFT READ buttonFaceLeft) + Q_PROPERTY(int BUTTON_FACE_TOP READ buttonFaceTop) + Q_PROPERTY(int BUTTON_BACK READ buttonBack) + Q_PROPERTY(int BUTTON_GUIDE READ buttonGuide) + Q_PROPERTY(int BUTTON_START READ buttonStart) + Q_PROPERTY(int BUTTON_LEFT_STICK READ buttonLeftStick) + Q_PROPERTY(int BUTTON_RIGHT_STICK READ buttonRightStick) + Q_PROPERTY(int BUTTON_LEFT_SHOULDER READ buttonLeftShoulder) + Q_PROPERTY(int BUTTON_RIGHT_SHOULDER READ buttonRightShoulder) + Q_PROPERTY(int BUTTON_DPAD_UP READ buttonDpadUp) + Q_PROPERTY(int BUTTON_DPAD_DOWN READ buttonDpadDown) + Q_PROPERTY(int BUTTON_DPAD_LEFT READ buttonDpadLeft) + Q_PROPERTY(int BUTTON_DPAD_RIGHT READ buttonDpadRight) + Q_PROPERTY(int BUTTON_MAX READ buttonMax) + + Q_PROPERTY(int BUTTON_PRESSED READ buttonPressed) + Q_PROPERTY(int BUTTON_RELEASED READ buttonRelease) +#endif + public: static JoystickScriptingInterface& getInstance(); - const QStringList& getAvailableJoystickNames() const { return _availableDeviceNames; } - void update(); public slots: - Joystick* joystickWithName(const QString& name); - void reset(); + const QObjectList getAllJoysticks() const; + +signals: + void joystickAdded(Joystick* joystick); + void joystickRemoved(Joystick* joystick); private: -#ifdef HAVE_SDL +#ifdef HAVE_SDL2 + int axisInvalid() const { return SDL_CONTROLLER_AXIS_INVALID; } + int axisLeftX() const { return SDL_CONTROLLER_AXIS_LEFTX; } + int axisLeftY() const { return SDL_CONTROLLER_AXIS_LEFTY; } + int axisRightX() const { return SDL_CONTROLLER_AXIS_RIGHTX; } + int axisRightY() const { return SDL_CONTROLLER_AXIS_RIGHTY; } + int axisTriggerLeft() const { return SDL_CONTROLLER_AXIS_TRIGGERLEFT; } + int axisTriggerRight() const { return SDL_CONTROLLER_AXIS_TRIGGERRIGHT; } + int axisMax() const { return SDL_CONTROLLER_AXIS_MAX; } + + int buttonInvalid() const { return SDL_CONTROLLER_BUTTON_INVALID; } + int buttonFaceBottom() const { return SDL_CONTROLLER_BUTTON_A; } + int buttonFaceRight() const { return SDL_CONTROLLER_BUTTON_B; } + int buttonFaceLeft() const { return SDL_CONTROLLER_BUTTON_X; } + int buttonFaceTop() const { return SDL_CONTROLLER_BUTTON_Y; } + int buttonBack() const { return SDL_CONTROLLER_BUTTON_BACK; } + int buttonGuide() const { return SDL_CONTROLLER_BUTTON_GUIDE; } + int buttonStart() const { return SDL_CONTROLLER_BUTTON_START; } + int buttonLeftStick() const { return SDL_CONTROLLER_BUTTON_LEFTSTICK; } + int buttonRightStick() const { return SDL_CONTROLLER_BUTTON_RIGHTSTICK; } + int buttonLeftShoulder() const { return SDL_CONTROLLER_BUTTON_LEFTSHOULDER; } + int buttonRightShoulder() const { return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; } + int buttonDpadUp() const { return SDL_CONTROLLER_BUTTON_DPAD_UP; } + int buttonDpadDown() const { return SDL_CONTROLLER_BUTTON_DPAD_DOWN; } + int buttonDpadLeft() const { return SDL_CONTROLLER_BUTTON_DPAD_LEFT; } + int buttonDpadRight() const { return SDL_CONTROLLER_BUTTON_DPAD_RIGHT; } + int buttonMax() const { return SDL_CONTROLLER_BUTTON_MAX; } + + int buttonPressed() const { return SDL_PRESSED; } + int buttonRelease() const { return SDL_RELEASED; } + SDL_Joystick* openSDLJoystickWithName(const QString& name); #endif JoystickScriptingInterface(); ~JoystickScriptingInterface(); - QMap _openJoysticks; - QStringList _availableDeviceNames; +#ifdef HAVE_SDL2 + QMap _openJoysticks; +#endif bool _isInitialized; }; From 1c2a971638d11fb82ab125cf900fbda5ad91db94 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 17:03:25 -0700 Subject: [PATCH 089/108] Update gamepad.js to use new Joysticks interface --- examples/gamepad.js | 97 +++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 64 deletions(-) diff --git a/examples/gamepad.js b/examples/gamepad.js index 4ec0309511..b4c2758edd 100644 --- a/examples/gamepad.js +++ b/examples/gamepad.js @@ -9,72 +9,23 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// TODO Update to work with any controller that is plugged in. -var CONTROLLER_NAMES = [ - "Wireless 360 Controller", - "Controller (XBOX 360 For Windows)", - "Controller", // Wired 360 controller -] +var gamepads = {}; -for (var i = 0; i < CONTROLLER_NAMES.length; i++) { - gamepad = Joysticks.joystickWithName(CONTROLLER_NAMES[i]); - if (gamepad) { - print("Found controller: " + CONTROLLER_NAMES[i]); - break; - } -} +// Function -> button/axis mappings +var AXIS_STRAFE = Joysticks.AXIS_LEFT_X; +var AXIS_FORWARD = Joysticks.AXIS_LEFT_Y; +var AXIS_ROTATE = Joysticks.AXIS_RIGHT_X; -if (!gamepad) { - print("No gamepad found."); -} +var BUTTON_TURN_AROUND = Joysticks.BUTTON_RIGHT_STICK; -// Controller axis/button mappings -var GAMEPAD = { - AXES: { - LEFT_JOYSTICK_X: 0, - LEFT_JOYSTICK_Y: 1, +var BUTTON_FLY_UP = Joysticks.BUTTON_RIGHT_SHOULDER; +var BUTTON_FLY_DOWN = Joysticks.BUTTON_LEFT_SHOULDER; +var BUTTON_WARP = Joysticks.BUTTON_FACE_BOTTOM; - RIGHT_JOYSTICK_X: 2, - RIGHT_JOYSTICK_Y: 3, - - LEFT_TRIGGER: 4, - RIGHT_TRIGGER: 5, - }, - BUTTONS: { - DPAD_UP: 0, - DPAD_DOWN: 1, - DPAD_LEFT: 2, - DPAD_RIGHT: 3, - - LEFT_JOYSTICK: 6, - RIGHT_JOYSTICK: 7, - - LEFT_BUMPER: 8, - RIGHT_BUMPER: 9, - - // Face buttons, ABXY on an XBOX controller - FACE_BOTTOM: 11, - FACE_RIGHT: 12, - FACE_LEFT: 13, - FACE_TOP: 14, - } -} - -// Button/axis mappings -var AXIS_STRAFE = GAMEPAD.AXES.LEFT_JOYSTICK_X; -var AXIS_FORWARD = GAMEPAD.AXES.LEFT_JOYSTICK_Y; -var AXIS_ROTATE = GAMEPAD.AXES.RIGHT_JOYSTICK_X; - -var BUTTON_TURN_AROUND = GAMEPAD.BUTTONS.RIGHT_JOYSTICK; - -var BUTTON_FLY_UP = GAMEPAD.BUTTONS.RIGHT_BUMPER; -var BUTTON_FLY_DOWN = GAMEPAD.BUTTONS.LEFT_BUMPER -var BUTTON_WARP = GAMEPAD.BUTTONS.FACE_BOTTOM; - -var BUTTON_WARP_FORWARD = GAMEPAD.BUTTONS.DPAD_UP; -var BUTTON_WARP_BACKWARD = GAMEPAD.BUTTONS.DPAD_DOWN; -var BUTTON_WARP_LEFT = GAMEPAD.BUTTONS.DPAD_LEFT; -var BUTTON_WARP_RIGHT = GAMEPAD.BUTTONS.DPAD_RIGHT; +var BUTTON_WARP_FORWARD = Joysticks.BUTTON_DPAD_UP; +var BUTTON_WARP_BACKWARD = Joysticks.BUTTON_DPAD_DOWN; +var BUTTON_WARP_LEFT = Joysticks.BUTTON_DPAD_LEFT; +var BUTTON_WARP_RIGHT = Joysticks.BUTTON_DPAD_RIGHT; // Distance in meters to warp via BUTTON_WARP_* var WARP_DISTANCE = 1; @@ -272,9 +223,27 @@ function update(dt) { updateWarp(); } -if (gamepad) { +function addJoystick(gamepad) { gamepad.axisValueChanged.connect(reportAxisValue); gamepad.buttonStateChanged.connect(reportButtonValue); - Script.update.connect(update); + gamepads[gamepad.instanceId] = gamepad; + + print("Added gamepad: " + gamepad.name + " (" + gamepad.instanceId + ")"); } + +function removeJoystick(gamepad) { + delete gamepads[gamepad.instanceId] + + print("Removed gamepad: " + gamepad.name + " (" + gamepad.instanceId + ")"); +} + +var allJoysticks = Joysticks.getAllJoysticks(); +for (var i = 0; i < allJoysticks.length; i++) { + addJoystick(allJoysticks[i]); +} + +Joysticks.joystickAdded.connect(addJoystick); +Joysticks.joystickRemoved.connect(removeJoystick); + +Script.update.connect(update); From 82a9429414ffa223f7e5ff6a86bb007b3ce0d511 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 17:10:27 -0700 Subject: [PATCH 090/108] add boundingBox.dimensions to entity properties --- libraries/entities/src/EntityItemProperties.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 2690bcd45b..90f56e36b9 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -179,9 +179,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions()); boundingBox.setProperty("brn", bottomRightNear); boundingBox.setProperty("tfl", topFarLeft); boundingBox.setProperty("center", center); + boundingBox.setProperty("dimensions", boundingBoxDimensions); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(boundingBox, boundingBox); // gettable, but not settable return properties; From cdafba603d21e20e1503e45fb23d87ba6f3075a8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 17:11:27 -0700 Subject: [PATCH 091/108] use axis aligned bounding box for highlight and selection box instead of non-axis aligned box --- examples/libraries/entitySelectionTool.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index b00876c969..bbcb86eb2a 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -393,14 +393,8 @@ SelectionDisplay = (function () { that.highlightSelectable = function(entityID) { var properties = Entities.getEntityProperties(entityID); - var center = { x: properties.position.x, y: properties.position.y, z: properties.position.z }; - Overlays.editOverlay(highlightBox, - { - visible: true, - position: center, - dimensions: properties.dimensions, - rotation: properties.rotation, - }); + Overlays.editOverlay(highlightBox, { visible: true, position: properties.boundingBox.center, + dimensions: properties.boundingBox.dimensions }); }; that.unhighlightSelectable = function(entityID) { @@ -642,8 +636,8 @@ SelectionDisplay = (function () { Overlays.editOverlay(highlightBox, { visible: false }); - Overlays.editOverlay(selectionBox, { visible: selectionBoxVisible, position: objectCenter, dimensions: properties.dimensions, - rotation: properties.rotation,}); + Overlays.editOverlay(selectionBox, { visible: selectionBoxVisible, position: properties.boundingBox.center, + dimensions: properties.boundingBox.dimensions }); Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: boundsCenter.x, y: top + grabberMoveUpOffset, z: boundsCenter.z } }); From 60da0f1567f1df85bae60591a03e0a53bcf3c32f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Oct 2014 17:18:52 -0700 Subject: [PATCH 092/108] Allow drawing cursor on dual contour surface, wired up material paint tool. --- .../shaders/metavoxel_voxel_cursor.frag | 32 ++++++ .../shaders/metavoxel_voxel_cursor.vert | 25 +++++ interface/src/MetavoxelSystem.cpp | 61 ++++++++-- interface/src/MetavoxelSystem.h | 6 + interface/src/ui/MetavoxelEditor.cpp | 76 +++++++++++-- interface/src/ui/MetavoxelEditor.h | 21 ++++ .../metavoxels/src/MetavoxelMessages.cpp | 105 ++++++++++++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 19 ++++ 8 files changed, 326 insertions(+), 19 deletions(-) create mode 100644 interface/resources/shaders/metavoxel_voxel_cursor.frag create mode 100644 interface/resources/shaders/metavoxel_voxel_cursor.vert diff --git a/interface/resources/shaders/metavoxel_voxel_cursor.frag b/interface/resources/shaders/metavoxel_voxel_cursor.frag new file mode 100644 index 0000000000..3a803ea309 --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_cursor.frag @@ -0,0 +1,32 @@ +#version 120 + +// +// metavoxel_voxel_base.frag +// fragment shader +// +// Created by Andrzej Kapolka on 10/10/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the inner radius of the outline, squared +const float SQUARED_OUTLINE_INNER_RADIUS = 0.81; + +// the outer radius of the outline, squared +const float SQUARED_OUTLINE_OUTER_RADIUS = 1.0; + +// the inner radius of the inset, squared +const float SQUARED_INSET_INNER_RADIUS = 0.855625; + +// the outer radius of the inset, squared +const float SQUARED_INSET_OUTER_RADIUS = 0.950625; + +void main(void) { + // use the distance to compute the ring color, then multiply it by the varying color + float squaredDistance = dot(gl_TexCoord[0].str, gl_TexCoord[0].str); + float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS); + float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS); + gl_FragColor = gl_Color * vec4(white, white, white, alpha); +} diff --git a/interface/resources/shaders/metavoxel_voxel_cursor.vert b/interface/resources/shaders/metavoxel_voxel_cursor.vert new file mode 100644 index 0000000000..586eef275a --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_cursor.vert @@ -0,0 +1,25 @@ +#version 120 + +// +// metavoxel_voxel_cursor.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/10/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +void main(void) { + // compute the view space coordinates + vec4 viewPosition = gl_ModelViewMatrix * gl_Vertex; + gl_Position = gl_ProjectionMatrix * viewPosition; + + // generate the texture coordinates from the view position + gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]), + dot(viewPosition, gl_EyePlaneR[4]), 1.0); + + // copy the color for interpolation + gl_FrontColor = gl_Color; +} diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 19a9a7ce89..24a1026ef4 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -454,10 +454,10 @@ float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { return visitor.height; } -class HeightfieldCursorRenderVisitor : public MetavoxelVisitor { +class CursorRenderVisitor : public MetavoxelVisitor { public: - HeightfieldCursorRenderVisitor(const Box& bounds); + CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds); virtual int visit(MetavoxelInfo& info); @@ -466,13 +466,12 @@ private: Box _bounds; }; -HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) : - MetavoxelVisitor(QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()), +CursorRenderVisitor::CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) : + MetavoxelVisitor(QVector() << attribute), _bounds(bounds) { } -int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) { +int CursorRenderVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; } @@ -508,7 +507,8 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); - HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents)); + CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), + Box(position - extents, position + extents)); guideToAugmented(visitor); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); @@ -521,6 +521,42 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glDepthFunc(GL_LESS); } +void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) { + glDepthFunc(GL_LEQUAL); + glEnable(GL_CULL_FACE); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnableClientState(GL_VERTEX_ARRAY); + + DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().bind(); + + glActiveTexture(GL_TEXTURE4); + float scale = 1.0f / radius; + glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x); + glm::vec4 tCoefficients(0.0f, scale, 0.0f, -scale * position.y); + glm::vec4 rCoefficients(0.0f, 0.0f, scale, -scale * position.z); + glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients); + glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients); + glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&rCoefficients); + glActiveTexture(GL_TEXTURE0); + + glm::vec3 extents(radius, radius, radius); + CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), + Box(position - extents, position + extents)); + guideToAugmented(visitor); + + DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release(); + + glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glDepthFunc(GL_LESS); +} + void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { glDeleteTextures(1, (GLuint*)&heightID); glDeleteTextures(1, (GLuint*)&colorID); @@ -1170,7 +1206,7 @@ void VoxelBuffer::render(bool cursor) { glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); - if (!_materials.isEmpty()) { + if (!(_materials.isEmpty() || cursor)) { Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); glDepthFunc(GL_LEQUAL); @@ -1252,7 +1288,7 @@ void VoxelBuffer::render(bool cursor) { _vertexBuffer.release(); _indexBuffer.release(); - if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { + if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData) && !cursor) { if (!_hermiteBuffer.isCreated()) { _hermiteBuffer.create(); _hermiteBuffer.bind(); @@ -1348,6 +1384,12 @@ void DefaultMetavoxelRendererImplementation::init() { _baseVoxelProgram.link(); loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations); + + _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_voxel_cursor.vert"); + _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_voxel_cursor.frag"); + _voxelCursorProgram.link(); } } @@ -2577,6 +2619,7 @@ ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram; ProgramObject DefaultMetavoxelRendererImplementation::_baseVoxelProgram; ProgramObject DefaultMetavoxelRendererImplementation::_splatVoxelProgram; DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatVoxelLocations; +ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram; static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { GLdouble coefficients[] = { x, y, z, w }; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index daa1bd6ca2..f479ba5e50 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -46,6 +46,8 @@ public: void renderHeightfieldCursor(const glm::vec3& position, float radius); + void renderVoxelCursor(const glm::vec3& position, float radius); + bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); @@ -321,6 +323,8 @@ public: static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; } static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; } + static ProgramObject& getVoxelCursorProgram() { return _voxelCursorProgram; } + Q_INVOKABLE DefaultMetavoxelRendererImplementation(); virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); @@ -354,6 +358,8 @@ private: static ProgramObject _baseVoxelProgram; static ProgramObject _splatVoxelProgram; static SplatLocations _splatVoxelLocations; + + static ProgramObject _voxelCursorProgram; }; /// Base class for spanner renderers; provides clipping. diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 6dfe3d77ed..ee7cf63789 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1529,6 +1529,15 @@ void VoxelMaterialSpannerTool::textureLoaded() { VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) : MetavoxelTool(editor, name, false, true) { + + QWidget* widget = new QWidget(); + widget->setLayout(_form = new QFormLayout()); + layout()->addWidget(widget); + + _form->addRow("Radius:", _radius = new QDoubleSpinBox()); + _radius->setSingleStep(0.01); + _radius->setMaximum(FLT_MAX); + _radius->setValue(0.25); } bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const { @@ -1548,24 +1557,71 @@ void VoxelBrushTool::render() { if (!Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, distance)) { return; } - _position = origin + distance * direction; - - glColor4f(1.0f, 0.0f, 0.0f, 1.0f); - - glPushMatrix(); - glTranslatef(_position.x, _position.y, _position.z); - - Application::getInstance()->getGeometryCache()->renderSphere(0.1f, 16, 16); - - glPopMatrix(); + Application::getInstance()->getMetavoxels()->renderVoxelCursor( + _position = origin + distance * direction, _radius->value()); } bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) { + if (event->type() == QEvent::Wheel) { + float angle = static_cast(event)->angleDelta().y(); + const float ANGLE_SCALE = 1.0f / 1000.0f; + _radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE)); + return true; + + } else if (event->type() == QEvent::MouseButtonPress) { + MetavoxelEditMessage message = { createEdit(static_cast(event)->button() == Qt::RightButton) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); + return true; + } return false; } VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) : VoxelBrushTool(editor, "Material Brush") { + + _form->addRow("Color:", _color = new QColorEditor(this)); + connect(_color, &QColorEditor::colorChanged, this, &VoxelMaterialBrushTool::clearTexture); + _form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false)); + connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBrushTool::updateTexture); } +QVariant VoxelMaterialBrushTool::createEdit(bool alternate) { + if (alternate) { + return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); + } else { + SharedObjectPointer material = _materialEditor->getObject(); + if (static_cast(material.data())->getDiffuse().isValid()) { + _materialEditor->detachObject(); + } else { + material = SharedObjectPointer(); + } + return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), material, _color->getColor())); + } +} +void VoxelMaterialBrushTool::clearTexture() { + _materialEditor->setObject(new MaterialObject()); +} + +void VoxelMaterialBrushTool::updateTexture() { + if (_texture) { + _texture->disconnect(this); + } + MaterialObject* material = static_cast(_materialEditor->getObject().data()); + if (!material->getDiffuse().isValid()) { + _texture.clear(); + return; + } + _texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); + if (_texture) { + if (_texture->isLoaded()) { + textureLoaded(); + } else { + connect(_texture.data(), &Resource::loaded, this, &VoxelMaterialBrushTool::textureLoaded); + } + } +} + +void VoxelMaterialBrushTool::textureLoaded() { + _color->setColor(_texture->getAverageColor()); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 725eac87f7..debe45ea2f 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -485,6 +485,11 @@ public: protected: + virtual QVariant createEdit(bool alternate) = 0; + + QFormLayout* _form; + QDoubleSpinBox* _radius; + glm::vec3 _position; }; @@ -495,6 +500,22 @@ class VoxelMaterialBrushTool : public VoxelBrushTool { public: VoxelMaterialBrushTool(MetavoxelEditor* editor); + +protected: + + virtual QVariant createEdit(bool alternate); + +private slots: + + void clearTexture(); + void updateTexture(); + void textureLoaded(); + +private: + + QColorEditor* _color; + SharedObjectEditor* _materialEditor; + QSharedPointer _texture; }; #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 1a18079257..29fbe182d3 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -848,3 +848,108 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject VoxelMaterialSpannerEditVisitor visitor(spanner, material, averageColor); data.guide(visitor); } + +PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& averageColor) : + position(position), + radius(radius), + material(material), + averageColor(averageColor) { +} + +class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor { +public: + + PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& color); + + virtual int visit(MetavoxelInfo& info); + +private: + + glm::vec3 _position; + float _radius; + SharedObjectPointer _material; + QColor _color; + Box _bounds; +}; + +PaintVoxelMaterialEditVisitor::PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& color) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << + AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute()), + _position(position), + _radius(radius), + _material(material), + _color(color) { + + glm::vec3 extents(_radius, _radius, _radius); + _bounds = Box(_position - extents, _position + extents); +} + +int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); + VoxelMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue(); + if (!(colorPointer && materialPointer && colorPointer->getSize() == materialPointer->getSize())) { + return STOP_RECURSION; + } + QVector colorContents = colorPointer->getContents(); + QByteArray materialContents = materialPointer->getContents(); + QVector materials = materialPointer->getMaterials(); + + Box overlap = info.getBounds().getIntersection(_bounds); + int size = colorPointer->getSize(); + int area = size * size; + float scale = (size - 1.0f) / info.size; + overlap.minimum = (overlap.minimum - info.minimum) * scale; + overlap.maximum = (overlap.maximum - info.minimum) * scale; + int minX = glm::ceil(overlap.minimum.x); + int minY = glm::ceil(overlap.minimum.y); + int minZ = glm::ceil(overlap.minimum.z); + int sizeX = (int)overlap.maximum.x - minX + 1; + int sizeY = (int)overlap.maximum.y - minY + 1; + int sizeZ = (int)overlap.maximum.z - minZ + 1; + + QRgb rgb = _color.rgba(); + float step = 1.0f / scale; + glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step); + uchar materialIndex = getMaterialIndex(_material, materials, materialContents); + QRgb* colorData = colorContents.data(); + uchar* materialData = (uchar*)materialContents.data(); + for (int destZ = minZ * area + minY * size + minX, endZ = destZ + sizeZ * area; destZ != endZ; + destZ += area, position.z += step) { + position.y = info.minimum.y + minY * step; + for (int destY = destZ, endY = destY + sizeY * size; destY != endY; destY += size, position.y += step) { + position.x = info.minimum.x + minX * step; + for (int destX = destY, endX = destX + sizeX; destX != endX; destX++, position.x += step) { + QRgb& color = colorData[destX]; + if (qAlpha(color) != 0 && glm::distance(position, _position) <= _radius) { + color = rgb; + materialData[destX] = materialIndex; + } + } + } + } + VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, size)); + info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), + encodeInline(newColorPointer)); + + clearUnusedMaterials(materials, materialContents); + VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, size, materials)); + info.outputValues[1] = AttributeValue(_inputs.at(1), encodeInline(newMaterialPointer)); + + return STOP_RECURSION; +} + +void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + PaintVoxelMaterialEditVisitor visitor(position, radius, material, averageColor); + data.guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index bf59a1e691..0ac0bf59ec 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -261,4 +261,23 @@ public: DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit) +/// An edit that sets a region of a voxel material. +class PaintVoxelMaterialEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM glm::vec3 position; + STREAM float radius; + STREAM SharedObjectPointer material; + STREAM QColor averageColor; + + PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, + const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(PaintVoxelMaterialEdit) + #endif // hifi_MetavoxelMessages_h From 1f63656a14d843655caebd0cd65e52e1a1a96be8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Oct 2014 17:25:25 -0700 Subject: [PATCH 093/108] Comment fix. --- interface/resources/shaders/metavoxel_voxel_cursor.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/shaders/metavoxel_voxel_cursor.frag b/interface/resources/shaders/metavoxel_voxel_cursor.frag index 3a803ea309..bdc6fb6d4a 100644 --- a/interface/resources/shaders/metavoxel_voxel_cursor.frag +++ b/interface/resources/shaders/metavoxel_voxel_cursor.frag @@ -1,7 +1,7 @@ #version 120 // -// metavoxel_voxel_base.frag +// metavoxel_voxel_cursor.frag // fragment shader // // Created by Andrzej Kapolka on 10/10/14. From a24b5c24a9ba8c88429c3a2e67898398c7f6e0bc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 17:28:09 -0700 Subject: [PATCH 094/108] Update max axis value to reflect maximum negative value --- interface/src/devices/Joystick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index b63d49eea2..e110027e1a 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -15,7 +15,7 @@ #include "Joystick.h" -const float MAX_AXIS = 32767.0f; +const float MAX_AXIS = 32768.0f; #ifdef HAVE_SDL2 From 1d4d36444f8d0f9f7238cb3be551cae3d7e105a1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Oct 2014 17:34:16 -0700 Subject: [PATCH 095/108] Bumped the metavoxel version number. --- libraries/networking/src/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 069b3f000d..eddd63ca7a 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -85,7 +85,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 5; + return 6; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: From d1c6aef953aa72d400c0be49c13f143416f8f7cd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 10 Oct 2014 17:36:21 -0700 Subject: [PATCH 096/108] Fixed my swizzle! --- interface/resources/shaders/metavoxel_voxel_cursor.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/shaders/metavoxel_voxel_cursor.frag b/interface/resources/shaders/metavoxel_voxel_cursor.frag index bdc6fb6d4a..88c6df6618 100644 --- a/interface/resources/shaders/metavoxel_voxel_cursor.frag +++ b/interface/resources/shaders/metavoxel_voxel_cursor.frag @@ -25,7 +25,7 @@ const float SQUARED_INSET_OUTER_RADIUS = 0.950625; void main(void) { // use the distance to compute the ring color, then multiply it by the varying color - float squaredDistance = dot(gl_TexCoord[0].str, gl_TexCoord[0].str); + float squaredDistance = dot(gl_TexCoord[0].stp, gl_TexCoord[0].stp); float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS); float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS); gl_FragColor = gl_Color * vec4(white, white, white, alpha); From 42479b1ae23ed315f44ce3d28fde749dbf7e707a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 18:03:08 -0700 Subject: [PATCH 097/108] Add joystickWithName for PrioVR --- .../scripting/JoystickScriptingInterface.cpp | 25 ++++++++++--------- .../scripting/JoystickScriptingInterface.h | 15 ++++++----- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/scripting/JoystickScriptingInterface.cpp index 3f0371e1cd..ced063c8fe 100644 --- a/interface/src/scripting/JoystickScriptingInterface.cpp +++ b/interface/src/scripting/JoystickScriptingInterface.cpp @@ -78,6 +78,19 @@ const QObjectList JoystickScriptingInterface::getAllJoysticks() const { return objectList; } +Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) { +#ifdef HAVE_SDL2 + QMap::iterator iter = _openJoysticks.begin(); + while (iter != _openJoysticks.end()) { + if (iter.value()->getName() == name) { + return iter.value(); + } + iter++; + } +#endif + return NULL; +} + void JoystickScriptingInterface::update() { #ifdef HAVE_SDL2 if (_isInitialized) { @@ -111,15 +124,3 @@ void JoystickScriptingInterface::update() { } #endif } - -#ifdef HAVE_SDL2 - -SDL_Joystick* JoystickScriptingInterface::openSDLJoystickWithName(const QString& name) { - // we haven't opened a joystick with this name yet - enumerate our SDL devices and see if it exists - int joystickCount = SDL_NumJoysticks(); - - - return NULL; -} - -#endif diff --git a/interface/src/scripting/JoystickScriptingInterface.h b/interface/src/scripting/JoystickScriptingInterface.h index 37546a5a85..c9a68d24b1 100644 --- a/interface/src/scripting/JoystickScriptingInterface.h +++ b/interface/src/scripting/JoystickScriptingInterface.h @@ -24,7 +24,7 @@ /// Handles joystick input through SDL. class JoystickScriptingInterface : public QObject { Q_OBJECT - + #ifdef HAVE_SDL2 Q_PROPERTY(int AXIS_INVALID READ axisInvalid) Q_PROPERTY(int AXIS_LEFT_X READ axisLeftX) @@ -59,16 +59,17 @@ class JoystickScriptingInterface : public QObject { public: static JoystickScriptingInterface& getInstance(); - + void update(); - + public slots: + Joystick* joystickWithName(const QString& name); const QObjectList getAllJoysticks() const; signals: void joystickAdded(Joystick* joystick); void joystickRemoved(Joystick* joystick); - + private: #ifdef HAVE_SDL2 int axisInvalid() const { return SDL_CONTROLLER_AXIS_INVALID; } @@ -100,13 +101,11 @@ private: int buttonPressed() const { return SDL_PRESSED; } int buttonRelease() const { return SDL_RELEASED; } - - SDL_Joystick* openSDLJoystickWithName(const QString& name); #endif - + JoystickScriptingInterface(); ~JoystickScriptingInterface(); - + #ifdef HAVE_SDL2 QMap _openJoysticks; #endif From dfff16d9e8e1c15ef59b797a4184de5a0754a68a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 21:12:28 -0700 Subject: [PATCH 098/108] make sure to not select other entities when clicking on a grab handle of a selected entity --- examples/libraries/entitySelectionTool.js | 6 +++++- examples/newEditEntities.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index bbcb86eb2a..7c18b791a1 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1749,6 +1749,7 @@ SelectionDisplay = (function () { }; that.mousePressEvent = function(event) { + var somethingClicked = false; var pickRay = Camera.computePickRay(event.x, event.y); @@ -2049,6 +2050,8 @@ SelectionDisplay = (function () { Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); + + return somethingClicked; }; that.mouseMoveEvent = function(event) { @@ -2150,7 +2153,8 @@ SelectionDisplay = (function () { }; - Controller.mousePressEvent.connect(that.mousePressEvent); + // NOTE: mousePressEvent from the main script should call us., so we don't + // hook the Controller.mousePressEvent.connect(that.mousePressEvent); ourselves. Controller.mouseMoveEvent.connect(that.mouseMoveEvent); Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index b753537631..1fb21caacc 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -341,7 +341,7 @@ function mousePressEvent(event) { entitySelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) { + if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { // Event handled; do nothing. return; } else { From f21ff76a38f1d15adacb3b898b2f13ad495a1b7a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 21:23:56 -0700 Subject: [PATCH 099/108] dont allow stretching below a minimum dimension --- examples/libraries/entitySelectionTool.js | 136 ++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 7c18b791a1..43c7849ab3 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -16,6 +16,8 @@ Script.include("libraries/globals.js"); SelectionDisplay = (function () { var that = {}; + var MINIMUM_DIMENSION = 0.001; + var mode = "UNKNOWN"; var overlayNames = new Array(); var lastAvatarPosition = MyAvatar.position; @@ -883,6 +885,11 @@ SelectionDisplay = (function () { // dimensions changes by: (oldNEAR - newNEAR) var changeInDimensions = { x: 0, y: 0, z: (oldNEAR - newNEAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: 0, y: 0, z: (oldNEAR - newNEAR) * -0.5 }; var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); var wantDebug = false; @@ -928,6 +935,11 @@ SelectionDisplay = (function () { var newFAR = oldFAR + vector.z; var changeInDimensions = { x: 0, y: 0, z: (newFAR - oldFAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: 0, y: 0, z: (newFAR - oldFAR) * 0.5 }; var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); var wantDebug = false; @@ -973,6 +985,11 @@ SelectionDisplay = (function () { var newTOP = oldTOP + vector.y; var changeInDimensions = { x: 0, y: (newTOP - oldTOP), z: 0 }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + var changeInPosition = { x: 0, y: (newTOP - oldTOP) * 0.5, z: 0 }; var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); var wantDebug = false; @@ -1017,6 +1034,11 @@ SelectionDisplay = (function () { var newBOTTOM = oldBOTTOM + vector.y; var changeInDimensions = { x: 0, y: (oldBOTTOM - newBOTTOM), z: 0 }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + var changeInPosition = { x: 0, y: (oldBOTTOM - newBOTTOM) * -0.5, z: 0 }; var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); var wantDebug = false; @@ -1061,6 +1083,11 @@ SelectionDisplay = (function () { var newRIGHT = oldRIGHT + vector.x; var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: 0 , z: 0 }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, y: 0, z: 0 }; var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); var wantDebug = false; @@ -1105,6 +1132,11 @@ SelectionDisplay = (function () { var newLEFT = oldLEFT + vector.x; var changeInDimensions = { x: (oldLEFT - newLEFT), y: 0, z: 0 }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (oldLEFT - newLEFT) * -0.5, y: 0, z: 0 }; var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); var wantDebug = false; @@ -1157,6 +1189,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newBOTTOM - oldBOTTOM) , z: (newNEAR - oldNEAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, y: (newBOTTOM - oldBOTTOM) * -0.5, z: (newNEAR - oldNEAR) * -0.5 }; @@ -1211,6 +1256,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newBOTTOM - oldBOTTOM) , z: (newNEAR - oldNEAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, y: (newBOTTOM - oldBOTTOM) * -0.5, z: (newNEAR - oldNEAR) * -0.5 }; @@ -1265,6 +1323,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newTOP - oldTOP) , z: (newNEAR - oldNEAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, y: (newTOP - oldTOP) * 0.5, z: (newNEAR - oldNEAR) * -0.5 }; @@ -1319,6 +1390,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newTOP - oldTOP) , z: (newNEAR - oldNEAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, y: (newTOP - oldTOP) * 0.5, z: (newNEAR - oldNEAR) * -0.5 }; @@ -1373,6 +1457,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newBOTTOM - oldBOTTOM) , z: (newFAR - oldFAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, y: (newBOTTOM - oldBOTTOM) * -0.5, z: (newFAR - oldFAR) * 0.5 }; @@ -1427,6 +1524,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newBOTTOM - oldBOTTOM) , z: (newFAR - oldFAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, y: (newBOTTOM - oldBOTTOM) * -0.5, z: (newFAR - oldFAR) * 0.5 }; @@ -1481,6 +1591,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newTOP - oldTOP) , z: (newFAR - oldFAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, y: (newTOP - oldTOP) * 0.5, z: (newFAR - oldFAR) * 0.5 }; @@ -1535,6 +1658,19 @@ SelectionDisplay = (function () { var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newTOP - oldTOP) , z: (newFAR - oldFAR) }; var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); + + if (newDimensions.x < MINIMUM_DIMENSION) { + newDimensions.x = MINIMUM_DIMENSION; + } + + if (newDimensions.y < MINIMUM_DIMENSION) { + newDimensions.y = MINIMUM_DIMENSION; + } + + if (newDimensions.z < MINIMUM_DIMENSION) { + newDimensions.z = MINIMUM_DIMENSION; + } + var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, y: (newTOP - oldTOP) * 0.5, z: (newFAR - oldFAR) * 0.5 }; From caac5881bf2544f7b2dbb7944db8a5bca7788b56 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 21:39:05 -0700 Subject: [PATCH 100/108] correctly handle unselect and dont show highlight when other tool is active --- examples/libraries/entitySelectionTool.js | 9 ++-- examples/newEditEntities.js | 56 ++++++++++++----------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 43c7849ab3..1e39096308 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -2254,8 +2254,9 @@ SelectionDisplay = (function () { break; default: // nothing to do by default - break; + return false; } + return true; }; that.mouseReleaseEvent = function(event) { @@ -2289,9 +2290,9 @@ SelectionDisplay = (function () { }; - // NOTE: mousePressEvent from the main script should call us., so we don't - // hook the Controller.mousePressEvent.connect(that.mousePressEvent); ourselves. - Controller.mouseMoveEvent.connect(that.mouseMoveEvent); + // 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; diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 1fb21caacc..5c50d37048 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -338,13 +338,15 @@ function rayPlaneIntersection(pickRay, point, normal) { function mousePressEvent(event) { mouseLastPosition = { x: event.x, y: event.y }; - entitySelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { // Event handled; do nothing. return; } else { + entitySelected = false; + selectionDisplay.unselectAll(); + // If we aren't active and didn't click on an overlay: quit if (!isActive) { return; @@ -440,34 +442,36 @@ function mouseMoveEvent(event) { if (!isActive) { return; } + + // allow the selectionDisplay to handle the event first, if it doesn't handle it, then do our own thing + if (selectionDisplay.mouseMoveEvent(event)) { + return; + } var pickRay = Camera.computePickRay(event.x, event.y); - if (!entitySelected) { - var entityIntersection = Entities.findRayIntersection(pickRay); - if (entityIntersection.accurate) { - if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) { - selectionDisplay.unhighlightSelectable(highlightedEntityID); - highlightedEntityID = { id: -1, isKnownID: false }; - } - - var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0; - - var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), - entityIntersection.properties.position)) * 180 / 3.14; - - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); - - if (entityIntersection.entityID.isKnownID && sizeOK) { - if (wantEntityGlow) { - Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); - } - highlightedEntityID = entityIntersection.entityID; - selectionDisplay.highlightSelectable(entityIntersection.entityID); - } - + var entityIntersection = Entities.findRayIntersection(pickRay); + if (entityIntersection.accurate) { + if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) { + selectionDisplay.unhighlightSelectable(highlightedEntityID); + highlightedEntityID = { id: -1, isKnownID: false }; } - return; + + var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0; + + var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), + entityIntersection.properties.position)) * 180 / 3.14; + + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) + && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + + if (entityIntersection.entityID.isKnownID && sizeOK) { + if (wantEntityGlow) { + Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); + } + highlightedEntityID = entityIntersection.entityID; + selectionDisplay.highlightSelectable(entityIntersection.entityID); + } + } } From e6d7add5dc46edd6f1900ea43d0cd965518f67f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 21:57:10 -0700 Subject: [PATCH 101/108] display tickmarks on the current rotation overlay --- examples/libraries/entitySelectionTool.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 1e39096308..8a84fa37ce 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -262,6 +262,9 @@ SelectionDisplay = (function () { 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("billboard", { @@ -1750,11 +1753,15 @@ SelectionDisplay = (function () { 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 }); + 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 }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, + majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); } } From a08ddecc3a3fc1b6f3dc7f692854bfbe562dcbe7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 22:06:12 -0700 Subject: [PATCH 102/108] fix shifting tickmarks --- interface/src/ui/overlays/Circle3DOverlay.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 62ddee4cf2..d7120a5ff8 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -168,7 +168,8 @@ void Circle3DOverlay::render() { xColor color = getMajorTickMarksColor(); glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - float angle = startAt; + float tickMarkAngle = getMajorTickMarksAngle(); + float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle; float angleInRadians = glm::radians(angle); float tickMarkLength = getMajorTickMarksLength(); float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius; @@ -183,7 +184,7 @@ void Circle3DOverlay::render() { glVertex2f(thisPointA.x, thisPointA.y); glVertex2f(thisPointB.x, thisPointB.y); - angle += getMajorTickMarksAngle(); + angle += tickMarkAngle; } } @@ -193,7 +194,8 @@ void Circle3DOverlay::render() { xColor color = getMinorTickMarksColor(); glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - float angle = startAt; + float tickMarkAngle = getMinorTickMarksAngle(); + float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle; float angleInRadians = glm::radians(angle); float tickMarkLength = getMinorTickMarksLength(); float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius; @@ -208,7 +210,7 @@ void Circle3DOverlay::render() { glVertex2f(thisPointA.x, thisPointA.y); glVertex2f(thisPointB.x, thisPointB.y); - angle += getMinorTickMarksAngle(); + angle += tickMarkAngle; } } From 8c85c3261e3d1263b54fe2280cb7a3f56d097211 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 10 Oct 2014 22:11:27 -0700 Subject: [PATCH 103/108] draw tickmarks for current roation correctly for pitch and roll as well --- examples/libraries/entitySelectionTool.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 8a84fa37ce..1746b59f32 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1817,11 +1817,15 @@ SelectionDisplay = (function () { 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 }); + 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 }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, + majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); } } }; @@ -1875,11 +1879,15 @@ SelectionDisplay = (function () { 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 }); + 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 }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, + majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); } } }; From c6bf09f509881cb988365b73048ea6acc9c94624 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 13 Oct 2014 08:35:52 -0700 Subject: [PATCH 104/108] Fix model uploading for the case that haven't uploaded model before --- examples/editModels.js | 1 - examples/newEditEntities.js | 1 - interface/src/scripting/SettingsScriptingInterface.cpp | 6 ++++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 2741bdccdd..0a908595a3 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -1312,7 +1312,6 @@ var toolBar = (function () { if (clickedOverlay === loadFileMenuItem) { toggleNewModelButton(false); - // TODO BUG: this is bug, if the user has never uploaded a model, this will throw an JS exception file = Window.browse("Select your model file ...", Settings.getValue("LastModelUploadLocation").path(), "Model files (*.fst *.fbx)"); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index b753537631..49dd46033b 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -238,7 +238,6 @@ var toolBar = (function () { if (clickedOverlay === loadFileMenuItem) { toggleNewModelButton(false); - // TODO BUG: this is bug, if the user has never uploaded a model, this will throw an JS exception file = Window.browse("Select your model file ...", Settings.getValue("LastModelUploadLocation").path(), "Model files (*.fst *.fbx)"); diff --git a/interface/src/scripting/SettingsScriptingInterface.cpp b/interface/src/scripting/SettingsScriptingInterface.cpp index ea9d645a94..21a5244138 100644 --- a/interface/src/scripting/SettingsScriptingInterface.cpp +++ b/interface/src/scripting/SettingsScriptingInterface.cpp @@ -22,6 +22,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting) { QSettings* settings = Application::getInstance()->lockSettings(); QVariant value = settings->value(setting); Application::getInstance()->unlockSettings(); + if (!value.isValid()) { + value = ""; + } return value; } @@ -29,6 +32,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting, const QVar QSettings* settings = Application::getInstance()->lockSettings(); QVariant value = settings->value(setting, defaultValue); Application::getInstance()->unlockSettings(); + if (!value.isValid()) { + value = ""; + } return value; } From 2b6cd6d29aa408d10f3e5516aedba13b49d42fc3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 13 Oct 2014 11:33:06 -0700 Subject: [PATCH 105/108] add debugging for ryan's rotate problems --- examples/libraries/entitySelectionTool.js | 64 ++++++++++++++++++++--- examples/newEditEntities.js | 2 + 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 1746b59f32..57f8bde369 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1706,6 +1706,13 @@ SelectionDisplay = (function () { if (!entitySelected || mode !== "ROTATE_YAW") { return; // not allowed } + + var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + + if (debug) { + print("rotateYaw()..."); + print(" event.x,y:" + event.x + "," + event.y); + } var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); @@ -1716,7 +1723,14 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); + + if (debug) { + print(" findRayIntersection() .... result.intersects:" + result.intersects); + } + if (result.intersects) { + + var properties = Entities.getEntityProperties(currentSelection); var center = yawCenter; var zero = yawZero; @@ -1732,7 +1746,11 @@ SelectionDisplay = (function () { } // for debugging - //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + if (debug) { + Vec3.print(" result.intersection:",result.intersection); + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + print(" angleFromZero:" + angleFromZero); + } var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); var newRotation = Quat.multiply(yawChange, originalRotation); @@ -1771,6 +1789,13 @@ SelectionDisplay = (function () { if (!entitySelected || mode !== "ROTATE_PITCH") { return; // not allowed } + var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + + if (debug) { + print("rotatePitch()..."); + print(" event.x,y:" + event.x + "," + event.y); + } + var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); @@ -1779,7 +1804,11 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); - + + if (debug) { + print(" findRayIntersection() .... result.intersects:" + result.intersects); + } + if (result.intersects) { var properties = Entities.getEntityProperties(currentSelection); var center = pitchCenter; @@ -1796,7 +1825,11 @@ SelectionDisplay = (function () { } // for debugging - //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + if (debug) { + Vec3.print(" result.intersection:",result.intersection); + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + print(" angleFromZero:" + angleFromZero); + } var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); var newRotation = Quat.multiply(pitchChange, originalRotation); @@ -1834,6 +1867,13 @@ SelectionDisplay = (function () { if (!entitySelected || mode !== "ROTATE_ROLL") { return; // not allowed } + var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + + if (debug) { + print("rotateRoll()..."); + print(" event.x,y:" + event.x + "," + event.y); + } + var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); @@ -1842,6 +1882,11 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); + + if (debug) { + print(" findRayIntersection() .... result.intersects:" + result.intersects); + } + if (result.intersects) { var properties = Entities.getEntityProperties(currentSelection); var center = rollCenter; @@ -1858,7 +1903,11 @@ SelectionDisplay = (function () { } // for debugging - //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + if (debug) { + Vec3.print(" result.intersection:",result.intersection); + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + print(" angleFromZero:" + angleFromZero); + } var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); var newRotation = Quat.multiply(rollChange, originalRotation); @@ -2124,8 +2173,11 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 0 }); // for debugging - //Overlays.editOverlay(rotateZeroOverlay, { visible: true, start: overlayCenter, end: result.intersection }); - //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: overlayCenter, end: result.intersection }); + var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + if (debug) { + Overlays.editOverlay(rotateZeroOverlay, { visible: true, start: overlayCenter, end: result.intersection }); + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: overlayCenter, end: result.intersection }); + } Overlays.editOverlay(yawHandle, { visible: false }); Overlays.editOverlay(pitchHandle, { visible: false }); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 5c50d37048..8657bac652 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -518,6 +518,7 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" }); + Menu.addMenuItem({ menuName: "Developer", menuItemName: "Debug Ryans Rotation Problems", isCheckable: true }); } setupModelMenus(); // do this when first running our script. @@ -537,6 +538,7 @@ function cleanupModelMenus() { Menu.removeSeparator("File", "Models"); Menu.removeMenuItem("File", "Export Models"); Menu.removeMenuItem("File", "Import Models"); + Menu.removeMenuItem("Developer", "Debug Ryans Rotation Problems"); } Script.scriptEnding.connect(function() { From 45a5768750ced85ee99ced92de0697af37991374 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 13 Oct 2014 12:19:38 -0700 Subject: [PATCH 106/108] highlight grabber handles on hover --- examples/libraries/entitySelectionTool.js | 99 ++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 57f8bde369..503d4415de 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -60,9 +60,17 @@ SelectionDisplay = (function () { var rotateHandleColor = { red: 0, green: 0, blue: 0 }; var rotateHandleAlpha = 0.7; + var highlightedHandleColor = { red: 255, green: 0, blue: 0 }; + var highlightedHandleAlpha = 0.7; + + var previousHandle = false; + var previousHandleColor; + var previousHandleAlpha; + var grabberSizeCorner = 0.025; 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 }; @@ -2320,7 +2328,96 @@ SelectionDisplay = (function () { that.stretchLEFT(event); break; default: - // nothing to do by default + // if not in any specific mode, then just look for handles to highlight... + var pickRay = Camera.computePickRay(event.x, event.y); + var result = Overlays.findRayIntersection(pickRay); + var pickedColor; + var pickedAlpha; + var highlightNeeded = false; + + if (result.intersects) { + switch(result.overlayID) { + case yawHandle: + case pitchHandle: + case rollHandle: + pickedColor = rotateHandleColor; + pickedAlpha = rotateHandleAlpha; + highlightNeeded = true; + break; + + case grabberMoveUp: + pickedColor = rotateHandleColor; + pickedAlpha = rotateHandleAlpha; + highlightNeeded = true; + break; + + case grabberLBN: + case grabberLBF: + case grabberRBN: + case grabberRBF: + case grabberLTN: + case grabberLTF: + case grabberRTN: + case grabberRTF: + pickedColor = grabberColorCorner; + pickedAlpha = grabberAlpha; + highlightNeeded = true; + break; + + case grabberTOP: + case grabberBOTTOM: + case grabberLEFT: + case grabberRIGHT: + case grabberNEAR: + case grabberFAR: + pickedColor = grabberColorFace; + pickedAlpha = grabberAlpha; + highlightNeeded = true; + break; + + case grabberEdgeTR: + case grabberEdgeTL: + case grabberEdgeTF: + case grabberEdgeTN: + case grabberEdgeBR: + case grabberEdgeBL: + case grabberEdgeBF: + case grabberEdgeBN: + case grabberEdgeNR: + case grabberEdgeNL: + case grabberEdgeFR: + case grabberEdgeFL: + pickedColor = grabberColorEdge; + pickedAlpha = grabberAlpha; + highlightNeeded = true; + break; + + default: + if (previousHandle) { + Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + previousHandle = false; + } + break; + } + + if (highlightNeeded) { + if (previousHandle) { + Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + previousHandle = false; + } + 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 }); + previousHandle = false; + } + } + return false; } return true; From bd9451d4c69d3d775ea191525b8a14e622d37258 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 13 Oct 2014 13:20:24 -0700 Subject: [PATCH 107/108] Log Oculus SDK version used in build --- interface/src/devices/OculusManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index fcbfd32350..62894510c2 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -78,6 +78,8 @@ void OculusManager::connect() { #ifdef HAVE_LIBOVR _calibrationState = UNCALIBRATED; + qDebug() << "Oculus SDK" << OVR_VERSION_STRING; + ovr_Initialize(); _ovrHmd = ovrHmd_Create(0); From 958acfcfe0a785fb4c1e0bacd7c6597cfa979d7b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 13 Oct 2014 13:23:31 -0700 Subject: [PATCH 108/108] Fix version logging include to include build number and write in log --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d3bed0c3ea..777f78c2c1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -187,8 +187,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // set the associated application properties applicationInfo.beginGroup("INFO"); - qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); - setApplicationName(applicationInfo.value("name").toString()); setApplicationVersion(BUILD_VERSION); setOrganizationName(applicationInfo.value("organizationName").toString()); @@ -207,6 +205,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : qInstallMessageHandler(messageHandler); + qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); + // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance());
" if (isArray) { - html += row + colIsArray = _.isArray(row) + colValue = colIsArray ? row : row[col.name] + html += colValue + // for arrays we add a hidden input to this td so that values can be posted appropriately - html += "" + html += "" + + } else if (row.hasOwnProperty(col.name)) { html += row[col.name] } @@ -260,6 +266,7 @@ function makeTable(setting, setting_name, setting_value) { html += "
" + row_num + "" + html += "" if (isArray) { colIsArray = _.isArray(row) @@ -292,7 +294,7 @@ function makeTableInputs(setting) { } _.each(setting.columns, function(col) { - html += "\ + html += "\ \