diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 9c4b08fd99..78107db699 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -135,7 +135,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) args->_elementsTouched++; // actually render it here... // we need to iterate the actual modelItems of the element - ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; + ModelTreeElement* modelTreeElement = static_cast(element); QList& modelItems = modelTreeElement->getModels(); diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp new file mode 100644 index 0000000000..40de565155 --- /dev/null +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -0,0 +1,136 @@ +// +// BillboardOverlay.cpp +// +// +// Created by Clement on 7/1/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 "../../Application.h" + +#include "BillboardOverlay.h" + +BillboardOverlay::BillboardOverlay() +: _manager(NULL), + _scale(1.0f), + _isFacingAvatar(true) { +} + +void BillboardOverlay::render() { + if (_billboard.isEmpty()) { + return; + } + if (!_billboardTexture) { + QImage image = QImage::fromData(_billboard); + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + _size = image.size(); + _billboardTexture.reset(new Texture()); + 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()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + } else { + glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); + } + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); + + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + if (_isFacingAvatar) { + // rotate about vertical to face the camera + glm::quat rotation = Application::getInstance()->getCamera()->getRotation(); + rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + } else { + glm::vec3 axis = glm::axis(_rotation); + glRotatef(glm::degrees(glm::angle(_rotation)), axis.x, axis.y, axis.z); + } + glScalef(_scale, _scale, _scale); + + float maxSize = glm::max(_size.width(), _size.height()); + float x = _size.width() / (2.0f * maxSize); + float y = -_size.height() / (2.0f * maxSize); + + glColor3f(1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); { + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-x, -y); + glTexCoord2f(1.0f, 0.0f); + glVertex2f(x, -y); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(x, y); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(-x, y); + } glEnd(); + + } glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void BillboardOverlay::setProperties(const QScriptValue &properties) { + Base3DOverlay::setProperties(properties); + + QScriptValue urlValue = properties.property("url"); + if (urlValue.isValid()) { + _url = urlValue.toVariant().toString(); + + setBillboardURL(_url); + } + + QScriptValue scaleValue = properties.property("scale"); + if (scaleValue.isValid()) { + _scale = scaleValue.toVariant().toFloat(); + } + + QScriptValue rotationValue = properties.property("rotation"); + if (rotationValue.isValid()) { + QScriptValue x = rotationValue.property("x"); + QScriptValue y = rotationValue.property("y"); + QScriptValue z = rotationValue.property("z"); + QScriptValue w = rotationValue.property("w"); + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + _rotation.x = x.toVariant().toFloat(); + _rotation.y = y.toVariant().toFloat(); + _rotation.z = z.toVariant().toFloat(); + _rotation.w = w.toVariant().toFloat(); + } + } + + QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar"); + if (isFacingAvatarValue.isValid()) { + _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); + } +} + +// TODO: handle setting image multiple times, how do we manage releasing the bound texture? +void BillboardOverlay::setBillboardURL(const QUrl url) { + // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? + _manager->deleteLater(); + _manager = new QNetworkAccessManager(); + connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); + _manager->get(QNetworkRequest(url)); +} + +void BillboardOverlay::replyFinished(QNetworkReply* reply) { + // replace our byte array with the downloaded data + _billboard = reply->readAll(); + _manager->deleteLater(); + _manager = NULL; +} diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h new file mode 100644 index 0000000000..473e8a066f --- /dev/null +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -0,0 +1,46 @@ +// +// BillboardOverlay.h +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BillboardOverlay_h +#define hifi_BillboardOverlay_h + +#include +#include + +#include "Base3DOverlay.h" +#include "../../renderer/TextureCache.h" + +class BillboardOverlay : public Base3DOverlay { + Q_OBJECT +public: + BillboardOverlay(); + + virtual void render(); + virtual void setProperties(const QScriptValue& properties); + +private slots: + void replyFinished(QNetworkReply* reply); + +private: + void setBillboardURL(const QUrl url); + + QNetworkAccessManager* _manager; + QUrl _url; + QByteArray _billboard; + QSize _size; + QScopedPointer _billboardTexture; + + glm::quat _rotation; + float _scale; + bool _isFacingAvatar; +}; + +#endif // hifi_BillboardOverlay_h \ No newline at end of file diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index aa4766488a..79b1b23de5 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -19,7 +19,7 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : - _manager(0), + _manager(NULL), _textureID(0), _renderImage(false), _textureBound(false), @@ -37,6 +37,7 @@ ImageOverlay::~ImageOverlay() { // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? + _manager->deleteLater(); _manager = new QNetworkAccessManager(); connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); _manager->get(QNetworkRequest(url)); @@ -49,6 +50,7 @@ void ImageOverlay::replyFinished(QNetworkReply* reply) { _textureImage.loadFromData(rawData); _renderImage = true; _manager->deleteLater(); + _manager = NULL; } void ImageOverlay::render() { diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp new file mode 100644 index 0000000000..bc0cc720c2 --- /dev/null +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -0,0 +1,115 @@ +// +// ModelOverlay.cpp +// +// +// Created by Clement on 6/30/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 "../../Menu.h" + +#include "ModelOverlay.h" + +ModelOverlay::ModelOverlay() + : _model(), + _scale(1.0f), + _updateModel(false) { + _model.init(); +} + +void ModelOverlay::update(float deltatime) { + if (_updateModel) { + _updateModel = false; + + _model.setScaleToFit(true, _scale); + _model.setSnapModelToCenter(true); + _model.setRotation(_rotation); + _model.setTranslation(_position); + _model.setURL(_url); + _model.simulate(deltatime, true); + } else { + _model.simulate(deltatime); + } +} + +void ModelOverlay::render() { + if (_model.isActive()) { + + if (_model.isRenderable()) { + _model.render(_alpha); + } + bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds); + if (displayModelBounds) { + glm::vec3 unRotatedMinimum = _model.getUnscaledMeshExtents().minimum; + glm::vec3 unRotatedMaximum = _model.getUnscaledMeshExtents().maximum; + glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; + + float width = unRotatedExtents.x; + float height = unRotatedExtents.y; + float depth = unRotatedExtents.z; + + Extents rotatedExtents = _model.getUnscaledMeshExtents(); + calculateRotatedExtents(rotatedExtents, _rotation); + + glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum; + + const glm::vec3& modelScale = _model.getScale(); + + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + + // draw the rotated bounding cube + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); + glPushMatrix(); { + glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z); + glutWireCube(1.0); + } glPopMatrix(); + + // draw the model relative bounding box + glm::vec3 axis = glm::axis(_rotation); + glRotatef(glm::degrees(glm::angle(_rotation)), axis.x, axis.y, axis.z); + glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z); + glColor3f(0.0f, 1.0f, 0.0f); + glutWireCube(1.0); + + } glPopMatrix(); + } + } +} + +void ModelOverlay::setProperties(const QScriptValue &properties) { + Base3DOverlay::setProperties(properties); + + QScriptValue urlValue = properties.property("url"); + if (urlValue.isValid()) { + _url = urlValue.toVariant().toString(); + _updateModel = true; + } + + QScriptValue scaleValue = properties.property("scale"); + if (scaleValue.isValid()) { + _scale = scaleValue.toVariant().toFloat(); + _updateModel = true; + } + + QScriptValue rotationValue = properties.property("rotation"); + if (rotationValue.isValid()) { + QScriptValue x = rotationValue.property("x"); + QScriptValue y = rotationValue.property("y"); + QScriptValue z = rotationValue.property("z"); + QScriptValue w = rotationValue.property("w"); + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + _rotation.x = x.toVariant().toFloat(); + _rotation.y = y.toVariant().toFloat(); + _rotation.z = z.toVariant().toFloat(); + _rotation.w = w.toVariant().toFloat(); + } + _updateModel = true; + } + + if (properties.property("position").isValid()) { + _updateModel = true; + } +} \ No newline at end of file diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h new file mode 100644 index 0000000000..e0f979676f --- /dev/null +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -0,0 +1,38 @@ +// +// ModelOverlay.h +// +// +// Created by Clement on 6/30/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ModelOverlay_h +#define hifi_ModelOverlay_h + +#include "Base3DOverlay.h" + +#include "../../renderer/Model.h" + +class ModelOverlay : public Base3DOverlay { + Q_OBJECT +public: + ModelOverlay(); + + virtual void update(float deltatime); + virtual void render(); + virtual void setProperties(const QScriptValue& properties); +private: + + Model _model; + + QUrl _url; + glm::quat _rotation; + float _scale; + + bool _updateModel; +}; + +#endif // hifi_ModelOverlay_h \ No newline at end of file diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 95f4f2b2fe..dd483da27a 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -10,13 +10,15 @@ #include +#include "BillboardOverlay.h" #include "Cube3DOverlay.h" #include "ImageOverlay.h" #include "Line3DOverlay.h" +#include "LocalVoxelsOverlay.h" +#include "ModelOverlay.h" #include "Overlays.h" #include "Sphere3DOverlay.h" #include "TextOverlay.h" -#include "LocalVoxelsOverlay.h" Overlays::Overlays() : _nextOverlayID(1) { } @@ -156,6 +158,18 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope 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; } if (created) {