diff options
Diffstat (limited to 'src/datavisualization/utils')
19 files changed, 2675 insertions, 0 deletions
diff --git a/src/datavisualization/utils/abstractobjecthelper.cpp b/src/datavisualization/utils/abstractobjecthelper.cpp new file mode 100644 index 00000000..d47f2fe6 --- /dev/null +++ b/src/datavisualization/utils/abstractobjecthelper.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "abstractobjecthelper_p.h" + +#include <QDebug> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +AbstractObjectHelper::AbstractObjectHelper() + : m_vertexbuffer(0), + m_normalbuffer(0), + m_uvbuffer(0), + m_elementbuffer(0), + m_indexCount(0), + m_meshDataLoaded(false) +{ +} + +AbstractObjectHelper::~AbstractObjectHelper() +{ + glDeleteBuffers(1, &m_vertexbuffer); + glDeleteBuffers(1, &m_uvbuffer); + glDeleteBuffers(1, &m_normalbuffer); + glDeleteBuffers(1, &m_elementbuffer); +} + +GLuint AbstractObjectHelper::vertexBuf() +{ + if (!m_meshDataLoaded) + qFatal("No loaded object"); + return m_vertexbuffer; +} + +GLuint AbstractObjectHelper::normalBuf() +{ + if (!m_meshDataLoaded) + qFatal("No loaded object"); + return m_normalbuffer; +} + +GLuint AbstractObjectHelper::uvBuf() +{ + if (!m_meshDataLoaded) + qFatal("No loaded object"); + return m_uvbuffer; +} + +GLuint AbstractObjectHelper::elementBuf() +{ + if (!m_meshDataLoaded) + qFatal("No loaded object"); + return m_elementbuffer; +} + +GLuint AbstractObjectHelper::indexCount() +{ + return m_indexCount; +} + +GLuint AbstractObjectHelper::indicesType() +{ + return m_indicesType; +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/abstractobjecthelper_p.h b/src/datavisualization/utils/abstractobjecthelper_p.h new file mode 100644 index 00000000..a6de6941 --- /dev/null +++ b/src/datavisualization/utils/abstractobjecthelper_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef ABSTRACTOBJECTHELPER_H +#define ABSTRACTOBJECTHELPER_H + +#include "datavisualizationglobal_p.h" +#include <QOpenGLFunctions> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class AbstractObjectHelper: protected QOpenGLFunctions +{ +protected: + AbstractObjectHelper(); +public: + ~AbstractObjectHelper(); + + GLuint vertexBuf(); + GLuint normalBuf(); + GLuint uvBuf(); + GLuint elementBuf(); + GLuint indexCount(); + GLuint indicesType(); + +public: + GLuint m_vertexbuffer; + GLuint m_normalbuffer; + GLuint m_uvbuffer; + GLuint m_elementbuffer; + + GLuint m_indexCount; + GLboolean m_meshDataLoaded; + + GLuint m_indicesType; +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif // ABSTRACTOBJECTHELPER_H diff --git a/src/datavisualization/utils/camerahelper.cpp b/src/datavisualization/utils/camerahelper.cpp new file mode 100644 index 00000000..29ed4d57 --- /dev/null +++ b/src/datavisualization/utils/camerahelper.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "camerahelper_p.h" + +#include <qmath.h> +#include <QMatrix4x4> +#include <QVector3D> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +CameraHelper::CameraHelper(QObject *parent) : + QObject(parent), + m_position(0, 0.25, 3), + m_target(0, 0, 0), + m_up(0, 1, 0), + m_previousMousePos(0,0), + m_xRotation(0), + m_yRotation(0), + m_defaultXRotation(0), + m_defaultYRotation(0), + m_rotationSpeed(100) +{ +} + +CameraHelper::~CameraHelper() +{ +} + + +// FUNCTIONS +void CameraHelper::setRotationSpeed(int speed) +{ + // increase for faster rotation + m_rotationSpeed = speed; +} + +void CameraHelper::setCameraRotation(const QPointF &rotation) +{ + m_xRotation = rotation.x(); + m_defaultXRotation = m_xRotation; + m_yRotation = rotation.y(); + m_defaultYRotation = m_yRotation; +} + +void CameraHelper::setDefaultCameraOrientation(const QVector3D &defaultPosition, + const QVector3D &defaultTarget, + const QVector3D &defaultUp) +{ + m_position = defaultPosition; + m_target = defaultTarget; + m_up = defaultUp; +} + +QMatrix4x4 CameraHelper::calculateViewMatrix(const QPoint &mousePos, int zoom, + int screenWidth, int screenHeight, bool showUnder) +{ + QMatrix4x4 viewMatrix; + GLfloat lowerLimit = 0.0f; + + if (showUnder) + lowerLimit = -90.0f; + + // Calculate mouse movement since last frame + GLfloat mouseMoveX = GLfloat(m_previousMousePos.x() - mousePos.x()) + / (screenWidth / m_rotationSpeed); + GLfloat mouseMoveY = GLfloat(m_previousMousePos.y() - mousePos.y()) + / (screenHeight / m_rotationSpeed); + // Apply to rotations + m_xRotation -= mouseMoveX; + m_yRotation -= mouseMoveY; + // Reset at 360 in x and limit to 0...90 in y + if (qAbs(m_xRotation) >= 360.0f) + m_xRotation = 0.0f; + if (m_yRotation >= 90.0f) + m_yRotation = 90.0f; + else if (m_yRotation <= lowerLimit) + m_yRotation = lowerLimit; + + // Apply to view matrix + viewMatrix.lookAt(m_position, m_target, m_up); + // Compensate for translation (if m_target is off origin) + viewMatrix.translate(m_target.x(), m_target.y(), m_target.z()); + // Apply rotations + // Handle x and z rotation when y -angle is other than 0 + viewMatrix.rotate(m_xRotation, 0, qCos(qDegreesToRadians(m_yRotation)), + qSin(qDegreesToRadians(m_yRotation))); + // y rotation is always "clean" + viewMatrix.rotate(m_yRotation, 1.0f, 0.0f, 0.0f); + // handle zoom by scaling + viewMatrix.scale((GLfloat)zoom / 100.0f); + // Compensate for translation (if m_target is off origin) + viewMatrix.translate(-m_target.x(), -m_target.y(), -m_target.z()); + //qDebug() << m_xRotation << m_yRotation; + + m_previousMousePos = mousePos; + return viewMatrix; +} + +QVector3D CameraHelper::calculateLightPosition(const QVector3D &lightPosition, + GLfloat fixedRotation, GLfloat distanceModifier) +{ + // Move light with camera + QVector3D newLightPosition; + GLfloat radiusFactor = lightPosition.z() * (1.5f + distanceModifier); // for making sure light is outside the scene at its lowest point + GLfloat xAngle; + GLfloat yAngle; + if (!fixedRotation) { + xAngle = qDegreesToRadians(m_xRotation); + yAngle = qDegreesToRadians(m_yRotation); + } else { + xAngle = qDegreesToRadians(fixedRotation); + yAngle = 0; + } + GLfloat radius = (radiusFactor + lightPosition.y()); // set radius to match the highest height of the light + GLfloat zPos = radius * qCos(xAngle) * qCos(yAngle); + GLfloat xPos = radius * qSin(xAngle) * qCos(yAngle); + GLfloat yPos = (radiusFactor + lightPosition.y()) * qSin(yAngle); + // Keep light in the set position in relation to camera + newLightPosition = QVector3D(-xPos + lightPosition.x(), + yPos + lightPosition.y(), + zPos + lightPosition.z()); + //qDebug() << newLightPosition << xAngle << yAngle << fixedRotation; + return newLightPosition; +} + +void CameraHelper::updateMousePos(const QPoint &mousePos) +{ + m_previousMousePos = mousePos; + // if mouse position is set to (0, 0), reset rotations + if (QPoint(0, 0) == mousePos) { + m_xRotation = m_defaultXRotation; + m_yRotation = m_defaultYRotation; + } +} + +QPointF CameraHelper::getCameraRotations() +{ + QPointF rotations(m_xRotation, m_yRotation); + return rotations; +} + +void CameraHelper::setCameraPreset(QDataVis::CameraPreset preset) +{ + switch (preset) { + case QDataVis::CameraPresetFrontLow: { + qDebug("CameraPresetFrontLow"); + CameraHelper::setCameraRotation(QPointF(0.0f, 0.0f)); + break; + } + case QDataVis::CameraPresetFront: { + qDebug("CameraPresetFront"); + CameraHelper::setCameraRotation(QPointF(0.0f, 22.5f)); + break; + } + case QDataVis::CameraPresetFrontHigh: { + qDebug("CameraPresetFrontHigh"); + CameraHelper::setCameraRotation(QPointF(0.0f, 45.0f)); + break; + } + case QDataVis::CameraPresetLeftLow: { + qDebug("CameraPresetLeftLow"); + CameraHelper::setCameraRotation(QPointF(90.0f, 0.0f)); + break; + } + case QDataVis::CameraPresetLeft: { + qDebug("CameraPresetLeft"); + CameraHelper::setCameraRotation(QPointF(90.0f, 22.5f)); + break; + } + case QDataVis::CameraPresetLeftHigh: { + qDebug("CameraPresetLeftHigh"); + CameraHelper::setCameraRotation(QPointF(90.0f, 45.0f)); + break; + } + case QDataVis::CameraPresetRightLow: { + qDebug("CameraPresetRightLow"); + CameraHelper::setCameraRotation(QPointF(-90.0f, 0.0f)); + break; + } + case QDataVis::CameraPresetRight: { + qDebug("CameraPresetRight"); + CameraHelper::setCameraRotation(QPointF(-90.0f, 22.5f)); + break; + } + case QDataVis::CameraPresetRightHigh: { + qDebug("CameraPresetRightHigh"); + CameraHelper::setCameraRotation(QPointF(-90.0f, 45.0f)); + break; + } + case QDataVis::CameraPresetBehindLow: { + qDebug("CameraPresetBehindLow"); + CameraHelper::setCameraRotation(QPointF(180.0f, 0.0f)); + break; + } + case QDataVis::CameraPresetBehind: { + qDebug("CameraPresetBehind"); + CameraHelper::setCameraRotation(QPointF(180.0f, 22.5f)); + break; + } + case QDataVis::CameraPresetBehindHigh: { + qDebug("CameraPresetBehindHigh"); + CameraHelper::setCameraRotation(QPointF(180.0f, 45.0f)); + break; + } + case QDataVis::CameraPresetIsometricLeft: { + qDebug("CameraPresetIsometricLeft"); + CameraHelper::setCameraRotation(QPointF(45.0f, 22.5f)); + break; + } + case QDataVis::CameraPresetIsometricLeftHigh: { + qDebug("CameraPresetIsometricLeftHigh"); + CameraHelper::setCameraRotation(QPointF(45.0f, 45.0f)); + break; + } + case QDataVis::CameraPresetIsometricRight: { + qDebug("CameraPresetIsometricRight"); + CameraHelper::setCameraRotation(QPointF(-45.0f, 22.5f)); + break; + } + case QDataVis::CameraPresetIsometricRightHigh: { + qDebug("CameraPresetIsometricRightHigh"); + CameraHelper::setCameraRotation(QPointF(-45.0f, 45.0f)); + break; + } + case QDataVis::CameraPresetDirectlyAbove: { + qDebug("CameraPresetDirectlyAbove"); + CameraHelper::setCameraRotation(QPointF(0.0f, 90.0f)); + break; + } + case QDataVis::CameraPresetDirectlyAboveCW45: { + qDebug("CameraPresetDirectlyAboveCW45"); + CameraHelper::setCameraRotation(QPointF(-45.0f, 90.0f)); + break; + } + case QDataVis::CameraPresetDirectlyAboveCCW45: { + qDebug("CameraPresetDirectlyAboveCCW45"); + CameraHelper::setCameraRotation(QPointF(45.0f, 90.0f)); + break; + } + case QDataVis::CameraPresetFrontBelow: { + qDebug("CameraPresetFrontBelow"); + CameraHelper::setCameraRotation(QPointF(0.0f, -45.0f)); + break; + } + case QDataVis::CameraPresetLeftBelow: { + qDebug("CameraPresetLeftBelow"); + CameraHelper::setCameraRotation(QPointF(90.0f, -45.0f)); + break; + } + case QDataVis::CameraPresetRightBelow: { + qDebug("CameraPresetRightBelow"); + CameraHelper::setCameraRotation(QPointF(-90.0f, -45.0f)); + break; + } + case QDataVis::CameraPresetBehindBelow: { + qDebug("CameraPresetBehindBelow"); + CameraHelper::setCameraRotation(QPointF(180.0f, -45.0f)); + break; + } + case QDataVis::CameraPresetDirectlyBelow: { + qDebug("CameraPresetDirectlyBelow"); + CameraHelper::setCameraRotation(QPointF(0.0f, -90.0f)); + break; + } + default: + break; + } +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/camerahelper_p.h b/src/datavisualization/utils/camerahelper_p.h new file mode 100644 index 00000000..1ef4d257 --- /dev/null +++ b/src/datavisualization/utils/camerahelper_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CAMERAPOSITIONER_P_H +#define CAMERAPOSITIONER_P_H + +#include "datavisualizationglobal_p.h" +#include "q3dbars.h" +#include <QObject> + +class QMatrix4x4; +class QVector3D; +class QPoint; +class QPointF; + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class CameraHelper : public QObject +{ + Q_OBJECT + +private: + QVector3D m_position; + QVector3D m_target; + QVector3D m_up; + + QPoint m_previousMousePos; + + GLfloat m_xRotation; + GLfloat m_yRotation; + GLfloat m_defaultXRotation; + GLfloat m_defaultYRotation; + + GLfloat m_rotationSpeed; + +public: + explicit CameraHelper(QObject *parent = 0); + ~CameraHelper(); + + // How fast camera rotates when mouse is dragged. Default is 100. + void setRotationSpeed(int speed); + // Set camera rotation in degrees + void setCameraRotation(const QPointF &rotation); + // Get camera rotations + QPointF getCameraRotations(); + // Set default camera orientation. Position's x and y should be 0. + void setDefaultCameraOrientation(const QVector3D &defaultPosition, + const QVector3D &defaultTarget, + const QVector3D &defaultUp); + // Calculate view matrix based on rotation and zoom + QMatrix4x4 calculateViewMatrix(const QPoint &mousePos, int zoom, + int screenWidth, int screenHeight, + bool showUnder = false); + // Calcluate light position based on rotation. Call after calling calculateViewMatrix to get + // up-to-date position + QVector3D calculateLightPosition(const QVector3D &lightPosition, + GLfloat fixedRotation = 0.0f, + GLfloat distanceModifier = 0.0f); + void updateMousePos(const QPoint &mousePos); + void setCameraPreset(QDataVis::CameraPreset preset); +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif diff --git a/src/datavisualization/utils/meshloader.cpp b/src/datavisualization/utils/meshloader.cpp new file mode 100644 index 00000000..119cde3a --- /dev/null +++ b/src/datavisualization/utils/meshloader.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "meshloader_p.h" + +#include <QFile> +#include <QStringList> +#include <QVector> +#include <QVector2D> +#include <QVector3D> + +#include <QDebug> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +QString slashTag = QStringLiteral("/"); + +bool MeshLoader::loadOBJ(const QString &path, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals) +{ + //qDebug() << "Loading OBJ file" << path; + + QVector<unsigned int> vertexIndices, uvIndices, normalIndices; + QVector<QVector3D> temp_vertices; + QVector<QVector2D> temp_uvs; + QVector<QVector3D> temp_normals; + + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning("Cannot open the file"); + return false; + } + + QTextStream textIn(&file); + while (!textIn.atEnd()) { + QString line = textIn.readLine(); + QStringList lineContents = line.split(QStringLiteral(" ")); + if (!lineContents.at(0).compare(QStringLiteral("v"))) { + QVector3D vertex; + vertex.setX(lineContents.at(1).toFloat()); + vertex.setY(lineContents.at(2).toFloat()); + vertex.setZ(lineContents.at(3).toFloat()); + temp_vertices.append(vertex); + } + else if (!lineContents.at(0).compare(QStringLiteral("vt"))) { + QVector2D uv; + uv.setX(lineContents.at(1).toFloat()); + uv.setY(lineContents.at(2).toFloat()); // invert this if using DDS textures + temp_uvs.append(uv); + } + else if (!lineContents.at(0).compare(QStringLiteral("vn"))) { + QVector3D normal; + normal.setX(lineContents.at(1).toFloat()); + normal.setY(lineContents.at(2).toFloat()); + normal.setZ(lineContents.at(3).toFloat()); + temp_normals.append(normal); + } + else if (!lineContents.at(0).compare(QStringLiteral("f"))) { + unsigned int vertexIndex[3], uvIndex[3], normalIndex[3]; + QStringList set1 = lineContents.at(1).split(slashTag); + QStringList set2 = lineContents.at(2).split(slashTag); + QStringList set3 = lineContents.at(3).split(slashTag); + vertexIndex[0] = set1.at(0).toUInt(); + vertexIndex[1] = set2.at(0).toUInt(); + vertexIndex[2] = set3.at(0).toUInt(); + uvIndex[0] = set1.at(1).toUInt(); + uvIndex[1] = set2.at(1).toUInt(); + uvIndex[2] = set3.at(1).toUInt(); + normalIndex[0] = set1.at(2).toUInt(); + normalIndex[1] = set2.at(2).toUInt(); + normalIndex[2] = set3.at(2).toUInt(); + vertexIndices.append(vertexIndex[0]); + vertexIndices.append(vertexIndex[1]); + vertexIndices.append(vertexIndex[2]); + uvIndices.append(uvIndex[0]); + uvIndices.append(uvIndex[1]); + uvIndices.append(uvIndex[2]); + normalIndices.append(normalIndex[0]); + normalIndices.append(normalIndex[1]); + normalIndices.append(normalIndex[2]); + } + else { + //qWarning("Line did not contain usable data"); + } + } + + // For each vertex of each triangle + for (int i = 0; i < vertexIndices.size(); i++) { + // Get the indices of its attributes + unsigned int vertexIndex = vertexIndices[i]; + unsigned int uvIndex = uvIndices[i]; + unsigned int normalIndex = normalIndices[i]; + + // Get the attributes thanks to the index + QVector3D vertex = temp_vertices[vertexIndex - 1]; + QVector2D uv = temp_uvs[uvIndex - 1]; + QVector3D normal = temp_normals[normalIndex - 1]; + + // Put the attributes in buffers + out_vertices.append(vertex); + out_uvs.append(uv); + out_normals.append(normal); + } + + return true; +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/meshloader_p.h b/src/datavisualization/utils/meshloader_p.h new file mode 100644 index 00000000..48551fff --- /dev/null +++ b/src/datavisualization/utils/meshloader_p.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef MESHLOADER_P_H +#define MESHLOADER_P_H + +#include "datavisualizationglobal_p.h" + +class QVector2D; +class QVector3D; + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class MeshLoader +{ + public: + static bool loadOBJ(const QString &path, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals); + // TODO: add loaders for other formats? +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif diff --git a/src/datavisualization/utils/objecthelper.cpp b/src/datavisualization/utils/objecthelper.cpp new file mode 100644 index 00000000..9660c215 --- /dev/null +++ b/src/datavisualization/utils/objecthelper.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "meshloader_p.h" +#include "vertexindexer_p.h" +#include "objecthelper_p.h" +#include "abstractobjecthelper_p.h" + +#include <QDebug> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +ObjectHelper::ObjectHelper(const QString &objectFile) + : m_objectFile(objectFile) +{ + m_indicesType = GL_UNSIGNED_SHORT; +} + +ObjectHelper::~ObjectHelper() +{ +} + +void ObjectHelper::setObjectFile(const QString &objectFile) +{ + m_objectFile = objectFile; +} + +void ObjectHelper::load() +{ + initializeOpenGLFunctions(); + if (m_meshDataLoaded) { + // Delete old data + glDeleteBuffers(1, &m_vertexbuffer); + glDeleteBuffers(1, &m_uvbuffer); + glDeleteBuffers(1, &m_normalbuffer); + glDeleteBuffers(1, &m_elementbuffer); + } + QVector<QVector3D> vertices; + QVector<QVector2D> uvs; + QVector<QVector3D> normals; + bool loadOk = MeshLoader::loadOBJ(m_objectFile, vertices, uvs, normals); + if (!loadOk) + qFatal("loading failed"); + + //qDebug() << "vertex count" << vertices.size();; + + // Index vertices + QVector<unsigned short> indices; + QVector<QVector3D> indexed_vertices; + QVector<QVector2D> indexed_uvs; + QVector<QVector3D> indexed_normals; + VertexIndexer::indexVBO(vertices, uvs, normals, indices, indexed_vertices, indexed_uvs, + indexed_normals); + + m_indexCount = indices.size(); + //qDebug() << "index count" << m_indexCount; + + glGenBuffers(1, &m_vertexbuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); + glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(QVector3D), + &indexed_vertices.at(0), + GL_STATIC_DRAW); + + glGenBuffers(1, &m_normalbuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_normalbuffer); + glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(QVector3D), + &indexed_normals.at(0), + GL_STATIC_DRAW); + + glGenBuffers(1, &m_uvbuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer); + glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(QVector2D), + &indexed_uvs.at(0), GL_STATIC_DRAW); + + glGenBuffers(1, &m_elementbuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), + &indices.at(0), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + m_meshDataLoaded = true; +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/objecthelper_p.h b/src/datavisualization/utils/objecthelper_p.h new file mode 100644 index 00000000..9d643fdd --- /dev/null +++ b/src/datavisualization/utils/objecthelper_p.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef OBJECTHELPER_P_H +#define OBJECTHELPER_P_H + +#include "datavisualizationglobal_p.h" +#include "abstractobjecthelper_p.h" +#include <QOpenGLFunctions> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class ObjectHelper : public AbstractObjectHelper +{ +public: + ObjectHelper(const QString &objectFile = QString()); + ~ObjectHelper(); + + void setObjectFile(const QString &objectFile); + + void load(); + +private: + QString m_objectFile; +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp new file mode 100644 index 00000000..7df1736c --- /dev/null +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "shaderhelper_p.h" + +#include <QOpenGLShader> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +ShaderHelper::ShaderHelper(QObject *parent, + const QString &vertexShader, + const QString &fragmentShader, + const QString &texture, + const QString &depthTexture) + : m_caller(parent), + m_program(0), + m_vertexShaderFile(vertexShader), + m_fragmentShaderFile(fragmentShader), + m_textureFile(texture), + m_depthTextureFile(depthTexture) +{ +} + +ShaderHelper::~ShaderHelper() +{ + delete m_program; +} + +void ShaderHelper::setShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + m_vertexShaderFile = vertexShader; + m_fragmentShaderFile = fragmentShader; +} + +void ShaderHelper::setTextures(const QString &texture, + const QString &depthTexture) +{ + m_textureFile = texture; + m_depthTextureFile = depthTexture; +} + +void ShaderHelper::initialize() +{ + if (m_program) + delete m_program; + m_program = new QOpenGLShaderProgram(m_caller); + if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, m_vertexShaderFile)) + qFatal("Compiling Vertex shader failed"); + if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, m_fragmentShaderFile)) + qFatal("Compiling Fragment shader failed"); + m_program->link(); + + m_positionAttr = m_program->attributeLocation("vertexPosition_mdl"); + m_normalAttr = m_program->attributeLocation("vertexNormal_mdl"); + m_uvAttr = m_program->attributeLocation("vertexUV"); + + m_mvpMatrixUniform = m_program->uniformLocation("MVP"); + m_viewMatrixUniform = m_program->uniformLocation("V"); + m_modelMatrixUniform = m_program->uniformLocation("M"); + m_invTransModelMatrixUniform = m_program->uniformLocation("itM"); + m_depthMatrixUniform = m_program->uniformLocation("depthMVP"); + m_lightPositionUniform = m_program->uniformLocation("lightPosition_wrld"); + m_lightStrengthUniform = m_program->uniformLocation("lightStrength"); + m_ambientStrengthUniform = m_program->uniformLocation("ambientStrength"); + m_shadowQualityUniform = m_program->uniformLocation("shadowQuality"); + m_colorUniform = m_program->uniformLocation("color_mdl"); + m_textureUniform = m_program->uniformLocation("textureSampler"); + m_shadowUniform = m_program->uniformLocation("shadowMap"); + + m_initialized = true; +} + +bool ShaderHelper::testCompile() +{ + if (m_program) + delete m_program; + m_program = new QOpenGLShaderProgram(m_caller); + if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, m_vertexShaderFile)) + return false; + if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, m_fragmentShaderFile)) + return false; + return true; +} + +void ShaderHelper::bind() +{ + m_program->bind(); +} + +void ShaderHelper::release() +{ + m_program->release(); +} + +void ShaderHelper::setUniformValue(GLuint uniform, const QVector3D &value) +{ + m_program->setUniformValue(uniform, value); +} + +void ShaderHelper::setUniformValue(GLuint uniform, const QVector4D &value) +{ + m_program->setUniformValue(uniform, value); +} + +void ShaderHelper::setUniformValue(GLuint uniform, const QMatrix4x4 &value) +{ + m_program->setUniformValue(uniform, value); +} + +void ShaderHelper::setUniformValue(GLuint uniform, GLfloat value) +{ + m_program->setUniformValue(uniform, value); +} + +void ShaderHelper::setUniformValue(GLuint uniform, GLint value) +{ + m_program->setUniformValue(uniform, value); +} + +GLuint ShaderHelper::MVP() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_mvpMatrixUniform; +} + +GLuint ShaderHelper::view() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_viewMatrixUniform; +} + +GLuint ShaderHelper::model() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_modelMatrixUniform; +} + +GLuint ShaderHelper::nModel() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_invTransModelMatrixUniform; +} + +GLuint ShaderHelper::depth() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_depthMatrixUniform; +} + +GLuint ShaderHelper::lightP() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_lightPositionUniform; +} + +GLuint ShaderHelper::lightS() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_lightStrengthUniform; +} + +GLuint ShaderHelper::ambientS() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_ambientStrengthUniform; +} + +GLuint ShaderHelper::shadowQ() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_shadowQualityUniform; +} + +GLuint ShaderHelper::color() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_colorUniform; +} + +GLuint ShaderHelper::texture() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_textureUniform; +} + +GLuint ShaderHelper::shadow() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_shadowUniform; +} + +GLuint ShaderHelper::posAtt() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_positionAttr; +} + +GLuint ShaderHelper::uvAtt() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_uvAttr; +} + +GLuint ShaderHelper::normalAtt() +{ + if (!m_initialized) + qFatal("Shader not initialized"); + return m_normalAttr; +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h new file mode 100644 index 00000000..73e5b9ee --- /dev/null +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef SHADERHELPER_P_H +#define SHADERHELPER_P_H + +#include "datavisualizationglobal_p.h" +#include <QOpenGLFunctions> + +class QOpenGLShaderProgram; + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class ShaderHelper +{ + public: + ShaderHelper(QObject *parent, + const QString &vertexShader = QString(), + const QString &fragmentShader = QString(), + const QString &texture = QString(), + const QString &depthTexture = QString()); + ~ShaderHelper(); + + void setShaders(const QString &vertexShader, const QString &fragmentShader); + void setTextures(const QString &texture, const QString &depthTexture); + + void initialize(); + 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 posAtt(); + GLuint uvAtt(); + GLuint normalAtt(); + + private: + QObject *m_caller; + QOpenGLShaderProgram *m_program; + + QString m_vertexShaderFile; + QString m_fragmentShaderFile; + + 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; + + GLboolean m_initialized; +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp new file mode 100644 index 00000000..f78fcec3 --- /dev/null +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "surfaceobject_p.h" +#include "abstractobjecthelper_p.h" + +#include <QVector3D> +#include <QVector2D> + +#include <QDebug> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +SurfaceObject::SurfaceObject() +{ + 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); +} + +SurfaceObject::~SurfaceObject() +{ + glDeleteBuffers(1, &m_gridElementbuffer); +} + +void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, + GLfloat yRange, GLfloat yMin, bool changeGeometry) +{ + m_columns = space.width(); + m_rows = space.height(); + int totalSize = m_rows * m_columns; + GLfloat xMin = dataArray.at(0)->at(0).x(); + GLfloat zMin = dataArray.at(0)->at(0).z(); + GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f; + GLfloat yNormalizer = yRange / 2.0f; + GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f; + GLfloat uvX = 1.0 / GLfloat(m_columns - 1); + GLfloat uvY = 1.0 / GLfloat(m_rows - 1); + + m_surfaceType = SurfaceSmooth; + + // Create/populate vertice table + if (changeGeometry) + m_vertices.resize(totalSize); + + QVector<QVector2D> uvs; + if (changeGeometry) + uvs.resize(totalSize); + int totalIndex = 0; + 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 = ((data.x() - xMin) / xNormalizer); + float normalizedY = ((data.y() - yMin) / yNormalizer); + float normalizedZ = ((data.z() - zMin) / zNormalizer); + m_vertices[totalIndex] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f, normalizedZ + 1.0f); + if (changeGeometry) + uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); + totalIndex++; + } + } + + // 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; + 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)); + } + int p = row + colLimit; + m_normals[totalIndex++] = normal(m_vertices.at(p), + m_vertices.at(p + m_columns), + m_vertices.at(p - 1)); + } + 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)); + } + int p = m_rows * colLimit; + m_normals[totalIndex++] = normal(m_vertices.at(p), + m_vertices.at(p - 1), + m_vertices.at(p - m_columns - 1)); + + // Create indices table + GLint *indices = 0; + if (changeGeometry) { + m_indexCount = 6 * colLimit * rowLimit; + indices = new GLint[m_indexCount]; + p = 0; + for (int row = 0; row < rowLimit * m_columns; row += m_columns) { + for (int j = 0; j < colLimit; 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; + } + } + } + + // Create line element indices + GLint *gridIndices = 0; + if (changeGeometry) { + m_gridIndexCount = 2 * m_columns * rowLimit + 2 * m_rows * colLimit; + gridIndices = new GLint[m_gridIndexCount]; + p = 0; + for (int i = 0, row = 0; i < m_rows; i++, row += m_columns) { + for (int j = 0; j < colLimit; j++) { + gridIndices[p++] = row + j; + gridIndices[p++] = row + j + 1; + } + } + for (int i = 0, row = 0; i < rowLimit; i++, row += m_columns) { + for (int j = 0; j < m_columns; j++) { + gridIndices[p++] = row + j; + gridIndices[p++] = row + j + m_columns; + } + } + } + + createBuffers(m_vertices, uvs, m_normals, indices, gridIndices, changeGeometry); + + delete[] indices; + delete[] gridIndices; +} + + +void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &space, + GLfloat yRange, GLfloat yMin, bool changeGeometry) +{ + m_columns = space.width(); + m_rows = space.height(); + int totalSize = m_rows * m_columns * 2; + GLfloat xMin = dataArray.at(0)->at(0).x(); + GLfloat zMin = dataArray.at(0)->at(0).z(); + GLfloat xNormalizer = (dataArray.at(0)->last().x() - xMin) / 2.0f; + GLfloat yNormalizer = yRange / 2.0f; + GLfloat zNormalizer = (dataArray.last()->at(0).z() - zMin) / -2.0f; + GLfloat uvX = 1.0 / GLfloat(m_columns - 1); + GLfloat uvY = 1.0 / GLfloat(m_rows - 1); + + m_surfaceType = SurfaceFlat; + + // Create vertice table + if (changeGeometry) + m_vertices.resize(totalSize); + + QVector<QVector2D> uvs; + if (changeGeometry) + uvs.resize(totalSize); + + int totalIndex = 0; + int rowLimit = m_rows - 1; + int colLimit = m_columns - 1; + int doubleColumns = m_columns * 2 - 2; + int rowColLimit = rowLimit * doubleColumns; + + 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 = ((data.x() - xMin) / xNormalizer); + float normalizedY = ((data.y() - yMin) / yNormalizer); + float normalizedZ = ((data.z() - zMin) / zNormalizer); + m_vertices[totalIndex] = QVector3D(normalizedX - 1.0f, normalizedY - 1.0f, normalizedZ + 1.0f); + if (changeGeometry) + uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY); + + totalIndex++; + + if (j > 0 && j < colLimit) { + m_vertices[totalIndex] = m_vertices[totalIndex - 1]; + if (changeGeometry) + uvs[totalIndex] = uvs[totalIndex - 1]; + totalIndex++; + } + } + } + + // Create normals & indices table + GLint *indices = 0; + int p = 0; + if (changeGeometry) { + int normalCount = 2 * colLimit * rowLimit; + m_indexCount = 3 * normalCount; + indices = new GLint[m_indexCount]; + m_normals.resize(normalCount); + } + + totalIndex = 0; + 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)); + + // 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)); + + if (changeGeometry) { + // 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; + } + } + } + + // Create grid line element indices + GLint *gridIndices = 0; + if (changeGeometry) { + m_gridIndexCount = 2 * m_columns * rowLimit + 2 * m_rows * colLimit; + gridIndices = new GLint[m_gridIndexCount]; + p = 0; + int fullRowLimit = m_rows * doubleColumns; + for (int row = 0; row < fullRowLimit; row += doubleColumns) { + for (int j = 0; j < doubleColumns; j += 2) { + gridIndices[p++] = row + j; + gridIndices[p++] = row + j + 1; + + if (row < rowColLimit) { + gridIndices[p++] = row + j; + gridIndices[p++] = row + j + doubleColumns; + } + } + } + for (int i = doubleColumns - 1; i < rowColLimit; i += doubleColumns) { + gridIndices[p++] = i; + gridIndices[p++] = i + doubleColumns; + } + } + + createBuffers(m_vertices, uvs, m_normals, indices, gridIndices, changeGeometry); + + delete[] indices; + delete[] gridIndices; +} + +void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs, + const QVector<QVector3D> &normals, const GLint *indices, + const GLint *gridIndices, bool changeGeometry) +{ + // Move to buffers + glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(QVector3D), + &vertices.at(0), GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, m_normalbuffer); + 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); + } + 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, m_gridElementbuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_gridIndexCount * sizeof(GLint), + gridIndices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_meshDataLoaded = true; +} + +GLuint SurfaceObject::gridElementBuf() +{ + if (!m_meshDataLoaded) + qFatal("No loaded object"); + return m_gridElementbuffer; +} + +GLuint SurfaceObject::gridIndexCount() +{ + return m_gridIndexCount; +} + +QVector3D SurfaceObject::vertexAt(int column, int row) +{ + int pos = 0; + if (m_surfaceType == SurfaceFlat) + pos = row * (m_columns * 2 - 2) + column * 2 - (column > 0); + else + pos = row * m_columns + column; + return m_vertices.at(pos); +} + +QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c) +{ + QVector3D v1 = b - a; + QVector3D v2 = c - a; + return QVector3D::crossProduct(v1, v2); +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h new file mode 100644 index 00000000..4f30f7c0 --- /dev/null +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef SURFACEOBJECT_P_H +#define SURFACEOBJECT_P_H + +#include "datavisualizationglobal_p.h" +#include "abstractobjecthelper_p.h" +#include "qsurfacedataproxy.h" + +#include <QOpenGLFunctions> +#include <QRect> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class SurfaceObject : public AbstractObjectHelper +{ +public: + SurfaceObject(); + ~SurfaceObject(); + + void setUpData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange, + GLfloat yMin, bool changeGeometry); + void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange, + GLfloat yMin, bool changeGeometry); + GLuint gridElementBuf(); + GLuint gridIndexCount(); + QVector3D vertexAt(int column, int row); + +private: + 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, + const GLint *gridIndices, bool changeGeometry); + +private: + enum SurfaceType { + SurfaceSmooth, + SurfaceFlat + }; + int m_surfaceType; + int m_columns; + int m_rows; + GLuint m_gridElementbuffer; + GLuint m_gridIndexCount; + QVector<QVector3D> m_vertices; + QVector<QVector3D> m_normals; +}; + +QT_DATAVISUALIZATION_END_NAMESPACE +#endif // SURFACEOBJECT_P_H diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp new file mode 100644 index 00000000..25fe17ac --- /dev/null +++ b/src/datavisualization/utils/texturehelper.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "texturehelper_p.h" +#include "utils_p.h" + +#include <QImage> + +#include <QDebug> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +TextureHelper::TextureHelper() +{ + initializeOpenGLFunctions(); +} + +TextureHelper::~TextureHelper() +{ +} + +GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFiltering, + bool convert, bool smoothScale) +{ + if (image.isNull()) + return 0; + + QImage texImage = image; + +#if defined(Q_OS_ANDROID) + GLuint temp; + //qDebug() << "old size" << image.size(); + 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); + } + //qDebug() << "new size" << texImage.size(); +#endif + + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); + if (convert) + texImage = convertToGLFormat(texImage); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texImage.width(), texImage.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage.bits()); + if (smoothScale) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + if (useTrilinearFiltering) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glGenerateMipmap(GL_TEXTURE_2D); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glBindTexture(GL_TEXTURE_2D, 0); + return textureId; +} + +GLuint TextureHelper::createCubeMapTexture(const QImage &image, bool useTrilinearFiltering) +{ + if (image.isNull()) + return 0; + + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_CUBE_MAP, textureId); + QImage glTexture = convertToGLFormat(image); + glTexImage2D(GL_TEXTURE_CUBE_MAP, 0, GL_RGBA, glTexture.width(), glTexture.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, glTexture.bits()); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (useTrilinearFiltering) { + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + } else { + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glBindTexture(GL_TEXTURE_2D, 0); + return textureId; +} + +GLuint TextureHelper::createSelectionBuffer(const QSize &size, GLuint &texture, + GLuint &depthTexture) +{ + GLuint framebuffer; + + // Create frame buffer + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + // Create texture for the selection buffer + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +//#if !defined(QT_OPENGL_ES_2) +// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGB, +// GL_UNSIGNED_BYTE, NULL); +//#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, + GL_UNSIGNED_BYTE, NULL); +//#endif + + // Create texture object for the depth buffer + glGenTextures(1, &depthTexture); + glBindTexture(GL_TEXTURE_2D, depthTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width(), size.height(), + 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + // Attach texture to color attachment + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + // Attach texture to depth attachment + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); + + // 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); + + return framebuffer; +} + +GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuffer, + GLuint &depthBuffer) +{ + GLuint textureid; + + // Create texture for the selection buffer + glGenTextures(1, &textureid); + glBindTexture(GL_TEXTURE_2D, textureid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#if !defined(QT_OPENGL_ES_2) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, + GL_UNSIGNED_BYTE, NULL); +#endif + glBindTexture(GL_TEXTURE_2D, 0); + + // Create render buffer + if (!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 + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + // Create frame buffer + if (!frameBuffer) + glGenFramebuffers(1, &frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + + // Attach texture to color attachment + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid, 0); + // Attach renderbuffer to depth attachment + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); + + // 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); + + return textureid; +} + +#if !defined(QT_OPENGL_ES_2) +GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint &frameBuffer, 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_COMPONENT16, size.width() * textureSize, + size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 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); + + return depthtextureid; +} +#endif + +void TextureHelper::deleteTexture(const GLuint *texture) +{ + glDeleteTextures(1, texture); +} + +QImage TextureHelper::convertToGLFormat(const QImage &srcImage) +{ + QImage res(srcImage.size(), QImage::Format_ARGB32); + convertToGLFormatHelper(res, srcImage.convertToFormat(QImage::Format_ARGB32), GL_RGBA); + return res; +} + +void TextureHelper::convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage, + GLenum texture_format) +{ + Q_ASSERT(dstImage.depth() == 32); + Q_ASSERT(srcImage.depth() == 32); + + if (dstImage.size() != srcImage.size()) { + int target_width = dstImage.width(); + int target_height = dstImage.height(); + qreal sx = target_width / qreal(srcImage.width()); + qreal sy = target_height / qreal(srcImage.height()); + + quint32 *dest = (quint32 *) dstImage.scanLine(0); // NB! avoid detach here + uchar *srcPixels = (uchar *) srcImage.scanLine(srcImage.height() - 1); + int sbpl = srcImage.bytesPerLine(); + int dbpl = dstImage.bytesPerLine(); + + int ix = int(0x00010000 / sx); + int iy = int(0x00010000 / sy); + + quint32 basex = int(0.5 * ix); + quint32 srcy = int(0.5 * iy); + + // scale, swizzle and mirror in one loop + while (target_height--) { + const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl); + int srcx = basex; + for (int x=0; x<target_width; ++x) { + dest[x] = qt_gl_convertToGLFormatHelper(src[srcx >> 16], texture_format); + srcx += ix; + } + dest = (quint32 *)(((uchar *) dest) + dbpl); + srcy += iy; + } + } else { + const int width = srcImage.width(); + const int height = srcImage.height(); + const uint *p = (const uint*) srcImage.scanLine(srcImage.height() - 1); + uint *q = (uint*) dstImage.scanLine(0); + +#if !defined(QT_OPENGL_ES_2) + if (texture_format == GL_BGRA) { +#else + if (texture_format == GL_BGRA8_EXT) { +#endif + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // mirror + swizzle + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = ((*p << 24) & 0xff000000) + | ((*p >> 24) & 0x000000ff) + | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00); + p++; + q++; + } + p -= 2 * width; + } + } else { + const uint bytesPerLine = srcImage.bytesPerLine(); + for (int i=0; i < height; ++i) { + memcpy(q, p, bytesPerLine); + q += width; + p -= width; + } + } + } else { + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = (*p << 8) | ((*p >> 24) & 0xff); + p++; + q++; + } + p -= 2 * width; + } + } else { + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + p++; + q++; + } + p -= 2 * width; + } + } + } + } +} + +QRgb TextureHelper::qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format) +{ +#if !defined(QT_OPENGL_ES_2) + if (texture_format == GL_BGRA) { +#else + if (texture_format == GL_BGRA8_EXT) { +#endif + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return ((src_pixel << 24) & 0xff000000) + | ((src_pixel >> 24) & 0x000000ff) + | ((src_pixel << 8) & 0x00ff0000) + | ((src_pixel >> 8) & 0x0000ff00); + } else { + return src_pixel; + } + } else { // GL_RGBA + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return (src_pixel << 8) | ((src_pixel >> 24) & 0xff); + } else { + return ((src_pixel << 16) & 0xff0000) + | ((src_pixel >> 16) & 0xff) + | (src_pixel & 0xff00ff00); + } + } +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h new file mode 100644 index 00000000..f7779b59 --- /dev/null +++ b/src/datavisualization/utils/texturehelper_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef TEXTUREHELPER_P_H +#define TEXTUREHELPER_P_H + +#include "datavisualizationglobal_p.h" +#include <QOpenGLFunctions> +#include <QRgb> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class TextureHelper : protected QOpenGLFunctions +{ + public: + TextureHelper(); + ~TextureHelper(); + + // Ownership of created texture is transferred to caller + GLuint create2DTexture(const QImage &image, bool useTrilinearFiltering = false, + bool convert = true, bool smoothScale = true); + GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false); + // Returns selection framebuffer and inserts generated texture id to texture parameters + GLuint createSelectionBuffer(const QSize &size, GLuint &texture, GLuint &depthTexture); + // Returns selection texture and inserts generated framebuffers to framebuffer parameters + GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer); +#if !defined(QT_OPENGL_ES_2) + // Returns depth texture and inserts generated framebuffer to parameter + GLuint createDepthTexture(const QSize &size, GLuint &frameBuffer, GLuint textureSize = 1); +#endif + void deleteTexture(const GLuint *texture); + + private: + QImage convertToGLFormat(const QImage &srcImage); + void convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage, GLenum texture_format); + QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format); + + friend class Bars3DRenderer; + friend class Surface3DRenderer; + friend class Scatter3DRenderer; +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp new file mode 100644 index 00000000..947dbfba --- /dev/null +++ b/src/datavisualization/utils/utils.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "utils_p.h" + +#include <QVector3D> +#include <QColor> +#include <QPainter> +#include <QPoint> +#include <QImage> +#include <QRegExp> +#include <qmath.h> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +#define NUM_IN_POWER(y, x) for (;y<x;y<<=1) +#define MIN_POWER 32 + +GLuint Utils::getNearestPowerOfTwo(GLuint value, GLuint &padding) +{ + GLuint powOfTwoValue = MIN_POWER; + NUM_IN_POWER(powOfTwoValue, value); + padding = powOfTwoValue - value; + return powOfTwoValue; +} + +QVector3D Utils::vectorFromColor(const QColor &color) +{ + return QVector3D(color.redF(), color.greenF(), color.blueF()); +} + +QImage Utils::printTextToImage(const QFont &font, const QString &text, const QColor &bgrColor, + const QColor &txtColor, QDataVis::LabelStyle style, + bool borders, int maxLabelWidth) +{ + GLuint paddingWidth = 20; + GLuint paddingHeight = 20; + // Calculate text dimensions + QFont valueFont = font; + valueFont.setPointSize(textureFontSize); + QFontMetrics valueFM(valueFont); + int valueStrWidth = valueFM.width(text); + if (maxLabelWidth && QDataVis::LabelStyleTransparent != style) + valueStrWidth = maxLabelWidth; + int valueStrHeight = valueFM.height(); + valueStrWidth += paddingWidth / 2; // Fix clipping problem with skewed fonts (italic or italic-style) + QSize labelSize; + +#if defined(Q_OS_ANDROID) + // Android 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; + // Android 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); + //qDebug() << "label size before padding" << text << labelSize; + labelSize.setWidth(getNearestPowerOfTwo(labelSize.width(), paddingWidth)); + labelSize.setHeight(getNearestPowerOfTwo(labelSize.height(), paddingHeight)); + //qDebug() << "label size after padding" << labelSize << paddingWidth << paddingHeight; +#else + if (QDataVis::LabelStyleTransparent == style) + labelSize = QSize(valueStrWidth, valueStrHeight); + else + labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); +#endif + + // Create image + QImage image = QImage(labelSize, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + // Init painter + QPainter painter(&image); + // Paint text + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.setFont(valueFont); + switch (style) { + case QDataVis::LabelStyleTransparent: { + painter.setPen(txtColor); +#if defined(Q_OS_ANDROID) + 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 + break; + } + case QDataVis::LabelStyleFromTheme: { + painter.setBrush(QBrush(bgrColor)); + 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); + } else { + painter.setPen(bgrColor); + painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), 10.0, 10.0); + } + painter.setPen(txtColor); + painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, + (labelSize.height() - valueStrHeight) / 2.0f, + valueStrWidth, valueStrHeight, + Qt::AlignCenter | Qt::AlignVCenter, + text); + break; + } + case QDataVis::LabelStyleOpaque: { + QColor labelColor = QColor(bgrColor); + labelColor.setAlphaF(1.0); + painter.setBrush(QBrush(labelColor)); + if (borders) { + painter.setPen(QPen(QBrush(txtColor), 7, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin)); + painter.drawRect(7, 7, labelSize.width() - 14, labelSize.height() - 14); + } else { + painter.setPen(labelColor); + painter.drawRect(0, 0, labelSize.width(), labelSize.height()); + } + painter.setPen(txtColor); + painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, + (labelSize.height() - valueStrHeight) / 2.0f, + valueStrWidth, valueStrHeight, + Qt::AlignCenter | Qt::AlignVCenter, + text); + break; + } + } + return image; +} + +QVector3D Utils::getSelection(QPoint mousepos, int height) +{ + QVector3D selectedColor; + + //#if defined(QT_OPENGL_ES_2) + // This is the only one that works with ANGLE (ES 2.0) + // Item count will be limited to 256*256*256 + GLubyte pixel[4]; + glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + (void *)pixel); + + //qDebug() << "rgba" << pixel[0] << pixel[1] << pixel[2] << pixel[3] << "mousepos:" << mousepos << "height:" << height; + + //#else + // These work with desktop OpenGL + // They offer a lot higher possible object count and a possibility to use object ids + //GLuint pixel[3]; + //glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, + // GL_RGB, GL_UNSIGNED_INT, (void *)pixel); + //qDebug() << "rgba" << pixel[0] << pixel[1] << pixel[2];// << pixel[3]; + + //GLfloat pixel3[3]; + //glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, + // GL_RGB, GL_FLOAT, (void *)pixel3); + //qDebug() << "rgba" << pixel3[0] << pixel3[1] << pixel3[2];// << pixel[3]; + //#endif + selectedColor = QVector3D(pixel[0], pixel[1], pixel[2]); + //qDebug() << selectedColor; + + return selectedColor; +} + +Utils::ParamType Utils::mapFormatCharToParamType(const QChar &formatChar) +{ + 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; + } + + return retVal; +} + +Utils::ParamType Utils::findFormatParamType(const QString &format) +{ + 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)); + } + + return ParamTypeUnknown; +} + +QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal value) +{ + switch (paramType) { + case ParamTypeInt: + return QString().sprintf(format, (qint64)value); + case ParamTypeUInt: + return QString().sprintf(format, (quint64)value); + case ParamTypeReal: + return QString().sprintf(format, value); + default: + return QString::fromUtf8(format); // To detect errors + } +} + +QString Utils::defaultLabelFormat() +{ + static const QString defaultFormat(QStringLiteral("%.2f")); + return defaultFormat; +} + +qreal Utils::wrapValue(qreal value, qreal min, qreal max) +{ + if (value > max) { + value = min + (value - max); + + // In case single wrap fails, jump to opposite end. + if (value > max) + value = min; + } + + if (value < min) { + value = max + (value - min); + + // In case single wrap fails, jump to opposite end. + if (value < min) + value = max; + } + + return value; +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/utils.pri b/src/datavisualization/utils/utils.pri new file mode 100644 index 00000000..cef5ebf0 --- /dev/null +++ b/src/datavisualization/utils/utils.pri @@ -0,0 +1,19 @@ +HEADERS += $$PWD/meshloader_p.h \ + $$PWD/vertexindexer_p.h \ + $$PWD/camerahelper_p.h \ + $$PWD/shaderhelper_p.h \ + $$PWD/objecthelper_p.h \ + $$PWD/texturehelper_p.h \ + $$PWD/utils_p.h \ + $$PWD/abstractobjecthelper_p.h \ + $$PWD/surfaceobject_p.h + +SOURCES += $$PWD/meshloader.cpp \ + $$PWD/vertexindexer.cpp \ + $$PWD/camerahelper.cpp \ + $$PWD/shaderhelper.cpp \ + $$PWD/objecthelper.cpp \ + $$PWD/texturehelper.cpp \ + $$PWD/utils.cpp \ + $$PWD/abstractobjecthelper.cpp \ + $$PWD/surfaceobject.cpp diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h new file mode 100644 index 00000000..e74b590d --- /dev/null +++ b/src/datavisualization/utils/utils_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef UTILS_P_H +#define UTILS_P_H + +#include "datavisualizationglobal_p.h" +#include "q3dbars.h" + +class QVector3D; +class QColor; +class QPainter; +class QString; +class QPoint; +class QImage; + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class Utils +{ +public: + enum ParamType { + ParamTypeUnknown = 0, + ParamTypeInt, + ParamTypeUInt, + ParamTypeReal + }; + + static GLuint getNearestPowerOfTwo(GLuint value, GLuint &padding); + static QVector3D vectorFromColor(const QColor &color); + static void printText(QPainter *painter, const QString &text, const QSize &position, + bool absoluteCoords = true, qreal rotation = 0, qreal scale = 1.0f); + static QImage printTextToImage(const QFont &font, + const QString &text, + const QColor &bgrColor, + const QColor &txtColor, + QDataVis::LabelStyle style, + bool borders = false, + int maxLabelWidth = 0); + static QVector3D getSelection(QPoint mousepos, int height); + + static ParamType findFormatParamType(const QString &format); + static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value); + static QString defaultLabelFormat(); + + static qreal wrapValue(qreal value, qreal min, qreal max); + +private: + static ParamType mapFormatCharToParamType(const QChar &formatChar); +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif diff --git a/src/datavisualization/utils/vertexindexer.cpp b/src/datavisualization/utils/vertexindexer.cpp new file mode 100644 index 00000000..63b9faaf --- /dev/null +++ b/src/datavisualization/utils/vertexindexer.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "vertexindexer_p.h" + +#include <string.h> // for memcmp +#include <qmath.h> + +#include <QDebug> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +int unique_vertices = 0; + +// Returns true if v1 can be considered equal to v2 +bool VertexIndexer::is_near(float v1, float v2) +{ + return qAbs(v1 - v2) < 0.01f; +} + +// Searches through all already-exported vertices +// for a similar one. +// Similar = same position + same UVs + same normal +bool VertexIndexer::getSimilarVertexIndex(const QVector3D &in_vertex, + const QVector2D &in_uv, + const QVector3D &in_normal, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals, + unsigned short &result) +{ + // Lame linear search + for (int i = 0; i < out_vertices.size(); i++) { + if (is_near(in_vertex.x() , out_vertices[i].x()) + && is_near(in_vertex.y() , out_vertices[i].y()) + && is_near(in_vertex.z() , out_vertices[i].z()) + && is_near(in_uv.x() , out_uvs [i].x()) + && is_near(in_uv.y() , out_uvs [i].y()) + && is_near(in_normal.x() , out_normals [i].x()) + && is_near(in_normal.y() , out_normals [i].y()) + && is_near(in_normal.z() , out_normals [i].z())) { + result = i; + return true; + } + } + // No other vertex could be used instead. + // Looks like we'll have to add it to the VBO. + return false; +} + +bool VertexIndexer::getSimilarVertexIndex_fast(const PackedVertex &packed, + QMap<PackedVertex, unsigned short> &VertexToOutIndex, + unsigned short &result) +{ + QMap<PackedVertex, unsigned short>::iterator it = VertexToOutIndex.find(packed); + if (it == VertexToOutIndex.end()) { + return false; + } else { + result = it.value(); + return true; + } +} + +void VertexIndexer::indexVBO(const QVector<QVector3D> &in_vertices, + const QVector<QVector2D> &in_uvs, + const QVector<QVector3D> &in_normals, + QVector<unsigned short> &out_indices, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals) +{ + unique_vertices = 0; + QMap<PackedVertex, unsigned short> VertexToOutIndex; + + // For each input vertex + for (int i = 0; i < in_vertices.size(); i++) { + PackedVertex packed = {in_vertices[i], in_uvs[i], in_normals[i]}; + + // Try to find a similar vertex in out_XXXX + unsigned short index; + bool found = getSimilarVertexIndex_fast(packed, VertexToOutIndex, index); + + if (found) { + out_indices.append(index); + } else { + unique_vertices++; + out_vertices.append(in_vertices[i]); + out_uvs.append(in_uvs[i]); + out_normals.append(in_normals[i]); + unsigned short newindex = (unsigned short)out_vertices.size() - 1; + out_indices.append(newindex); + VertexToOutIndex[packed] = newindex; + } + } + //qDebug() << "unique vertices" << unique_vertices; +} + +void VertexIndexer::indexVBO_TBN(const QVector<QVector3D> &in_vertices, + const QVector<QVector2D> &in_uvs, + const QVector<QVector3D> &in_normals, + const QVector<QVector3D> &in_tangents, + const QVector<QVector3D> &in_bitangents, + QVector<unsigned short> &out_indices, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals, + QVector<QVector3D> &out_tangents, + QVector<QVector3D> &out_bitangents) +{ + unique_vertices = 0; + // For each input vertex + for (int i = 0; i < in_vertices.size(); i++) { + + // Try to find a similar vertex in out_XXXX + unsigned short index; + bool found = getSimilarVertexIndex(in_vertices[i], in_uvs[i], in_normals[i], + out_vertices, out_uvs, out_normals, index); + + if (found) { + out_indices.append(index); + + // Average the tangents and the bitangents + out_tangents[index] += in_tangents[i]; + out_bitangents[index] += in_bitangents[i]; + } else { + unique_vertices++; + out_vertices.append(in_vertices[i]); + out_uvs.append(in_uvs[i]); + out_normals.append(in_normals[i]); + out_tangents.append(in_tangents[i]); + out_bitangents.append(in_bitangents[i]); + out_indices.append((unsigned short)out_vertices.size() - 1); + } + } + //qDebug() << "unique vertices" << unique_vertices; +} + +QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/utils/vertexindexer_p.h b/src/datavisualization/utils/vertexindexer_p.h new file mode 100644 index 00000000..0cf1857b --- /dev/null +++ b/src/datavisualization/utils/vertexindexer_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef VERTEXINDEXER_P_H +#define VERTEXINDEXER_P_H + +#include "datavisualizationglobal_p.h" + +#include <QVector> +#include <QVector2D> +#include <QVector3D> + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +class VertexIndexer +{ + public: + struct PackedVertex { + QVector3D position; + QVector2D uv; + QVector3D normal; + bool operator<(const PackedVertex that) const { + return memcmp((void*)this, (void*)&that, sizeof(PackedVertex)) > 0; + } + }; + + static void indexVBO(const QVector<QVector3D> &in_vertices, + const QVector<QVector2D> &in_uvs, + const QVector<QVector3D> &in_normals, + QVector<unsigned short> &out_indices, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals); + + static void indexVBO_TBN(const QVector<QVector3D> &in_vertices, + const QVector<QVector2D> &in_uvs, + const QVector<QVector3D> &in_normals, + const QVector<QVector3D> &in_tangents, + const QVector<QVector3D> &in_bitangents, + QVector<unsigned short> &out_indices, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals, + QVector<QVector3D> &out_tangents, + QVector<QVector3D> &out_bitangents); + + private: + static bool is_near(float v1, float v2); + static bool getSimilarVertexIndex(const QVector3D &in_vertex, + const QVector2D &in_uv, + const QVector3D &in_normal, + QVector<QVector3D> &out_vertices, + QVector<QVector2D> &out_uvs, + QVector<QVector3D> &out_normals, + unsigned short &result); + static bool getSimilarVertexIndex_fast(const PackedVertex &packed, + QMap<PackedVertex, unsigned short> &VertexToOutIndex, + unsigned short &result); +}; + +QT_DATAVISUALIZATION_END_NAMESPACE + +#endif |