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

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