mirror of
https://github.com/lubosz/overte.git
synced 2025-04-19 17:03:43 +02:00
More OSX work, refactoring functions to shared, removing erroneuous Qt 5.4 dependency, fixing PrioVR text api usage
This commit is contained in:
parent
ba277e802a
commit
1745ba9a7e
9 changed files with 493 additions and 477 deletions
|
@ -216,7 +216,7 @@ void PrioVR::renderCalibrationCountdown() {
|
|||
false, TextRenderer::OUTLINE_EFFECT, 2);
|
||||
QByteArray text = "Assume T-Pose in " + QByteArray::number(secondsRemaining) + "...";
|
||||
auto glCanvas = DependencyManager::get<GLCanvas>();
|
||||
textRenderer->draw((glCanvas->width() - textRenderer->computeWidth(text.constData())) / 2,
|
||||
textRenderer->draw((glCanvas->width() - textRenderer->computeExtent(text.constData()).x) / 2,
|
||||
glCanvas->height() / 2,
|
||||
text, glm::vec4(1,1,1,1));
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <TextRenderer.h>
|
||||
|
||||
#include "RenderableTextEntityItem.h"
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
const int FIXED_FONT_POINT_SIZE = 40;
|
||||
const float LINE_SCALE_RATIO = 1.2f;
|
||||
|
@ -26,11 +27,6 @@ EntityItem* RenderableTextEntityItem::factory(const EntityItemID& entityID, cons
|
|||
return new RenderableTextEntityItem(entityID, properties);
|
||||
}
|
||||
|
||||
glm::vec3 toGlm(const xColor & color) {
|
||||
static const float MAX_COLOR = 255.0f;
|
||||
return std::move(glm::vec3(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR));
|
||||
}
|
||||
|
||||
void RenderableTextEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
|
||||
assert(getType() == EntityTypes::Text);
|
||||
|
|
|
@ -1,6 +1 @@
|
|||
#include "MatrixStack.h"
|
||||
|
||||
QMatrix4x4 fromGlm(const glm::mat4 & m) {
|
||||
return QMatrix4x4(&m[0][0]).transposed();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <glm/gtc/epsilon.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <stack>
|
||||
#include <QMatrix4x4>
|
||||
|
||||
#include <gpu/GPUConfig.h>
|
||||
|
||||
|
@ -201,8 +200,5 @@ public:
|
|||
f();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
QMatrix4x4 fromGlm(const glm::mat4 & m);
|
||||
|
||||
|
|
|
@ -31,49 +31,40 @@
|
|||
|
||||
#include "gpu/GLBackend.h"
|
||||
#include "gpu/Stream.h"
|
||||
#include "GLMHelpers.h"
|
||||
#include "FontInconsolataMedium.h"
|
||||
#include "FontRoboto.h"
|
||||
#include "FontTimeless.h"
|
||||
#include "FontCourierPrime.h"
|
||||
|
||||
namespace Shaders {
|
||||
// Normally we could use 'enum class' to avoid namespace pollution,
|
||||
// but we want easy conversion to GLuint
|
||||
namespace Attributes {
|
||||
enum {
|
||||
Position = 0,
|
||||
TexCoord = 1,
|
||||
};
|
||||
};
|
||||
// Normally we could use 'enum class' to avoid namespace pollution,
|
||||
// but we want easy conversion to GLuint
|
||||
namespace Attributes {
|
||||
enum {
|
||||
Position = 0, TexCoord = 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for reading binary data from an IO device
|
||||
template <class T>
|
||||
template<class T>
|
||||
void readStream(QIODevice & in, T & t) {
|
||||
in.read((char*)&t, sizeof(t));
|
||||
in.read((char*) &t, sizeof(t));
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
void readStream(QIODevice & in, T(&t)[N]) {
|
||||
in.read((char*)t, N);
|
||||
template<typename T, size_t N>
|
||||
void readStream(QIODevice & in, T (&t)[N]) {
|
||||
in.read((char*) t, N);
|
||||
}
|
||||
|
||||
glm::uvec2 toGlm(const QSize & size) {
|
||||
return glm::uvec2(size.width(), size.height());
|
||||
template<class T, size_t N>
|
||||
void fillBuffer(QBuffer & buffer, T (&t)[N]) {
|
||||
buffer.setData((const char*) t, N);
|
||||
}
|
||||
|
||||
template <class T, size_t N >
|
||||
void fillBuffer(QBuffer & buffer, T(&t)[N]) {
|
||||
buffer.setData((const char*)t, N);
|
||||
}
|
||||
|
||||
QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) {
|
||||
QRectF result(pos.x, pos.y, size.x, size.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const char SHADER_TEXT_VS[] = R"XXXX(#version 330
|
||||
const char SHADER_TEXT_VS[] =
|
||||
R"XXXX(#version 330
|
||||
|
||||
uniform mat4 Projection = mat4(1);
|
||||
uniform mat4 ModelView = mat4(1);
|
||||
|
@ -94,7 +85,6 @@ void main() {
|
|||
gl_Position = Projection * clipVertex;
|
||||
})XXXX";
|
||||
|
||||
|
||||
// FIXME
|
||||
// Work in progress to support clipping text to a specific region in
|
||||
// worldspace. Right now this is a passthrough goemetry shader but
|
||||
|
@ -105,7 +95,8 @@ void main() {
|
|||
// However, it might be simpler to do this calculation inside the CPU
|
||||
// draw call by aborting any letter where the bounding box falls outside
|
||||
// given rectangle
|
||||
const char SHADER_TEXT_GS[] = R"XXXX(#version 330
|
||||
const char SHADER_TEXT_GS[] =
|
||||
R"XXXX(#version 330
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 3) out;
|
||||
|
||||
|
@ -125,7 +116,8 @@ const char SHADER_TEXT_GS[] = R"XXXX(#version 330
|
|||
|
||||
// FIXME figure out how to improve the anti-aliasing on the
|
||||
// interior of the outline fonts
|
||||
const char SHADER_TEXT_FS[] = R"XXXX(#version 330
|
||||
const char SHADER_TEXT_FS[] =
|
||||
R"XXXX(#version 330
|
||||
|
||||
uniform sampler2D Font;
|
||||
uniform vec4 Color = vec4(1);
|
||||
|
@ -168,419 +160,414 @@ void main() {
|
|||
|
||||
// stores the font metrics for a single character
|
||||
struct Glyph {
|
||||
QChar c;
|
||||
glm::vec2 texOffset;
|
||||
glm::vec2 texSize;
|
||||
glm::vec2 size;
|
||||
glm::vec2 offset;
|
||||
float d; // xadvance - adjusts character positioning
|
||||
size_t indexOffset;
|
||||
QChar c;
|
||||
glm::vec2 texOffset;
|
||||
glm::vec2 texSize;
|
||||
glm::vec2 size;
|
||||
glm::vec2 offset;
|
||||
float d; // xadvance - adjusts character positioning
|
||||
size_t indexOffset;
|
||||
|
||||
QRectF bounds() const;
|
||||
QRectF textureBounds(const glm::vec2 & textureSize) const;
|
||||
QRectF bounds() const;
|
||||
QRectF textureBounds(const glm::vec2 & textureSize) const;
|
||||
|
||||
void read(QIODevice & in);
|
||||
void read(QIODevice & in);
|
||||
};
|
||||
|
||||
void Glyph::read(QIODevice & in) {
|
||||
uint16_t charcode;
|
||||
readStream(in, charcode);
|
||||
c = charcode;
|
||||
readStream(in, texOffset);
|
||||
readStream(in, size);
|
||||
readStream(in, offset);
|
||||
readStream(in, d);
|
||||
texSize = size;
|
||||
uint16_t charcode;
|
||||
readStream(in, charcode);
|
||||
c = charcode;
|
||||
readStream(in, texOffset);
|
||||
readStream(in, size);
|
||||
readStream(in, offset);
|
||||
readStream(in, d);
|
||||
texSize = size;
|
||||
}
|
||||
|
||||
const float DEFAULT_POINT_SIZE = 12;
|
||||
|
||||
class Font {
|
||||
public:
|
||||
using TexturePtr = QSharedPointer < QOpenGLTexture >;
|
||||
using VertexArrayPtr = QSharedPointer< QOpenGLVertexArrayObject >;
|
||||
using ProgramPtr = QSharedPointer < QOpenGLShaderProgram >;
|
||||
using BufferPtr = QSharedPointer < QOpenGLBuffer >;
|
||||
using TexturePtr = QSharedPointer < QOpenGLTexture >;
|
||||
using VertexArrayPtr = QSharedPointer< QOpenGLVertexArrayObject >;
|
||||
using ProgramPtr = QSharedPointer < QOpenGLShaderProgram >;
|
||||
using BufferPtr = QSharedPointer < QOpenGLBuffer >;
|
||||
|
||||
// maps characters to cached glyph info
|
||||
QHash<QChar, Glyph> _glyphs;
|
||||
|
||||
// maps characters to cached glyph info
|
||||
QHash<QChar, Glyph> _glyphs;
|
||||
|
||||
// the id of the glyph texture to which we're currently writing
|
||||
GLuint _currentTextureID;
|
||||
// the id of the glyph texture to which we're currently writing
|
||||
GLuint _currentTextureID;
|
||||
|
||||
int _pointSize;
|
||||
int _pointSize;
|
||||
|
||||
// the height of the current row of characters
|
||||
int _rowHeight;
|
||||
// the height of the current row of characters
|
||||
int _rowHeight;
|
||||
|
||||
QString _family;
|
||||
float _fontSize{ 0 };
|
||||
float _leading{ 0 };
|
||||
float _ascent{ 0 };
|
||||
float _descent{ 0 };
|
||||
float _spaceWidth{ 0 };
|
||||
QString _family;
|
||||
float _fontSize { 0 };
|
||||
float _leading { 0 };
|
||||
float _ascent { 0 };
|
||||
float _descent { 0 };
|
||||
float _spaceWidth { 0 };
|
||||
|
||||
BufferPtr _vertices;
|
||||
BufferPtr _indices;
|
||||
TexturePtr _texture;
|
||||
VertexArrayPtr _vao;
|
||||
QImage _image;
|
||||
ProgramPtr _program;
|
||||
BufferPtr _vertices;
|
||||
BufferPtr _indices;
|
||||
TexturePtr _texture;
|
||||
VertexArrayPtr _vao;
|
||||
QImage _image;
|
||||
ProgramPtr _program;
|
||||
|
||||
const Glyph & getGlyph(const QChar & c) const;
|
||||
void read(QIODevice & path);
|
||||
// Initialize the OpenGL structures
|
||||
void setupGL();
|
||||
const Glyph & getGlyph(const QChar & c);
|
||||
void read(QIODevice & path);
|
||||
// Initialize the OpenGL structures
|
||||
void setupGL();
|
||||
|
||||
glm::vec2 computeExtent(const QString & str) const;
|
||||
|
||||
glm::vec2 computeExtent(const QString & str) const;
|
||||
|
||||
glm::vec2 drawString(
|
||||
float x, float y,
|
||||
const QString & str,
|
||||
const glm::vec4& color,
|
||||
TextRenderer::EffectType effectType,
|
||||
float maxWidth) const;
|
||||
glm::vec2 drawString(float x, float y, const QString & str,
|
||||
const glm::vec4& color, TextRenderer::EffectType effectType,
|
||||
float maxWidth) const;
|
||||
};
|
||||
|
||||
static QHash<QString, Font*> LOADED_FONTS;
|
||||
|
||||
Font * loadFont(QIODevice & buffer) {
|
||||
Font * result = new Font();
|
||||
result->read(buffer);
|
||||
return result;
|
||||
Font * result = new Font();
|
||||
result->read(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, size_t N >
|
||||
Font * loadFont(T(&t)[N]) {
|
||||
QBuffer buffer;
|
||||
buffer.setData((const char*)t, N);
|
||||
buffer.open(QBuffer::ReadOnly);
|
||||
return loadFont(buffer);
|
||||
template<class T, size_t N>
|
||||
Font * loadFont(T (&t)[N]) {
|
||||
QBuffer buffer;
|
||||
buffer.setData((const char*) t, N);
|
||||
buffer.open(QBuffer::ReadOnly);
|
||||
return loadFont(buffer);
|
||||
}
|
||||
|
||||
Font * loadFont(const QString & family) {
|
||||
if (!LOADED_FONTS.contains(family)) {
|
||||
if (family == MONO_FONT_FAMILY) {
|
||||
if (!LOADED_FONTS.contains(family)) {
|
||||
if (family == MONO_FONT_FAMILY) {
|
||||
|
||||
LOADED_FONTS[family] = loadFont(SDFF_COURIER_PRIME);
|
||||
} else if (family == INCONSOLATA_FONT_FAMILY) {
|
||||
LOADED_FONTS[family] = loadFont(SDFF_INCONSOLATA_MEDIUM);
|
||||
} else if (family == SANS_FONT_FAMILY) {
|
||||
LOADED_FONTS[family] = loadFont(SDFF_ROBOTO);
|
||||
} else {
|
||||
if (!LOADED_FONTS.contains(SERIF_FONT_FAMILY)) {
|
||||
LOADED_FONTS[SERIF_FONT_FAMILY] = loadFont(SDFF_TIMELESS);
|
||||
}
|
||||
LOADED_FONTS[family] = LOADED_FONTS[SERIF_FONT_FAMILY];
|
||||
LOADED_FONTS[family] = loadFont(SDFF_COURIER_PRIME);
|
||||
} else if (family == INCONSOLATA_FONT_FAMILY) {
|
||||
LOADED_FONTS[family] = loadFont(SDFF_INCONSOLATA_MEDIUM);
|
||||
} else if (family == SANS_FONT_FAMILY) {
|
||||
LOADED_FONTS[family] = loadFont(SDFF_ROBOTO);
|
||||
} else {
|
||||
if (!LOADED_FONTS.contains(SERIF_FONT_FAMILY)) {
|
||||
LOADED_FONTS[SERIF_FONT_FAMILY] = loadFont(SDFF_TIMELESS);
|
||||
}
|
||||
LOADED_FONTS[family] = LOADED_FONTS[SERIF_FONT_FAMILY];
|
||||
}
|
||||
}
|
||||
}
|
||||
return LOADED_FONTS[family];
|
||||
return LOADED_FONTS[family];
|
||||
}
|
||||
|
||||
const Glyph & Font::getGlyph(const QChar & c) const {
|
||||
if (!_glyphs.contains(c)) {
|
||||
return _glyphs[QChar('?')];
|
||||
}
|
||||
return _glyphs[c];
|
||||
};
|
||||
// NERD RAGE: why doesn't QHash have a 'const T & operator[] const' member
|
||||
const Glyph & Font::getGlyph(const QChar & c) {
|
||||
if (!_glyphs.contains(c)) {
|
||||
return _glyphs[QChar('?')];
|
||||
}
|
||||
return _glyphs[c];
|
||||
}
|
||||
|
||||
void Font::read(QIODevice & in) {
|
||||
uint8_t header[4];
|
||||
readStream(in, header);
|
||||
if (memcmp(header, "SDFF", 4)) {
|
||||
qFatal("Bad SDFF file");
|
||||
}
|
||||
|
||||
uint16_t version;
|
||||
readStream(in, version);
|
||||
|
||||
// read font name
|
||||
_family = "";
|
||||
if (version > 0x0001) {
|
||||
char c;
|
||||
readStream(in, c);
|
||||
while (c) {
|
||||
_family += c;
|
||||
readStream(in, c);
|
||||
uint8_t header[4];
|
||||
readStream(in, header);
|
||||
if (memcmp(header, "SDFF", 4)) {
|
||||
qFatal("Bad SDFF file");
|
||||
}
|
||||
}
|
||||
|
||||
// read font data
|
||||
readStream(in, _leading);
|
||||
readStream(in, _ascent);
|
||||
readStream(in, _descent);
|
||||
readStream(in, _spaceWidth);
|
||||
_fontSize = _ascent + _descent;
|
||||
_rowHeight = _fontSize + _descent;
|
||||
uint16_t version;
|
||||
readStream(in, version);
|
||||
|
||||
// Read character count
|
||||
uint16_t count;
|
||||
readStream(in, count);
|
||||
// read metrics data for each character
|
||||
QVector<Glyph> glyphs(count);
|
||||
// std::for_each instead of Qt foreach because we need non-const references
|
||||
std::for_each(glyphs.begin(), glyphs.end(), [&](Glyph & g){
|
||||
g.read(in);
|
||||
});
|
||||
// read font name
|
||||
_family = "";
|
||||
if (version > 0x0001) {
|
||||
char c;
|
||||
readStream(in, c);
|
||||
while (c) {
|
||||
_family += c;
|
||||
readStream(in, c);
|
||||
}
|
||||
}
|
||||
|
||||
// read image data
|
||||
if (!_image.loadFromData(in.readAll(), "PNG")) {
|
||||
qFatal("Failed to read SDFF image");
|
||||
}
|
||||
// read font data
|
||||
readStream(in, _leading);
|
||||
readStream(in, _ascent);
|
||||
readStream(in, _descent);
|
||||
readStream(in, _spaceWidth);
|
||||
_fontSize = _ascent + _descent;
|
||||
_rowHeight = _fontSize + _descent;
|
||||
|
||||
_glyphs.clear();
|
||||
foreach(Glyph g, glyphs) {
|
||||
// Adjust the pixel texture coordinates into UV coordinates,
|
||||
glm::vec2 imageSize = toGlm(_image.size());
|
||||
g.texSize /= imageSize;
|
||||
g.texOffset /= imageSize;
|
||||
// store in the character to glyph hash
|
||||
_glyphs[g.c] = g;
|
||||
};
|
||||
// Read character count
|
||||
uint16_t count;
|
||||
readStream(in, count);
|
||||
// read metrics data for each character
|
||||
QVector<Glyph> glyphs(count);
|
||||
// std::for_each instead of Qt foreach because we need non-const references
|
||||
std::for_each(glyphs.begin(), glyphs.end(), [&](Glyph & g) {
|
||||
g.read(in);
|
||||
});
|
||||
|
||||
setupGL();
|
||||
// read image data
|
||||
if (!_image.loadFromData(in.readAll(), "PNG")) {
|
||||
qFatal("Failed to read SDFF image");
|
||||
}
|
||||
|
||||
_glyphs.clear();
|
||||
foreach(Glyph g, glyphs) {
|
||||
// Adjust the pixel texture coordinates into UV coordinates,
|
||||
glm::vec2 imageSize = toGlm(_image.size());
|
||||
g.texSize /= imageSize;
|
||||
g.texOffset /= imageSize;
|
||||
// store in the character to glyph hash
|
||||
_glyphs[g.c] = g;
|
||||
};
|
||||
|
||||
setupGL();
|
||||
}
|
||||
|
||||
struct TextureVertex {
|
||||
glm::vec2 pos;
|
||||
glm::vec2 tex;
|
||||
TextureVertex() {
|
||||
}
|
||||
TextureVertex(const glm::vec2 & pos, const glm::vec2 & tex)
|
||||
: pos(pos), tex(tex) {
|
||||
}
|
||||
TextureVertex(const QPointF & pos, const QPointF & tex)
|
||||
: pos(pos.x(), pos.y()), tex(tex.x(), tex.y()) {
|
||||
}
|
||||
glm::vec2 pos;
|
||||
glm::vec2 tex;
|
||||
TextureVertex() {
|
||||
}
|
||||
TextureVertex(const glm::vec2 & pos, const glm::vec2 & tex) :
|
||||
pos(pos), tex(tex) {
|
||||
}
|
||||
TextureVertex(const QPointF & pos, const QPointF & tex) :
|
||||
pos(pos.x(), pos.y()), tex(tex.x(), tex.y()) {
|
||||
}
|
||||
};
|
||||
|
||||
struct QuadBuilder {
|
||||
TextureVertex vertices[4];
|
||||
QuadBuilder(const QRectF & r, const QRectF & tr) {
|
||||
vertices[0] = TextureVertex(r.bottomLeft(), tr.topLeft());
|
||||
vertices[1] = TextureVertex(r.bottomRight(), tr.topRight());
|
||||
vertices[2] = TextureVertex(r.topRight(), tr.bottomRight());
|
||||
vertices[3] = TextureVertex(r.topLeft(), tr.bottomLeft());
|
||||
}
|
||||
TextureVertex vertices[4];
|
||||
QuadBuilder(const QRectF & r, const QRectF & tr) {
|
||||
vertices[0] = TextureVertex(r.bottomLeft(), tr.topLeft());
|
||||
vertices[1] = TextureVertex(r.bottomRight(), tr.topRight());
|
||||
vertices[2] = TextureVertex(r.topRight(), tr.bottomRight());
|
||||
vertices[3] = TextureVertex(r.topLeft(), tr.bottomLeft());
|
||||
}
|
||||
};
|
||||
|
||||
QRectF Glyph::bounds() const {
|
||||
return glmToRect(offset, size);
|
||||
return glmToRect(offset, size);
|
||||
}
|
||||
|
||||
QRectF Glyph::textureBounds(const glm::vec2 & textureSize) const {
|
||||
return glmToRect(texOffset, texSize);
|
||||
return glmToRect(texOffset, texSize);
|
||||
}
|
||||
|
||||
void Font::setupGL() {
|
||||
_texture = TexturePtr(new QOpenGLTexture(_image, QOpenGLTexture::DontGenerateMipMaps));
|
||||
_program = ProgramPtr(new QOpenGLShaderProgram());
|
||||
if (!_program->create()) {
|
||||
qFatal("Could not create text shader");
|
||||
}
|
||||
if (
|
||||
!_program->addShaderFromSourceCode(QOpenGLShader::Vertex, SHADER_TEXT_VS) ||
|
||||
!_program->addShaderFromSourceCode(QOpenGLShader::Fragment, SHADER_TEXT_FS) ||
|
||||
!_program->addShaderFromSourceCode(QOpenGLShader::Geometry, SHADER_TEXT_GS) ||
|
||||
!_program->link()) {
|
||||
qFatal(_program->log().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
std::vector<TextureVertex> vertexData;
|
||||
std::vector<GLuint> indexData;
|
||||
vertexData.reserve(_glyphs.size() * 4);
|
||||
std::for_each(_glyphs.begin(), _glyphs.end(), [&](Glyph & m){
|
||||
GLuint index = (GLuint)vertexData.size();
|
||||
|
||||
QRectF bounds = m.bounds();
|
||||
QRectF texBounds = m.textureBounds(toGlm(_image.size()));
|
||||
QuadBuilder qb(bounds, texBounds);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
vertexData.push_back(qb.vertices[i]);
|
||||
_texture = TexturePtr(
|
||||
new QOpenGLTexture(_image, QOpenGLTexture::DontGenerateMipMaps));
|
||||
_program = ProgramPtr(new QOpenGLShaderProgram());
|
||||
if (!_program->create()) {
|
||||
qFatal("Could not create text shader");
|
||||
}
|
||||
if (!_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
|
||||
SHADER_TEXT_VS)
|
||||
|| !_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
|
||||
SHADER_TEXT_FS)
|
||||
|| !_program->addShaderFromSourceCode(QOpenGLShader::Geometry,
|
||||
SHADER_TEXT_GS) || !_program->link()) {
|
||||
qFatal(_program->log().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
m.indexOffset = indexData.size() * sizeof(GLuint);
|
||||
// FIXME use triangle strips + primitive restart index
|
||||
indexData.push_back(index + 0);
|
||||
indexData.push_back(index + 1);
|
||||
indexData.push_back(index + 2);
|
||||
indexData.push_back(index + 0);
|
||||
indexData.push_back(index + 2);
|
||||
indexData.push_back(index + 3);
|
||||
});
|
||||
std::vector<TextureVertex> vertexData;
|
||||
std::vector<GLuint> indexData;
|
||||
vertexData.reserve(_glyphs.size() * 4);
|
||||
std::for_each(_glyphs.begin(), _glyphs.end(), [&](Glyph & m) {
|
||||
GLuint index = (GLuint)vertexData.size();
|
||||
|
||||
//_vao = VertexArrayPtr(new VertexArray());
|
||||
_vao = VertexArrayPtr(new QOpenGLVertexArrayObject());
|
||||
_vao->create();
|
||||
_vao->bind();
|
||||
QRectF bounds = m.bounds();
|
||||
QRectF texBounds = m.textureBounds(toGlm(_image.size()));
|
||||
QuadBuilder qb(bounds, texBounds);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
vertexData.push_back(qb.vertices[i]);
|
||||
}
|
||||
|
||||
m.indexOffset = indexData.size() * sizeof(GLuint);
|
||||
// FIXME use triangle strips + primitive restart index
|
||||
indexData.push_back(index + 0);
|
||||
indexData.push_back(index + 1);
|
||||
indexData.push_back(index + 2);
|
||||
indexData.push_back(index + 0);
|
||||
indexData.push_back(index + 2);
|
||||
indexData.push_back(index + 3);
|
||||
});
|
||||
|
||||
_vertices = BufferPtr(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
|
||||
_vertices->create();
|
||||
_vertices->bind();
|
||||
_vertices->allocate(&vertexData[0], sizeof(TextureVertex) * vertexData.size());
|
||||
//_vao = VertexArrayPtr(new VertexArray());
|
||||
_vao = VertexArrayPtr(new QOpenGLVertexArrayObject());
|
||||
_vao->create();
|
||||
_vao->bind();
|
||||
|
||||
_indices = BufferPtr(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer));
|
||||
_indices->create();
|
||||
_indices->bind();
|
||||
_indices->allocate(&indexData[0], sizeof(GLuint) * indexData.size());
|
||||
_vertices = BufferPtr(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
|
||||
_vertices->create();
|
||||
_vertices->bind();
|
||||
_vertices->allocate(&vertexData[0],
|
||||
sizeof(TextureVertex) * vertexData.size());
|
||||
|
||||
GLsizei stride = (GLsizei)sizeof(TextureVertex);
|
||||
void* offset = (void*)offsetof(TextureVertex, tex);
|
||||
_indices = BufferPtr(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer));
|
||||
_indices->create();
|
||||
_indices->bind();
|
||||
_indices->allocate(&indexData[0], sizeof(GLuint) * indexData.size());
|
||||
|
||||
glEnableVertexAttribArray(Shaders::Attributes::Position);
|
||||
glVertexAttribPointer(Shaders::Attributes::Position, 3, GL_FLOAT, false, stride, nullptr);
|
||||
glEnableVertexAttribArray(Shaders::Attributes::TexCoord);
|
||||
glVertexAttribPointer(Shaders::Attributes::TexCoord, 2, GL_FLOAT, false, stride, offset);
|
||||
_vao->release();
|
||||
GLsizei stride = (GLsizei) sizeof(TextureVertex);
|
||||
void* offset = (void*) offsetof(TextureVertex, tex);
|
||||
|
||||
glEnableVertexAttribArray(Shaders::Attributes::Position);
|
||||
glVertexAttribPointer(Shaders::Attributes::Position, 3, GL_FLOAT, false,
|
||||
stride, nullptr);
|
||||
glEnableVertexAttribArray(Shaders::Attributes::TexCoord);
|
||||
glVertexAttribPointer(Shaders::Attributes::TexCoord, 2, GL_FLOAT, false,
|
||||
stride, offset);
|
||||
_vao->release();
|
||||
}
|
||||
|
||||
glm::vec2 Font::computeExtent(const QString & str) const {
|
||||
glm::vec2 advance(0, _rowHeight - _descent);
|
||||
float maxX = 0;
|
||||
foreach(QString token, str.split(" ")) {
|
||||
foreach(QChar c, token) {
|
||||
if (QChar('\n') == c) {
|
||||
maxX = std::max(advance.x, maxX);
|
||||
advance.x = 0;
|
||||
advance.y += _rowHeight;
|
||||
continue;
|
||||
}
|
||||
if (!_glyphs.contains(c)) {
|
||||
c = QChar('?');
|
||||
}
|
||||
const Glyph & m = _glyphs[c];
|
||||
advance.x += m.d;
|
||||
glm::vec2 advance(0, _rowHeight - _descent);
|
||||
float maxX = 0;
|
||||
foreach(QString token, str.split(" ")) {
|
||||
foreach(QChar c, token) {
|
||||
if (QChar('\n') == c) {
|
||||
maxX = std::max(advance.x, maxX);
|
||||
advance.x = 0;
|
||||
advance.y += _rowHeight;
|
||||
continue;
|
||||
}
|
||||
if (!_glyphs.contains(c)) {
|
||||
c = QChar('?');
|
||||
}
|
||||
const Glyph & m = _glyphs[c];
|
||||
advance.x += m.d;
|
||||
}
|
||||
advance.x += _spaceWidth;
|
||||
}
|
||||
advance.x += _spaceWidth;
|
||||
}
|
||||
return glm::vec2(maxX, advance.y);
|
||||
return glm::vec2(maxX, advance.y);
|
||||
}
|
||||
|
||||
// FIXME support the maxWidth parameter and allow the text to automatically wrap
|
||||
// even without explicit line feeds.
|
||||
glm::vec2 Font::drawString(
|
||||
float x, float y,
|
||||
const QString & str,
|
||||
const glm::vec4& color,
|
||||
TextRenderer::EffectType effectType,
|
||||
float maxWidth) const {
|
||||
glm::vec2 Font::drawString(float x, float y, const QString & str,
|
||||
const glm::vec4& color, TextRenderer::EffectType effectType,
|
||||
float maxWidth) const {
|
||||
|
||||
// Stores how far we've moved from the start of the string, in DTP units
|
||||
glm::vec2 advance(0, -_rowHeight - _descent);
|
||||
// Stores how far we've moved from the start of the string, in DTP units
|
||||
glm::vec2 advance(0, -_rowHeight - _descent);
|
||||
|
||||
_program->bind();
|
||||
_program->setUniformValue("Color", color.r, color.g, color.b, color.a);
|
||||
_program->setUniformValue("Projection", fromGlm(MatrixStack::projection().top()));
|
||||
if (effectType == TextRenderer::OUTLINE_EFFECT) {
|
||||
_program->setUniformValue("Outline", true);
|
||||
}
|
||||
_texture->bind();
|
||||
_vao->bind();
|
||||
|
||||
MatrixStack & mv = MatrixStack::modelview();
|
||||
// scale the modelview into font units
|
||||
mv.translate(glm::vec3(0, _ascent, 0));
|
||||
foreach(QString token, str.split(" ")) {
|
||||
// float tokenWidth = measureWidth(token, fontSize);
|
||||
// if (wrap && 0 != advance.x && (advance.x + tokenWidth) > maxWidth) {
|
||||
// advance.x = 0;
|
||||
// advance.y -= _rowHeight;
|
||||
// }
|
||||
|
||||
foreach(QChar c, token) {
|
||||
if (QChar('\n') == c) {
|
||||
advance.x = 0;
|
||||
advance.y -= _rowHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_glyphs.contains(c)) {
|
||||
c = QChar('?');
|
||||
}
|
||||
|
||||
// get metrics for this character to speed up measurements
|
||||
const Glyph & m = _glyphs[c];
|
||||
//if (wrap && ((advance.x + m.d) > maxWidth)) {
|
||||
// advance.x = 0;
|
||||
// advance.y -= _rowHeight;
|
||||
//}
|
||||
|
||||
// We create an offset vec2 to hold the local offset of this character
|
||||
// This includes compensating for the inverted Y axis of the font
|
||||
// coordinates
|
||||
glm::vec2 offset(advance);
|
||||
offset.y -= m.size.y;
|
||||
// Bind the new position
|
||||
mv.withPush([&] {
|
||||
mv.translate(offset);
|
||||
// FIXME find a better (and GL ES 3.1 compatible) way of rendering the text
|
||||
// that doesn't involve a single GL call per character.
|
||||
// Most likely an 'indirect' call or an 'instanced' call.
|
||||
_program->setUniformValue("ModelView", fromGlm(mv.top()));
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)(m.indexOffset));
|
||||
});
|
||||
advance.x += m.d;
|
||||
_program->bind();
|
||||
_program->setUniformValue("Color", color.r, color.g, color.b, color.a);
|
||||
_program->setUniformValue("Projection",
|
||||
fromGlm(MatrixStack::projection().top()));
|
||||
if (effectType == TextRenderer::OUTLINE_EFFECT) {
|
||||
_program->setUniformValue("Outline", true);
|
||||
}
|
||||
advance.x += _spaceWidth;
|
||||
}
|
||||
_texture->bind();
|
||||
_vao->bind();
|
||||
|
||||
_vao->release();
|
||||
_program->release();
|
||||
return advance;
|
||||
MatrixStack & mv = MatrixStack::modelview();
|
||||
// scale the modelview into font units
|
||||
mv.translate(glm::vec3(0, _ascent, 0));
|
||||
foreach(QString token, str.split(" ")) {
|
||||
// float tokenWidth = measureWidth(token, fontSize);
|
||||
// if (wrap && 0 != advance.x && (advance.x + tokenWidth) > maxWidth) {
|
||||
// advance.x = 0;
|
||||
// advance.y -= _rowHeight;
|
||||
// }
|
||||
|
||||
foreach(QChar c, token) {
|
||||
if (QChar('\n') == c) {
|
||||
advance.x = 0;
|
||||
advance.y -= _rowHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_glyphs.contains(c)) {
|
||||
c = QChar('?');
|
||||
}
|
||||
|
||||
// get metrics for this character to speed up measurements
|
||||
const Glyph & m = _glyphs[c];
|
||||
//if (wrap && ((advance.x + m.d) > maxWidth)) {
|
||||
// advance.x = 0;
|
||||
// advance.y -= _rowHeight;
|
||||
//}
|
||||
|
||||
// We create an offset vec2 to hold the local offset of this character
|
||||
// This includes compensating for the inverted Y axis of the font
|
||||
// coordinates
|
||||
glm::vec2 offset(advance);
|
||||
offset.y -= m.size.y;
|
||||
// Bind the new position
|
||||
mv.withPush([&] {
|
||||
mv.translate(offset);
|
||||
// FIXME find a better (and GL ES 3.1 compatible) way of rendering the text
|
||||
// that doesn't involve a single GL call per character.
|
||||
// Most likely an 'indirect' call or an 'instanced' call.
|
||||
_program->setUniformValue("ModelView", fromGlm(mv.top()));
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)(m.indexOffset));
|
||||
});
|
||||
advance.x += m.d;
|
||||
}
|
||||
advance.x += _spaceWidth;
|
||||
}
|
||||
|
||||
_vao->release();
|
||||
_program->release();
|
||||
return advance;
|
||||
}
|
||||
|
||||
TextRenderer* TextRenderer::getInstance(const char* family, float pointSize, int weight, bool italic,
|
||||
EffectType effect, int effectThickness, const QColor& color) {
|
||||
if (pointSize < 0) {
|
||||
pointSize = DEFAULT_POINT_SIZE;
|
||||
}
|
||||
return new TextRenderer(family, pointSize, weight, italic, effect, effectThickness, color);
|
||||
TextRenderer* TextRenderer::getInstance(const char* family, float pointSize,
|
||||
int weight, bool italic, EffectType effect, int effectThickness,
|
||||
const QColor& color) {
|
||||
if (pointSize < 0) {
|
||||
pointSize = DEFAULT_POINT_SIZE;
|
||||
}
|
||||
return new TextRenderer(family, pointSize, weight, italic, effect,
|
||||
effectThickness, color);
|
||||
}
|
||||
|
||||
TextRenderer::TextRenderer(const char* family, float pointSize, int weight, bool italic,
|
||||
EffectType effect, int effectThickness, const QColor& color) :
|
||||
_effectType(effect),
|
||||
_pointSize(pointSize),
|
||||
_effectThickness(effectThickness),
|
||||
_color(color),
|
||||
_font(loadFont(family))
|
||||
{
|
||||
|
||||
TextRenderer::TextRenderer(const char* family, float pointSize, int weight,
|
||||
bool italic, EffectType effect, int effectThickness,
|
||||
const QColor& color) :
|
||||
_effectType(effect), _pointSize(pointSize), _effectThickness(
|
||||
effectThickness), _color(color), _font(loadFont(family)) {
|
||||
|
||||
}
|
||||
|
||||
TextRenderer::~TextRenderer() {
|
||||
}
|
||||
|
||||
glm::vec2 TextRenderer::computeExtent(const QString & str) const {
|
||||
float scale = (_pointSize / DEFAULT_POINT_SIZE) * 0.25f;
|
||||
return _font->computeExtent(str) * scale;
|
||||
float scale = (_pointSize / DEFAULT_POINT_SIZE) * 0.25f;
|
||||
return _font->computeExtent(str) * scale;
|
||||
}
|
||||
|
||||
float TextRenderer::draw(
|
||||
float x, float y,
|
||||
const QString & str,
|
||||
const glm::vec4& color,
|
||||
float maxWidth) {
|
||||
glm::vec4 actualColor(color);
|
||||
if (actualColor.r < 0) {
|
||||
actualColor = glm::vec4(_color.redF(), _color.greenF(), _color.blueF(), 1.0);
|
||||
}
|
||||
float TextRenderer::draw(float x, float y, const QString & str,
|
||||
const glm::vec4& color, float maxWidth) {
|
||||
glm::vec4 actualColor(color);
|
||||
if (actualColor.r < 0) {
|
||||
actualColor = glm::vec4(_color.redF(), _color.greenF(), _color.blueF(),
|
||||
1.0);
|
||||
}
|
||||
|
||||
float scale = (_pointSize / DEFAULT_POINT_SIZE) * 0.25f;
|
||||
glm::vec2 result;
|
||||
MatrixStack::withGlMatrices([&] {
|
||||
MatrixStack & mv = MatrixStack::modelview();
|
||||
// scale the modelview into font units
|
||||
// FIXME migrate the constant scale factor into the geometry of the
|
||||
// fonts so we don't have to flip the Y axis here and don't have to
|
||||
// scale at all.
|
||||
mv.translate(glm::vec2(x, y)).scale(glm::vec3(scale, -scale, scale));
|
||||
// The font does all the OpenGL work
|
||||
result = _font->drawString(x, y, str, actualColor, _effectType, maxWidth);
|
||||
});
|
||||
return result.x;
|
||||
float scale = (_pointSize / DEFAULT_POINT_SIZE) * 0.25f;
|
||||
glm::vec2 result;
|
||||
MatrixStack::withGlMatrices([&] {
|
||||
MatrixStack & mv = MatrixStack::modelview();
|
||||
// scale the modelview into font units
|
||||
// FIXME migrate the constant scale factor into the geometry of the
|
||||
// fonts so we don't have to flip the Y axis here and don't have to
|
||||
// scale at all.
|
||||
mv.translate(glm::vec2(x, y)).scale(glm::vec3(scale, -scale, scale));
|
||||
// The font does all the OpenGL work
|
||||
result = _font->drawString(x, y, str, actualColor, _effectType, maxWidth);
|
||||
});
|
||||
return result.x;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ set(TARGET_NAME shared)
|
|||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
# TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp)
|
||||
setup_hifi_library(Network Script Widgets)
|
||||
setup_hifi_library(Gui Network Script Widgets)
|
||||
|
||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||
include_dependency_includes()
|
|
@ -302,4 +302,32 @@ bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, f
|
|||
// Compute the distance between the two points
|
||||
float positionDistance = glm::distance(positionA, positionB);
|
||||
return (positionDistance <= similarEnough);
|
||||
}
|
||||
}
|
||||
|
||||
glm::uvec2 toGlm(const QSize & size) {
|
||||
return glm::uvec2(size.width(), size.height());
|
||||
}
|
||||
|
||||
glm::ivec2 toGlm(const QPoint & pt) {
|
||||
return glm::ivec2(pt.x(), pt.y());
|
||||
}
|
||||
|
||||
glm::vec2 toGlm(const QPointF & pt) {
|
||||
return glm::vec2(pt.x(), pt.y());
|
||||
}
|
||||
|
||||
glm::vec3 toGlm(const xColor & color) {
|
||||
static const float MAX_COLOR = 255.0f;
|
||||
return std::move(glm::vec3(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR));
|
||||
}
|
||||
|
||||
QMatrix4x4 fromGlm(const glm::mat4 & m) {
|
||||
return QMatrix4x4(&m[0][0]).transposed();
|
||||
}
|
||||
|
||||
QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) {
|
||||
QRectF result(pos.x, pos.y, size.x, size.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtGui/QMatrix4x4>
|
||||
|
||||
#include "SharedUtil.h"
|
||||
|
||||
|
@ -86,5 +87,13 @@ bool isSimilarOrientation(const glm::quat& orientionA, const glm::quat& orientio
|
|||
const float POSITION_SIMILAR_ENOUGH = 0.1f; // 0.1 meter
|
||||
bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, float similarEnough = POSITION_SIMILAR_ENOUGH);
|
||||
|
||||
glm::uvec2 toGlm(const QSize & size);
|
||||
glm::ivec2 toGlm(const QPoint & pt);
|
||||
glm::vec2 toGlm(const QPointF & pt);
|
||||
glm::vec3 toGlm(const xColor & color);
|
||||
|
||||
#endif // hifi_GLMHelpers_h
|
||||
QMatrix4x4 fromGlm(const glm::mat4 & m);
|
||||
|
||||
QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size);
|
||||
|
||||
#endif // hifi_GLMHelpers_h
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <QOpenGLContext>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLWindow>
|
||||
#include <QResizeEvent>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
|
@ -31,147 +30,153 @@
|
|||
#include <PathUtils.h>
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class QTestWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
QOpenGLContext * _context;
|
||||
QSize _size;
|
||||
TextRenderer* _textRenderer[4];
|
||||
class QTestWindow: public QWindow {
|
||||
Q_OBJECT
|
||||
QOpenGLContext * _context;
|
||||
QSize _size;
|
||||
TextRenderer* _textRenderer[4];
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent * ev) override {
|
||||
QWindow::resizeEvent(ev);
|
||||
_size = ev->size();
|
||||
resizeGl();
|
||||
}
|
||||
void resizeEvent(QResizeEvent * ev) override {
|
||||
QWindow::resizeEvent(ev);
|
||||
_size = ev->size();
|
||||
resizeGl();
|
||||
}
|
||||
|
||||
void resizeGl() {
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, _size.width(), _size.height(), 0, 1, -1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glViewport(0, 0, _size.width(), _size.height());
|
||||
}
|
||||
void resizeGl() {
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, _size.width(), _size.height(), 0, 1, -1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glViewport(0, 0, _size.width(), _size.height());
|
||||
}
|
||||
|
||||
public:
|
||||
QTestWindow();
|
||||
virtual ~QTestWindow() {
|
||||
QTestWindow();
|
||||
virtual ~QTestWindow() {
|
||||
|
||||
}
|
||||
void makeCurrent() {
|
||||
_context->makeCurrent(this);
|
||||
}
|
||||
}
|
||||
void makeCurrent() {
|
||||
_context->makeCurrent(this);
|
||||
}
|
||||
|
||||
void draw();
|
||||
void draw();
|
||||
};
|
||||
|
||||
QTestWindow::QTestWindow() {
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
|
||||
QSurfaceFormat format;
|
||||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||
format.setDepthBufferSize(16);
|
||||
format.setStencilBufferSize(8);
|
||||
format.setVersion(4, 3);
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
|
||||
setFormat(format);
|
||||
QSurfaceFormat format;
|
||||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||
format.setDepthBufferSize(16);
|
||||
format.setStencilBufferSize(8);
|
||||
format.setVersion(3, 2);
|
||||
format.setProfile(
|
||||
QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
|
||||
setFormat(format);
|
||||
|
||||
_context = new QOpenGLContext;
|
||||
_context->setFormat(format);
|
||||
_context->create();
|
||||
_context = new QOpenGLContext;
|
||||
_context->setFormat(format);
|
||||
_context->create();
|
||||
|
||||
show();
|
||||
makeCurrent();
|
||||
show();
|
||||
makeCurrent();
|
||||
qDebug() << (const char*)glGetString(GL_VERSION);
|
||||
|
||||
#ifdef WIN32
|
||||
glewExperimental = true;
|
||||
GLenum err = glewInit();
|
||||
if (GLEW_OK != err) {
|
||||
/* Problem: glewInit failed, something is seriously wrong. */
|
||||
const GLubyte * errStr = glewGetErrorString(err);
|
||||
qDebug("Error: %s\n", errStr);
|
||||
}
|
||||
qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
|
||||
glewExperimental = true;
|
||||
GLenum err = glewInit();
|
||||
if (GLEW_OK != err) {
|
||||
/* Problem: glewInit failed, something is seriously wrong. */
|
||||
const GLubyte * errStr = glewGetErrorString(err);
|
||||
qDebug("Error: %s\n", errStr);
|
||||
}
|
||||
qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
|
||||
|
||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||
int swapInterval = wglGetSwapIntervalEXT();
|
||||
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
||||
}
|
||||
glGetError();
|
||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||
int swapInterval = wglGetSwapIntervalEXT();
|
||||
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
||||
}
|
||||
glGetError();
|
||||
#endif
|
||||
|
||||
setFramePosition(QPoint(100, -900));
|
||||
resize(QSize(800, 600));
|
||||
_size = QSize(800, 600);
|
||||
setFramePosition(QPoint(100, -900));
|
||||
resize(QSize(800, 600));
|
||||
_size = QSize(800, 600);
|
||||
|
||||
_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false);
|
||||
_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false, TextRenderer::SHADOW_EFFECT);
|
||||
_textRenderer[2] = TextRenderer::getInstance(MONO_FONT_FAMILY, 48, -1, false, TextRenderer::OUTLINE_EFFECT);
|
||||
_textRenderer[3] = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, 24);
|
||||
_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false);
|
||||
_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false,
|
||||
TextRenderer::SHADOW_EFFECT);
|
||||
_textRenderer[2] = TextRenderer::getInstance(MONO_FONT_FAMILY, 48, -1,
|
||||
false, TextRenderer::OUTLINE_EFFECT);
|
||||
_textRenderer[3] = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, 24);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glClearColor(0.2f, 0.2f, 0.2f, 1);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
resizeGl();
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glClearColor(0.2f, 0.2f, 0.2f, 1);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
resizeGl();
|
||||
}
|
||||
|
||||
|
||||
static const wchar_t * EXAMPLE_TEXT = L"\xC1y Hello 1.0\ny\xC1 line 2\n\xC1y";
|
||||
static const glm::uvec2 QUAD_OFFSET(10, 10);
|
||||
|
||||
static const glm::vec3 COLORS[4] = {
|
||||
{ 1.0, 1.0, 1.0 },
|
||||
{ 0.5, 1.0, 0.5 },
|
||||
{ 1.0, 0.5, 0.5 },
|
||||
{ 0.5, 0.5, 1.0 },
|
||||
{ 1.0, 1.0, 1.0 },
|
||||
{ 0.5, 1.0, 0.5 },
|
||||
{ 1.0, 0.5, 0.5 },
|
||||
{ 0.5, 0.5, 1.0 }
|
||||
};
|
||||
|
||||
void QTestWindow::draw() {
|
||||
makeCurrent();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
makeCurrent();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
const glm::uvec2 size = glm::uvec2(_size.width() / 2, _size.height() / 2);
|
||||
const glm::uvec2 offsets[4] = {
|
||||
{ QUAD_OFFSET.x, QUAD_OFFSET.y },
|
||||
{ size.x + QUAD_OFFSET.x, QUAD_OFFSET.y },
|
||||
{ size.x + QUAD_OFFSET.x, size.y + QUAD_OFFSET.y },
|
||||
{ QUAD_OFFSET.x, size.y + QUAD_OFFSET.y },
|
||||
};
|
||||
const glm::uvec2 size = glm::uvec2(_size.width() / 2, _size.height() / 2);
|
||||
const glm::uvec2 offsets[4] = { { QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x
|
||||
+ QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x + QUAD_OFFSET.x, size.y
|
||||
+ QUAD_OFFSET.y }, { QUAD_OFFSET.x, size.y + QUAD_OFFSET.y }, };
|
||||
|
||||
QString str = QString::fromWCharArray(EXAMPLE_TEXT);
|
||||
QString str = QString::fromWCharArray(EXAMPLE_TEXT);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
glm::vec2 bounds = _textRenderer[i]->computeExtent(str);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
glm::vec2 bounds = _textRenderer[i]->computeExtent(str);
|
||||
|
||||
// Draw backgrounds around where the text will appear
|
||||
glPushMatrix(); {
|
||||
glTranslatef(offsets[i].x, offsets[i].y, 0);
|
||||
glColor3f(0, 0, 0);
|
||||
glBegin(GL_QUADS); {
|
||||
glVertex2f(0, 0);
|
||||
glVertex2f(0, bounds.y);
|
||||
glVertex2f(bounds.x, bounds.y);
|
||||
glVertex2f(bounds.x, 0);
|
||||
} glEnd();
|
||||
} glPopMatrix();
|
||||
// Draw backgrounds around where the text will appear
|
||||
glPushMatrix();
|
||||
{
|
||||
glTranslatef(offsets[i].x, offsets[i].y, 0);
|
||||
glColor3f(0, 0, 0);
|
||||
glBegin(GL_QUADS);
|
||||
{
|
||||
glVertex2f(0, 0);
|
||||
glVertex2f(0, bounds.y);
|
||||
glVertex2f(bounds.x, bounds.y);
|
||||
glVertex2f(bounds.x, 0);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
// Draw the text itself
|
||||
_textRenderer[i]->draw(offsets[i].x, offsets[i].y, str, glm::vec4(COLORS[i], 1.0f));
|
||||
}
|
||||
// Draw the text itself
|
||||
_textRenderer[i]->draw(offsets[i].x, offsets[i].y, str,
|
||||
glm::vec4(COLORS[i], 1.0f));
|
||||
}
|
||||
|
||||
_context->swapBuffers(this);
|
||||
_context->swapBuffers(this);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
QTestWindow window;
|
||||
QTimer timer;
|
||||
timer.setInterval(10);
|
||||
app.connect(&timer, &QTimer::timeout, &app, [&] {
|
||||
window.draw();
|
||||
});
|
||||
timer.start();
|
||||
app.exec();
|
||||
return 0;
|
||||
QApplication app(argc, argv);
|
||||
QTestWindow window;
|
||||
QTimer timer;
|
||||
timer.setInterval(10);
|
||||
app.connect(&timer, &QTimer::timeout, &app, [&] {
|
||||
window.draw();
|
||||
});
|
||||
timer.start();
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "main.moc"
|
||||
|
|
Loading…
Reference in a new issue