1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Data Visualization module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "drawer_p.h"
31#include "shaderhelper_p.h"
32#include "surfaceobject_p.h"
33#include "utils_p.h"
34#include "texturehelper_p.h"
35#include "abstract3drenderer_p.h"
36#include "scatterpointbufferhelper_p.h"
37
38#include <QtGui/QMatrix4x4>
39#include <QtCore/qmath.h>
40
41// Resources need to be explicitly initialized when building as static library
42class StaticLibInitializer
43{
44public:
45 StaticLibInitializer()
46 {
47 Q_INIT_RESOURCE(engine);
48 }
49};
50StaticLibInitializer staticLibInitializer;
51
52QT_BEGIN_NAMESPACE_DATAVISUALIZATION
53
54// Vertex array buffer for point
55const GLfloat point_data[] = {0.0f, 0.0f, 0.0f};
56
57// Vertex array buffer for line
58const GLfloat line_data[] = {
59 -1.0f, 0.0f, 0.0f,
60 1.0f, 0.0f, 0.0f,
61};
62
63Drawer::Drawer(Q3DTheme *theme)
64 : m_theme(theme),
65 m_textureHelper(0),
66 m_pointbuffer(0),
67 m_linebuffer(0),
68 m_scaledFontSize(0.0f)
69{
70}
71
72Drawer::~Drawer()
73{
74 delete m_textureHelper;
75 if (QOpenGLContext::currentContext()) {
76 glDeleteBuffers(n: 1, buffers: &m_pointbuffer);
77 glDeleteBuffers(n: 1, buffers: &m_linebuffer);
78 }
79}
80
81void Drawer::initializeOpenGL()
82{
83 initializeOpenGLFunctions();
84 if (!m_textureHelper)
85 m_textureHelper = new TextureHelper();
86}
87
88void Drawer::setTheme(Q3DTheme *theme)
89{
90 m_theme = theme;
91 m_scaledFontSize = 0.05f + m_theme->font().pointSizeF() / 500.0f;
92 emit drawerChanged();
93}
94
95Q3DTheme *Drawer::theme() const
96{
97 return m_theme;
98}
99
100QFont Drawer::font() const
101{
102 return m_theme->font();
103}
104
105void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId,
106 GLuint depthTextureId, GLuint textureId3D)
107{
108#if defined(QT_OPENGL_ES_2)
109 Q_UNUSED(textureId3D)
110#endif
111 if (textureId) {
112 // Activate texture
113 glActiveTexture(GL_TEXTURE0);
114 glBindTexture(GL_TEXTURE_2D, texture: textureId);
115 shader->setUniformValue(uniform: shader->texture(), value: 0);
116 }
117
118 if (depthTextureId) {
119 // Activate depth texture
120 glActiveTexture(GL_TEXTURE1);
121 glBindTexture(GL_TEXTURE_2D, texture: depthTextureId);
122 shader->setUniformValue(uniform: shader->shadow(), value: 1);
123 }
124#if !defined(QT_OPENGL_ES_2)
125 if (textureId3D) {
126 // Activate texture
127 glActiveTexture(GL_TEXTURE2);
128 glBindTexture(GL_TEXTURE_3D, texture: textureId3D);
129 shader->setUniformValue(uniform: shader->texture(), value: 2);
130 }
131#endif
132
133 // 1st attribute buffer : vertices
134 glEnableVertexAttribArray(index: shader->posAtt());
135 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->vertexBuf());
136 glVertexAttribPointer(indx: shader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
137
138 // 2nd attribute buffer : normals
139 if (shader->normalAtt() >= 0) {
140 glEnableVertexAttribArray(index: shader->normalAtt());
141 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->normalBuf());
142 glVertexAttribPointer(indx: shader->normalAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
143 }
144
145 // 3rd attribute buffer : UVs
146 if (shader->uvAtt() >= 0) {
147 glEnableVertexAttribArray(index: shader->uvAtt());
148 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->uvBuf());
149 glVertexAttribPointer(indx: shader->uvAtt(), size: 2, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
150 }
151
152 // Index buffer
153 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: object->elementBuf());
154
155 // Draw the triangles
156 glDrawElements(GL_TRIANGLES, count: object->indexCount(), GL_UNSIGNED_INT, indices: (void*)0);
157
158 // Free buffers
159 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
160 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
161
162 if (shader->uvAtt() >= 0)
163 glDisableVertexAttribArray(index: shader->uvAtt());
164 if (shader->normalAtt() >= 0)
165 glDisableVertexAttribArray(index: shader->normalAtt());
166 glDisableVertexAttribArray(index: shader->posAtt());
167
168 // Release textures
169#if !defined(QT_OPENGL_ES_2)
170 if (textureId3D) {
171 glActiveTexture(GL_TEXTURE2);
172 glBindTexture(GL_TEXTURE_3D, texture: 0);
173 }
174#endif
175 if (depthTextureId) {
176 glActiveTexture(GL_TEXTURE1);
177 glBindTexture(GL_TEXTURE_2D, texture: 0);
178 }
179 if (textureId) {
180 glActiveTexture(GL_TEXTURE0);
181 glBindTexture(GL_TEXTURE_2D, texture: 0);
182 }
183}
184
185void Drawer::drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object)
186{
187 glEnableVertexAttribArray(index: shader->posAtt());
188 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->vertexBuf());
189 glVertexAttribPointer(indx: shader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void *)0);
190 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: object->elementBuf());
191 glDrawElements(GL_TRIANGLES, count: object->indexCount(), GL_UNSIGNED_INT, indices: (void *)0);
192 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
193 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
194 glDisableVertexAttribArray(index: shader->posAtt());
195}
196
197void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object)
198{
199 // 1st attribute buffer : vertices
200 glEnableVertexAttribArray(index: shader->posAtt());
201 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->vertexBuf());
202 glVertexAttribPointer(indx: shader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
203
204 // Index buffer
205 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: object->gridElementBuf());
206
207 // Draw the lines
208 glDrawElements(GL_LINES, count: object->gridIndexCount(), GL_UNSIGNED_INT, indices: (void*)0);
209
210 // Free buffers
211 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
212 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
213
214 glDisableVertexAttribArray(index: shader->posAtt());
215}
216
217void Drawer::drawPoint(ShaderHelper *shader)
218{
219 // Draw a single point
220
221 // Generate vertex buffer for point if it does not exist
222 if (!m_pointbuffer) {
223 glGenBuffers(n: 1, buffers: &m_pointbuffer);
224 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_pointbuffer);
225 glBufferData(GL_ARRAY_BUFFER, size: sizeof(point_data), data: point_data, GL_STATIC_DRAW);
226 }
227
228 // 1st attribute buffer : vertices
229 glEnableVertexAttribArray(index: shader->posAtt());
230 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_pointbuffer);
231 glVertexAttribPointer(indx: shader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
232
233 // Draw the point
234 glDrawArrays(GL_POINTS, first: 0, count: 1);
235
236 // Free buffers
237 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
238
239 glDisableVertexAttribArray(index: shader->posAtt());
240}
241
242void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId)
243{
244 if (textureId) {
245 // Activate texture
246 glActiveTexture(GL_TEXTURE0);
247 glBindTexture(GL_TEXTURE_2D, texture: textureId);
248 shader->setUniformValue(uniform: shader->texture(), value: 0);
249 }
250
251 // 1st attribute buffer : vertices
252 glEnableVertexAttribArray(index: shader->posAtt());
253 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->pointBuf());
254 glVertexAttribPointer(indx: shader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
255
256 // 2nd attribute buffer : UVs
257 if (textureId) {
258 glEnableVertexAttribArray(index: shader->uvAtt());
259 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->uvBuf());
260 glVertexAttribPointer(indx: shader->uvAtt(), size: 2, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
261 }
262
263 // Draw the points
264 glDrawArrays(GL_POINTS, first: 0, count: object->indexCount());
265
266 // Free buffers
267 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
268
269 glDisableVertexAttribArray(index: shader->posAtt());
270
271 if (textureId) {
272 glDisableVertexAttribArray(index: shader->uvAtt());
273 glActiveTexture(GL_TEXTURE0);
274 glBindTexture(GL_TEXTURE_2D, texture: 0);
275 }
276}
277
278void Drawer::drawLine(ShaderHelper *shader)
279{
280 // Draw a single line
281
282 // Generate vertex buffer for line if it does not exist
283 if (!m_linebuffer) {
284 glGenBuffers(n: 1, buffers: &m_linebuffer);
285 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_linebuffer);
286 glBufferData(GL_ARRAY_BUFFER, size: sizeof(line_data), data: line_data, GL_STATIC_DRAW);
287 }
288
289 // 1st attribute buffer : vertices
290 glEnableVertexAttribArray(index: shader->posAtt());
291 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_linebuffer);
292 glVertexAttribPointer(indx: shader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0, ptr: (void*)0);
293
294 // Draw the line
295 glDrawArrays(GL_LINES, first: 0, count: 2);
296
297 // Free buffers
298 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
299
300 glDisableVertexAttribArray(index: shader->posAtt());
301}
302
303void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem,
304 const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix,
305 const QVector3D &positionComp, const QQuaternion &rotation,
306 GLfloat itemHeight, QAbstract3DGraph::SelectionFlags mode,
307 ShaderHelper *shader, ObjectHelper *object,
308 const Q3DCamera *camera, bool useDepth, bool rotateAlong,
309 LabelPosition position, Qt::Alignment alignment, bool isSlicing,
310 bool isSelecting)
311{
312 // Draw label
313 if (!labelItem.textureId())
314 return; // No texture, skip
315
316 QSize textureSize = labelItem.size();
317 QMatrix4x4 modelMatrix;
318 QMatrix4x4 MVPMatrix;
319 GLfloat xPosition = 0.0f;
320 GLfloat yPosition = 0.0f;
321 GLfloat zPosition = positionComp.z();
322
323 switch (position) {
324 case LabelBelow: {
325 yPosition = item.translation().y() - (positionComp.y() / 2.0f) + itemHeight - 0.1f;
326 break;
327 }
328 case LabelLow: {
329 yPosition = -positionComp.y();
330 break;
331 }
332 case LabelMid: {
333 yPosition = item.translation().y();
334 break;
335 }
336 case LabelHigh: {
337 yPosition = item.translation().y() + itemHeight / 2.0f;
338 break;
339 }
340 case LabelOver: {
341 yPosition = item.translation().y() - (positionComp.y() / 2.0f) + itemHeight + 0.1f;
342 break;
343 }
344 case LabelBottom: {
345 yPosition = -2.75f + positionComp.y();
346 xPosition = 0.0f;
347 break;
348 }
349 case LabelTop: {
350 yPosition = 2.75f - positionComp.y();
351 xPosition = 0.0f;
352 break;
353 }
354 case LabelLeft: {
355 yPosition = 0.0f;
356 xPosition = -2.75f;
357 break;
358 }
359 case LabelRight: {
360 yPosition = 0.0f;
361 xPosition = 2.75f;
362 break;
363 }
364 }
365
366 // Calculate scale factor to get uniform font size
367 GLfloat scaleFactor = m_scaledFontSize / (GLfloat)textureSize.height();
368
369 // Apply alignment
370 QVector3D anchorPoint;
371
372 if (alignment & Qt::AlignLeft)
373 anchorPoint.setX(float(textureSize.width()) * scaleFactor);
374 else if (alignment & Qt::AlignRight)
375 anchorPoint.setX(float(-textureSize.width()) * scaleFactor);
376
377 if (alignment & Qt::AlignTop)
378 anchorPoint.setY(float(-textureSize.height()) * scaleFactor);
379 else if (alignment & Qt::AlignBottom)
380 anchorPoint.setY(float(textureSize.height()) * scaleFactor);
381
382 if (position < LabelBottom) {
383 xPosition = item.translation().x();
384 if (useDepth)
385 zPosition = item.translation().z();
386 else if (mode.testFlag(flag: QAbstract3DGraph::SelectionColumn) && isSlicing)
387 xPosition = -(item.translation().z()) + positionComp.z(); // flip first to left
388 }
389
390 // Position label
391 modelMatrix.translate(x: xPosition, y: yPosition, z: zPosition);
392
393 // Rotate
394 if (useDepth && !rotateAlong) {
395 float yComp = float(qRadiansToDegrees(radians: qTan(v: positionComp.y() / cameraDistance)));
396 // Apply negative camera rotations to keep labels facing camera
397 float camRotationX = camera->xRotation();
398 float camRotationY = camera->yRotation();
399 modelMatrix.rotate(angle: -camRotationX, x: 0.0f, y: 1.0f, z: 0.0f);
400 modelMatrix.rotate(angle: -camRotationY - yComp, x: 1.0f, y: 0.0f, z: 0.0f);
401 } else {
402 modelMatrix.rotate(quaternion: rotation);
403 }
404 modelMatrix.translate(vector: anchorPoint);
405
406 // Scale label based on text size
407 modelMatrix.scale(vector: QVector3D((GLfloat)textureSize.width() * scaleFactor,
408 m_scaledFontSize,
409 0.0f));
410
411 MVPMatrix = projectionmatrix * viewmatrix * modelMatrix;
412
413 shader->setUniformValue(uniform: shader->MVP(), value: MVPMatrix);
414
415 if (isSelecting) {
416 // Draw the selection object
417 drawSelectionObject(shader, object);
418 } else {
419 // Draw the object
420 drawObject(shader, object, textureId: labelItem.textureId());
421 }
422}
423
424void Drawer::generateSelectionLabelTexture(Abstract3DRenderer *renderer)
425{
426 LabelItem &labelItem = renderer->selectionLabelItem();
427 generateLabelItem(item&: labelItem, text: renderer->selectionLabel());
428}
429
430void Drawer::generateLabelItem(LabelItem &item, const QString &text, int widestLabel)
431{
432 initializeOpenGL();
433
434 item.clear();
435
436 if (!text.isEmpty()) {
437 // Create labels
438 // Print label into a QImage using QPainter
439 QImage label = Utils::printTextToImage(font: m_theme->font(),
440 text,
441 bgrColor: m_theme->labelBackgroundColor(),
442 txtColor: m_theme->labelTextColor(),
443 labelBackground: m_theme->isLabelBackgroundEnabled(),
444 borders: m_theme->isLabelBorderEnabled(),
445 maxLabelWidth: widestLabel);
446
447 // Set label size
448 item.setSize(label.size());
449 // Insert text texture into label (also deletes the old texture)
450 item.setTextureId(m_textureHelper->create2DTexture(image: label, useTrilinearFiltering: true, convert: true));
451 }
452}
453
454QT_END_NAMESPACE_DATAVISUALIZATION
455

source code of qtdatavis3d/src/datavisualization/engine/drawer.cpp