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 |
42 | class StaticLibInitializer |
43 | { |
44 | public: |
45 | StaticLibInitializer() |
46 | { |
47 | Q_INIT_RESOURCE(engine); |
48 | } |
49 | }; |
50 | StaticLibInitializer staticLibInitializer; |
51 | |
52 | QT_BEGIN_NAMESPACE_DATAVISUALIZATION |
53 | |
54 | // Vertex array buffer for point |
55 | const GLfloat point_data[] = {0.0f, 0.0f, 0.0f}; |
56 | |
57 | // Vertex array buffer for line |
58 | const GLfloat line_data[] = { |
59 | -1.0f, 0.0f, 0.0f, |
60 | 1.0f, 0.0f, 0.0f, |
61 | }; |
62 | |
63 | Drawer::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 | |
72 | Drawer::~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 | |
81 | void Drawer::initializeOpenGL() |
82 | { |
83 | initializeOpenGLFunctions(); |
84 | if (!m_textureHelper) |
85 | m_textureHelper = new TextureHelper(); |
86 | } |
87 | |
88 | void 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 | |
95 | Q3DTheme *Drawer::theme() const |
96 | { |
97 | return m_theme; |
98 | } |
99 | |
100 | QFont Drawer::font() const |
101 | { |
102 | return m_theme->font(); |
103 | } |
104 | |
105 | void 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 | |
185 | void 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 | |
197 | void 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 | |
217 | void 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 | |
242 | void 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 | |
278 | void 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 | |
303 | void 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 | |
424 | void Drawer::generateSelectionLabelTexture(Abstract3DRenderer *renderer) |
425 | { |
426 | LabelItem &labelItem = renderer->selectionLabelItem(); |
427 | generateLabelItem(item&: labelItem, text: renderer->selectionLabel()); |
428 | } |
429 | |
430 | void 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 | |
454 | QT_END_NAMESPACE_DATAVISUALIZATION |
455 | |