Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #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 <QtGui/qquaternion.h>
++
++ #include <QtCore/qmath.h>
++
++ static void initResources()
++ {
++ Q_INIT_RESOURCE(datavisualizationshaders);
++ Q_INIT_RESOURCE(datavisualizationmeshes);
++ }
++
++ Q_CONSTRUCTOR_FUNCTION(initResources)
++
++ QT_BEGIN_NAMESPACE
++
++ // 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 QT_CONFIG(opengles2)
++ 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 !QT_CONFIG(opengles2)
++ 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 !QT_CONFIG(opengles2)
++ 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)
++ {
++ // Get grid line color
++ QVector4D lineColor = Utils::vectorFromColor(object->wireframeColor());
++ shader->setUniformValue(shader->color(), lineColor);
++
++ // 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