From 99adb413e3b9a13626bc11fa937212137a33b60d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 21 Oct 2014 08:20:15 -0700 Subject: [PATCH] add 3D text overlay support --- interface/src/ui/overlays/Base3DOverlay.h | 1 + interface/src/ui/overlays/Overlay.h | 1 + interface/src/ui/overlays/Overlay2D.h | 2 + interface/src/ui/overlays/Overlays.cpp | 7 +- interface/src/ui/overlays/Text3DOverlay.cpp | 197 ++++++++++++++++++++ interface/src/ui/overlays/Text3DOverlay.h | 72 +++++++ 6 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 interface/src/ui/overlays/Text3DOverlay.cpp create mode 100644 interface/src/ui/overlays/Text3DOverlay.h diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index e293bae5a9..fa750e4fe7 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -26,6 +26,7 @@ public: ~Base3DOverlay(); // getters + virtual bool is3D() const { return true; } const glm::vec3& getPosition() const { return _position; } const glm::vec3& getCenter() const { return _position; } // TODO: consider implementing registration points in this class float getLineWidth() const { return _lineWidth; } diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index dfa9b11d74..d4d0c40135 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -40,6 +40,7 @@ public: virtual void render() = 0; // getters + virtual bool is3D() const = 0; bool isLoaded() { return _isLoaded; } bool getVisible() const { return _visible; } xColor getColor(); diff --git a/interface/src/ui/overlays/Overlay2D.h b/interface/src/ui/overlays/Overlay2D.h index 34028de89a..283e7b7b23 100644 --- a/interface/src/ui/overlays/Overlay2D.h +++ b/interface/src/ui/overlays/Overlay2D.h @@ -29,6 +29,8 @@ class Overlay2D : public Overlay { public: Overlay2D(); ~Overlay2D(); + + virtual bool is3D() const { return false; } // getters int getX() const { return _bounds.x(); } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 922c6a0408..46b7d5afdc 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -23,6 +23,7 @@ #include "Rectangle3DOverlay.h" #include "Sphere3DOverlay.h" #include "TextOverlay.h" +#include "Text3DOverlay.h" Overlays::Overlays() : _nextOverlayID(1) { } @@ -131,6 +132,9 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = new ImageOverlay(); } else if (type == "text") { thisOverlay = new TextOverlay(); + } else if (type == "text3d") { + thisOverlay = new Text3DOverlay(); +qDebug() << "created Text3DOverlay:" << thisOverlay; } else if (type == "cube") { thisOverlay = new Cube3DOverlay(); } else if (type == "sphere") { @@ -167,8 +171,7 @@ unsigned int Overlays::addOverlay(Overlay* overlay) { QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; _nextOverlayID++; - bool is3D = typeid(*overlay) != typeid(ImageOverlay) && typeid(*overlay) != typeid(TextOverlay); - if (is3D) { + if (overlay->is3D()) { _overlays3D[thisID] = overlay; } else { _overlays2D[thisID] = overlay; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp new file mode 100644 index 0000000000..de72ed2692 --- /dev/null +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -0,0 +1,197 @@ +// +// Text3DOverlay.cpp +// interface/src/ui/overlays +// +// 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 this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include "Application.h" +#include "Text3DOverlay.h" +#include "ui/TextRenderer.h" + +const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; +const float DEFAULT_MARGIN = 0.1f; + +Text3DOverlay::Text3DOverlay() : + _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _lineHeight(0.1f), + _leftMargin(DEFAULT_MARGIN), + _topMargin(DEFAULT_MARGIN), + _rightMargin(DEFAULT_MARGIN), + _bottomMargin(DEFAULT_MARGIN), + _isFacingAvatar(false) +{ +} + +Text3DOverlay::~Text3DOverlay() { +} + +xColor Text3DOverlay::getBackgroundColor() { + if (_colorPulse == 0.0f) { + return _backgroundColor; + } + + float pulseLevel = updatePulse(); + xColor result = _backgroundColor; + if (_colorPulse < 0.0f) { + result.red *= (1.0f - pulseLevel); + result.green *= (1.0f - pulseLevel); + result.blue *= (1.0f - pulseLevel); + } else { + result.red *= pulseLevel; + result.green *= pulseLevel; + result.blue *= pulseLevel; + } + return result; +} + + +void Text3DOverlay::render() { + if (!_visible) { + return; // do nothing if we're not visible + } + + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + glm::quat rotation; + if (_isFacingAvatar) { + // rotate about vertical to face the camera + rotation = Application::getInstance()->getCamera()->getRotation(); + rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); + } else { + rotation = getRotation(); + } + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + const float MAX_COLOR = 255.0f; + xColor backgroundColor = getBackgroundColor(); + float alpha = getAlpha(); + glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, alpha); + + glm::vec2 dimensions = getDimensions(); + glm::vec2 halfDimensions = dimensions * 0.5f; + + const float SLIGHTLY_BEHIND = -0.005f; + + glBegin(GL_QUADS); + glVertex3f(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); + glVertex3f(halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); + glVertex3f(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); + glVertex3f(-halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); + glEnd(); + + //TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false, + // EffectType effect = NO_EFFECT, int effectThickness = 1); + + const int FIXED_FONT_POINT_SIZE = 40; + const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation + + TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); + float LINE_SCALE_RATIO = 1.2f; + float maxHeight = (float)textRenderer->calculateHeight("Xy") * LINE_SCALE_RATIO; + + float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; + + glTranslatef(-(halfDimensions.x - _leftMargin), halfDimensions.y - _topMargin, 0.0f); + + glm::vec2 clipMinimum(0.0f, 0.0f); + glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, + (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); + + glScalef(scaleFactor, -scaleFactor, 1.0); + int x = 0; + int y = 0; + + bool wantClipping = true; + + if (wantClipping) { + enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipDimensions.x); + enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); + enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipDimensions.y); + enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); + } + + glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR); + QStringList lines = _text.split("\n"); + int lineOffset = maxHeight; + foreach(QString thisLine, lines) { + textRenderer->draw(x, y + lineOffset, qPrintable(thisLine)); + lineOffset += maxHeight; + } + + if (wantClipping) { + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + } + + } glPopMatrix(); + +} + +void Text3DOverlay::enableClipPlane(GLenum plane, float x, float y, float z, float w) { + GLdouble coefficients[] = { x, y, z, w }; + glClipPlane(plane, coefficients); + glEnable(plane); +} + +void Text3DOverlay::setProperties(const QScriptValue& properties) { + Planar3DOverlay::setProperties(properties); + + QScriptValue text = properties.property("text"); + if (text.isValid()) { + setText(text.toVariant().toString()); + } + + QScriptValue backgroundColor = properties.property("backgroundColor"); + if (backgroundColor.isValid()) { + QScriptValue red = backgroundColor.property("red"); + QScriptValue green = backgroundColor.property("green"); + QScriptValue blue = backgroundColor.property("blue"); + if (red.isValid() && green.isValid() && blue.isValid()) { + _backgroundColor.red = red.toVariant().toInt(); + _backgroundColor.green = green.toVariant().toInt(); + _backgroundColor.blue = blue.toVariant().toInt(); + } + } + + if (properties.property("lineHeight").isValid()) { + setLineHeight(properties.property("lineHeight").toVariant().toFloat()); + } + + if (properties.property("leftMargin").isValid()) { + setLeftMargin(properties.property("leftMargin").toVariant().toFloat()); + } + + if (properties.property("topMargin").isValid()) { + setTopMargin(properties.property("topMargin").toVariant().toFloat()); + } + + if (properties.property("rightMargin").isValid()) { + setRightMargin(properties.property("rightMargin").toVariant().toFloat()); + } + + if (properties.property("bottomMargin").isValid()) { + setBottomMargin(properties.property("bottomMargin").toVariant().toFloat()); + } + + + QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar"); + if (isFacingAvatarValue.isValid()) { + _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); + } + +} + + diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h new file mode 100644 index 0000000000..35ca352891 --- /dev/null +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -0,0 +1,72 @@ +// +// Text3DOverlay.h +// interface/src/ui/overlays +// +// 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_Text3DOverlay_h +#define hifi_Text3DOverlay_h + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +//#include "Overlay.h" +//#include "Overlay3D.h" +#include "Planar3DOverlay.h" + +class Text3DOverlay : public Planar3DOverlay { + Q_OBJECT + +public: + Text3DOverlay(); + ~Text3DOverlay(); + virtual void render(); + + // getters + const QString& getText() const { return _text; } + float getLineHeight() const { return _lineHeight; } + float getLeftMargin() const { return _leftMargin; } + float getTopMargin() const { return _topMargin; } + float getRightMargin() const { return _rightMargin; } + float getBottomMargin() const { return _bottomMargin; } + xColor getBackgroundColor(); + + // setters + void setText(const QString& text) { _text = text; } + void setLineHeight(float value) { _lineHeight = value; } + void setLeftMargin(float margin) { _leftMargin = margin; } + void setTopMargin(float margin) { _topMargin = margin; } + void setRightMargin(float margin) { _rightMargin = margin; } + void setBottomMargin(float margin) { _bottomMargin = margin; } + + virtual void setProperties(const QScriptValue& properties); + +private: + void enableClipPlane(GLenum plane, float x, float y, float z, float w); + + QString _text; + xColor _backgroundColor; + float _lineHeight; + float _leftMargin; + float _topMargin; + float _rightMargin; + float _bottomMargin; + bool _isFacingAvatar; +}; + + +#endif // hifi_Text3DOverlay_h