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 |
16 | class StaticLibInitializer |
17 | { |
18 | public: |
19 | StaticLibInitializer() |
20 | { |
21 | Q_INIT_RESOURCE(datavisualizationshaders); |
22 | Q_INIT_RESOURCE(datavisualizationmeshes); |
23 | } |
24 | }; |
25 | StaticLibInitializer staticLibInitializer; |
26 | |
27 | QT_BEGIN_NAMESPACE |
28 | |
29 | // Vertex array buffer for point |
30 | const GLfloat point_data[] = {0.0f, 0.0f, 0.0f}; |
31 | |
32 | // Vertex array buffer for line |
33 | const GLfloat line_data[] = { |
34 | -1.0f, 0.0f, 0.0f, |
35 | 1.0f, 0.0f, 0.0f, |
36 | }; |
37 | |
38 | Drawer::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 | |
47 | Drawer::~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 | |
56 | void Drawer::initializeOpenGL() |
57 | { |
58 | initializeOpenGLFunctions(); |
59 | if (!m_textureHelper) |
60 | m_textureHelper = new TextureHelper(); |
61 | } |
62 | |
63 | void 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 | |
70 | Q3DTheme *Drawer::theme() const |
71 | { |
72 | return m_theme; |
73 | } |
74 | |
75 | QFont Drawer::font() const |
76 | { |
77 | return m_theme->font(); |
78 | } |
79 | |
80 | void 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 | |
160 | void 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 | |
172 | void 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 | |
196 | void 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 | |
221 | void 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 | |
257 | void 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 | |
282 | void 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 | |
403 | void Drawer::generateSelectionLabelTexture(Abstract3DRenderer *renderer) |
404 | { |
405 | LabelItem &labelItem = renderer->selectionLabelItem(); |
406 | generateLabelItem(item&: labelItem, text: renderer->selectionLabel()); |
407 | } |
408 | |
409 | void 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 | |
433 | QT_END_NAMESPACE |
434 | |