From 173a79867ce16f78dfe28b8d8affb06b9cde5909 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 13 Jul 2015 16:38:16 -0700 Subject: [PATCH] Extend Overlays API to allow for 3D UI panels. Currently, only BillboardOverlays can be added to a panel, but more types of overlays will be supported in the future. --- examples/example/ui/floatingUIExample.js | 173 ++++++++++++++++++ .../src/ui/overlays/BillboardOverlay.cpp | 50 ++++- interface/src/ui/overlays/BillboardOverlay.h | 5 +- interface/src/ui/overlays/FloatingUIPanel.cpp | 89 +++++++++ interface/src/ui/overlays/FloatingUIPanel.h | 47 +++++ interface/src/ui/overlays/Overlays.cpp | 139 ++++++++++---- interface/src/ui/overlays/Overlays.h | 24 +++ interface/src/ui/overlays/PanelAttachable.h | 44 +++++ 8 files changed, 530 insertions(+), 41 deletions(-) create mode 100644 examples/example/ui/floatingUIExample.js create mode 100644 interface/src/ui/overlays/FloatingUIPanel.cpp create mode 100644 interface/src/ui/overlays/FloatingUIPanel.h create mode 100644 interface/src/ui/overlays/PanelAttachable.h diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js new file mode 100644 index 0000000000..09deca4ec7 --- /dev/null +++ b/examples/example/ui/floatingUIExample.js @@ -0,0 +1,173 @@ +// +// floatingUI.js +// examples/example/ui +// +// Created by Alexander Otavka +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include(["../../libraries/globals.js"]); + +var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; +var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; +var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg"; + +var BLANK_ROTATION = { x: 0, y: 0, z: 0, w: 0 }; + +function isBlank(rotation) { + return rotation.x == BLANK_ROTATION.x && + rotation.y == BLANK_ROTATION.y && + rotation.z == BLANK_ROTATION.z && + rotation.w == BLANK_ROTATION.w; +} + +var panel = Overlays.addPanel({ + offsetPosition: { x: 0, y: 0, z: 1 }, +}); + +var panelChildren = []; + +var bg = Overlays.addOverlay("billboard", { + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, +}); +panelChildren.push(bg); + +var redDot = Overlays.addOverlay("billboard", { + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.15, + y: -0.15, + z: -0.001 + } +}); +panelChildren.push(redDot); + +var redDot2 = Overlays.addOverlay("billboard", { + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.15, + y: 0, + z: -0.001 + } +}); +panelChildren.push(redDot2); + +var blueSquare = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: 0.1, + y: 0, + z: -0.001 + } +}); +panelChildren.push(blueSquare); + +var blueSquare2 = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: 0.1, + y: 0.11, + z: -0.001 + } +}); +panelChildren.push(blueSquare2); + +var blueSquare3 = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.01, + y: 0.11, + z: -0.001 + } +}); +panelChildren.push(blueSquare3); + +Controller.mousePressEvent.connect(function(event) { + if (event.isRightButton) { + var newOffsetRotation = BLANK_ROTATION; + if (isBlank(Overlays.getPanelProperty(panel, "offsetRotation"))) { + newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); + } + Overlays.editPanel(panel, { + offsetRotation: newOffsetRotation + }); + } else if (event.isLeftButton) { + var pickRay = Camera.computePickRay(event.x, event.y) + var rayPickResult = Overlays.findRayIntersection(pickRay); + print(String(rayPickResult.overlayID)); + if (rayPickResult.intersects) { + for (var i in panelChildren) { + if (panelChildren[i] == rayPickResult.overlayID) { + var oldPos = Overlays.getProperty(rayPickResult.overlayID, "offsetPosition"); + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 + } + Overlays.editOverlay(rayPickResult.overlayID, { offsetPosition: newPos }); + } + } + } + } +}); + +Script.scriptEnding.connect(function() { + Overlays.deletePanel(panel); +}); \ No newline at end of file diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 37e197130b..9e1a9a44de 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -36,6 +36,13 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } +void BillboardOverlay::update(float deltatime) { + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } +} + void BillboardOverlay::render(RenderArgs* args) { if (!_texture) { _isLoaded = true; @@ -46,15 +53,17 @@ void BillboardOverlay::render(RenderArgs* args) { return; } + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } + glm::quat rotation; if (_isFacingAvatar) { // LOL, quaternions are hard. - // rotate about vertical to face the camera // glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); // dPos = glm::normalize(dPos); // rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); - rotation = args->_viewFrustum->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); // float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); // glm::vec3 zAxis = glm::vec3(0, 0, 1); // rotation = rotationBetween(zAxis, dPos); @@ -70,9 +79,24 @@ void BillboardOverlay::render(RenderArgs* args) { // rotation = yawQuat * pitchQuat; // glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); // rotation = glm::quat(glm::vec3(pitch, yaw, 0)); + // rotate about vertical to be perpendicular to the camera + rotation = args->_viewFrustum->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); rotation *= getRotation(); } else { rotation = getRotation(); + if (getAttachedPanel()) { + rotation *= getAttachedPanel()->getOffsetRotation() * + getAttachedPanel()->getFacingRotation(); +// if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { +// rotation *= getAttachedPanel()->getFacingRotation(); +// } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { +// rotation *= getAttachedPanel()->getOffsetRotation(); +// } else { +// rotation *= Application::getInstance()->getCamera()->getOrientation() * +// glm::quat(0, 0, 1, 0); +// } + } } float imageWidth = _texture->getWidth(); @@ -114,7 +138,7 @@ void BillboardOverlay::render(RenderArgs* args) { Transform transform = _transform; transform.postScale(glm::vec3(getDimensions(), 1.0f)); transform.setRotation(rotation); - + batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); @@ -171,6 +195,21 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { if (isFacingAvatarValue.isValid()) { _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); } + + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } } QScriptValue BillboardOverlay::getProperty(const QString& property) { @@ -183,6 +222,9 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) { if (property == "isFacingAvatar") { return _isFacingAvatar; } + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + } return Planar3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 15be0419a9..f7bbfd1817 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -15,8 +15,9 @@ #include #include "Planar3DOverlay.h" +#include "PanelAttachable.h" -class BillboardOverlay : public Planar3DOverlay { +class BillboardOverlay : public Planar3DOverlay, public PanelAttachable { Q_OBJECT public: BillboardOverlay(); @@ -24,6 +25,8 @@ public: virtual void render(RenderArgs* args); + virtual void update(float deltatime); + // setters void setURL(const QString& url); void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp new file mode 100644 index 0000000000..e655a75f07 --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -0,0 +1,89 @@ +// +// FloatingUIPanel.cpp +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FloatingUIPanel.h" + +#include +#include + +#include "Application.h" + + +glm::quat FloatingUIPanel::getOffsetRotation() const { + if (getActualOffsetRotation() == glm::quat(0, 0, 0, 0)) { + return Application::getInstance()->getCamera()->getOrientation() * glm::quat(0, 0, 1, 0); + } + return getActualOffsetRotation(); +} + +QScriptValue FloatingUIPanel::getProperty(const QString &property) { + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + } + if (property == "offsetRotation") { + return quatToScriptValue(_scriptEngine, getActualOffsetRotation()); + } + if (property == "facingRotation") { + return quatToScriptValue(_scriptEngine, getFacingRotation()); + } + + return QScriptValue(); +} + +void FloatingUIPanel::setProperties(const QScriptValue &properties) { + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } + + QScriptValue offsetRotation = properties.property("offsetRotation"); + if (offsetRotation.isValid()) { + QScriptValue x = offsetRotation.property("x"); + QScriptValue y = offsetRotation.property("y"); + QScriptValue z = offsetRotation.property("z"); + QScriptValue w = offsetRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setOffsetRotation(newRotation); + } + } + + QScriptValue facingRotation = properties.property("facingRotation"); + if (offsetRotation.isValid()) { + QScriptValue x = facingRotation.property("x"); + QScriptValue y = facingRotation.property("y"); + QScriptValue z = facingRotation.property("z"); + QScriptValue w = facingRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setFacingRotation(newRotation); + } + } +} diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h new file mode 100644 index 0000000000..7f8d42eb5b --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -0,0 +1,47 @@ +// +// FloatingUIPanel.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FloatingUIPanel_h +#define hifi_FloatingUIPanel_h + +#include +#include +#include + +class FloatingUIPanel : public QObject { + Q_OBJECT +public: + typedef std::shared_ptr Pointer; + + QList children; + + void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + glm::quat getOffsetRotation() const; + glm::quat getActualOffsetRotation() const { return _offsetRotation; } + glm::quat getFacingRotation() const { return _facingRotation; } + + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; }; + void setOffsetRotation(glm::quat rotation) { _offsetRotation = rotation; }; + void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; }; + + QScriptValue getProperty(const QString& property); + void setProperties(const QScriptValue& properties); + +private: + glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); + glm::quat _offsetRotation = glm::quat(0, 0, 0, 0); + glm::quat _facingRotation = glm::quat(1, 0, 0, 0); + QScriptEngine* _scriptEngine; +}; + +#endif // hifi_FloatingUIPanel_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index ff218db844..fe489847ee 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -48,6 +48,7 @@ Overlays::~Overlays() { } _overlaysHUD.clear(); _overlaysWorld.clear(); + _panels.clear(); } cleanupOverlaysToDelete(); @@ -124,11 +125,36 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { } } +Overlay::Pointer Overlays::getOverlay(unsigned int id) const { + if (_overlaysHUD.contains(id)) { + return _overlaysHUD[id]; + } + if (_overlaysWorld.contains(id)) { + return _overlaysWorld[id]; + } + return nullptr; +} + +void Overlays::setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property) { + if (PanelAttachable* attachable = dynamic_cast(overlay)) { + if (property.isValid()) { + unsigned int attachedPanelId = property.toVariant().toUInt(); + FloatingUIPanel* panel = nullptr; + if (_panels.contains(attachedPanelId)) { + panel = _panels[attachedPanelId].get(); + panel->children.append(overlayId); + attachable->setAttachedPanel(panel); + } else { + attachable->getAttachedPanel()->children.removeAll(overlayId); + attachable->setAttachedPanel(nullptr); + } + } + } +} + unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { - unsigned int thisID = 0; Overlay* thisOverlay = NULL; - - bool created = true; + if (type == "image") { thisOverlay = new ImageOverlay(); } else if (type == "text") { @@ -153,16 +179,15 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = new ModelOverlay(); } else if (type == "billboard") { thisOverlay = new BillboardOverlay(); - } else { - created = false; } - if (created) { + if (thisOverlay) { thisOverlay->setProperties(properties); - thisID = addOverlay(thisOverlay); + unsigned int overlayId = addOverlay(thisOverlay); + setAttachedPanel(thisOverlay, overlayId, properties.property("attachedPanel")); + return overlayId; } - - return thisID; + return 0; } unsigned int Overlays::addOverlay(Overlay* overlay) { @@ -189,17 +214,12 @@ unsigned int Overlays::addOverlay(Overlay* overlay) { } else { _overlaysHUD[thisID] = overlayPointer; } - + return thisID; } unsigned int Overlays::cloneOverlay(unsigned int id) { - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { return addOverlay(thisOverlay->createClone()); @@ -210,14 +230,8 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { QWriteLocker lock(&_lock); - Overlay::Pointer thisOverlay; - - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { if (thisOverlay->is3D()) { auto overlay3D = std::static_pointer_cast(thisOverlay); @@ -239,6 +253,8 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { thisOverlay->setProperties(properties); } + setAttachedPanel(thisOverlay.get(), id, properties.property("attachedPanel")); + return true; } return false; @@ -302,15 +318,18 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) { OverlayPropertyResult result; - Overlay::Pointer thisOverlay; + Overlay::Pointer thisOverlay = getOverlay(id); QReadLocker lock(&_lock); - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } if (thisOverlay) { - result.value = thisOverlay->getProperty(property); + if (property == "attachedPanel") { + if (FloatingUIPanel* panel = dynamic_cast(thisOverlay.get())) { + result.value = _panels.key(FloatingUIPanel::Pointer(panel)); + } else { + result.value = 0; + } + } else { + result.value = thisOverlay->getProperty(property); + } } return result; } @@ -456,12 +475,8 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R bool Overlays::isLoaded(unsigned int id) { QReadLocker lock(&_lock); - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } else { + Overlay::Pointer thisOverlay = getOverlay(id); + if (!thisOverlay) { return false; // not found } return thisOverlay->isLoaded(); @@ -483,3 +498,55 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const { } return QSizeF(0.0f, 0.0f); } + +unsigned int Overlays::addPanel(FloatingUIPanel* panel) { + QWriteLocker lock(&_lock); + + FloatingUIPanel::Pointer panelPointer(panel); + unsigned int thisID = _nextOverlayID; + _nextOverlayID++; + _panels[thisID] = panelPointer; + + return thisID; +} + +unsigned int Overlays::addPanel(const QScriptValue& properties) { + FloatingUIPanel* panel = new FloatingUIPanel(); + panel->init(_scriptEngine); + panel->setProperties(properties); + return addPanel(panel); +} + +void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) { + if (_panels.contains(panelId)) { + _panels[panelId]->setProperties(properties); + } +} + +OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QString& property) { + OverlayPropertyResult result; + if (_panels.contains(panelId)) { + FloatingUIPanel::Pointer thisPanel = _panels[panelId]; + QReadLocker lock(&_lock); + result.value = thisPanel->getProperty(property); + } + return result; +} + + +void Overlays::deletePanel(unsigned int panelId) { + FloatingUIPanel::Pointer panelToDelete; + + { + QWriteLocker lock(&_lock); + if (_panels.contains(panelId)) { + panelToDelete = _panels.take(panelId); + } else { + return; + } + } + + while (!panelToDelete->children.isEmpty()) { + deleteOverlay(panelToDelete->children.takeLast()); + } +} diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index cd5b0f1d10..2ba2ec8f45 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -16,6 +16,9 @@ #include "Overlay.h" +#include "FloatingUIPanel.h" +#include "PanelAttachable.h" + class PickRay; class OverlayPropertyResult { @@ -90,12 +93,33 @@ public slots: /// overlay; in meters if it is a 3D text overlay QSizeF textSize(unsigned int id, const QString& text) const; + + /// adds a panel that has already been created + unsigned int addPanel(FloatingUIPanel* panel); + + /// creates and adds a panel based on a set of properties + unsigned int addPanel(const QScriptValue& properties); + + /// edit the properties of a panel + void editPanel(unsigned int panelId, const QScriptValue& properties); + + /// get a property of a panel + OverlayPropertyResult getPanelProperty(unsigned int panelId, const QString& property); + + /// deletes a panel and all child overlays + void deletePanel(unsigned int panelId); + private: void cleanupOverlaysToDelete(); + Overlay::Pointer getOverlay(unsigned int id) const; + void setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property); + QMap _overlaysHUD; QMap _overlaysWorld; + QMap _panels; QList _overlaysToDelete; unsigned int _nextOverlayID; + QReadWriteLock _lock; QReadWriteLock _deleteLock; QScriptEngine* _scriptEngine; diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h new file mode 100644 index 0000000000..c364d62fa7 --- /dev/null +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -0,0 +1,44 @@ +// +// PanelAttachable.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/1/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PanelAttachable_h +#define hifi_PanelAttachable_h + +#include "FloatingUIPanel.h" + +#include + +class PanelAttachable { +public: + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } + + FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } + void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + + glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) { + if (getAttachedPanel()) { + glm::vec3 totalOffsetPosition = + getAttachedPanel()->getFacingRotation() * getOffsetPosition() + + getAttachedPanel()->getOffsetPosition(); + + return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + + avatarPosition; + } + return glm::vec3(); + } + +private: + FloatingUIPanel* _attachedPanel = nullptr; + glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); +}; + +#endif // hifi_PanelAttachable_h