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

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