blob: 2e0805e014f090a2c14ae7552bcba8aa31ea649e [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Data Visualization module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "drawer_p.h"
#include "shaderhelper_p.h"
#include "surfaceobject_p.h"
#include "utils_p.h"
#include "texturehelper_p.h"
#include "abstract3drenderer_p.h"
#include "scatterpointbufferhelper_p.h"
#include <QtGui/QMatrix4x4>
#include <QtCore/qmath.h>
// Resources need to be explicitly initialized when building as static library
class StaticLibInitializer
{
public:
StaticLibInitializer()
{
Q_INIT_RESOURCE(engine);
}
};
StaticLibInitializer staticLibInitializer;
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
// Vertex array buffer for point
const GLfloat point_data[] = {0.0f, 0.0f, 0.0f};
// Vertex array buffer for line
const GLfloat line_data[] = {
-1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
};
Drawer::Drawer(Q3DTheme *theme)
: m_theme(theme),
m_textureHelper(0),
m_pointbuffer(0),
m_linebuffer(0),
m_scaledFontSize(0.0f)
{
}
Drawer::~Drawer()
{
delete m_textureHelper;
if (QOpenGLContext::currentContext()) {
glDeleteBuffers(1, &m_pointbuffer);
glDeleteBuffers(1, &m_linebuffer);
}
}
void Drawer::initializeOpenGL()
{
initializeOpenGLFunctions();
if (!m_textureHelper)
m_textureHelper = new TextureHelper();
}
void Drawer::setTheme(Q3DTheme *theme)
{
m_theme = theme;
m_scaledFontSize = 0.05f + m_theme->font().pointSizeF() / 500.0f;
emit drawerChanged();
}
Q3DTheme *Drawer::theme() const
{
return m_theme;
}
QFont Drawer::font() const
{
return m_theme->font();
}
void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId,
GLuint depthTextureId, GLuint textureId3D)
{
#if defined(QT_OPENGL_ES_2)
Q_UNUSED(textureId3D)
#endif
if (textureId) {
// Activate texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
shader->setUniformValue(shader->texture(), 0);
}
if (depthTextureId) {
// Activate depth texture
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depthTextureId);
shader->setUniformValue(shader->shadow(), 1);
}
#if !defined(QT_OPENGL_ES_2)
if (textureId3D) {
// Activate texture
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_3D, textureId3D);
shader->setUniformValue(shader->texture(), 2);
}
#endif
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// 2nd attribute buffer : normals
if (shader->normalAtt() >= 0) {
glEnableVertexAttribArray(shader->normalAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf());
glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
// 3rd attribute buffer : UVs
if (shader->uvAtt() >= 0) {
glEnableVertexAttribArray(shader->uvAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
// Draw the triangles
glDrawElements(GL_TRIANGLES, object->indexCount(), GL_UNSIGNED_INT, (void*)0);
// Free buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if (shader->uvAtt() >= 0)
glDisableVertexAttribArray(shader->uvAtt());
if (shader->normalAtt() >= 0)
glDisableVertexAttribArray(shader->normalAtt());
glDisableVertexAttribArray(shader->posAtt());
// Release textures
#if !defined(QT_OPENGL_ES_2)
if (textureId3D) {
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_3D, 0);
}
#endif
if (depthTextureId) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
}
if (textureId) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
void Drawer::drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object)
{
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
glDrawElements(GL_TRIANGLES, object->indexCount(), GL_UNSIGNED_INT, (void *)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->posAtt());
}
void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object)
{
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->gridElementBuf());
// Draw the lines
glDrawElements(GL_LINES, object->gridIndexCount(), GL_UNSIGNED_INT, (void*)0);
// Free buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->posAtt());
}
void Drawer::drawPoint(ShaderHelper *shader)
{
// Draw a single point
// Generate vertex buffer for point if it does not exist
if (!m_pointbuffer) {
glGenBuffers(1, &m_pointbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(point_data), point_data, GL_STATIC_DRAW);
}
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// Draw the point
glDrawArrays(GL_POINTS, 0, 1);
// Free buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->posAtt());
}
void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId)
{
if (textureId) {
// Activate texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
shader->setUniformValue(shader->texture(), 0);
}
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->pointBuf());
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// 2nd attribute buffer : UVs
if (textureId) {
glEnableVertexAttribArray(shader->uvAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
// Draw the points
glDrawArrays(GL_POINTS, 0, object->indexCount());
// Free buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->posAtt());
if (textureId) {
glDisableVertexAttribArray(shader->uvAtt());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
void Drawer::drawLine(ShaderHelper *shader)
{
// Draw a single line
// Generate vertex buffer for line if it does not exist
if (!m_linebuffer) {
glGenBuffers(1, &m_linebuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_linebuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(line_data), line_data, GL_STATIC_DRAW);
}
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, m_linebuffer);
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// Draw the line
glDrawArrays(GL_LINES, 0, 2);
// Free buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->posAtt());
}
void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem,
const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix,
const QVector3D &positionComp, const QQuaternion &rotation,
GLfloat itemHeight, QAbstract3DGraph::SelectionFlags mode,
ShaderHelper *shader, ObjectHelper *object,
const Q3DCamera *camera, bool useDepth, bool rotateAlong,
LabelPosition position, Qt::Alignment alignment, bool isSlicing,
bool isSelecting)
{
// Draw label
if (!labelItem.textureId())
return; // No texture, skip
QSize textureSize = labelItem.size();
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
GLfloat xPosition = 0.0f;
GLfloat yPosition = 0.0f;
GLfloat zPosition = positionComp.z();
switch (position) {
case LabelBelow: {
yPosition = item.translation().y() - (positionComp.y() / 2.0f) + itemHeight - 0.1f;
break;
}
case LabelLow: {
yPosition = -positionComp.y();
break;
}
case LabelMid: {
yPosition = item.translation().y();
break;
}
case LabelHigh: {
yPosition = item.translation().y() + itemHeight / 2.0f;
break;
}
case LabelOver: {
yPosition = item.translation().y() - (positionComp.y() / 2.0f) + itemHeight + 0.1f;
break;
}
case LabelBottom: {
yPosition = -2.75f + positionComp.y();
xPosition = 0.0f;
break;
}
case LabelTop: {
yPosition = 2.75f - positionComp.y();
xPosition = 0.0f;
break;
}
case LabelLeft: {
yPosition = 0.0f;
xPosition = -2.75f;
break;
}
case LabelRight: {
yPosition = 0.0f;
xPosition = 2.75f;
break;
}
}
// Calculate scale factor to get uniform font size
GLfloat scaleFactor = m_scaledFontSize / (GLfloat)textureSize.height();
// Apply alignment
QVector3D anchorPoint;
if (alignment & Qt::AlignLeft)
anchorPoint.setX(float(textureSize.width()) * scaleFactor);
else if (alignment & Qt::AlignRight)
anchorPoint.setX(float(-textureSize.width()) * scaleFactor);
if (alignment & Qt::AlignTop)
anchorPoint.setY(float(-textureSize.height()) * scaleFactor);
else if (alignment & Qt::AlignBottom)
anchorPoint.setY(float(textureSize.height()) * scaleFactor);
if (position < LabelBottom) {
xPosition = item.translation().x();
if (useDepth)
zPosition = item.translation().z();
else if (mode.testFlag(QAbstract3DGraph::SelectionColumn) && isSlicing)
xPosition = -(item.translation().z()) + positionComp.z(); // flip first to left
}
// Position label
modelMatrix.translate(xPosition, yPosition, zPosition);
// Rotate
if (useDepth && !rotateAlong) {
float yComp = float(qRadiansToDegrees(qTan(positionComp.y() / cameraDistance)));
// Apply negative camera rotations to keep labels facing camera
float camRotationX = camera->xRotation();
float camRotationY = camera->yRotation();
modelMatrix.rotate(-camRotationX, 0.0f, 1.0f, 0.0f);
modelMatrix.rotate(-camRotationY - yComp, 1.0f, 0.0f, 0.0f);
} else {
modelMatrix.rotate(rotation);
}
modelMatrix.translate(anchorPoint);
// Scale label based on text size
modelMatrix.scale(QVector3D((GLfloat)textureSize.width() * scaleFactor,
m_scaledFontSize,
0.0f));
MVPMatrix = projectionmatrix * viewmatrix * modelMatrix;
shader->setUniformValue(shader->MVP(), MVPMatrix);
if (isSelecting) {
// Draw the selection object
drawSelectionObject(shader, object);
} else {
// Draw the object
drawObject(shader, object, labelItem.textureId());
}
}
void Drawer::generateSelectionLabelTexture(Abstract3DRenderer *renderer)
{
LabelItem &labelItem = renderer->selectionLabelItem();
generateLabelItem(labelItem, renderer->selectionLabel());
}
void Drawer::generateLabelItem(LabelItem &item, const QString &text, int widestLabel)
{
initializeOpenGL();
item.clear();
if (!text.isEmpty()) {
// Create labels
// Print label into a QImage using QPainter
QImage label = Utils::printTextToImage(m_theme->font(),
text,
m_theme->labelBackgroundColor(),
m_theme->labelTextColor(),
m_theme->isLabelBackgroundEnabled(),
m_theme->isLabelBorderEnabled(),
widestLabel);
// Set label size
item.setSize(label.size());
// Insert text texture into label (also deletes the old texture)
item.setTextureId(m_textureHelper->create2DTexture(label, true, true));
}
}
QT_END_NAMESPACE_DATAVISUALIZATION