Skip to content

Commit

Permalink
Merge pull request #160 from mcjohnalds/text-annotations
Browse files Browse the repository at this point in the history
Added billboard text annotations
  • Loading branch information
c42f authored Feb 24, 2017
2 parents 45adf5b + 99a8567 commit c328ed3
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 9 deletions.
49 changes: 49 additions & 0 deletions shaders/annotation.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#version 150
// Copyright 2015, Christopher J. Foster and the other displaz contributors.
// Use of this code is governed by the BSD-style license found in LICENSE.txt

// Draws annotations, which are textured quads that always face the camera,
// arent't obscured by other objects, and don't shrink when they're further
// away.

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 modelViewProjectionMatrix;
uniform vec2 annotationSize; // In pixels
uniform vec2 viewportSize; // In pixels

//------------------------------------------------------------------------------
#if defined(VERTEX_SHADER)

// Position of the current quad vertex. The bottom-left vertex is at (-1,-1)
// and the top-right at (1,1).
in vec2 position;
in vec2 texCoord;

out vec2 ftexCoord;

void main()
{
gl_Position = modelViewProjectionMatrix * vec4(0, 0, 0, 1.0);
gl_Position /= gl_Position.w;
gl_Position.xy += position * annotationSize / viewportSize;
ftexCoord = texCoord;
}


//------------------------------------------------------------------------------
#elif defined(FRAGMENT_SHADER)

uniform sampler2D texture0;

in vec2 ftexCoord;

// Output fragment color
out vec4 fragColor;

void main()
{
fragColor = texture(texture0, vec2(ftexCoord));
}

#endif
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ set(gui_srcs
render/View3D.cpp
render/Shader.cpp
render/GeometryMutator.cpp
render/Annotation.cpp

../thirdparty/rply/rply.c
../thirdparty/polypartition/polypartition.cpp
Expand Down
1 change: 0 additions & 1 deletion src/geometrycollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class GeometryCollection : public QAbstractListModel
void addGeometry(std::shared_ptr<Geometry> geom, bool replaceLabel = false, bool reloaded = false);
void mutateGeometry(std::shared_ptr<GeometryMutator> mutator);


private:
void loadPointFilesImpl(const QStringList& fileNames, bool removeAfterLoad);
int findMatchingRow(const QRegExp & filenameRegex);
Expand Down
27 changes: 27 additions & 0 deletions src/gui/PointViewerMainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ PointViewerMainWindow::PointViewerMainWindow(const QGLFormat& format)
QAction* drawGrid = viewMenu->addAction(tr("Draw &Grid"));
drawGrid->setCheckable(true);
drawGrid->setChecked(false);
QAction* drawAnnotations = viewMenu->addAction(tr("Draw A&nnotations"));
drawAnnotations->setCheckable(true);
drawAnnotations->setChecked(true);

// Shader menu
QMenu* shaderMenu = menuBar()->addMenu(tr("&Shader"));
Expand Down Expand Up @@ -193,6 +196,8 @@ PointViewerMainWindow::PointViewerMainWindow(const QGLFormat& format)
m_pointView, SLOT(toggleDrawAxes()));
connect(drawGrid, SIGNAL(triggered()),
m_pointView, SLOT(toggleDrawGrid()));
connect(drawAnnotations, SIGNAL(triggered()),
m_pointView, SLOT(toggleDrawAnnotations()));
connect(trackballMode, SIGNAL(triggered()),
m_pointView, SLOT(toggleCameraMode()));
connect(m_geometries, SIGNAL(rowsInserted(QModelIndex,int,int)),
Expand Down Expand Up @@ -400,6 +405,7 @@ void PointViewerMainWindow::handleMessage(QByteArray message)
return;
}
m_geometries->unloadFiles(regex);
m_pointView->removeAnnotations(regex);
}
else if (commandTokens[0] == "SET_VIEW_LABEL")
{
Expand All @@ -415,6 +421,27 @@ void PointViewerMainWindow::handleMessage(QByteArray message)
if (index.isValid())
m_pointView->centerOnGeometry(index);
}
else if (commandTokens[0] == "ANNOTATE")
{
if (commandTokens.size() - 1 != 5)
{
tfm::format(std::cerr, "Expected five arguments, got %d\n",
commandTokens.size() - 1);
return;
}
QString label = commandTokens[1];
QString text = commandTokens[2];
bool xOk = false, yOk = false, zOk = false;
double x = commandTokens[3].toDouble(&xOk);
double y = commandTokens[4].toDouble(&yOk);
double z = commandTokens[5].toDouble(&zOk);
if (!zOk || !yOk || !zOk)
{
std::cerr << "Could not parse XYZ coordinates for position\n";
return;
}
m_pointView->addAnnotation(label, text, Imath::V3d(x, y, z));
}
else if (commandTokens[0] == "SET_VIEW_POSITION")
{
if (commandTokens.size()-1 != 3)
Expand Down
27 changes: 22 additions & 5 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,17 @@ int main(int argc, char *argv[])
double viewRadius = -DBL_MAX;

std::string shaderName;
std::string unloadFiles;
std::string unloadRegex;
std::string viewLabelName;
bool noServer = false;

bool clearFiles = false;
bool addFiles = false;
bool mutateData = false;
std::string annotationText;
double annotationX = -DBL_MAX;
double annotationY = -DBL_MAX;
double annotationZ = -DBL_MAX;
bool deleteAfterLoad = false;
bool quitRemote = false;
bool queryCursor = false;
Expand Down Expand Up @@ -109,10 +113,11 @@ int main(int argc, char *argv[])
"This alternative to -viewangles is supplied to simplify "
"setting camera rotations from a script.",
"-clear", &clearFiles, "Remote: clear all currently loaded files",
"-unload %s", &unloadFiles, "Remote: unload loaded files matching the given (unix shell style) pattern",
"-unload %s", &unloadRegex, "Remote: unload loaded files or annotations who's label matches the given (unix shell style) pattern",
"-quit", &quitRemote, "Remote: close the existing displaz window",
"-add", &addFiles, "Remote: add files to currently open set, instead of replacing those with duplicate labels",
"-modify", &mutateData, "Remote: mutate data already loaded with the matching label (requires displaz .ply with an \"index\" field to indicate mutated points)",
"-annotation %s %F %F %F", &annotationText, &annotationX, &annotationY, &annotationZ, "Add a text annotation [text, x, y, z]",
"-rmtemp", &deleteAfterLoad, "*Delete* files after loading - use with caution to clean up single-use temporary files after loading",
"-querycursor", &queryCursor, "Query 3D cursor location from displaz instance",
"-script", &script, "Script mode: enable several settings which are useful when calling displaz from a script:"
Expand Down Expand Up @@ -175,7 +180,7 @@ int main(int argc, char *argv[])
std::cerr << "ERROR: No remote displaz instance found\n";
return EXIT_FAILURE;
}
if (quitRemote || !unloadFiles.empty())
if (quitRemote || !unloadRegex.empty())
{
return EXIT_SUCCESS;
}
Expand Down Expand Up @@ -244,9 +249,21 @@ int main(int argc, char *argv[])
}
channel->sendMessage(command);
}
if (!unloadFiles.empty())
if (annotationText != "" &&
annotationX != -DBL_MAX &&
annotationY != -DBL_MAX &&
annotationZ != -DBL_MAX)
{
channel->sendMessage(QByteArray("UNLOAD_FILES\n") + unloadFiles.c_str());
channel->sendMessage(QByteArray("ANNOTATE\n") +
g_dataSetLabel.c_str() + "\n" +
annotationText.c_str() + "\n" +
QByteArray().setNum(annotationX, 'e', 17) + "\n" +
QByteArray().setNum(annotationY, 'e', 17) + "\n" +
QByteArray().setNum(annotationZ, 'e', 17));
}
if (!unloadRegex.empty())
{
channel->sendMessage(QByteArray("UNLOAD_FILES\n") + unloadRegex.c_str());
}
if (!viewLabelName.empty())
{
Expand Down
122 changes: 122 additions & 0 deletions src/render/Annotation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2015, Christopher J. Foster and the other displaz contributors.
// Use of this code is governed by the BSD-style license found in LICENSE.txt

#include "Annotation.h"

#include <cmath>

#include <QFont>
#include <QFontMetrics>
#include <QColor>
#include <QImage>
#include <QPainter>

/// Creates and returns a texture containing some text.
static std::unique_ptr<Texture> makeTextureFromText(const QString& text)
{
QFont font("Sans", 13);
// Text outline makes the text thinner so we have to bold it
font.setWeight(QFont::DemiBold);
QFontMetrics fm(font);
int width = fm.width(text);
int height = fm.ascent() + fm.descent();
QImage image(width, height, QImage::Format_RGBA8888);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::TextAntialiasing, true);
painter.translate(0, height + fm.descent());
painter.scale(1, -1);
painter.setBrush(Qt::white);
// Add a black outline for visibility when on a white background
QPen pen;
pen.setWidthF(1.2);
pen.setColor(Qt::black);
painter.setPen(pen);
QPainterPath path;
path.addText(0, height, font, text);
painter.drawPath(path);
std::unique_ptr<Texture> texture(new Texture(image));
// The text is blurry when using GL_LINEAR. I suspect this is a result of
// floating point inaccuracies in the vertex shader.
texture->setResizeFilter(GL_NEAREST);
return texture;
}

/// Creates a texturable rectangle VAO and returns it's name.
static GLuint makeVAO(GLuint annotationShaderProg)
{
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// Setup buffer
GlBuffer buffer;
buffer.bind(GL_ARRAY_BUFFER);
float data[] = { // x, y, s, t
-1, -1, 0, 0, // bottom-left
1, -1, 1, 0, // bottom-right
-1, 1, 0, 1, // top-left
1, 1, 1, 1, }; // top-right
size_t rowSize = sizeof (float) * 4;
size_t xyzOffset = 0;
size_t stOffset = sizeof (float) * 2;
glBufferData(GL_ARRAY_BUFFER, sizeof data, data, GL_STATIC_DRAW);

// Setup attributes
GLint positionAttribute = glGetAttribLocation(annotationShaderProg, "position");
glVertexAttribPointer(positionAttribute,
2,
GL_FLOAT,
GL_FALSE,
rowSize,
(void*) xyzOffset);
glEnableVertexAttribArray(positionAttribute);

GLint texCoordAttribute = glGetAttribLocation(annotationShaderProg, "texCoord");
glVertexAttribPointer(texCoordAttribute,
2,
GL_FLOAT,
GL_FALSE,
rowSize,
(void*) stOffset);
glEnableVertexAttribArray(texCoordAttribute);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
return vao;
}

Annotation::Annotation(const QString& label,
GLuint annotationShaderProg,
const QString& text,
Imath::V3d position)
: m_label(label),
m_text(text),
m_position(position),
m_texture(makeTextureFromText(text)),
m_vao(makeVAO(annotationShaderProg))
{
}

Annotation::~Annotation()
{
glDeleteVertexArrays(1, &m_vao);
}

void Annotation::draw(QGLShaderProgram& annotationShaderProg,
const TransformState& transState) const
{
glBindVertexArray(m_vao);
GLint texture0 = glGetUniformLocation(annotationShaderProg.programId(),
"texture0");
m_texture->bind(texture0);
annotationShaderProg.setUniformValue("annotationSize",
m_texture->width(),
m_texture->height());
transState.translate(m_position)
.setUniforms(annotationShaderProg.programId());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
glBindVertexArray(0);
}
47 changes: 47 additions & 0 deletions src/render/Annotation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2015, Christopher J. Foster and the other displaz contributors.
// Use of this code is governed by the BSD-style license found in LICENSE.txt

#ifndef DISPLAZ_ANNOTATION_H_INCLUDED
#define DISPLAZ_ANNOTATION_H_INCLUDED

#include <memory>

#include "glutil.h"
#include <QString>
#include <QGLShaderProgram>

struct TransformState;

/// Annotations display text, always face the camera, and aren't obscured by
/// other objects
class Annotation
{

public:
Annotation(const QString& label,
GLuint annotationShaderProg,
const QString& text,
Imath::V3d position);

~Annotation();

QString label() const { return m_label; }

/// Draw the annotation using the given shader program
///
/// Requires that `annotationShaderProg` is already bound and that
/// the viewportSize uniform variable has been set.
///
/// transState specifies the camera transform.
void draw(QGLShaderProgram& annotationShaderProg,
const TransformState& transState) const;

private:
QString m_label;
QString m_text;
Imath::V3d m_position;
std::shared_ptr<Texture> m_texture;
GLuint m_vao;
};

#endif // DISPLAZ_ANNOTATION_H_INCLUDED
2 changes: 1 addition & 1 deletion src/render/Geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Geometry : public QObject
//--------------------------------------------------
/// Mutate a geometry
///
/// Apply a per-vertex modification of geometry data with the data in a
/// Apply a per-vertex modification of geometry data with the data in a
/// GeometryMutator, which keeps an index to a subset of vertices and
/// new data for a subset of fields. The number of vertices remains
/// constant.
Expand Down
Loading

0 comments on commit c328ed3

Please sign in to comment.