diff options
Diffstat (limited to 'src/datavisualization/utils')
18 files changed, 1465 insertions, 544 deletions
diff --git a/src/datavisualization/utils/abstractobjecthelper.cpp b/src/datavisualization/utils/abstractobjecthelper.cpp index c350d096..40b3a45e 100644 --- a/src/datavisualization/utils/abstractobjecthelper.cpp +++ b/src/datavisualization/utils/abstractobjecthelper.cpp @@ -28,6 +28,7 @@ AbstractObjectHelper::AbstractObjectHelper() m_indexCount(0), m_meshDataLoaded(false) { + initializeOpenGLFunctions(); } AbstractObjectHelper::~AbstractObjectHelper() diff --git a/src/datavisualization/utils/abstractobjecthelper_p.h b/src/datavisualization/utils/abstractobjecthelper_p.h index c1618909..99f85dab 100644 --- a/src/datavisualization/utils/abstractobjecthelper_p.h +++ b/src/datavisualization/utils/abstractobjecthelper_p.h @@ -38,11 +38,11 @@ class AbstractObjectHelper: protected QOpenGLFunctions protected: AbstractObjectHelper(); public: - ~AbstractObjectHelper(); + virtual ~AbstractObjectHelper(); GLuint vertexBuf(); GLuint normalBuf(); - GLuint uvBuf(); + virtual GLuint uvBuf(); GLuint elementBuf(); GLuint indexCount(); GLuint indicesType(); diff --git a/src/datavisualization/utils/objecthelper.cpp b/src/datavisualization/utils/objecthelper.cpp index a66e0f7e..4240d6f5 100644 --- a/src/datavisualization/utils/objecthelper.cpp +++ b/src/datavisualization/utils/objecthelper.cpp @@ -111,7 +111,6 @@ ObjectHelper *ObjectHelper::getObjectHelper(const Abstract3DRenderer *cacheId, void ObjectHelper::load() { - initializeOpenGLFunctions(); if (m_meshDataLoaded) { // Delete old data glDeleteBuffers(1, &m_vertexbuffer); @@ -122,6 +121,10 @@ void ObjectHelper::load() m_indexedVertices.clear(); m_indexedUVs.clear(); m_indexedNormals.clear(); + m_vertexbuffer = 0; + m_uvbuffer = 0; + m_normalbuffer = 0; + m_elementbuffer = 0; } QVector<QVector3D> vertices; QVector<QVector2D> uvs; diff --git a/src/datavisualization/utils/objecthelper_p.h b/src/datavisualization/utils/objecthelper_p.h index c84f53bd..b00e5d8d 100644 --- a/src/datavisualization/utils/objecthelper_p.h +++ b/src/datavisualization/utils/objecthelper_p.h @@ -41,7 +41,7 @@ class ObjectHelper : public AbstractObjectHelper private: ObjectHelper(const QString &objectFile); public: - ~ObjectHelper(); + virtual ~ObjectHelper(); static void resetObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj, const QString &meshFile); diff --git a/src/datavisualization/utils/qutils.h b/src/datavisualization/utils/qutils.h index 43375a9c..d4acfc99 100644 --- a/src/datavisualization/utils/qutils.h +++ b/src/datavisualization/utils/qutils.h @@ -28,17 +28,21 @@ inline static QSurfaceFormat qDefaultSurfaceFormat(bool antialias = true) QSurfaceFormat surfaceFormat; surfaceFormat.setDepthBufferSize(24); + surfaceFormat.setStencilBufferSize(8); surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); -#if !defined(QT_OPENGL_ES_2) - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); -#else + surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType); +#if defined(QT_OPENGL_ES_2) // Antialias not supported for ES antialias = false; - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); + surfaceFormat.setRedBufferSize(8); + surfaceFormat.setBlueBufferSize(8); + surfaceFormat.setGreenBufferSize(8); #endif if (antialias) surfaceFormat.setSamples(8); + else + surfaceFormat.setSamples(0); return surfaceFormat; } diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp index d68b9df4..44c84ae0 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp +++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp @@ -20,12 +20,14 @@ #include "objecthelper_p.h" #include <QtGui/QVector2D> #include <QtGui/QMatrix4x4> +#include <QtCore/qmath.h> QT_BEGIN_NAMESPACE_DATAVISUALIZATION const GLfloat itemScaler = 3.0f; ScatterObjectBufferHelper::ScatterObjectBufferHelper() + : m_scaleY(0.0f) { m_indicesType = GL_UNSIGNED_INT; } @@ -36,34 +38,40 @@ ScatterObjectBufferHelper::~ScatterObjectBufferHelper() void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale) { - initializeOpenGLFunctions(); - m_meshDataLoaded = false; m_indexCount = 0; ObjectHelper *dotObj = cache->object(); - ScatterRenderItemArray &renderArray = cache->renderArray(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); const uint renderArraySize = renderArray.size(); + if (renderArraySize == 0) return; // No use to go forward - uint itemCount = renderArraySize; + + uint itemCount = 0; QQuaternion seriesRotation(cache->meshRotation()); + if (m_meshDataLoaded) { // Delete old data glDeleteBuffers(1, &m_vertexbuffer); glDeleteBuffers(1, &m_uvbuffer); glDeleteBuffers(1, &m_normalbuffer); glDeleteBuffers(1, &m_elementbuffer); + m_vertexbuffer = 0; + m_uvbuffer = 0; + m_normalbuffer = 0; + m_elementbuffer = 0; } + // Index vertices const QVector<unsigned short> indices = dotObj->indices(); const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices(); const QVector<QVector2D> indexed_uvs = dotObj->indexedUVs(); const QVector<QVector3D> indexed_normals = dotObj->indexedNormals(); - int indicesCount = indices.count(); - int verticeCount = indexed_vertices.count(); - int uvsCount = indexed_uvs.count(); - int normalsCount = indexed_normals.count(); + const int indicesCount = indices.count(); + const int verticeCount = indexed_vertices.count(); + const int uvsCount = indexed_uvs.count(); + const int normalsCount = indexed_normals.count(); float itemSize = cache->itemSize() / itemScaler; if (itemSize == 0.0f) @@ -89,47 +97,58 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal buffered_indices.resize(indicesCount * renderArraySize); buffered_vertices.resize(verticeCount * renderArraySize); - buffered_uvs.resize(uvsCount * renderArraySize); buffered_normals.resize(normalsCount * renderArraySize); - uint pos = 0; + buffered_uvs.resize(uvsCount * renderArraySize); + + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + createRangeGradientUVs(cache, buffered_uvs); + else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) + createObjectGradientUVs(cache, buffered_uvs, indexed_vertices); + + QVector2D dummyUV(0.0f, 0.0f); + + cache->bufferIndices().resize(renderArraySize); for (uint i = 0; i < renderArraySize; i++) { - ScatterRenderItem &item = renderArray[i]; - if (!item.isVisible()) { - itemCount--; + const ScatterRenderItem &item = renderArray.at(i); + if (!item.isVisible()) continue; - } + else + cache->bufferIndices()[i] = itemCount; - int offset = pos * verticeCount; + int offset = itemCount * verticeCount; if (item.rotation().isIdentity()) { - for (int j = 0; j < verticeCount; j++) + for (int j = 0; j < verticeCount; j++) { buffered_vertices[j + offset] = scaled_vertices[j] + item.translation(); + buffered_normals[j + offset] = indexed_normals[j]; + } } else { QMatrix4x4 matrix; - matrix.rotate(seriesRotation * item.rotation()); - modelMatrix = matrix.transposed(); - modelMatrix.scale(modelScaler); + QQuaternion totalRotation = seriesRotation * item.rotation(); + matrix.rotate(totalRotation); + matrix.scale(modelScaler); + QMatrix4x4 itModelMatrix = matrix.inverted(); + modelMatrix = matrix.transposed(); // Because of row-column major difference - for (int j = 0; j < verticeCount; j++) + for (int j = 0; j < verticeCount; j++) { buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix + item.translation(); + buffered_normals[j + offset] = indexed_normals[j] * itModelMatrix; + } } - offset = pos * normalsCount; - for (int j = 0; j < normalsCount; j++) - buffered_normals[j + offset] = indexed_normals[j]; - - offset = pos * uvsCount; - for (int j = 0; j < uvsCount; j++) - buffered_uvs[j + offset] = indexed_uvs[j]; + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { + offset = itemCount * uvsCount; + for (int j = 0; j < uvsCount; j++) + buffered_uvs[j + offset] = dummyUV; + } - int offsetVertice = i * verticeCount; - offset = pos * indicesCount; - for (int j = 0; j < indicesCount; j++) { + int offsetVertice = itemCount * verticeCount; + offset = itemCount * indicesCount; + for (int j = 0; j < indicesCount; j++) buffered_indices[j + offset] = GLuint(indices[j] + offsetVertice); - } - pos++; + itemCount++; } m_indexCount = indicesCount * itemCount; @@ -164,15 +183,128 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal } } -void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale) +void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) +{ + ObjectHelper *dotObj = cache->object(); + const int uvsCount = dotObj->indexedUVs().count(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); + + if (!updateSize) + return; + + QVector<QVector2D> buffered_uvs; + buffered_uvs.resize(uvsCount * updateSize); + + uint itemCount = 0; + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) { + itemCount = createRangeGradientUVs(cache, buffered_uvs); + } else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { + const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices(); + itemCount = createObjectGradientUVs(cache, buffered_uvs, indexed_vertices); + } + + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + int itemSize = uvsCount * sizeof(QVector2D); + if (cache->updateIndices().size()) { + int pos = 0; + for (int i = 0; i < updateSize; i++) { + int index = cache->updateIndices().at(i); + if (renderArray.at(index).isVisible()) { + int dataPos = cache->bufferIndices().at(index); + glBufferSubData(GL_ARRAY_BUFFER, itemSize * dataPos, itemSize, + &buffered_uvs.at(uvsCount * pos++)); + } + } + } else { + glBufferData(GL_ARRAY_BUFFER, itemSize * itemCount, &buffered_uvs.at(0), GL_STATIC_DRAW); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector<QVector2D> &buffered_uvs) { - initializeOpenGLFunctions(); + ObjectHelper *dotObj = cache->object(); + const int uvsCount = dotObj->indexedUVs().count(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); + const float yAdjustment = 0.1f; + const float flippedYAdjustment = 0.9f; + + QVector2D uv; + uv.setX(0.0f); + uint pos = 0; + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); + if (!item.isVisible()) + continue; + + float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY; + // Avoid values near gradient texel boundary, as this causes artifacts + // with some graphics cards. + const float floorY = float(qFloor(y * gradientTextureHeight)); + const float diff = (y * gradientTextureHeight) - floorY; + if (diff < yAdjustment) + y += yAdjustment / gradientTextureHeight; + else if (diff > flippedYAdjustment) + y -= yAdjustment / gradientTextureHeight; + uv.setY(y); + + int offset = pos * uvsCount; + for (int j = 0; j < uvsCount; j++) + buffered_uvs[j + offset] = uv; + + pos++; + } + + return pos; +} + +uint ScatterObjectBufferHelper::createObjectGradientUVs(ScatterSeriesRenderCache *cache, + QVector<QVector2D> &buffered_uvs, + const QVector<QVector3D> &indexed_vertices) +{ ObjectHelper *dotObj = cache->object(); - ScatterRenderItemArray &renderArray = cache->renderArray(); - const int renderArraySize = renderArray.size(); + const int uvsCount = dotObj->indexedUVs().count(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const uint renderArraySize = renderArray.size(); + + QVector2D uv; + uv.setX(0.0f); + uint pos = 0; + for (uint i = 0; i < renderArraySize; i++) { + const ScatterRenderItem &item = renderArray.at(i); + if (!item.isVisible()) + continue; + + int offset = pos * uvsCount; + for (int j = 0; j < uvsCount; j++) { + uv.setY((indexed_vertices.at(j).y() + 1.0f) / 2.0f); + buffered_uvs[j + offset] = uv; + } + + pos++; + } + + return pos; +} + +void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale) +{ + ObjectHelper *dotObj = cache->object(); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); QQuaternion seriesRotation(cache->meshRotation()); + if (!updateSize) + return; + // Index vertices const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices(); int verticeCount = indexed_vertices.count(); @@ -195,14 +327,16 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do scaled_vertices[i] = indexed_vertices[i] * modelMatrix; QVector<QVector3D> buffered_vertices; + buffered_vertices.resize(verticeCount * updateSize); - buffered_vertices.resize(verticeCount * renderArraySize); - for (int i = 0; i < renderArraySize; i++) { - ScatterRenderItem &item = renderArray[i]; + int itemCount = 0; + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); if (!item.isVisible()) continue; - const int offset = i * verticeCount; + const int offset = itemCount * verticeCount; if (item.rotation().isIdentity()) { for (int j = 0; j < verticeCount; j++) buffered_vertices[j + offset] = scaled_vertices[j] + item.translation(); @@ -216,15 +350,28 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix + item.translation(); } + itemCount++; } glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); - glBufferData(GL_ARRAY_BUFFER, buffered_vertices.size() * sizeof(QVector3D), - &buffered_vertices.at(0), - GL_DYNAMIC_DRAW); - + int sizeOfItem = verticeCount * sizeof(QVector3D); + if (updateAll) { + if (itemCount) { + glBufferData(GL_ARRAY_BUFFER, itemCount * sizeOfItem, + &buffered_vertices.at(0), GL_STATIC_DRAW); + } + } else { + itemCount = 0; + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + if (renderArray.at(index).isVisible()) { + glBufferSubData(GL_ARRAY_BUFFER, cache->bufferIndices().at(index) * sizeOfItem, + sizeOfItem, &buffered_vertices.at(itemCount * verticeCount)); + itemCount++; + } + } + } glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); m_meshDataLoaded = true; } diff --git a/src/datavisualization/utils/scatterobjectbufferhelper_p.h b/src/datavisualization/utils/scatterobjectbufferhelper_p.h index 952c3d7d..08a42900 100644 --- a/src/datavisualization/utils/scatterobjectbufferhelper_p.h +++ b/src/datavisualization/utils/scatterobjectbufferhelper_p.h @@ -39,10 +39,21 @@ class ScatterObjectBufferHelper : public AbstractObjectHelper { public: ScatterObjectBufferHelper(); - ~ScatterObjectBufferHelper(); + virtual ~ScatterObjectBufferHelper(); void fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale); void update(ScatterSeriesRenderCache *cache, qreal dotScale); + void updateUVs(ScatterSeriesRenderCache *cache); + void setScaleY(float scale) { m_scaleY = scale; } + +private: + uint createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector<QVector2D> &buffered_uvs); + uint createObjectGradientUVs(ScatterSeriesRenderCache *cache, + QVector<QVector2D> &buffered_uvs, + const QVector<QVector3D> &indexed_vertices); + + float m_scaleY; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp index b14d84ad..22e76f92 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper.cpp +++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp @@ -17,6 +17,7 @@ ****************************************************************************/ #include "scatterpointbufferhelper_p.h" +#include <QtGui/QVector2D> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -24,8 +25,7 @@ const QVector3D hiddenPos(-1000.0f, -1000.0f, -1000.0f); ScatterPointBufferHelper::ScatterPointBufferHelper() : m_pointbuffer(0), - m_oldRemoveIndex(0), - m_oldRemove(false) + m_oldRemoveIndex(-1) { m_indicesType = GL_UNSIGNED_INT; } @@ -47,7 +47,8 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex) { glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); - if (m_oldRemove && m_oldRemoveIndex < pointIndex) { + // Pop the previous point if it is still pushed + if (m_oldRemoveIndex >= 0) { glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D), sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex)); } @@ -59,26 +60,22 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex) glBindBuffer(GL_ARRAY_BUFFER, 0); m_oldRemoveIndex = pointIndex; - m_oldRemove = true; } void ScatterPointBufferHelper::popPoint() { - if (m_oldRemove) { + if (m_oldRemoveIndex >= 0) { glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D), sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex)); glBindBuffer(GL_ARRAY_BUFFER, 0); } - m_oldRemoveIndex = 0; - m_oldRemove = false; + m_oldRemoveIndex = -1; } void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) { - initializeOpenGLFunctions(); - ScatterRenderItemArray &renderArray = cache->renderArray(); const int renderArraySize = renderArray.size(); m_indexCount = 0; @@ -86,13 +83,16 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) if (m_meshDataLoaded) { // Delete old data glDeleteBuffers(1, &m_pointbuffer); + glDeleteBuffers(1, &m_uvbuffer); m_bufferedPoints.clear(); + m_pointbuffer = 0; + m_uvbuffer = 0; } bool itemsVisible = false; m_bufferedPoints.resize(renderArraySize); for (int i = 0; i < renderArraySize; i++) { - ScatterRenderItem &item = renderArray[i]; + const ScatterRenderItem &item = renderArray.at(i); if (!item.isVisible()) { m_bufferedPoints[i] = hiddenPos; } else { @@ -101,19 +101,108 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache) } } + QVector<QVector2D> buffered_uvs; if (itemsVisible) m_indexCount = renderArraySize; if (m_indexCount > 0) { + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + createRangeGradientUVs(cache, buffered_uvs); + glGenBuffers(1, &m_pointbuffer); glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); glBufferData(GL_ARRAY_BUFFER, m_bufferedPoints.size() * sizeof(QVector3D), &m_bufferedPoints.at(0), GL_DYNAMIC_DRAW); + + if (buffered_uvs.size()) { + glGenBuffers(1, &m_uvbuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D), + &buffered_uvs.at(0), GL_STATIC_DRAW); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); m_meshDataLoaded = true; } } +void ScatterPointBufferHelper::update(ScatterSeriesRenderCache *cache) +{ + // It may be that the buffer hasn't yet been initialized, in case the entire series was + // hidden items. No need to update in that case. + if (m_indexCount > 0) { + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const int updateSize = cache->updateIndices().size(); + + glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer); + for (int i = 0; i < updateSize; i++) { + int index = cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); + if (!item.isVisible()) + m_bufferedPoints[index] = hiddenPos; + else + m_bufferedPoints[index] = item.translation(); + + if (index != m_oldRemoveIndex) { + glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector3D), + sizeof(QVector3D), &m_bufferedPoints.at(index)); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +} + +void ScatterPointBufferHelper::updateUVs(ScatterSeriesRenderCache *cache) +{ + // It may be that the buffer hasn't yet been initialized, in case the entire series was + // hidden items. No need to update in that case. + if (m_indexCount > 0) { + QVector<QVector2D> buffered_uvs; + createRangeGradientUVs(cache, buffered_uvs); + + if (buffered_uvs.size()) { + if (!m_uvbuffer) + glGenBuffers(1, &m_uvbuffer); + + int updateSize = cache->updateIndices().size(); + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + if (updateSize) { + for (int i = 0; i < updateSize; i++) { + int index = cache->updateIndices().at(i); + glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector2D), + sizeof(QVector2D), &buffered_uvs.at(i)); + + } + } else { + glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D), + &buffered_uvs.at(0), GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + } +} + +void ScatterPointBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector<QVector2D> &buffered_uvs) +{ + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const bool updateAll = (cache->updateIndices().size() == 0); + const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size(); + buffered_uvs.resize(updateSize); + + QVector2D uv; + uv.setX(0.0f); + for (int i = 0; i < updateSize; i++) { + int index = updateAll ? i : cache->updateIndices().at(i); + const ScatterRenderItem &item = renderArray.at(index); + + float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY; + uv.setY(y); + buffered_uvs[i] = uv; + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/scatterpointbufferhelper_p.h b/src/datavisualization/utils/scatterpointbufferhelper_p.h index b3adcfa8..8b34542d 100644 --- a/src/datavisualization/utils/scatterpointbufferhelper_p.h +++ b/src/datavisualization/utils/scatterpointbufferhelper_p.h @@ -39,21 +39,28 @@ class ScatterPointBufferHelper : public AbstractObjectHelper { public: ScatterPointBufferHelper(); - ~ScatterPointBufferHelper(); + virtual ~ScatterPointBufferHelper(); GLuint pointBuf(); void pushPoint(uint pointIndex); void popPoint(); void load(ScatterSeriesRenderCache *cache); + void update(ScatterSeriesRenderCache *cache); + void setScaleY(float scale) { m_scaleY = scale; } + void updateUVs(ScatterSeriesRenderCache *cache); public: GLuint m_pointbuffer; private: + void createRangeGradientUVs(ScatterSeriesRenderCache *cache, + QVector<QVector2D> &buffered_uvs); + +private: QVector<QVector3D> m_bufferedPoints; - uint m_oldRemoveIndex; - bool m_oldRemove; + int m_oldRemoveIndex; + float m_scaleY; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index 7fb237c6..bbd7fe0e 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -40,7 +40,37 @@ ShaderHelper::ShaderHelper(QObject *parent, m_vertexShaderFile(vertexShader), m_fragmentShaderFile(fragmentShader), m_textureFile(texture), - m_depthTextureFile(depthTexture) + m_depthTextureFile(depthTexture), + m_positionAttr(0), + m_uvAttr(0), + m_normalAttr(0), + m_colorUniform(0), + m_viewMatrixUniform(0), + m_modelMatrixUniform(0), + m_invTransModelMatrixUniform(0), + m_depthMatrixUniform(0), + m_mvpMatrixUniform(0), + m_lightPositionUniform(0), + m_lightStrengthUniform(0), + m_ambientStrengthUniform(0), + m_shadowQualityUniform(0), + m_textureUniform(0), + m_shadowUniform(0), + m_gradientMinUniform(0), + m_gradientHeightUniform(0), + m_lightColorUniform(0), + m_volumeSliceIndicesUniform(0), + m_colorIndexUniform(0), + m_cameraPositionRelativeToModelUniform(0), + m_color8BitUniform(0), + m_textureDimensionsUniform(0), + m_sampleCountUniform(0), + m_alphaMultiplierUniform(0), + m_preserveOpacityUniform(0), + m_minBoundsUniform(0), + m_maxBoundsUniform(0), + m_sliceFrameWidthUniform(0), + m_initialized(false) { } @@ -93,6 +123,17 @@ void ShaderHelper::initialize() m_gradientMinUniform = m_program->uniformLocation("gradMin"); m_gradientHeightUniform = m_program->uniformLocation("gradHeight"); m_lightColorUniform = m_program->uniformLocation("lightColor"); + m_volumeSliceIndicesUniform = m_program->uniformLocation("volumeSliceIndices"); + m_colorIndexUniform = m_program->uniformLocation("colorIndex"); + m_cameraPositionRelativeToModelUniform = m_program->uniformLocation("cameraPositionRelativeToModel"); + m_color8BitUniform = m_program->uniformLocation("color8Bit"); + m_textureDimensionsUniform = m_program->uniformLocation("textureDimensions"); + m_sampleCountUniform = m_program->uniformLocation("sampleCount"); + m_alphaMultiplierUniform = m_program->uniformLocation("alphaMultiplier"); + m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity"); + m_minBoundsUniform = m_program->uniformLocation("minBounds"); + m_maxBoundsUniform = m_program->uniformLocation("maxBounds"); + m_sliceFrameWidthUniform = m_program->uniformLocation("sliceFrameWidth"); m_initialized = true; } @@ -125,151 +166,239 @@ void ShaderHelper::release() m_program->release(); } -void ShaderHelper::setUniformValue(GLuint uniform, const QVector3D &value) +void ShaderHelper::setUniformValue(GLint uniform, const QVector2D &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, const QVector4D &value) +void ShaderHelper::setUniformValue(GLint uniform, const QVector3D &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, const QMatrix4x4 &value) +void ShaderHelper::setUniformValue(GLint uniform, const QVector4D &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, GLfloat value) +void ShaderHelper::setUniformValue(GLint uniform, const QMatrix4x4 &value) { m_program->setUniformValue(uniform, value); } -void ShaderHelper::setUniformValue(GLuint uniform, GLint value) +void ShaderHelper::setUniformValue(GLint uniform, GLfloat value) { m_program->setUniformValue(uniform, value); } -GLuint ShaderHelper::MVP() +void ShaderHelper::setUniformValue(GLint uniform, GLint value) +{ + m_program->setUniformValue(uniform, value); +} + +void ShaderHelper::setUniformValueArray(GLint uniform, const QVector4D *values, int count) +{ + m_program->setUniformValueArray(uniform, values, count); +} + +GLint ShaderHelper::MVP() { if (!m_initialized) qFatal("Shader not initialized"); return m_mvpMatrixUniform; } -GLuint ShaderHelper::view() +GLint ShaderHelper::view() { if (!m_initialized) qFatal("Shader not initialized"); return m_viewMatrixUniform; } -GLuint ShaderHelper::model() +GLint ShaderHelper::model() { if (!m_initialized) qFatal("Shader not initialized"); return m_modelMatrixUniform; } -GLuint ShaderHelper::nModel() +GLint ShaderHelper::nModel() { if (!m_initialized) qFatal("Shader not initialized"); return m_invTransModelMatrixUniform; } -GLuint ShaderHelper::depth() +GLint ShaderHelper::depth() { if (!m_initialized) qFatal("Shader not initialized"); return m_depthMatrixUniform; } -GLuint ShaderHelper::lightP() +GLint ShaderHelper::lightP() { if (!m_initialized) qFatal("Shader not initialized"); return m_lightPositionUniform; } -GLuint ShaderHelper::lightS() +GLint ShaderHelper::lightS() { if (!m_initialized) qFatal("Shader not initialized"); return m_lightStrengthUniform; } -GLuint ShaderHelper::ambientS() +GLint ShaderHelper::ambientS() { if (!m_initialized) qFatal("Shader not initialized"); return m_ambientStrengthUniform; } -GLuint ShaderHelper::shadowQ() +GLint ShaderHelper::shadowQ() { if (!m_initialized) qFatal("Shader not initialized"); return m_shadowQualityUniform; } -GLuint ShaderHelper::color() +GLint ShaderHelper::color() { if (!m_initialized) qFatal("Shader not initialized"); return m_colorUniform; } -GLuint ShaderHelper::texture() +GLint ShaderHelper::texture() { if (!m_initialized) qFatal("Shader not initialized"); return m_textureUniform; } -GLuint ShaderHelper::shadow() +GLint ShaderHelper::shadow() { if (!m_initialized) qFatal("Shader not initialized"); return m_shadowUniform; } -GLuint ShaderHelper::gradientMin() +GLint ShaderHelper::gradientMin() { if (!m_initialized) qFatal("Shader not initialized"); return m_gradientMinUniform; } -GLuint ShaderHelper::gradientHeight() +GLint ShaderHelper::gradientHeight() { if (!m_initialized) qFatal("Shader not initialized"); return m_gradientHeightUniform; } -GLuint ShaderHelper::lightColor() +GLint ShaderHelper::lightColor() { if (!m_initialized) qFatal("Shader not initialized"); return m_lightColorUniform; } -GLuint ShaderHelper::posAtt() +GLint ShaderHelper::volumeSliceIndices() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_volumeSliceIndicesUniform; +} + +GLint ShaderHelper::colorIndex() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_colorIndexUniform; +} + +GLint ShaderHelper::cameraPositionRelativeToModel() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_cameraPositionRelativeToModelUniform; +} + +GLint ShaderHelper::color8Bit() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_color8BitUniform; +} + +GLint ShaderHelper::textureDimensions() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_textureDimensionsUniform; +} + +GLint ShaderHelper::sampleCount() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_sampleCountUniform; +} + +GLint ShaderHelper::alphaMultiplier() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_alphaMultiplierUniform; +} + +GLint ShaderHelper::preserveOpacity() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_preserveOpacityUniform; +} + +GLint ShaderHelper::maxBounds() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_maxBoundsUniform; +} + +GLint ShaderHelper::minBounds() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_minBoundsUniform; +} + +GLint ShaderHelper::sliceFrameWidth() +{ + + if (!m_initialized) + qFatal("Shader not initialized"); + return m_sliceFrameWidthUniform; +} + +GLint ShaderHelper::posAtt() { if (!m_initialized) qFatal("Shader not initialized"); return m_positionAttr; } -GLuint ShaderHelper::uvAtt() +GLint ShaderHelper::uvAtt() { if (!m_initialized) qFatal("Shader not initialized"); return m_uvAttr; } -GLuint ShaderHelper::normalAtt() +GLint ShaderHelper::normalAtt() { if (!m_initialized) qFatal("Shader not initialized"); diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index fdef0dff..812cba18 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -52,31 +52,44 @@ class ShaderHelper bool testCompile(); void bind(); void release(); - void setUniformValue(GLuint uniform, const QVector3D &value); - void setUniformValue(GLuint uniform, const QVector4D &value); - void setUniformValue(GLuint uniform, const QMatrix4x4 &value); - void setUniformValue(GLuint uniform, GLfloat value); - void setUniformValue(GLuint uniform, GLint value); - - GLuint MVP(); - GLuint view(); - GLuint model(); - GLuint nModel(); - GLuint depth(); - GLuint lightP(); - GLuint lightS(); - GLuint ambientS(); - GLuint shadowQ(); - GLuint color(); - GLuint texture(); - GLuint shadow(); - GLuint gradientMin(); - GLuint gradientHeight(); - GLuint lightColor(); - - GLuint posAtt(); - GLuint uvAtt(); - GLuint normalAtt(); + void setUniformValue(GLint uniform, const QVector2D &value); + void setUniformValue(GLint uniform, const QVector3D &value); + void setUniformValue(GLint uniform, const QVector4D &value); + void setUniformValue(GLint uniform, const QMatrix4x4 &value); + void setUniformValue(GLint uniform, GLfloat value); + void setUniformValue(GLint uniform, GLint value); + void setUniformValueArray(GLint uniform, const QVector4D *values, int count); + + GLint MVP(); + GLint view(); + GLint model(); + GLint nModel(); + GLint depth(); + GLint lightP(); + GLint lightS(); + GLint ambientS(); + GLint shadowQ(); + GLint color(); + GLint texture(); + GLint shadow(); + GLint gradientMin(); + GLint gradientHeight(); + GLint lightColor(); + GLint volumeSliceIndices(); + GLint colorIndex(); + GLint cameraPositionRelativeToModel(); + GLint color8Bit(); + GLint textureDimensions(); + GLint sampleCount(); + GLint alphaMultiplier(); + GLint preserveOpacity(); + GLint maxBounds(); + GLint minBounds(); + GLint sliceFrameWidth(); + + GLint posAtt(); + GLint uvAtt(); + GLint normalAtt(); private: QObject *m_caller; @@ -88,25 +101,36 @@ class ShaderHelper QString m_textureFile; QString m_depthTextureFile; - GLuint m_positionAttr; - GLuint m_uvAttr; - GLuint m_normalAttr; - - GLuint m_colorUniform; - GLuint m_viewMatrixUniform; - GLuint m_modelMatrixUniform; - GLuint m_invTransModelMatrixUniform; - GLuint m_depthMatrixUniform; - GLuint m_mvpMatrixUniform; - GLuint m_lightPositionUniform; - GLuint m_lightStrengthUniform; - GLuint m_ambientStrengthUniform; - GLuint m_shadowQualityUniform; - GLuint m_textureUniform; - GLuint m_shadowUniform; - GLuint m_gradientMinUniform; - GLuint m_gradientHeightUniform; - GLuint m_lightColorUniform; + GLint m_positionAttr; + GLint m_uvAttr; + GLint m_normalAttr; + + GLint m_colorUniform; + GLint m_viewMatrixUniform; + GLint m_modelMatrixUniform; + GLint m_invTransModelMatrixUniform; + GLint m_depthMatrixUniform; + GLint m_mvpMatrixUniform; + GLint m_lightPositionUniform; + GLint m_lightStrengthUniform; + GLint m_ambientStrengthUniform; + GLint m_shadowQualityUniform; + GLint m_textureUniform; + GLint m_shadowUniform; + GLint m_gradientMinUniform; + GLint m_gradientHeightUniform; + GLint m_lightColorUniform; + GLint m_volumeSliceIndicesUniform; + GLint m_colorIndexUniform; + GLint m_cameraPositionRelativeToModelUniform; + GLint m_color8BitUniform; + GLint m_textureDimensionsUniform; + GLint m_sampleCountUniform; + GLint m_alphaMultiplierUniform; + GLint m_preserveOpacityUniform; + GLint m_minBoundsUniform; + GLint m_maxBoundsUniform; + GLint m_sliceFrameWidthUniform; GLboolean m_initialized; }; diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index d999ba90..b93030b1 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -30,26 +30,31 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer) m_gridIndexCount(0), m_axisCacheX(renderer->m_axisCacheX), m_axisCacheY(renderer->m_axisCacheY), - m_axisCacheZ(renderer->m_axisCacheZ) - + m_axisCacheZ(renderer->m_axisCacheZ), + m_renderer(renderer), + m_returnTextureBuffer(false), + m_dataDimension(0), + m_oldDataDimension(-1) { m_indicesType = GL_UNSIGNED_INT; - initializeOpenGLFunctions(); glGenBuffers(1, &m_vertexbuffer); glGenBuffers(1, &m_normalbuffer); glGenBuffers(1, &m_uvbuffer); glGenBuffers(1, &m_elementbuffer); glGenBuffers(1, &m_gridElementbuffer); + glGenBuffers(1, &m_uvTextureBuffer); } SurfaceObject::~SurfaceObject() { - if (QOpenGLContext::currentContext()) + if (QOpenGLContext::currentContext()) { glDeleteBuffers(1, &m_gridElementbuffer); + glDeleteBuffers(1, &m_uvTextureBuffer); + } } void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ) + bool changeGeometry, bool polar, bool flipXZ) { m_columns = space.width(); m_rows = space.height(); @@ -59,6 +64,12 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR m_surfaceType = SurfaceSmooth; + checkDirections(dataArray); + bool indicesDirty = false; + if (m_dataDimension != m_oldDataDimension) + indicesDirty = true; + m_oldDataDimension = m_dataDimension; + // Create/populate vertix table if (changeGeometry) m_vertices.resize(totalSize); @@ -68,17 +79,14 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR uvs.resize(totalSize); int totalIndex = 0; - AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX; - AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ; + // Init min and max to ridiculous values + m_minY = 10000000.0; + m_maxY = -10000000.0f; for (int i = 0; i < m_rows; i++) { const QSurfaceDataRow &p = *dataArray.at(i); for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = p.at(j); - float normalizedX = xCache.positionAt(data.x()); - float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = zCache.positionAt(data.z()); - m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ); + getNormalizedVertex(p.at(j), m_vertices[totalIndex], polar, flipXZ); if (changeGeometry) uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); totalIndex++; @@ -95,161 +103,305 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR // Create normals int rowLimit = m_rows - 1; int colLimit = m_columns - 1; - int rowColLimit = rowLimit * m_columns; - int totalLimit = totalSize - 1; if (changeGeometry) m_normals.resize(totalSize); totalIndex = 0; - const bool flipNormal = checkFlipNormal(dataArray); - for (int row = 0; row < rowColLimit; row += m_columns) { - for (int j = 0; j < colLimit; j++) { - m_normals[totalIndex++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(row + m_columns + j), - flipNormal); - } - int p = row + colLimit; - m_normals[totalIndex++] = normal(m_vertices.at(p), - m_vertices.at(p + m_columns), - m_vertices.at(p - 1), - flipNormal); - } - for (int j = rowColLimit; j < totalLimit; j++) { - m_normals[totalIndex++] = normal(m_vertices.at(j), - m_vertices.at(j - m_columns), - m_vertices.at(j + 1), - flipNormal); + + if ((m_dataDimension == BothAscending) || (m_dataDimension == XDescending)) { + for (int row = 0; row < rowLimit; row++) + createSmoothNormalBodyLine(totalIndex, row * m_columns); + createSmoothNormalUpperLine(totalIndex); + } else { // BothDescending || ZDescending + createSmoothNormalUpperLine(totalIndex); + for (int row = 1; row < m_rows; row++) + createSmoothNormalBodyLine(totalIndex, row * m_columns); } - m_normals[totalIndex++] = normal(m_vertices.at(totalLimit), - m_vertices.at(totalLimit - 1), - m_vertices.at(totalLimit - m_columns), - flipNormal); // Create indices table - if (changeGeometry) + if (changeGeometry || indicesDirty) createSmoothIndices(0, 0, colLimit, rowLimit); // Create line element indices if (changeGeometry) createSmoothGridlineIndices(0, 0, colLimit, rowLimit); - createBuffers(m_vertices, uvs, m_normals, 0, changeGeometry); + createBuffers(m_vertices, uvs, m_normals, 0); } -void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex) +void SurfaceObject::createSmoothNormalBodyLine(int &totalIndex, int column) +{ + int colLimit = m_columns - 1; + + if (m_dataDimension == BothAscending) { + int end = colLimit + column; + for (int j = column; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + 1), + m_vertices.at(j + m_columns)); + } + m_normals[totalIndex++] = normal(m_vertices.at(end), + m_vertices.at(end + m_columns), + m_vertices.at(end - 1)); + } else if (m_dataDimension == XDescending) { + m_normals[totalIndex++] = normal(m_vertices.at(column), + m_vertices.at(column + m_columns), + m_vertices.at(column + 1)); + int end = column + m_columns; + for (int j = column + 1; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - 1), + m_vertices.at(j + m_columns)); + } + } else if (m_dataDimension == ZDescending) { + int end = colLimit + column; + for (int j = column; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + 1), + m_vertices.at(j - m_columns)); + } + m_normals[totalIndex++] = normal(m_vertices.at(end), + m_vertices.at(end - m_columns), + m_vertices.at(end - 1)); + } else { // BothDescending + m_normals[totalIndex++] = normal(m_vertices.at(column), + m_vertices.at(column - m_columns), + m_vertices.at(column + 1)); + int end = column + m_columns; + for (int j = column + 1; j < end; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - 1), + m_vertices.at(j - m_columns)); + } + } +} + +void SurfaceObject::createSmoothNormalUpperLine(int &totalIndex) +{ + if (m_dataDimension == BothAscending) { + int lineEnd = m_rows * m_columns - 1; + for (int j = (m_rows - 1) * m_columns; j < lineEnd; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - m_columns), + m_vertices.at(j + 1)); + } + m_normals[totalIndex++] = normal(m_vertices.at(lineEnd), + m_vertices.at(lineEnd - 1), + m_vertices.at(lineEnd - m_columns)); + } else if (m_dataDimension == XDescending) { + int lineStart = (m_rows - 1) * m_columns; + int lineEnd = m_rows * m_columns; + m_normals[totalIndex++] = normal(m_vertices.at(lineStart), + m_vertices.at(lineStart + 1), + m_vertices.at(lineStart - m_columns)); + for (int j = lineStart + 1; j < lineEnd; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j - m_columns), + m_vertices.at(j - 1)); + } + } else if (m_dataDimension == ZDescending) { + int colLimit = m_columns - 1; + for (int j = 0; j < colLimit; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + m_columns), + m_vertices.at(j + 1)); + } + m_normals[totalIndex++] = normal(m_vertices.at(colLimit), + m_vertices.at(colLimit - 1), + m_vertices.at(colLimit + m_columns)); + } else { // BothDescending + m_normals[totalIndex++] = normal(m_vertices.at(0), + m_vertices.at(1), + m_vertices.at(m_columns)); + for (int j = 1; j < m_columns; j++) { + m_normals[totalIndex++] = normal(m_vertices.at(j), + m_vertices.at(j + m_columns), + m_vertices.at(j - 1)); + } + } +} + +QVector3D SurfaceObject::createSmoothNormalBodyLineItem(int x, int y) +{ + int p = y * m_columns + x; + if (m_dataDimension == BothAscending) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p + 1), + m_vertices.at(p + m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p - 1)); + } + } else if (m_dataDimension == XDescending) { + if (x == 0) { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p + m_columns)); + } + } else if (m_dataDimension == ZDescending) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p + 1), + m_vertices.at(p - m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p - 1)); + } + } else { // BothDescending + if (x == 0) { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p - m_columns)); + } + } +} + +QVector3D SurfaceObject::createSmoothNormalUpperLineItem(int x, int y) +{ + int p = y * m_columns + x; + if (m_dataDimension == BothAscending) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p - m_columns)); + } + } else if (m_dataDimension == XDescending) { + if (x == 0) { + return normal(m_vertices.at(p), m_vertices.at(p + 1), + m_vertices.at(p - m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - m_columns), + m_vertices.at(p - 1)); + } + } else if (m_dataDimension == ZDescending) { + if (x < m_columns - 1) { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p + 1)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p - 1), + m_vertices.at(p + m_columns)); + } + } else { // BothDescending + if (x == 0) { + return normal(m_vertices.at(0), m_vertices.at(1), + m_vertices.at(m_columns)); + } else { + return normal(m_vertices.at(p), m_vertices.at(p + m_columns), + m_vertices.at(p - 1)); + } + } +} + +void SurfaceObject::smoothUVs(const QSurfaceDataArray &dataArray, + const QSurfaceDataArray &modelArray) +{ + int columns = dataArray.at(0)->size(); + int rows = dataArray.size(); + float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x(); + float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z(); + float xMin = dataArray.at(0)->at(0).x(); + float zMin = dataArray.at(0)->at(0).z(); + const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending); + const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending); + + QVector<QVector2D> uvs; + uvs.resize(m_rows * m_columns); + int index = 0; + for (int i = 0; i < m_rows; i++) { + float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer; + if (zDescending) + y = 1.0f - y; + const QSurfaceDataRow &p = *modelArray.at(i); + for (int j = 0; j < m_columns; j++) { + float x = (p.at(j).x() - xMin) / xRangeNormalizer; + if (xDescending) + x = 1.0f - x; + uvs[index] = QVector2D(x, y); + index++; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer); + glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D), + &uvs.at(0), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_returnTextureBuffer = true; +} + +void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar) { // Update vertices int p = rowIndex * m_columns; const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex); - for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = dataRow.at(j); - float normalizedX = m_axisCacheX.positionAt(data.x()); - float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); - m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ); - } + for (int j = 0; j < m_columns; j++) + getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false); // Create normals - int colLimit = m_columns - 1; + bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending); int startRow = rowIndex; - if (startRow > 0) + if ((startRow > 0) && upwards) startRow--; + int endRow = rowIndex; + if (!upwards && (rowIndex < m_rows - 1)) + endRow++; + if ((endRow == m_rows - 1) && upwards) + endRow--; int totalIndex = startRow * m_columns; - int rowLimit = (rowIndex + 1) * m_columns; - if (rowIndex == m_rows - 1) - rowLimit = rowIndex * m_columns; // The rowIndex is top most row, special handling - const bool flipNormal = checkFlipNormal(dataArray); - for (int row = totalIndex; row < rowLimit; row += m_columns) { - for (int j = 0; j < colLimit; j++) { - // One right and one up - m_normals[totalIndex++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(row + m_columns + j), - flipNormal); - } - int p = row + colLimit; - // One up and one left - m_normals[totalIndex++] = normal(m_vertices.at(p), - m_vertices.at(p + m_columns), - m_vertices.at(p - 1), - flipNormal); + if ((startRow == 0) && !upwards) { + createSmoothNormalUpperLine(totalIndex); + startRow++; } - if (rowIndex == m_rows - 1) { - // Top most line, nothing above, must have different handling. - // Take from one down and one right. Read till second-to-last - rowLimit = (rowIndex + 1) * m_columns - 1; - for (int j = rowIndex * m_columns; j < rowLimit; j++) { - m_normals[totalIndex++] = normal(m_vertices.at(j), - m_vertices.at(j - m_columns), - m_vertices.at(j + 1), - flipNormal); - } - // Top left corner. Take from one left and one down - m_normals[totalIndex++] = normal(m_vertices.at(rowLimit), - m_vertices.at(rowLimit - 1), - m_vertices.at(rowLimit - m_columns), - flipNormal); - } + for (int row = startRow; row <= endRow; row++) + createSmoothNormalBodyLine(totalIndex, row * m_columns); + + if ((rowIndex == m_rows - 1) && upwards) + createSmoothNormalUpperLine(totalIndex); } -void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column) +void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, + bool polar) { // Update a vertice - const QSurfaceDataItem &data = dataArray.at(row)->at(column); - float normalizedX = m_axisCacheX.positionAt(data.x()); - float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); - m_vertices[row * m_columns + column] = QVector3D(normalizedX, normalizedY, normalizedZ); + getNormalizedVertex(dataArray.at(row)->at(column), + m_vertices[row * m_columns + column], polar, false); // Create normals + bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending); + bool rightwards = (m_dataDimension == BothAscending) || (m_dataDimension == ZDescending); int startRow = row; - if (startRow > 0) - startRow--; // Change the normal for previous row also + if ((startRow > 0) && upwards) + startRow--; + int endRow = row; + if (!upwards && (row < m_rows - 1)) + endRow++; + if ((endRow == m_rows - 1) && upwards) + endRow--; int startCol = column; - if (startCol > 0) + if ((startCol > 0) && rightwards) startCol--; - int rightCol = m_columns - 1; - int topRow = m_rows - 1; + int endCol = column; + if ((endCol < m_columns - 1) && !rightwards) + endCol++; - const bool flipNormal = checkFlipNormal(dataArray); - for (int i = startRow; i <= row; i++) { - for (int j = startCol; j <= column; j++) { + for (int i = startRow; i <= endRow; i++) { + for (int j = startCol; j <= endCol; j++) { int p = i * m_columns + j; - if (i < topRow) { - if (j < rightCol) { - // One right and one up - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + 1), - m_vertices.at(p + m_columns), - flipNormal); - } else { - // Last item, nothing on the right. One up and one left - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + m_columns), - m_vertices.at(p - 1), - flipNormal); - } - } else { - // Top most line, nothing above, must have different handling. - if (j < rightCol) { - // Take from one down and one right. Read till second-to-last - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p - m_columns), - m_vertices.at(p + 1), - flipNormal); - } else { - // Top left corner. Take from one left and one down - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p - 1), - m_vertices.at(p - m_columns), - flipNormal); - } - } - } + if ((i == 0) && !upwards) + m_normals[p] = createSmoothNormalUpperLineItem(j, i); + else if ((i == m_rows - 1) && upwards) + m_normals[p] = createSmoothNormalUpperLineItem(j, i); + else + m_normals[p] = createSmoothNormalBodyLineItem(j, i); + } } } @@ -271,15 +423,38 @@ void SurfaceObject::createSmoothIndices(int x, int y, int endX, int endY) int rowEnd = endY * m_columns; for (int row = y * m_columns; row < rowEnd; row += m_columns) { for (int j = x; j < endX; j++) { - // Left triangle - indices[p++] = row + j + 1; - indices[p++] = row + m_columns + j; - indices[p++] = row + j; - - // Right triangle - indices[p++] = row + m_columns + j + 1; - indices[p++] = row + m_columns + j; - indices[p++] = row + j + 1; + if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) { + // Left triangle + indices[p++] = row + j + 1; + indices[p++] = row + m_columns + j; + indices[p++] = row + j; + + // Right triangle + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + m_columns + j; + indices[p++] = row + j + 1; + } else if (m_dataDimension == XDescending) { + // Right triangle + indices[p++] = row + m_columns + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j; + + // Left triangle + indices[p++] = row + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j + 1; + } else { + // Left triangle + indices[p++] = row + m_columns + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j; + + // Right triangle + indices[p++] = row + j; + indices[p++] = row + m_columns + j + 1; + indices[p++] = row + j + 1; + + } } } @@ -331,7 +506,7 @@ void SurfaceObject::createSmoothGridlineIndices(int x, int y, int endX, int endY } void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ) + bool changeGeometry, bool polar, bool flipXZ) { m_columns = space.width(); m_rows = space.height(); @@ -339,6 +514,12 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s GLfloat uvX = 1.0f / GLfloat(m_columns - 1); GLfloat uvY = 1.0f / GLfloat(m_rows - 1); + checkDirections(dataArray); + bool indicesDirty = false; + if (m_dataDimension != m_oldDataDimension) + indicesDirty = true; + m_oldDataDimension = m_dataDimension; + m_surfaceType = SurfaceFlat; // Create vertix table @@ -355,17 +536,14 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s int doubleColumns = m_columns * 2 - 2; int rowColLimit = rowLimit * doubleColumns; - AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX; - AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ; + // Init min and max to ridiculous values + m_minY = 10000000.0; + m_maxY = -10000000.0f; for (int i = 0; i < m_rows; i++) { const QSurfaceDataRow &row = *dataArray.at(i); for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = row.at(j); - float normalizedX = xCache.positionAt(data.x()); - float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = zCache.positionAt(data.z()); - m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ); + getNormalizedVertex(row.at(j), m_vertices[totalIndex], polar, flipXZ); if (changeGeometry) uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); @@ -389,42 +567,23 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s // Create normals & indices table GLint *indices = 0; - int p = 0; - if (changeGeometry) { + if (changeGeometry || indicesDirty) { int normalCount = 2 * colLimit * rowLimit; m_indexCount = 3 * normalCount; indices = new GLint[m_indexCount]; m_normals.resize(normalCount); } + int p = 0; totalIndex = 0; - const bool flipNormal = checkFlipNormal(dataArray); for (int row = 0, upperRow = doubleColumns; row < rowColLimit; row += doubleColumns, upperRow += doubleColumns) { for (int j = 0; j < doubleColumns; j += 2) { - // Normal for the left triangle - m_normals[totalIndex++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - - // Normal for the right triangle - m_normals[totalIndex++] = normal(m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - - if (changeGeometry) { - // Left triangle - indices[p++] = row + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j; + createNormals(totalIndex, row, upperRow, j); - // Right triangle - indices[p++] = upperRow + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j + 1; + if (changeGeometry || indicesDirty) { + createCoarseIndices(indices, p, row, upperRow, j); } } } @@ -433,12 +592,54 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s if (changeGeometry) createCoarseGridlineIndices(0, 0, colLimit, rowLimit); - createBuffers(m_vertices, uvs, m_normals, indices, changeGeometry); + createBuffers(m_vertices, uvs, m_normals, indices); delete[] indices; } -void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex) +void SurfaceObject::coarseUVs(const QSurfaceDataArray &dataArray, + const QSurfaceDataArray &modelArray) +{ + int columns = dataArray.at(0)->size(); + int rows = dataArray.size(); + float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x(); + float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z(); + float xMin = dataArray.at(0)->at(0).x(); + float zMin = dataArray.at(0)->at(0).z(); + const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending); + const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending); + + QVector<QVector2D> uvs; + uvs.resize(m_rows * m_columns * 2); + int index = 0; + int colLimit = m_columns - 1; + for (int i = 0; i < m_rows; i++) { + float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer; + if (zDescending) + y = 1.0f - y; + const QSurfaceDataRow &p = *modelArray.at(i); + for (int j = 0; j < m_columns; j++) { + float x = (p.at(j).x() - xMin) / xRangeNormalizer; + if (xDescending) + x = 1.0f - x; + uvs[index] = QVector2D(x, y); + index++; + if (j > 0 && j < colLimit) { + uvs[index] = uvs[index - 1]; + index++; + } + } + } + + glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer); + glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D), + &uvs.at(0), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_returnTextureBuffer = true; +} + +void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar) { int colLimit = m_columns - 1; int doubleColumns = m_columns * 2 - 2; @@ -447,12 +648,7 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex); for (int j = 0; j < m_columns; j++) { - const QSurfaceDataItem &data = dataRow.at(j); - float normalizedX = m_axisCacheX.positionAt(data.x()); - float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); - m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ); - + getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false); if (j > 0 && j < colLimit) { m_vertices[p] = m_vertices[p - 1]; p++; @@ -466,39 +662,23 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI int rowLimit = (rowIndex + 1) * doubleColumns; if (rowIndex == m_rows - 1) rowLimit = rowIndex * doubleColumns; //Topmost row, no normals - const bool flipNormal = checkFlipNormal(dataArray); for (int row = p, upperRow = p + doubleColumns; row < rowLimit; row += doubleColumns, upperRow += doubleColumns) { - for (int j = 0; j < doubleColumns; j += 2) { - // Normal for the left triangle - m_normals[p++] = normal(m_vertices.at(row + j), - m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - - // Normal for the right triangle - m_normals[p++] = normal(m_vertices.at(row + j + 1), - m_vertices.at(upperRow + j + 1), - m_vertices.at(upperRow + j), - flipNormal); - } + for (int j = 0; j < doubleColumns; j += 2) + createNormals(p, row, upperRow, j); } } -void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column) +void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column, + bool polar) { int colLimit = m_columns - 1; int doubleColumns = m_columns * 2 - 2; // Update a vertice int p = row * doubleColumns + column * 2 - (column > 0); - const QSurfaceDataItem &data = dataArray.at(row)->at(column); - float normalizedX = m_axisCacheX.positionAt(data.x()); - float normalizedY = m_axisCacheY.positionAt(data.y()); - float normalizedZ = m_axisCacheZ.positionAt(data.z()); - m_vertices[p] = QVector3D(normalizedX, normalizedY, normalizedZ); - p++; + getNormalizedVertex(dataArray.at(row)->at(column), m_vertices[p++], polar, false); if (column > 0 && column < colLimit) m_vertices[p] = m_vertices[p - 1]; @@ -515,27 +695,15 @@ void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row if (column == m_columns - 1) column--; - const bool flipNormal = checkFlipNormal(dataArray); for (int i = startRow; i <= row; i++) { for (int j = startCol; j <= column; j++) { p = i * doubleColumns + j * 2; - // Normal for the left triangle - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + 1), - m_vertices.at(p + doubleColumns), - flipNormal); - p++; - - // Normal for the right triangle - m_normals[p] = normal(m_vertices.at(p), - m_vertices.at(p + doubleColumns), - m_vertices.at(p + doubleColumns - 1), - flipNormal); + createNormals(p, i * doubleColumns, (i + 1) * doubleColumns, j * 2); } } } -void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows) +void SurfaceObject::createCoarseSubSection(int x, int y, int columns, int rows) { if (columns > m_columns) columns = m_columns; @@ -557,17 +725,8 @@ void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows) for (int row = y * doubleColumns, upperRow = (y + 1) * doubleColumns; row < rowColLimit; row += doubleColumns, upperRow += doubleColumns) { - for (int j = 2 * x; j < doubleColumnsLimit; j += 2) { - // Left triangle - indices[p++] = row + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j; - - // Right triangle - indices[p++] = upperRow + j + 1; - indices[p++] = upperRow + j; - indices[p++] = row + j + 1; - } + for (int j = 2 * x; j < doubleColumnsLimit; j += 2) + createCoarseIndices(indices, p, row, upperRow, j); } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer); @@ -631,12 +790,11 @@ void SurfaceObject::createCoarseGridlineIndices(int x, int y, int endX, int endY void SurfaceObject::uploadBuffers() { QVector<QVector2D> uvs; // Empty dummy - createBuffers(m_vertices, uvs, m_normals, 0, false); + createBuffers(m_vertices, uvs, m_normals, 0); } void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs, - const QVector<QVector3D> &normals, const GLint *indices, - bool changeGeometry) + const QVector<QVector3D> &normals, const GLint *indices) { // Move to buffers glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); @@ -647,30 +805,62 @@ void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVec glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(QVector3D), &normals.at(0), GL_DYNAMIC_DRAW); - if (changeGeometry) { + if (uvs.size()) { glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D), &uvs.at(0), GL_STATIC_DRAW); + } - if (indices) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint), - indices, GL_STATIC_DRAW); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + if (indices) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint), + indices, GL_STATIC_DRAW); } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); m_meshDataLoaded = true; } -bool SurfaceObject::checkFlipNormal(const QSurfaceDataArray &array) +void SurfaceObject::checkDirections(const QSurfaceDataArray &array) +{ + m_dataDimension = BothAscending; + + if (array.at(0)->at(0).x() > array.at(0)->at(array.at(0)->size() - 1).x()) + m_dataDimension |= XDescending; + if (m_axisCacheX.reversed()) + m_dataDimension ^= XDescending; + + if (array.at(0)->at(0).z() > array.at(array.size() - 1)->at(0).z()) + m_dataDimension |= ZDescending; + if (m_axisCacheZ.reversed()) + m_dataDimension ^= ZDescending; +} + +void SurfaceObject::getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, + bool polar, bool flipXZ) { - const bool ascendingX = array.at(0)->at(0).x() < array.at(0)->at(array.at(0)->size() - 1).x(); - const bool ascendingZ = array.at(0)->at(0).z() < array.at(array.size() - 1)->at(0).z(); - return ascendingX != ascendingZ; + float normalizedX; + float normalizedZ; + if (polar) { + // Slice don't use polar, so don't care about flip + m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ); + } else { + if (flipXZ) { + normalizedX = m_axisCacheZ.positionAt(data.x()); + normalizedZ = m_axisCacheX.positionAt(data.z()); + } else { + normalizedX = m_axisCacheX.positionAt(data.x()); + normalizedZ = m_axisCacheZ.positionAt(data.z()); + } + } + float normalizedY = m_axisCacheY.positionAt(data.y()); + m_minY = qMin(normalizedY, m_minY); + m_maxY = qMax(normalizedY, m_maxY); + vertex.setX(normalizedX); + vertex.setY(normalizedY); + vertex.setZ(normalizedZ); } GLuint SurfaceObject::gridElementBuf() @@ -680,6 +870,17 @@ GLuint SurfaceObject::gridElementBuf() return m_gridElementbuffer; } +GLuint SurfaceObject::uvBuf() +{ + if (!m_meshDataLoaded) + qFatal("No loaded object"); + + if (m_returnTextureBuffer) + return m_uvTextureBuffer; + else + return m_uvbuffer; +} + GLuint SurfaceObject::gridIndexCount() { return m_gridIndexCount; @@ -707,14 +908,74 @@ void SurfaceObject::clear() m_normals.clear(); } -QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, - bool flipNormal) +void SurfaceObject::createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j) +{ + if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) { + // Left triangle + indices[p++] = row + j + 1; + indices[p++] = upperRow + j; + indices[p++] = row + j; + + // Right triangle + indices[p++] = upperRow + j + 1; + indices[p++] = upperRow + j; + indices[p++] = row + j + 1; + } else if (m_dataDimension == XDescending) { + indices[p++] = upperRow + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j; + + indices[p++] = row + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j + 1; + } else { + // Left triangle + indices[p++] = upperRow + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j; + + // Right triangle + indices[p++] = row + j; + indices[p++] = upperRow + j + 1; + indices[p++] = row + j + 1; + } +} + +void SurfaceObject::createNormals(int &p, int row, int upperRow, int j) +{ + if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) { + m_normals[p++] = normal(m_vertices.at(row + j), + m_vertices.at(row + j + 1), + m_vertices.at(upperRow + j)); + + m_normals[p++] = normal(m_vertices.at(row + j + 1), + m_vertices.at(upperRow + j + 1), + m_vertices.at(upperRow + j)); + } else if (m_dataDimension == XDescending) { + m_normals[p++] = normal(m_vertices.at(row + j), + m_vertices.at(upperRow + j), + m_vertices.at(upperRow + j + 1)); + + m_normals[p++] = normal(m_vertices.at(row + j + 1), + m_vertices.at(row + j), + m_vertices.at(upperRow + j + 1)); + } else { + m_normals[p++] = normal(m_vertices.at(row + j), + m_vertices.at(upperRow + j), + m_vertices.at(upperRow + j + 1)); + + m_normals[p++] = normal(m_vertices.at(row + j + 1), + m_vertices.at(row + j), + m_vertices.at(upperRow + j + 1)); + } +} + +QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c) { QVector3D v1 = b - a; QVector3D v2 = c - a; QVector3D normal = QVector3D::crossProduct(v1, v2); - if (flipNormal) - normal *= -1; + return normal; } diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index 9c18dcb2..e7b61310 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -49,34 +49,55 @@ public: Undefined }; + enum DataDimension { + BothAscending = 0, + XDescending = 1, + ZDescending = 2, + BothDescending = XDescending | ZDescending + }; + Q_DECLARE_FLAGS(DataDimensions, DataDimension) + public: SurfaceObject(Surface3DRenderer *renderer); - ~SurfaceObject(); + virtual ~SurfaceObject(); void setUpData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ = false); + bool changeGeometry, bool polar, bool flipXZ = false); void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, - bool changeGeometry, bool flipXZ = false); - void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex); - void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow); - void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column); - void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column); + bool changeGeometry, bool polar, bool flipXZ = false); + void smoothUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray); + void coarseUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray); + void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar); + void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow, bool polar); + void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar); + void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar); void createSmoothIndices(int x, int y, int endX, int endY); - void createCoarseIndices(int x, int y, int columns, int rows); + void createCoarseSubSection(int x, int y, int columns, int rows); void createSmoothGridlineIndices(int x, int y, int endX, int endY); void createCoarseGridlineIndices(int x, int y, int endX, int endY); void uploadBuffers(); GLuint gridElementBuf(); + GLuint uvBuf(); GLuint gridIndexCount(); QVector3D vertexAt(int column, int row); void clear(); + float minYValue() const { return m_minY; } + float maxYValue() const { return m_maxY; } + inline void activateSurfaceTexture(bool value) { m_returnTextureBuffer = value; } private: - QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, bool flipNormal); + void createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j); + void createNormals(int &p, int row, int upperRow, int j); + void createSmoothNormalBodyLine(int &totalIndex, int column); + void createSmoothNormalUpperLine(int &totalIndex); + QVector3D createSmoothNormalBodyLineItem(int x, int y); + QVector3D createSmoothNormalUpperLineItem(int x, int y); + QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c); void createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs, - const QVector<QVector3D> &normals, const GLint *indices, - bool changeGeometry); - bool checkFlipNormal(const QSurfaceDataArray &array); + const QVector<QVector3D> &normals, const GLint *indices); + void checkDirections(const QSurfaceDataArray &array); + inline void getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, bool polar, + bool flipXZ); private: SurfaceType m_surfaceType; @@ -90,6 +111,13 @@ private: AxisRenderCache &m_axisCacheX; AxisRenderCache &m_axisCacheY; AxisRenderCache &m_axisCacheZ; + Surface3DRenderer *m_renderer; + float m_minY; + float m_maxY; + GLuint m_uvTextureBuffer; + bool m_returnTextureBuffer; + SurfaceObject::DataDimensions m_dataDimension; + SurfaceObject::DataDimensions m_oldDataDimension; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp index 2a2a89dd..3944fb0c 100644 --- a/src/datavisualization/utils/texturehelper.cpp +++ b/src/datavisualization/utils/texturehelper.cpp @@ -21,12 +21,29 @@ #include <QtGui/QImage> #include <QtGui/QPainter> +#include <QtCore/QTime> QT_BEGIN_NAMESPACE_DATAVISUALIZATION +// Defined in shaderhelper.cpp +extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg); + TextureHelper::TextureHelper() { initializeOpenGLFunctions(); +#if !defined(QT_OPENGL_ES_2) + if (!Utils::isOpenGLES()) { + // Discard warnings about deprecated functions + QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs); + + m_openGlFunctions_2_1 = + QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>(); + m_openGlFunctions_2_1->initializeOpenGLFunctions(); + + // Restore original message handler + qInstallMessageHandler(handler); + } +#endif } TextureHelper::~TextureHelper() @@ -41,17 +58,16 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt QImage texImage = image; -#if defined(QT_OPENGL_ES_2) - GLuint temp; - GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width(), temp); - GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height(), temp); - if (smoothScale) { - texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - } else { - texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio); + if (!Utils::isOpenGLES()) { + GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width()); + GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height()); + if (smoothScale) { + texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + } else { + texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio); + } } -#endif GLuint textureId; glGenTextures(1, &textureId); @@ -73,6 +89,53 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt if (clampY) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); + + return textureId; +} + +GLuint TextureHelper::create3DTexture(const QVector<uchar> *data, int width, int height, int depth, + QImage::Format dataFormat) +{ + if (Utils::isOpenGLES() || !width || !height || !depth) + return 0; + + GLuint textureId = 0; +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(dataFormat) + Q_UNUSED(data) +#else + glEnable(GL_TEXTURE_3D); + + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_3D, textureId); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + GLenum status = glGetError(); + // glGetError docs advise to call glGetError in loop to clear all error flags + while (status) + status = glGetError(); + + GLint internalFormat = 4; + GLint format = GL_BGRA; + if (dataFormat == QImage::Format_Indexed8) { + internalFormat = 1; + format = GL_RED; + // Align width to 32bits + width = width + width % 4; + } + m_openGlFunctions_2_1->glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth, 0, + format, GL_UNSIGNED_BYTE, data->constData()); + status = glGetError(); + if (status) + qWarning() << __FUNCTION__ << "3D texture creation failed:" << status; + + glBindTexture(GL_TEXTURE_3D, 0); + glDisable(GL_TEXTURE_3D); +#endif return textureId; } @@ -113,14 +176,27 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf glBindTexture(GL_TEXTURE_2D, 0); // Create render buffer - if (!depthBuffer) - glGenRenderbuffers(1, &depthBuffer); + if (depthBuffer) + glDeleteRenderbuffers(1, &depthBuffer); + + glGenRenderbuffers(1, &depthBuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); -#if !defined(QT_OPENGL_ES_2) - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); -#else - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); -#endif + GLenum status = glGetError(); + // glGetError docs advise to call glGetError in loop to clear all error flags + while (status) + status = glGetError(); + if (Utils::isOpenGLES()) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); + else + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); + + status = glGetError(); + if (status) { + qCritical() << "Selection texture render buffer creation failed:" << status; + glDeleteTextures(1, &textureid); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + return 0; + } glBindRenderbuffer(GL_RENDERBUFFER, 0); // Create frame buffer @@ -134,10 +210,11 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); // Verify that the frame buffer is complete - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { - qCritical() << "Frame buffer creation failed" << status; - return 0; + qCritical() << "Selection texture frame buffer creation failed:" << status; + glDeleteTextures(1, &textureid); + textureid = 0; } // Restore the default framebuffer @@ -146,6 +223,31 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf return textureid; } +GLuint TextureHelper::createCursorPositionTexture(const QSize &size, GLuint &frameBuffer) +{ + GLuint textureid; + glGenTextures(1, &textureid); + glBindTexture(GL_TEXTURE_2D, textureid); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + textureid, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + qCritical() << "Cursor position mapper frame buffer creation failed:" << status; + glDeleteTextures(1, &textureid); + textureid = 0; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return textureid; +} + GLuint TextureHelper::createUniformTexture(const QColor &color) { QImage image(QSize(int(uniformTextureWidth), int(uniformTextureHeight)), @@ -170,76 +272,64 @@ GLuint TextureHelper::createGradientTexture(const QLinearGradient &gradient) return create2DTexture(image, false, true, false, true); } -#if !defined(QT_OPENGL_ES_2) GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint textureSize) { - GLuint depthtextureid; - - // Create depth texture for the shadow mapping - glGenTextures(1, &depthtextureid); - glBindTexture(GL_TEXTURE_2D, depthtextureid); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize, - size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); - glBindTexture(GL_TEXTURE_2D, 0); - + GLuint depthtextureid = 0; +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(size) + Q_UNUSED(textureSize) +#else + if (!Utils::isOpenGLES()) { + // Create depth texture for the shadow mapping + glGenTextures(1, &depthtextureid); + glBindTexture(GL_TEXTURE_2D, depthtextureid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize, + size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + } +#endif return depthtextureid; } -#endif -#if !defined(QT_OPENGL_ES_2) GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize) { GLuint depthtextureid = createDepthTexture(size, textureSize); +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(frameBuffer) +#else + if (!Utils::isOpenGLES()) { + // Create frame buffer + if (!frameBuffer) + glGenFramebuffers(1, &frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + + // Attach texture to depth attachment + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0); + + m_openGlFunctions_2_1->glDrawBuffer(GL_NONE); + m_openGlFunctions_2_1->glReadBuffer(GL_NONE); + + // Verify that the frame buffer is complete + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + qCritical() << "Depth texture frame buffer creation failed" << status; + glDeleteTextures(1, &depthtextureid); + depthtextureid = 0; + } - // Create frame buffer - if (!frameBuffer) - glGenFramebuffers(1, &frameBuffer); - glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); - - // Attach texture to depth attachment - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0); - - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - - // Verify that the frame buffer is complete - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - qCritical() << "Frame buffer creation failed" << status; - return 0; + // Restore the default framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); } - - // Restore the default framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - return depthtextureid; -} #endif - -#if !defined(QT_OPENGL_ES_2) -void TextureHelper::fillDepthTexture(GLuint texture,const QSize &size, GLuint textureSize, - GLfloat value) -{ - int nItems = size.width() * textureSize * size.height() * textureSize; - GLfloat *bits = new GLfloat[nItems]; - for (int i = 0; i < nItems; i++) - bits[i] = value; - - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize, - size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, bits); - glBindTexture(GL_TEXTURE_2D, 0); - - delete[] bits; + return depthtextureid; } -#endif void TextureHelper::deleteTexture(GLuint *texture) { diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h index aec137a4..6c0aa3de 100644 --- a/src/datavisualization/utils/texturehelper_p.h +++ b/src/datavisualization/utils/texturehelper_p.h @@ -32,6 +32,10 @@ #include "datavisualizationglobal_p.h" #include <QtGui/QRgb> #include <QtGui/QLinearGradient> +#if !defined(QT_OPENGL_ES_2) +// 3D Textures are not supported by ES set +# include <QtGui/QOpenGLFunctions_2_1> +#endif QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -44,17 +48,17 @@ class TextureHelper : protected QOpenGLFunctions // Ownership of created texture is transferred to caller GLuint create2DTexture(const QImage &image, bool useTrilinearFiltering = false, bool convert = true, bool smoothScale = true, bool clampY = false); + GLuint create3DTexture(const QVector<uchar> *data, int width, int height, int depth, + QImage::Format dataFormat); GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false); // Returns selection texture and inserts generated framebuffers to framebuffer parameters GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer); + GLuint createCursorPositionTexture(const QSize &size, GLuint &frameBuffer); GLuint createUniformTexture(const QColor &color); GLuint createGradientTexture(const QLinearGradient &gradient); -#if !defined(QT_OPENGL_ES_2) GLuint createDepthTexture(const QSize &size, GLuint textureSize); // Returns depth texture and inserts generated framebuffer to parameter GLuint createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize); - void fillDepthTexture(GLuint texture, const QSize &size, GLuint textureSize, GLfloat value); -#endif void deleteTexture(GLuint *texture); private: @@ -62,9 +66,13 @@ class TextureHelper : protected QOpenGLFunctions void convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage, GLenum texture_format); QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format); +#if !defined(QT_OPENGL_ES_2) + QOpenGLFunctions_2_1 *m_openGlFunctions_2_1; // Not owned +#endif friend class Bars3DRenderer; friend class Surface3DRenderer; friend class Scatter3DRenderer; + friend class Abstract3DRenderer; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp index 2368d86a..cb64eb8f 100644 --- a/src/datavisualization/utils/utils.cpp +++ b/src/datavisualization/utils/utils.cpp @@ -17,6 +17,7 @@ ****************************************************************************/ #include "utils_p.h" +#include "qutils.h" #include <QtGui/QPainter> @@ -25,11 +26,12 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION #define NUM_IN_POWER(y, x) for (;y<x;y<<=1) #define MIN_POWER 2 -GLuint Utils::getNearestPowerOfTwo(GLuint value, GLuint &padding) +static GLint maxTextureSize = 0; // Safe, as all instances have the same texture size + +GLuint Utils::getNearestPowerOfTwo(GLuint value) { GLuint powOfTwoValue = MIN_POWER; NUM_IN_POWER(powOfTwoValue, value); - padding = powOfTwoValue - value; return powOfTwoValue; } @@ -54,35 +56,76 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo const QColor &txtColor, bool labelBackground, bool borders, int maxLabelWidth) { + if (maxTextureSize == 0) { + QOpenGLContext::currentContext()->functions()->glGetIntegerv( + GL_MAX_TEXTURE_SIZE, &maxTextureSize); + } GLuint paddingWidth = 20; GLuint paddingHeight = 20; + GLuint prePadding = 20; + GLint targetWidth = maxTextureSize; + // Calculate text dimensions QFont valueFont = font; valueFont.setPointSize(textureFontSize); QFontMetrics valueFM(valueFont); int valueStrWidth = valueFM.width(text); - if (maxLabelWidth && labelBackground) + + // ES2 needs to use maxLabelWidth always (when given) because of the power of 2 -issue. + if (maxLabelWidth && (labelBackground || Utils::isOpenGLES())) valueStrWidth = maxLabelWidth; int valueStrHeight = valueFM.height(); valueStrWidth += paddingWidth / 2; // Fix clipping problem with skewed fonts (italic or italic-style) QSize labelSize; + qreal fontRatio = 1.0; -#if defined(QT_OPENGL_ES_2) - // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly. - // Add some padding before converting to power of two to avoid too tight fit - GLuint prePadding = 5; - // ES2 needs to use this always (when given) because of the power of 2 -issue. - if (maxLabelWidth) - valueStrWidth = maxLabelWidth + paddingWidth / 2; - labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding); - labelSize.setWidth(getNearestPowerOfTwo(labelSize.width(), paddingWidth)); - labelSize.setHeight(getNearestPowerOfTwo(labelSize.height(), paddingHeight)); -#else - if (!labelBackground) - labelSize = QSize(valueStrWidth, valueStrHeight); - else - labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); -#endif + if (Utils::isOpenGLES()) { + // Test if text with slighly smaller font would fit into one step smaller texture + // ie. if the text is just exceeded the smaller texture boundary, it would + // make a label with large empty space + uint testWidth = getNearestPowerOfTwo(valueStrWidth + prePadding) >> 1; + int diffToFit = (valueStrWidth + prePadding) - testWidth; + int maxSqueeze = int((valueStrWidth + prePadding) * 0.1f); + if (diffToFit < maxSqueeze && maxTextureSize > GLint(testWidth)) + targetWidth = testWidth; + } + + bool sizeOk = false; + int currentFontSize = textureFontSize; + do { + if (Utils::isOpenGLES()) { + // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly. + // Add some padding before converting to power of two to avoid too tight fit + labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding); + labelSize.setWidth(getNearestPowerOfTwo(labelSize.width())); + labelSize.setHeight(getNearestPowerOfTwo(labelSize.height())); + } else { + if (!labelBackground) + labelSize = QSize(valueStrWidth, valueStrHeight); + else + labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); + } + + if (!maxTextureSize || (labelSize.width() <= maxTextureSize + && (labelSize.width() <= targetWidth || !Utils::isOpenGLES()))) { + // Make sure the label is not too wide + sizeOk = true; + } else if (--currentFontSize == 4) { + qCritical() << "Label" << text << "is too long to be generated."; + return QImage(); + } else { + fontRatio = (qreal)currentFontSize / (qreal)textureFontSize; + // Reduce font size and try again + valueFont.setPointSize(currentFontSize); + QFontMetrics currentValueFM(valueFont); + if (maxLabelWidth && (labelBackground || Utils::isOpenGLES())) + valueStrWidth = maxLabelWidth * fontRatio; + else + valueStrWidth = currentValueFM.width(text); + valueStrHeight = currentValueFM.height(); + valueStrWidth += paddingWidth / 2; + } + } while (!sizeOk); // Create image QImage image = QImage(labelSize, QImage::Format_ARGB32); @@ -96,27 +139,30 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo painter.setFont(valueFont); if (!labelBackground) { painter.setPen(txtColor); -#if defined(QT_OPENGL_ES_2) - painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, - (labelSize.height() - valueStrHeight) / 2.0f, - valueStrWidth, valueStrHeight, - Qt::AlignCenter | Qt::AlignVCenter, - text); -#else - painter.drawText(0, 0, - valueStrWidth, valueStrHeight, - Qt::AlignCenter | Qt::AlignVCenter, - text); -#endif + if (Utils::isOpenGLES()) { + painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, + (labelSize.height() - valueStrHeight) / 2.0f, + valueStrWidth, valueStrHeight, + Qt::AlignCenter | Qt::AlignVCenter, + text); + } else { + painter.drawText(0, 0, + valueStrWidth, valueStrHeight, + Qt::AlignCenter | Qt::AlignVCenter, + text); + } } else { painter.setBrush(QBrush(bgrColor)); + qreal radius = 10.0 * fontRatio; if (borders) { - painter.setPen(QPen(QBrush(txtColor), 5, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin)); - painter.drawRoundedRect(5, 5, labelSize.width() - 10, labelSize.height() - 10, - 10.0, 10.0); + painter.setPen(QPen(QBrush(txtColor), 5.0 * fontRatio, + Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin)); + painter.drawRoundedRect(5, 5, + labelSize.width() - 10, labelSize.height() - 10, + radius, radius); } else { painter.setPen(bgrColor); - painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), 10.0, 10.0); + painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), radius, radius); } painter.setPen(txtColor); painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, @@ -133,62 +179,74 @@ QVector4D Utils::getSelection(QPoint mousepos, int height) // This is the only one that works with OpenGL ES 2.0, so we're forced to use it // Item count will be limited to 256*256*256 GLubyte pixel[4] = {255, 255, 255, 255}; - glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, - (void *)pixel); + QOpenGLContext::currentContext()->functions()->glReadPixels(mousepos.x(), height - mousepos.y(), + 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + (void *)pixel); QVector4D selectedColor(pixel[0], pixel[1], pixel[2], pixel[3]); return selectedColor; } -QImage Utils::getGradientImage(const QLinearGradient &gradient) +QImage Utils::getGradientImage(QLinearGradient &gradient) { - QImage image(QSize(1, 101), QImage::Format_RGB32); + QImage image(QSize(gradientTextureWidth, gradientTextureHeight), QImage::Format_RGB32); + gradient.setFinalStop(qreal(gradientTextureWidth), qreal(gradientTextureHeight)); + gradient.setStart(0.0, 0.0); + QPainter pmp(&image); pmp.setBrush(QBrush(gradient)); pmp.setPen(Qt::NoPen); - pmp.drawRect(0, 0, 1, 101); + pmp.drawRect(0, 0, int(gradientTextureWidth), int(gradientTextureHeight)); return image; } -Utils::ParamType Utils::mapFormatCharToParamType(const QChar &formatChar) +Utils::ParamType Utils::preParseFormat(const QString &format, QString &preStr, QString &postStr, + int &precision, char &formatSpec) { - ParamType retVal = ParamTypeUnknown; - if (formatChar == QLatin1Char('d') - || formatChar == QLatin1Char('i') - || formatChar == QLatin1Char('c')) { - retVal = ParamTypeInt; - } else if (formatChar == QLatin1Char('u') - || formatChar == QLatin1Char('o') - || formatChar == QLatin1Char('x') - || formatChar == QLatin1Char('X')) { - retVal = ParamTypeUInt; - } else if (formatChar == QLatin1Char('f') - || formatChar == QLatin1Char('F') - || formatChar == QLatin1Char('e') - || formatChar == QLatin1Char('E') - || formatChar == QLatin1Char('g') - || formatChar == QLatin1Char('G')) { - retVal = ParamTypeReal; + static QRegExp formatMatcher(QStringLiteral("^([^%]*)%([\\-\\+#\\s\\d\\.lhjztL]*)([dicuoxfegXFEG])(.*)$")); + static QRegExp precisionMatcher(QStringLiteral("\\.(\\d+)")); + + Utils::ParamType retVal; + + if (formatMatcher.indexIn(format, 0) != -1) { + preStr = formatMatcher.cap(1); + // Six and 'g' are defaults in Qt API + precision = 6; + if (!formatMatcher.cap(2).isEmpty()) { + if (precisionMatcher.indexIn(formatMatcher.cap(2), 0) != -1) + precision = precisionMatcher.cap(1).toInt(); + } + if (formatMatcher.cap(3).isEmpty()) + formatSpec = 'g'; + else + formatSpec = formatMatcher.cap(3).at(0).toLatin1(); + postStr = formatMatcher.cap(4); + retVal = mapFormatCharToParamType(formatSpec); + } else { + retVal = ParamTypeUnknown; + // The out parameters are irrelevant in unknown case } return retVal; } -Utils::ParamType Utils::findFormatParamType(const QString &format) +Utils::ParamType Utils::mapFormatCharToParamType(char formatSpec) { - static QRegExp formatMatcher(QStringLiteral("%[\\-\\+#\\s\\d\\.lhjztL]*([dicuoxfegXFEG])")); - - if (formatMatcher.indexIn(format, 0) != -1) { - QString capStr = formatMatcher.cap(1); - if (capStr.isEmpty()) - return ParamTypeUnknown; - else - return mapFormatCharToParamType(capStr.at(0)); + ParamType retVal = ParamTypeUnknown; + if (formatSpec == 'd' || formatSpec == 'i' || formatSpec == 'c') { + retVal = ParamTypeInt; + } else if (formatSpec == 'u' || formatSpec == 'o' + || formatSpec == 'x'|| formatSpec == 'X') { + retVal = ParamTypeUInt; + } else if (formatSpec == 'f' || formatSpec == 'F' + || formatSpec == 'e' || formatSpec == 'E' + || formatSpec == 'g' || formatSpec == 'G') { + retVal = ParamTypeReal; } - return ParamTypeUnknown; + return retVal; } -QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal value) +QString Utils::formatLabelSprintf(const QByteArray &format, Utils::ParamType paramType, qreal value) { switch (paramType) { case ParamTypeInt: @@ -198,7 +256,24 @@ QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal case ParamTypeReal: return QString().sprintf(format, value); default: - return QString::fromUtf8(format); // To detect errors + // Return format string to detect errors. Bars selection label logic also depends on this. + return QString::fromUtf8(format); + } +} + +QString Utils::formatLabelLocalized(Utils::ParamType paramType, qreal value, + const QLocale &locale, const QString &preStr, const QString &postStr, + int precision, char formatSpec, const QByteArray &format) +{ + switch (paramType) { + case ParamTypeInt: + case ParamTypeUInt: + return preStr + locale.toString(qint64(value)) + postStr; + case ParamTypeReal: + return preStr + locale.toString(value, formatSpec, precision) + postStr; + default: + // Return format string to detect errors. Bars selection label logic also depends on this. + return QString::fromUtf8(format); } } @@ -238,4 +313,41 @@ QQuaternion Utils::calculateRotation(const QVector3D &xyzRotations) return totalRotation; } +bool Utils::isOpenGLES() +{ +#if defined(QT_OPENGL_ES_2) + return true; +#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) + return false; +#else + static bool resolved = false; + static bool isES = false; + if (!resolved) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QWindow *dummySurface = 0; + if (!ctx) { + QSurfaceFormat surfaceFormat = qDefaultSurfaceFormat(); + dummySurface = new QWindow(); + dummySurface->setSurfaceType(QWindow::OpenGLSurface); + dummySurface->setFormat(surfaceFormat); + dummySurface->create(); + ctx = new QOpenGLContext; + ctx->setFormat(surfaceFormat); + ctx->create(); + ctx->makeCurrent(dummySurface); + } + + isES = ctx->isOpenGLES(); + resolved = true; + + if (dummySurface) { + ctx->doneCurrent(); + delete ctx; + delete dummySurface; + } + } + return isES; +#endif +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/utils/utils.pri b/src/datavisualization/utils/utils.pri index 30bfb156..904407b8 100644 --- a/src/datavisualization/utils/utils.pri +++ b/src/datavisualization/utils/utils.pri @@ -22,3 +22,5 @@ SOURCES += $$PWD/meshloader.cpp \ $$PWD/surfaceobject.cpp \ $$PWD/scatterobjectbufferhelper.cpp \ $$PWD/scatterpointbufferhelper.cpp + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h index d7187c16..1a46c731 100644 --- a/src/datavisualization/utils/utils_p.h +++ b/src/datavisualization/utils/utils_p.h @@ -45,7 +45,7 @@ public: ParamTypeReal }; - static GLuint getNearestPowerOfTwo(GLuint value, GLuint &padding); + static GLuint getNearestPowerOfTwo(GLuint value); static QVector4D vectorFromColor(const QColor &color); static QColor colorFromVector(const QVector3D &colorVector); static QColor colorFromVector(const QVector4D &colorVector); @@ -57,17 +57,22 @@ public: bool borders = false, int maxLabelWidth = 0); static QVector4D getSelection(QPoint mousepos, int height); - static QImage getGradientImage(const QLinearGradient &gradient); + static QImage getGradientImage(QLinearGradient &gradient); - static ParamType findFormatParamType(const QString &format); - static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value); + static ParamType preParseFormat(const QString &format, QString &preStr, QString &postStr, + int &precision, char &formatSpec); + static QString formatLabelSprintf(const QByteArray &format, ParamType paramType, qreal value); + static QString formatLabelLocalized(ParamType paramType, qreal value, + const QLocale &locale, const QString &preStr, const QString &postStr, + int precision, char formatSpec, const QByteArray &format); static QString defaultLabelFormat(); static float wrapValue(float value, float min, float max); static QQuaternion calculateRotation(const QVector3D &xyzRotations); + static bool isOpenGLES(); private: - static ParamType mapFormatCharToParamType(const QChar &formatChar); + static ParamType mapFormatCharToParamType(char formatSpec); }; QT_END_NAMESPACE_DATAVISUALIZATION |