From 841ede5c34a5cb001b540ed13a8439dc8df7d2a9 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Sat, 19 Dec 2015 22:11:33 +0100 Subject: Experimenting using VoronoiSplat as a texture. --- main.cpp | 14 +-- main_view.qml | 14 +-- scatterplot.cpp | 118 ++++++++++++++------ scatterplot.h | 4 + voronoisplat.cpp | 326 ++++++++++++++++++++++++++++--------------------------- voronoisplat.h | 91 ++++------------ 6 files changed, 290 insertions(+), 277 deletions(-) diff --git a/main.cpp b/main.cpp index 64560d3..b9d7232 100644 --- a/main.cpp +++ b/main.cpp @@ -157,7 +157,6 @@ int main(int argc, char **argv) m->setSubsample(Ys); qmlRegisterType("PM", 1, 0, "Scatterplot"); - qmlRegisterType("PM", 1, 0, "VoronoiSplat"); qmlRegisterType("PM", 1, 0, "HistoryGraph"); qmlRegisterType("PM", 1, 0, "InteractionHandler"); qmlRegisterSingletonType
("PM", 1, 0, "Main", mainProvider); @@ -190,8 +189,7 @@ int main(int argc, char **argv) // subsamplePlot->setColorData(arma::zeros(subsampleSize)); subsamplePlot->setColorScale(&colorScale); Scatterplot *plot = engine.rootObjects()[0]->findChild("plot"); - VoronoiSplat *splat = engine.rootObjects()[0]->findChild("splat"); - skelft2DInitialization(splat->width()); + skelft2DInitialization(plot->width()); // Keep track of the current subsample (in order to save them later, if requested) QObject::connect(subsamplePlot, SIGNAL(xyChanged(const arma::mat &)), @@ -211,10 +209,10 @@ int main(int argc, char **argv) m->setTechnique(InteractionHandler::TECHNIQUE_LAMP); // Update splat whenever the main plot is also updated - QObject::connect(plot, SIGNAL(xyChanged(const arma::mat &)), - splat, SLOT(setPoints(const arma::mat &))); - QObject::connect(plot, SIGNAL(colorDataChanged(const arma::vec &)), - splat, SLOT(setValues(const arma::vec &))); + //QObject::connect(plot, SIGNAL(xyChanged(const arma::mat &)), + // splat, SLOT(setPoints(const arma::mat &))); + //QObject::connect(plot, SIGNAL(colorDataChanged(const arma::vec &)), + // splat, SLOT(setValues(const arma::vec &))); // Linking between selections in subsample plot and full dataset plot SelectionHandler selectionHandler(sampleIndices); @@ -252,7 +250,7 @@ int main(int argc, char **argv) subsamplePlot->setXY(Ys); subsamplePlot->setColorData(labels(sampleIndices)); plot->setColorScale(&colorScale); - splat->setColorScale(&colorScale); + //splat->setColorScale(&colorScale); plot->setColorData(labels); auto ret = app.exec(); diff --git a/main_view.qml b/main_view.qml index 62c3a67..9e43137 100644 --- a/main_view.qml +++ b/main_view.qml @@ -9,7 +9,7 @@ ApplicationWindow { id: mainWindow title: "Projection Manipulation" visible: true - minimumWidth: 900 + minimumWidth: 300 minimumHeight: 600 maximumWidth: minimumWidth maximumHeight: minimumHeight @@ -91,18 +91,12 @@ ApplicationWindow { border.width: 1 border.color: "#cccccc" - VoronoiSplat { - id: splat - objectName: "splat" - width: 256 - height: 256 - } - Scatterplot { id: plot objectName: "plot" - anchors.fill: parent - visible: false + width: 512 + height: 512 + anchors.margins: 10 } } } diff --git a/scatterplot.cpp b/scatterplot.cpp index 10e9765..b9d7aea 100644 --- a/scatterplot.cpp +++ b/scatterplot.cpp @@ -1,5 +1,6 @@ #include "scatterplot.h" +#include "voronoisplat.h" #include "geometry.h" #include @@ -104,6 +105,26 @@ void Scatterplot::updateMaterials() update(); } +QSGNode *Scatterplot::createSplatNode() +{ + if (m_xy.n_rows < 1) { + return 0; + } + + QSGSimpleTextureNode *node = new QSGSimpleTextureNode; + VoronoiSplatTexture *tex = new VoronoiSplatTexture(QSize(width(), height())); + tex->setSites(m_xy); + tex->setValues(m_colorData); + tex->setColormap(m_colorScale); + tex->updateTexture(); + window()->resetOpenGLState(); + node->setTexture(tex); + node->setOwnsTexture(true); + node->setRect(x(), y(), width(), height()); + node->setSourceRect(0, 0, width(), height()); + return node; +} + QSGNode *Scatterplot::createGlyphNodeTree() { if (m_xy.n_rows < 1) { @@ -153,6 +174,10 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) QSGNode *root = 0; if (!oldNode) { root = new QSGNode; + QSGNode *splatNode = createSplatNode(); + if (splatNode) { + root->appendChildNode(splatNode); + } QSGNode *glyphTreeRoot = createGlyphNodeTree(); if (glyphTreeRoot) { root->appendChildNode(glyphTreeRoot); @@ -165,6 +190,67 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) return root; } + QSGNode *splatNode = root->firstChild(); + updateSplat(splatNode); + + QSGNode *glyphsRootNode = root->firstChild()->nextSibling()->firstChild(); + updateGlyphs(glyphsRootNode); + + if (m_shouldUpdateGeometry) { + m_shouldUpdateGeometry = false; + } + if (m_shouldUpdateMaterials) { + m_shouldUpdateMaterials = false; + } + + // Selection rect + if (m_currentInteractionState == INTERACTION_SELECTING) { + QSGSimpleRectNode *selectionNode = 0; + if (!root->firstChild()->nextSibling()->nextSibling()) { + selectionNode = new QSGSimpleRectNode; + selectionNode->setColor(SELECTION_COLOR); + root->appendChildNode(selectionNode); + } else { + selectionNode = static_cast(root->firstChild()->nextSibling()->nextSibling()); + } + + selectionNode->setRect(QRectF(m_dragOriginPos, m_dragCurrentPos)); + selectionNode->markDirty(QSGNode::DirtyGeometry); + } else { + QSGNode *node = root->firstChild()->nextSibling()->nextSibling(); + if (node) { + root->removeChildNode(node); + delete node; + } + } + + animationTick(); + return root; +} + +void Scatterplot::updateSplat(QSGNode *node) +{ + QSGSimpleTextureNode *texNode = static_cast(node); + VoronoiSplatTexture *tex = + static_cast(texNode->texture()); + + if (m_shouldUpdateGeometry) { + tex->setSites(m_xy); + } + + if (m_shouldUpdateMaterials) { + tex->setValues(m_colorData); + tex->setColormap(m_colorScale); + } + + if (tex->updateTexture()) { + window()->resetOpenGLState(); + } +} + +void Scatterplot::updateGlyphs(QSGNode *node) +{ + qreal x, y, tx, ty, moveTranslationF; if (m_currentInteractionState == INTERACTION_MOVING) { @@ -177,7 +263,6 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) m_sx.setRange(PADDING, width() - PADDING); m_sy.setRange(height() - PADDING, PADDING); - QSGNode *node = root->firstChild()->firstChild(); float t = m_animationEasing.valueForProgress(m_t); for (arma::uword i = 0; i < m_xy.n_rows; i++) { const arma::rowvec &oldRow = m_oldXY.row(i); @@ -211,37 +296,6 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) node = node->nextSibling(); } - - if (m_shouldUpdateGeometry) { - m_shouldUpdateGeometry = false; - } - if (m_shouldUpdateMaterials) { - m_shouldUpdateMaterials = false; - } - - // Selection rect - if (m_currentInteractionState == INTERACTION_SELECTING) { - QSGSimpleRectNode *selectionNode = 0; - if (!root->firstChild()->nextSibling()) { - selectionNode = new QSGSimpleRectNode; - selectionNode->setColor(SELECTION_COLOR); - root->appendChildNode(selectionNode); - } else { - selectionNode = static_cast(root->firstChild()->nextSibling()); - } - - selectionNode->setRect(QRectF(m_dragOriginPos, m_dragCurrentPos)); - selectionNode->markDirty(QSGNode::DirtyGeometry); - } else { - node = root->firstChild()->nextSibling(); - if (node) { - root->removeChildNode(node); - delete node; - } - } - - animationTick(); - return root; } void Scatterplot::resetAnimation() diff --git a/scatterplot.h b/scatterplot.h index 255649a..2887752 100644 --- a/scatterplot.h +++ b/scatterplot.h @@ -38,6 +38,7 @@ protected: void mouseReleaseEvent(QMouseEvent *event); private: + QSGNode *createSplatNode(); QSGNode *createGlyphNodeTree(); bool updateSelection(bool mergeSelection); @@ -46,6 +47,9 @@ private: void updateGeometry(); void updateMaterials(); + void updateSplat(QSGNode *node); + void updateGlyphs(QSGNode *node); + void resetAnimation(); void startAnimation(); void animationTick(); diff --git a/voronoisplat.cpp b/voronoisplat.cpp index da30d3c..7695d97 100644 --- a/voronoisplat.cpp +++ b/voronoisplat.cpp @@ -10,18 +10,33 @@ static const float RAD_BLUR = 5.0f; static const int COLORMAP_SAMPLES = 128; +static const float PADDING = 10.0f; // in screen pixels -VoronoiSplatRenderer::VoronoiSplatRenderer(const QSize &size) - : m_item(0) - , gl(QOpenGLContext::currentContext()) - , m_size(size) +static int nextPow2(int n) { + // TODO: check for overflows + n--; + for (int shift = 1; ((n + 1) & n); shift <<= 1) { + n |= n >> shift; + } + return n + 1; +} + +VoronoiSplatTexture::VoronoiSplatTexture(const QSize &size) + : gl(QOpenGLContext::currentContext()) + , m_cmap(3*COLORMAP_SAMPLES) +{ + int baseSize = nextPow2(std::min(size.width(), size.height())); + m_size.setWidth(baseSize); + m_size.setHeight(baseSize); + + gl.glGenFramebuffers(1, &m_FBO); setupShaders(); setupVAOs(); setupTextures(); } -void VoronoiSplatRenderer::setupShaders() +void VoronoiSplatTexture::setupShaders() { m_program1 = new QOpenGLShaderProgram; m_program1->addShaderFromSourceCode(QOpenGLShader::Vertex, @@ -116,7 +131,7 @@ void main() { m_program2->link(); } -void VoronoiSplatRenderer::setupVAOs() +void VoronoiSplatTexture::setupVAOs() { gl.glGenBuffers(3, m_VBOs); @@ -148,7 +163,7 @@ void VoronoiSplatRenderer::setupVAOs() m_2ndPassVAO.release(); } -void VoronoiSplatRenderer::setupTextures() +void VoronoiSplatTexture::setupTextures() { gl.glGenTextures(2, m_textures); @@ -167,162 +182,59 @@ void VoronoiSplatRenderer::setupTextures() gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Used for colormap lookup in the frag shader - gl.glGenTextures(1, &m_colorMapTex); - gl.glBindTexture(GL_TEXTURE_2D, m_colorMapTex); + gl.glGenTextures(1, &m_colormapTex); + gl.glBindTexture(GL_TEXTURE_2D, m_colormapTex); gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, COLORMAP_SAMPLES, 1, 0, GL_RGB, GL_FLOAT, 0); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // The output texture + gl.glGenTextures(1, &m_tex); + gl.glBindTexture(GL_TEXTURE_2D, m_tex); + gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_size.width(), + m_size.height(), 0, GL_RGBA, GL_FLOAT, 0); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } -VoronoiSplatRenderer::~VoronoiSplatRenderer() +VoronoiSplatTexture::~VoronoiSplatTexture() { gl.glDeleteBuffers(3, m_VBOs); gl.glDeleteTextures(2, m_textures); - gl.glDeleteTextures(1, &m_colorMapTex); + gl.glDeleteTextures(1, &m_colormapTex); + gl.glDeleteTextures(1, &m_tex); + + gl.glDeleteFramebuffers(1, &m_FBO); delete m_program1; delete m_program2; } -void VoronoiSplatRenderer::setSites(const arma::mat &points) -{ - if (points.n_rows < 1 || points.n_cols != 2 - || (m_values.size() != 0 && points.n_rows != m_values.size())) { - return; - } - - // Copy 'points' to internal data structure(s) - copyPoints(points); - - // Update VBO with the new data - gl.glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[0]); - gl.glBufferData(GL_ARRAY_BUFFER, m_sites.size() * sizeof(float), - m_sites.data(), GL_STATIC_DRAW); - - // Compute DT values for the new positions - computeDT(); -} - -void VoronoiSplatRenderer::copyPoints(const arma::mat &points) -{ - double minX = points.col(0).min(); - double maxX = points.col(0).max(); - double minY = points.col(1).min(); - double maxY = points.col(1).max(); - - // Coords are tighly packed into 'm_sites' as [ (x1, y1), (x2, y2), ... ] - m_sites.resize(2*points.n_rows); - LinearScale sx(minX, maxX, 1, m_size.width() - 1); - const double *col = points.colptr(0); - for (unsigned i = 0; i < points.n_rows; i++) { - m_sites[2*i] = sx(col[i]); - } - - col = points.colptr(1); - LinearScale sy(minY, maxY, m_size.height() - 1, 1); - for (unsigned i = 0; i < points.n_rows; i++) { - m_sites[2*i + 1] = sy(col[i]); - } -} - -void VoronoiSplatRenderer::computeDT() -{ - int w = m_size.width(), h = m_size.height(); - - std::vector buf(w*h, 0.0f); - for (unsigned i = 0; i < m_sites.size(); i += 2) { - buf[int(m_sites[i + 1])*h + int(m_sites[i])] = (float) i/2 + 1; - } - - // Compute FT of the sites - skelft2DFT(0, buf.data(), 0, 0, w, h, w); - // Compute DT of the sites (from the resident FT) - skelft2DDT(buf.data(), 0, 0, w, h); - - std::vector dtTexData(2*w*h, 0.0f); - for (unsigned i = 0; i < buf.size(); i++) { - dtTexData[2*i] = buf[i]; - } - - // Upload result to lookup texture - gl.glBindTexture(GL_TEXTURE_2D, m_textures[0]); - gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RG, GL_FLOAT, - dtTexData.data()); -} - -void VoronoiSplatRenderer::setValues(const arma::vec &values) +void VoronoiSplatTexture::bind() { - if (values.n_elem == 0 - || (m_sites.size() != 0 && values.n_elem != m_sites.size() / 2)) { - return; - } - - m_values.resize(values.n_elem); - LinearScale scale(values.min(), values.max(), 0, 1.0f); - std::transform(values.begin(), values.end(), m_values.begin(), scale); - - // Update VBO with the new data - gl.glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[1]); - gl.glBufferData(GL_ARRAY_BUFFER, m_values.size() * sizeof(float), - m_values.data(), GL_DYNAMIC_DRAW); + gl.glBindTexture(GL_TEXTURE_2D, m_tex); } -void VoronoiSplatRenderer::setColorMap(const ColorScale *scale) +bool VoronoiSplatTexture::updateTexture() { - if (!scale) { - return; + if (!m_sitesUpdated && !m_valuesUpdated && !m_colormapUpdated) { + // Texture is already updated + return false; } - float t = scale->min(); - float step = (scale->max() - scale->min()) / COLORMAP_SAMPLES; - qreal r, g, b; - std::vector cmap(3*COLORMAP_SAMPLES); // R,G,B - for (int i = 0; i < 3*COLORMAP_SAMPLES; i += 3) { - scale->color(t).getRgbF(&r, &g, &b); - cmap[i + 0] = r; - cmap[i + 1] = g; - cmap[i + 2] = b; - - t += step; - } - - // Update texture data - gl.glBindTexture(GL_TEXTURE_2D, m_colorMapTex); - gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, COLORMAP_SAMPLES, 1, GL_RGB, - GL_FLOAT, cmap.data()); -} - -QOpenGLFramebufferObject *VoronoiSplatRenderer::createFramebufferObject(const QSize &size) -{ - return new QOpenGLFramebufferObject(size); -} - -void VoronoiSplatRenderer::synchronize(QQuickFramebufferObject *item) -{ - m_item = static_cast(item); - - if (m_item->colorScaleUpdated()) { - setColorMap(m_item->colorScale()); + // Update OpenGL buffers and textures as needed + if (m_sitesUpdated) { + updateSites(); } - if (m_item->pointsUpdated()) { - setSites(m_item->points()); + if (m_valuesUpdated) { + updateValues(); } - if (m_item->valuesUpdated()) { - setValues(m_item->values()); + if (m_colormapUpdated) { + updateColormap(); } - m_item->setUpdated(false); -} - -void VoronoiSplatRenderer::render() -{ - // Store the final texture to render to, as we use a two-pass rendering - // which first renders to an intermediate texture - GLint targetTexture; - gl.glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - &targetTexture); + gl.glBindFramebuffer(GL_FRAMEBUFFER, m_FBO); // The first pass m_program1->bind(); @@ -364,14 +276,14 @@ void VoronoiSplatRenderer::render() gl.glBindTexture(GL_TEXTURE_2D, m_textures[1]); m_program2->setUniformValue("accumTex", 1); gl.glActiveTexture(GL_TEXTURE2); - gl.glBindTexture(GL_TEXTURE_2D, m_colorMapTex); + gl.glBindTexture(GL_TEXTURE_2D, m_colormapTex); m_program2->setUniformValue("colormap", 2); gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // Restore the original framebuffer texture + // Now we render to the output texture gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, targetTexture, 0); + GL_TEXTURE_2D, m_tex, 0); glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); @@ -382,31 +294,127 @@ void VoronoiSplatRenderer::render() m_program2->release(); - m_item->window()->resetOpenGLState(); + return true; } -static int nextPow2(int n) +void VoronoiSplatTexture::updateSites() { - // TODO: check for overflows - n--; - for (int shift = 1; ((n + 1) & n); shift <<= 1) { - n |= n >> shift; + // Update VBO with the new data + gl.glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[0]); + gl.glBufferData(GL_ARRAY_BUFFER, m_sites.size() * sizeof(float), + m_sites.data(), GL_STATIC_DRAW); + + // Compute DT values for the new positions + computeDT(); + + m_sitesUpdated = false; +} + +void VoronoiSplatTexture::updateValues() +{ + // Update VBO with the new data + gl.glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[1]); + gl.glBufferData(GL_ARRAY_BUFFER, m_values.size() * sizeof(float), + m_values.data(), GL_DYNAMIC_DRAW); + + m_valuesUpdated = false; +} + +void VoronoiSplatTexture::updateColormap() +{ + gl.glBindTexture(GL_TEXTURE_2D, m_colormapTex); + gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, COLORMAP_SAMPLES, 1, GL_RGB, + GL_FLOAT, m_cmap.data()); + + m_colormapUpdated = false; +} + +void VoronoiSplatTexture::computeDT() +{ + int w = m_size.width(), h = m_size.height(); + + std::vector buf(w*h, 0.0f); + for (unsigned i = 0; i < m_sites.size(); i += 2) { + buf[int(m_sites[i + 1])*h + int(m_sites[i])] = (float) i/2 + 1; } - return n + 1; + + // Compute FT of the sites + skelft2DFT(0, buf.data(), 0, 0, w, h, w); + // Compute DT of the sites (from the resident FT) + skelft2DDT(buf.data(), 0, 0, w, h); + + std::vector dtTexData(2*w*h, 0.0f); + for (unsigned i = 0; i < buf.size(); i++) { + dtTexData[2*i] = buf[i]; + } + + // Upload result to lookup texture + gl.glBindTexture(GL_TEXTURE_2D, m_textures[0]); + gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RG, GL_FLOAT, + dtTexData.data()); +} + +void VoronoiSplatTexture::setSites(const arma::mat &points) +{ + if (points.n_rows < 1 || points.n_cols != 2 + || (m_values.size() != 0 && points.n_rows != m_values.size())) { + return; + } + + // Copy 'points' to internal data structure(s) + double minX = points.col(0).min(); + double maxX = points.col(0).max(); + double minY = points.col(1).min(); + double maxY = points.col(1).max(); + + // Coords are tighly packed into 'm_sites' as [ (x1, y1), (x2, y2), ... ] + m_sites.resize(2*points.n_rows); + LinearScale sx(minX, maxX, PADDING, m_size.width() - PADDING); + const double *col = points.colptr(0); + for (unsigned i = 0; i < points.n_rows; i++) { + m_sites[2*i] = sx(col[i]); + } + + col = points.colptr(1); + LinearScale sy(minY, maxY, m_size.height() - PADDING, PADDING); + for (unsigned i = 0; i < points.n_rows; i++) { + m_sites[2*i + 1] = sy(col[i]); + } + + m_sitesUpdated = true; } -VoronoiSplat::VoronoiSplat(QQuickItem *parent) - : QQuickFramebufferObject(parent) - , m_colorScaleUpdated(false) - , m_pointsUpdated(false) - , m_valuesUpdated(false) - , m_colorScale(0) +void VoronoiSplatTexture::setValues(const arma::vec &values) { - setTextureFollowsItemSize(false); + if (values.n_elem == 0 + || (m_sites.size() != 0 && values.n_elem != m_sites.size() / 2)) { + return; + } + + m_values.resize(values.n_elem); + LinearScale scale(values.min(), values.max(), 0, 1.0f); + std::transform(values.begin(), values.end(), m_values.begin(), scale); + + m_valuesUpdated = true; } -QQuickFramebufferObject::Renderer *VoronoiSplat::createRenderer() const +void VoronoiSplatTexture::setColormap(const ColorScale *scale) { - int baseSize = nextPow2(std::min(width(), height())); - return new VoronoiSplatRenderer(QSize(baseSize, baseSize)); + if (!scale) { + return; + } + + float t = scale->min(); + float step = (scale->max() - scale->min()) / COLORMAP_SAMPLES; + qreal r, g, b; + for (unsigned i = 0; i < m_cmap.size(); i += 3) { + scale->color(t).getRgbF(&r, &g, &b); + m_cmap[i + 0] = r; + m_cmap[i + 1] = g; + m_cmap[i + 2] = b; + + t += step; + } + + m_colormapUpdated = true; } diff --git a/voronoisplat.h b/voronoisplat.h index 5913218..78dfd3e 100644 --- a/voronoisplat.h +++ b/voronoisplat.h @@ -1,26 +1,31 @@ #ifndef VORONOISPLAT_H #define VORONOISPLAT_H -#include +#include #include -#include #include #include #include #include "colorscale.h" -class VoronoiSplat; // defined after this class - -class VoronoiSplatRenderer - : public QQuickFramebufferObject::Renderer +class VoronoiSplatTexture + : public QSGDynamicTexture { public: // 'size' must be square (and power of 2) - VoronoiSplatRenderer(const QSize &size); - ~VoronoiSplatRenderer(); + VoronoiSplatTexture(const QSize &size); + virtual ~VoronoiSplatTexture(); + + void bind(); - void synchronize(QQuickFramebufferObject *item); + // When true is returned, should probably call resetOpenGLState() on window + bool updateTexture(); + + bool hasAlphaChannel() const { return true; } + bool hasMipmaps() const { return false; } + int textureId() const { return m_tex; } + QSize textureSize() const { return m_size; } // 'points' should be a 2D points matrix (each point in a row) void setSites(const arma::mat &points); @@ -29,79 +34,29 @@ public: void setValues(const arma::vec &values); // Set colormap data based on the given color scale; - void setColorMap(const ColorScale *scale); - - QOpenGLFramebufferObject *createFramebufferObject(const QSize &size); - void render(); + void setColormap(const ColorScale *scale); private: void setupShaders(); void setupVAOs(); void setupTextures(); - void copyPoints(const arma::mat &points); + + void updateSites(); + void updateValues(); + void updateColormap(); void computeDT(); - VoronoiSplat *m_item; QOpenGLFunctions gl; QOpenGLShaderProgram *m_program1, *m_program2; + GLuint m_FBO; GLuint m_VBOs[3]; - GLuint m_textures[2], m_colorMapTex; + GLuint m_textures[2], m_colormapTex, m_tex; QOpenGLVertexArrayObject m_sitesVAO, m_2ndPassVAO; - std::vector m_sites, m_values; + std::vector m_sites, m_values, m_cmap; QSize m_size; -}; - -class VoronoiSplat - : public QQuickFramebufferObject -{ - Q_OBJECT -public: - VoronoiSplat(QQuickItem *parent = 0); - Renderer *createRenderer() const; - - const arma::mat &points() const { return m_points; } - const arma::vec &values() const { return m_values; } - const ColorScale *colorScale() const { return m_colorScale; } - - bool colorScaleUpdated() const { return m_colorScaleUpdated; } - bool pointsUpdated() const { return m_pointsUpdated; } - bool valuesUpdated() const { return m_valuesUpdated; } - -public slots: - void setColorScale(const ColorScale *scale) - { - m_colorScale = scale; - m_colorScaleUpdated = true; - update(); - } - void setPoints(const arma::mat &points) - { - m_points = points; - m_pointsUpdated = true; - update(); - } - - void setValues(const arma::vec &values) - { - m_values = values; - m_valuesUpdated = true; - update(); - } - - void setUpdated(bool updated) - { - m_colorScaleUpdated = updated; - m_pointsUpdated = updated; - m_valuesUpdated = updated; - } - -private: - bool m_colorScaleUpdated, m_pointsUpdated, m_valuesUpdated; - const ColorScale *m_colorScale; - arma::mat m_points; - arma::vec m_values; + bool m_sitesUpdated, m_valuesUpdated, m_colormapUpdated; }; #endif // VORONOISPLAT_H -- cgit v1.2.3