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 "surface3drenderer_p.h"
31#include "q3dcamera_p.h"
32#include "shaderhelper_p.h"
33#include "texturehelper_p.h"
34#include "utils_p.h"
35
36#include <QtCore/qmath.h>
37
38static const int ID_TO_RGBA_MASK = 0xff;
39
40QT_BEGIN_NAMESPACE_DATAVISUALIZATION
41
42//#define SHOW_DEPTH_TEXTURE_SCENE
43
44const GLfloat sliceZScale = 0.1f;
45const GLfloat sliceUnits = 2.5f;
46const uint greenMultiplier = 256;
47const uint blueMultiplier = 65536;
48const uint alphaMultiplier = 16777216;
49
50Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
51 : Abstract3DRenderer(controller),
52 m_cachedIsSlicingActivated(false),
53 m_depthShader(0),
54 m_backgroundShader(0),
55 m_surfaceFlatShader(0),
56 m_surfaceSmoothShader(0),
57 m_surfaceTexturedSmoothShader(0),
58 m_surfaceTexturedFlatShader(0),
59 m_surfaceGridShader(0),
60 m_surfaceSliceFlatShader(0),
61 m_surfaceSliceSmoothShader(0),
62 m_selectionShader(0),
63 m_heightNormalizer(0.0f),
64 m_scaleX(0.0f),
65 m_scaleY(0.0f),
66 m_scaleZ(0.0f),
67 m_depthFrameBuffer(0),
68 m_selectionFrameBuffer(0),
69 m_selectionDepthBuffer(0),
70 m_selectionResultTexture(0),
71 m_shadowQualityToShader(33.3f),
72 m_flatSupported(true),
73 m_selectionActive(false),
74 m_shadowQualityMultiplier(3),
75 m_selectedPoint(Surface3DController::invalidSelectionPosition()),
76 m_selectedSeries(0),
77 m_clickedPosition(Surface3DController::invalidSelectionPosition()),
78 m_selectionTexturesDirty(false),
79 m_noShadowTexture(0)
80{
81 // Check if flat feature is supported
82 ShaderHelper tester(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
83 QStringLiteral(":/shaders/fragmentSurfaceFlat"));
84 if (!tester.testCompile()) {
85 m_flatSupported = false;
86 connect(sender: this, signal: &Surface3DRenderer::flatShadingSupportedChanged,
87 receiver: controller, slot: &Surface3DController::handleFlatShadingSupportedChange);
88 emit flatShadingSupportedChanged(supported: m_flatSupported);
89 qWarning() << "Warning: Flat qualifier not supported on your platform's GLSL language."
90 " Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.";
91 }
92
93 initializeOpenGL();
94}
95
96Surface3DRenderer::~Surface3DRenderer()
97{
98 contextCleanup();
99 delete m_depthShader;
100 delete m_backgroundShader;
101 delete m_selectionShader;
102 delete m_surfaceFlatShader;
103 delete m_surfaceSmoothShader;
104 delete m_surfaceTexturedSmoothShader;
105 delete m_surfaceTexturedFlatShader;
106 delete m_surfaceGridShader;
107 delete m_surfaceSliceFlatShader;
108 delete m_surfaceSliceSmoothShader;
109}
110
111void Surface3DRenderer::contextCleanup()
112{
113 if (QOpenGLContext::currentContext()) {
114 m_textureHelper->glDeleteFramebuffers(n: 1, framebuffers: &m_depthFrameBuffer);
115 m_textureHelper->glDeleteRenderbuffers(n: 1, renderbuffers: &m_selectionDepthBuffer);
116 m_textureHelper->glDeleteFramebuffers(n: 1, framebuffers: &m_selectionFrameBuffer);
117
118 m_textureHelper->deleteTexture(texture: &m_noShadowTexture);
119 m_textureHelper->deleteTexture(texture: &m_depthTexture);
120 m_textureHelper->deleteTexture(texture: &m_selectionResultTexture);
121 }
122}
123
124void Surface3DRenderer::initializeOpenGL()
125{
126 Abstract3DRenderer::initializeOpenGL();
127
128 // Initialize shaders
129 initSurfaceShaders();
130
131 if (!m_isOpenGLES) {
132 initDepthShader(); // For shadows
133 loadGridLineMesh();
134 }
135
136 // Init selection shader
137 initSelectionShaders();
138
139 // Resize in case we've missed resize events
140 // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here
141 handleResize();
142
143 // Load background mesh (we need to be initialized first)
144 loadBackgroundMesh();
145
146 // Create texture for no shadows
147 QImage image(2, 2, QImage::Format_RGB32);
148 image.fill(color: Qt::white);
149 m_noShadowTexture = m_textureHelper->create2DTexture(image, useTrilinearFiltering: false, convert: true, smoothScale: false, clampY: true);
150}
151
152void Surface3DRenderer::fixCameraTarget(QVector3D &target)
153{
154 target.setX(target.x() * m_scaleX);
155 target.setY(target.y() * m_scaleY);
156 target.setZ(target.z() * -m_scaleZ);
157}
158
159void Surface3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
160{
161 // The inputs are the item bounds in OpenGL coordinates.
162 // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
163 // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
164 float itemRangeX = (maxBounds.x() - minBounds.x());
165 float itemRangeY = (maxBounds.y() - minBounds.y());
166 float itemRangeZ = (maxBounds.z() - minBounds.z());
167
168 if (minBounds.x() < -m_scaleX)
169 minBounds.setX(-1.0f + (2.0f * qAbs(t: minBounds.x() + m_scaleX) / itemRangeX));
170 else
171 minBounds.setX(-1.0f);
172
173 if (minBounds.y() < -m_scaleY)
174 minBounds.setY(-(-1.0f + (2.0f * qAbs(t: minBounds.y() + m_scaleY) / itemRangeY)));
175 else
176 minBounds.setY(1.0f);
177
178 if (minBounds.z() < -m_scaleZ)
179 minBounds.setZ(-(-1.0f + (2.0f * qAbs(t: minBounds.z() + m_scaleZ) / itemRangeZ)));
180 else
181 minBounds.setZ(1.0f);
182
183 if (maxBounds.x() > m_scaleX)
184 maxBounds.setX(1.0f - (2.0f * qAbs(t: maxBounds.x() - m_scaleX) / itemRangeX));
185 else
186 maxBounds.setX(1.0f);
187
188 if (maxBounds.y() > m_scaleY)
189 maxBounds.setY(-(1.0f - (2.0f * qAbs(t: maxBounds.y() - m_scaleY) / itemRangeY)));
190 else
191 maxBounds.setY(-1.0f);
192
193 if (maxBounds.z() > m_scaleZ)
194 maxBounds.setZ(-(1.0f - (2.0f * qAbs(t: maxBounds.z() - m_scaleZ) / itemRangeZ)));
195 else
196 maxBounds.setZ(-1.0f);
197}
198
199void Surface3DRenderer::updateData()
200{
201 calculateSceneScalingFactors();
202
203 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
204 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
205 if (cache->isVisible() && cache->dataDirty()) {
206 const QSurface3DSeries *currentSeries = cache->series();
207 QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
208 const QSurfaceDataArray &array = *dataProxy->array();
209 QSurfaceDataArray &dataArray = cache->dataArray();
210 QRect sampleSpace;
211
212 // Need minimum of 2x2 array to draw a surface
213 if (array.size() >= 2 && array.at(i: 0)->size() >= 2)
214 sampleSpace = calculateSampleRect(array);
215
216 bool dimensionsChanged = false;
217 if (cache->sampleSpace() != sampleSpace) {
218 if (sampleSpace.width() >= 2)
219 m_selectionTexturesDirty = true;
220
221 dimensionsChanged = true;
222 cache->setSampleSpace(sampleSpace);
223
224 for (int i = 0; i < dataArray.size(); i++)
225 delete dataArray.at(i);
226 dataArray.clear();
227 }
228
229 if (sampleSpace.width() >= 2 && sampleSpace.height() >= 2) {
230 if (dimensionsChanged) {
231 dataArray.reserve(alloc: sampleSpace.height());
232 for (int i = 0; i < sampleSpace.height(); i++)
233 dataArray << new QSurfaceDataRow(sampleSpace.width());
234 }
235 for (int i = 0; i < sampleSpace.height(); i++) {
236 for (int j = 0; j < sampleSpace.width(); j++) {
237 (*(dataArray.at(i)))[j] = array.at(i: i + sampleSpace.y())->at(
238 i: j + sampleSpace.x());
239 }
240 }
241
242 checkFlatSupport(cache);
243 updateObjects(cache, dimensionChanged: dimensionsChanged);
244 cache->setFlatStatusDirty(false);
245 } else {
246 cache->surfaceObject()->clear();
247 }
248 cache->setDataDirty(false);
249 }
250 }
251
252 if (m_selectionTexturesDirty && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone)
253 updateSelectionTextures();
254
255 updateSelectedPoint(position: m_selectedPoint, series: m_selectedSeries);
256}
257
258void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
259{
260 Abstract3DRenderer::updateSeries(seriesList);
261
262 bool noSelection = true;
263 foreach (QAbstract3DSeries *series, seriesList) {
264 QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
265 SurfaceSeriesRenderCache *cache =
266 static_cast<SurfaceSeriesRenderCache *>( m_renderCacheList.value(akey: series));
267 if (noSelection
268 && surfaceSeries->selectedPoint() != QSurface3DSeries::invalidSelectionPosition()) {
269 if (selectionLabel() != cache->itemLabel())
270 m_selectionLabelDirty = true;
271 noSelection = false;
272 }
273
274 if (cache->isFlatStatusDirty() && cache->sampleSpace().width()) {
275 checkFlatSupport(cache);
276 updateObjects(cache, dimensionChanged: true);
277 cache->setFlatStatusDirty(false);
278 }
279 }
280
281 if (noSelection && !selectionLabel().isEmpty()) {
282 m_selectionLabelDirty = true;
283 updateSelectedPoint(position: Surface3DController::invalidSelectionPosition(), series: 0);
284 }
285
286 // Selection pointer issues
287 if (m_selectedSeries) {
288 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
289 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
290 QVector4D highlightColor =
291 Utils::vectorFromColor(color: cache->series()->singleHighlightColor());
292 SelectionPointer *slicePointer = cache->sliceSelectionPointer();
293 if (slicePointer) {
294 slicePointer->setHighlightColor(highlightColor);
295 slicePointer->setPointerObject(cache->object());
296 slicePointer->setRotation(cache->meshRotation());
297 }
298 SelectionPointer *mainPointer = cache->mainSelectionPointer();
299 if (mainPointer) {
300 mainPointer->setHighlightColor(highlightColor);
301 mainPointer->setPointerObject(cache->object());
302 mainPointer->setRotation(cache->meshRotation());
303 }
304 }
305 }
306}
307
308void Surface3DRenderer::updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList)
309{
310 foreach (QSurface3DSeries *series, seriesList) {
311 SurfaceSeriesRenderCache *cache =
312 static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(akey: series));
313 if (cache) {
314 GLuint oldTexture = cache->surfaceTexture();
315 m_textureHelper->deleteTexture(texture: &oldTexture);
316 cache->setSurfaceTexture(0);
317
318 const QSurface3DSeries *currentSeries = cache->series();
319 QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
320 const QSurfaceDataArray &array = *dataProxy->array();
321
322 if (!series->texture().isNull()) {
323 GLuint texId = m_textureHelper->create2DTexture(image: series->texture(),
324 useTrilinearFiltering: true, convert: true, smoothScale: true, clampY: true);
325 glBindTexture(GL_TEXTURE_2D, texture: texId);
326 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
327 glBindTexture(GL_TEXTURE_2D, texture: 0);
328 cache->setSurfaceTexture(texId);
329
330 if (cache->isFlatShadingEnabled())
331 cache->surfaceObject()->coarseUVs(dataArray: array, modelArray: cache->dataArray());
332 else
333 cache->surfaceObject()->smoothUVs(dataArray: array, modelArray: cache->dataArray());
334 }
335 }
336 }
337}
338
339SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series)
340{
341 m_selectionTexturesDirty = true;
342 return new SurfaceSeriesRenderCache(series, this);
343}
344
345void Surface3DRenderer::cleanCache(SeriesRenderCache *cache)
346{
347 Abstract3DRenderer::cleanCache(cache);
348 m_selectionTexturesDirty = true;
349}
350
351void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow> &rows)
352{
353 foreach (Surface3DController::ChangeRow item, rows) {
354 SurfaceSeriesRenderCache *cache =
355 static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(akey: item.series));
356 QSurfaceDataArray &dstArray = cache->dataArray();
357 const QRect &sampleSpace = cache->sampleSpace();
358
359 const QSurfaceDataArray *srcArray = 0;
360 QSurfaceDataProxy *dataProxy = item.series->dataProxy();
361 if (dataProxy)
362 srcArray = dataProxy->array();
363
364 if (cache && srcArray->size() >= 2 && srcArray->at(i: 0)->size() >= 2 &&
365 sampleSpace.width() >= 2 && sampleSpace.height() >= 2) {
366 bool updateBuffers = false;
367 int sampleSpaceTop = sampleSpace.y() + sampleSpace.height();
368 int row = item.row;
369 if (row >= sampleSpace.y() && row <= sampleSpaceTop) {
370 updateBuffers = true;
371 for (int j = 0; j < sampleSpace.width(); j++) {
372 (*(dstArray.at(i: row - sampleSpace.y())))[j] =
373 srcArray->at(i: row)->at(i: j + sampleSpace.x());
374 }
375
376 if (cache->isFlatShadingEnabled()) {
377 cache->surfaceObject()->updateCoarseRow(dataArray: dstArray, rowIndex: row - sampleSpace.y(),
378 polar: m_polarGraph);
379 } else {
380 cache->surfaceObject()->updateSmoothRow(dataArray: dstArray, startRow: row - sampleSpace.y(),
381 polar: m_polarGraph);
382 }
383 }
384 if (updateBuffers)
385 cache->surfaceObject()->uploadBuffers();
386 }
387 }
388
389 updateSelectedPoint(position: m_selectedPoint, series: m_selectedSeries);
390}
391
392void Surface3DRenderer::updateItems(const QVector<Surface3DController::ChangeItem> &points)
393{
394 foreach (Surface3DController::ChangeItem item, points) {
395 SurfaceSeriesRenderCache *cache =
396 static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(akey: item.series));
397 QSurfaceDataArray &dstArray = cache->dataArray();
398 const QRect &sampleSpace = cache->sampleSpace();
399
400 const QSurfaceDataArray *srcArray = 0;
401 QSurfaceDataProxy *dataProxy = item.series->dataProxy();
402 if (dataProxy)
403 srcArray = dataProxy->array();
404
405 if (cache && srcArray->size() >= 2 && srcArray->at(i: 0)->size() >= 2 &&
406 sampleSpace.width() >= 2 && sampleSpace.height() >= 2) {
407 int sampleSpaceTop = sampleSpace.y() + sampleSpace.height();
408 int sampleSpaceRight = sampleSpace.x() + sampleSpace.width();
409 bool updateBuffers = false;
410 // Note: Point is (row, column), samplespace is (columns x rows)
411 QPoint point = item.point;
412
413 if (point.x() <= sampleSpaceTop && point.x() >= sampleSpace.y() &&
414 point.y() <= sampleSpaceRight && point.y() >= sampleSpace.x()) {
415 updateBuffers = true;
416 int x = point.y() - sampleSpace.x();
417 int y = point.x() - sampleSpace.y();
418 (*(dstArray.at(i: y)))[x] = srcArray->at(i: point.x())->at(i: point.y());
419
420 if (cache->isFlatShadingEnabled())
421 cache->surfaceObject()->updateCoarseItem(dataArray: dstArray, row: y, column: x, polar: m_polarGraph);
422 else
423 cache->surfaceObject()->updateSmoothItem(dataArray: dstArray, row: y, column: x, polar: m_polarGraph);
424 }
425 if (updateBuffers)
426 cache->surfaceObject()->uploadBuffers();
427 }
428
429 }
430
431 updateSelectedPoint(position: m_selectedPoint, series: m_selectedSeries);
432}
433
434void Surface3DRenderer::updateSliceDataModel(const QPoint &point)
435{
436 foreach (SeriesRenderCache *baseCache, m_renderCacheList)
437 static_cast<SurfaceSeriesRenderCache *>(baseCache)->sliceSurfaceObject()->clear();
438
439 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries)) {
440 // Find axis coordinates for the selected point
441 SeriesRenderCache *selectedCache =
442 m_renderCacheList.value(akey: const_cast<QSurface3DSeries *>(m_selectedSeries));
443 QSurfaceDataArray &dataArray =
444 static_cast<SurfaceSeriesRenderCache *>(selectedCache)->dataArray();
445 QSurfaceDataItem item = dataArray.at(i: point.x())->at(i: point.y());
446 QPointF coords(item.x(), item.z());
447
448 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
449 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
450 if (cache->series() != m_selectedSeries) {
451 QPoint mappedPoint = mapCoordsToSampleSpace(cache, coords);
452 updateSliceObject(cache, point: mappedPoint);
453 } else {
454 updateSliceObject(cache, point);
455 }
456 }
457 } else {
458 if (m_selectedSeries) {
459 SurfaceSeriesRenderCache *cache =
460 static_cast<SurfaceSeriesRenderCache *>(
461 m_renderCacheList.value(akey: m_selectedSeries));
462 if (cache)
463 updateSliceObject(cache: static_cast<SurfaceSeriesRenderCache *>(cache), point);
464 }
465 }
466}
467
468QPoint Surface3DRenderer::mapCoordsToSampleSpace(SurfaceSeriesRenderCache *cache,
469 const QPointF &coords)
470{
471 QPoint point(-1, -1);
472
473 QSurfaceDataArray &dataArray = cache->dataArray();
474 int top = dataArray.size() - 1;
475 int right = dataArray.at(i: top)->size() - 1;
476 QSurfaceDataItem itemBottomLeft = dataArray.at(i: 0)->at(i: 0);
477 QSurfaceDataItem itemTopRight = dataArray.at(i: top)->at(i: right);
478
479 if (itemBottomLeft.x() <= coords.x() && itemTopRight.x() >= coords.x()) {
480 float modelX = coords.x() - itemBottomLeft.x();
481 float spanX = itemTopRight.x() - itemBottomLeft.x();
482 float stepX = spanX / float(right);
483 int sampleX = int((modelX + (stepX / 2.0f)) / stepX);
484
485 QSurfaceDataItem item = dataArray.at(i: 0)->at(i: sampleX);
486 if (!::qFuzzyCompare(p1: float(coords.x()), p2: item.x())) {
487 int direction = 1;
488 if (item.x() > coords.x())
489 direction = -1;
490
491 findMatchingColumn(x: coords.x(), sample&: sampleX, direction, dataArray);
492 }
493
494 if (sampleX >= 0 && sampleX <= right)
495 point.setY(sampleX);
496 }
497
498 if (itemBottomLeft.z() <= coords.y() && itemTopRight.z() >= coords.y()) {
499 float modelY = coords.y() - itemBottomLeft.z();
500 float spanY = itemTopRight.z() - itemBottomLeft.z();
501 float stepY = spanY / float(top);
502 int sampleY = int((modelY + (stepY / 2.0f)) / stepY);
503
504 QSurfaceDataItem item = dataArray.at(i: sampleY)->at(i: 0);
505 if (!::qFuzzyCompare(p1: float(coords.y()), p2: item.z())) {
506 int direction = 1;
507 if (item.z() > coords.y())
508 direction = -1;
509
510 findMatchingRow(z: coords.y(), sample&: sampleY, direction, dataArray);
511 }
512
513 if (sampleY >= 0 && sampleY <= top)
514 point.setX(sampleY);
515 }
516
517 return point;
518}
519
520void Surface3DRenderer::findMatchingRow(float z, int &sample, int direction,
521 QSurfaceDataArray &dataArray)
522{
523 int maxZ = dataArray.size() - 1;
524 QSurfaceDataItem item = dataArray.at(i: sample)->at(i: 0);
525 float distance = qAbs(t: z - item.z());
526 int newSample = sample + direction;
527 while (newSample >= 0 && newSample <= maxZ) {
528 item = dataArray.at(i: newSample)->at(i: 0);
529 float newDist = qAbs(t: z - item.z());
530 if (newDist < distance) {
531 sample = newSample;
532 distance = newDist;
533 } else {
534 break;
535 }
536 newSample = sample + direction;
537 }
538}
539
540void Surface3DRenderer::findMatchingColumn(float x, int &sample, int direction,
541 QSurfaceDataArray &dataArray)
542{
543 int maxX = dataArray.at(i: 0)->size() - 1;
544 QSurfaceDataItem item = dataArray.at(i: 0)->at(i: sample);
545 float distance = qAbs(t: x - item.x());
546 int newSample = sample + direction;
547 while (newSample >= 0 && newSample <= maxX) {
548 item = dataArray.at(i: 0)->at(i: newSample);
549 float newDist = qAbs(t: x - item.x());
550 if (newDist < distance) {
551 sample = newSample;
552 distance = newDist;
553 } else {
554 break;
555 }
556 newSample = sample + direction;
557 }
558}
559
560void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const QPoint &point)
561{
562 int column = point.y();
563 int row = point.x();
564
565 if ((m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow) && row == -1) ||
566 (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn) && column == -1)) {
567 cache->sliceSurfaceObject()->clear();
568 return;
569 }
570
571 QSurfaceDataArray &sliceDataArray = cache->sliceDataArray();
572 for (int i = 0; i < sliceDataArray.size(); i++)
573 delete sliceDataArray.at(i);
574 sliceDataArray.clear();
575 sliceDataArray.reserve(alloc: 2);
576
577 QSurfaceDataRow *sliceRow;
578 QSurfaceDataArray &dataArray = cache->dataArray();
579 float adjust = (0.025f * m_heightNormalizer) / 2.0f;
580 float doubleAdjust = 2.0f * adjust;
581 bool flipZX = false;
582 float zBack;
583 float zFront;
584 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)) {
585 QSurfaceDataRow *src = dataArray.at(i: row);
586 sliceRow = new QSurfaceDataRow(src->size());
587 zBack = m_axisCacheZ.min();
588 zFront = m_axisCacheZ.max();
589 for (int i = 0; i < sliceRow->size(); i++)
590 (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, zFront));
591 } else {
592 flipZX = true;
593 const QRect &sampleSpace = cache->sampleSpace();
594 sliceRow = new QSurfaceDataRow(sampleSpace.height());
595 zBack = m_axisCacheX.min();
596 zFront = m_axisCacheX.max();
597 for (int i = 0; i < sampleSpace.height(); i++) {
598 (*sliceRow)[i].setPosition(QVector3D(dataArray.at(i)->at(i: column).z(),
599 dataArray.at(i)->at(i: column).y() + adjust,
600 zFront));
601 }
602 }
603 sliceDataArray << sliceRow;
604
605 // Make a duplicate, so that we get a little bit depth
606 QSurfaceDataRow *duplicateRow = new QSurfaceDataRow(*sliceRow);
607 for (int i = 0; i < sliceRow->size(); i++) {
608 (*sliceRow)[i].setPosition(QVector3D(sliceRow->at(i).x(),
609 sliceRow->at(i).y() - doubleAdjust,
610 zBack));
611 }
612 sliceDataArray << duplicateRow;
613
614 QRect sliceRect(0, 0, sliceRow->size(), 2);
615 if (sliceRow->size() > 0) {
616 if (cache->isFlatShadingEnabled()) {
617 cache->sliceSurfaceObject()->setUpData(dataArray: sliceDataArray, space: sliceRect, changeGeometry: true, polar: false, flipXZ: flipZX);
618 } else {
619 cache->sliceSurfaceObject()->setUpSmoothData(dataArray: sliceDataArray, space: sliceRect, changeGeometry: true, polar: false,
620 flipXZ: flipZX);
621 }
622 }
623}
624
625inline static float getDataValue(const QSurfaceDataArray &array, bool searchRow, int index)
626{
627 if (searchRow)
628 return array.at(i: 0)->at(i: index).x();
629 else
630 return array.at(i: index)->at(i: 0).z();
631}
632
633inline static int binarySearchArray(const QSurfaceDataArray &array, int maxIdx, float limitValue,
634 bool searchRow, bool lowBound, bool ascending)
635{
636 int min = 0;
637 int max = maxIdx;
638 int mid = 0;
639 int retVal;
640 while (max >= min) {
641 mid = (min + max) / 2;
642 float arrayValue = getDataValue(array, searchRow, index: mid);
643 if (arrayValue == limitValue)
644 return mid;
645 if (ascending) {
646 if (arrayValue < limitValue)
647 min = mid + 1;
648 else
649 max = mid - 1;
650 } else {
651 if (arrayValue > limitValue)
652 min = mid + 1;
653 else
654 max = mid - 1;
655 }
656 }
657
658 // Exact match not found, return closest depending on bound.
659 // The boundary is between last mid and min/max.
660 if (lowBound == ascending) {
661 if (mid > max)
662 retVal = mid;
663 else
664 retVal = min;
665 } else {
666 if (mid > max)
667 retVal = max;
668 else
669 retVal = mid;
670 }
671 if (retVal < 0 || retVal > maxIdx) {
672 retVal = -1;
673 } else if (lowBound) {
674 if (getDataValue(array, searchRow, index: retVal) < limitValue)
675 retVal = -1;
676 } else {
677 if (getDataValue(array, searchRow, index: retVal) > limitValue)
678 retVal = -1;
679 }
680 return retVal;
681}
682
683QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array)
684{
685 QRect sampleSpace;
686
687 const int maxRow = array.size() - 1;
688 const int maxColumn = array.at(i: 0)->size() - 1;
689
690 // We assume data is ordered sequentially in rows for X-value and in columns for Z-value.
691 // Determine if data is ascending or descending in each case.
692 const bool ascendingX = array.at(i: 0)->at(i: 0).x() < array.at(i: 0)->at(i: maxColumn).x();
693 const bool ascendingZ = array.at(i: 0)->at(i: 0).z() < array.at(i: maxRow)->at(i: 0).z();
694
695 int idx = binarySearchArray(array, maxIdx: maxColumn, limitValue: m_axisCacheX.min(), searchRow: true, lowBound: true, ascending: ascendingX);
696 if (idx != -1) {
697 if (ascendingX)
698 sampleSpace.setLeft(idx);
699 else
700 sampleSpace.setRight(idx);
701 } else {
702 sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
703 return sampleSpace;
704 }
705
706 idx = binarySearchArray(array, maxIdx: maxColumn, limitValue: m_axisCacheX.max(), searchRow: true, lowBound: false, ascending: ascendingX);
707 if (idx != -1) {
708 if (ascendingX)
709 sampleSpace.setRight(idx);
710 else
711 sampleSpace.setLeft(idx);
712 } else {
713 sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
714 return sampleSpace;
715 }
716
717 idx = binarySearchArray(array, maxIdx: maxRow, limitValue: m_axisCacheZ.min(), searchRow: false, lowBound: true, ascending: ascendingZ);
718 if (idx != -1) {
719 if (ascendingZ)
720 sampleSpace.setTop(idx);
721 else
722 sampleSpace.setBottom(idx);
723 } else {
724 sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
725 return sampleSpace;
726 }
727
728 idx = binarySearchArray(array, maxIdx: maxRow, limitValue: m_axisCacheZ.max(), searchRow: false, lowBound: false, ascending: ascendingZ);
729 if (idx != -1) {
730 if (ascendingZ)
731 sampleSpace.setBottom(idx);
732 else
733 sampleSpace.setTop(idx);
734 } else {
735 sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
736 return sampleSpace;
737 }
738
739 return sampleSpace;
740}
741
742void Surface3DRenderer::updateScene(Q3DScene *scene)
743{
744 Abstract3DRenderer::updateScene(scene);
745
746 if (m_selectionActive
747 && m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionItem)) {
748 m_selectionDirty = true; // Ball may need repositioning if scene changes
749 }
750
751 updateSlicingActive(isSlicing: scene->isSlicingActive());
752}
753
754void Surface3DRenderer::render(GLuint defaultFboHandle)
755{
756 // Handle GL state setup for FBO buffers and clearing of the render surface
757 Abstract3DRenderer::render(defaultFboHandle);
758
759 if (m_axisCacheX.positionsDirty())
760 m_axisCacheX.updateAllPositions();
761 if (m_axisCacheY.positionsDirty())
762 m_axisCacheY.updateAllPositions();
763 if (m_axisCacheZ.positionsDirty())
764 m_axisCacheZ.updateAllPositions();
765
766 drawScene(defaultFboHandle);
767 if (m_cachedIsSlicingActivated)
768 drawSlicedScene();
769
770 // Render selection label
771 if (m_selectionActive
772 && m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionItem)) {
773 for (SeriesRenderCache *baseCache: m_renderCacheList) {
774 const SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
775 if (cache->slicePointerActive() && cache->renderable() &&
776 m_cachedIsSlicingActivated ) {
777 cache->sliceSelectionPointer()->renderSelectionLabel(defaultFboHandle);
778 }
779 if (cache->mainPointerActive() && cache->renderable()) {
780 cache->mainSelectionPointer()->renderSelectionLabel(defaultFboHandle,
781 useOrtho: m_useOrthoProjection);
782 }
783 }
784 }
785}
786
787void Surface3DRenderer::drawSlicedScene()
788{
789 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)
790 == m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) {
791 qWarning(msg: "Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
792 " QAbstract3DGraph::SelectionColumn must be set before calling"
793 " setSlicingActive(true).");
794 return;
795 }
796
797 QVector3D lightPos;
798
799 QVector4D lightColor = Utils::vectorFromColor(color: m_cachedTheme->lightColor());
800
801 // Specify viewport
802 glViewport(x: m_secondarySubViewport.x(),
803 y: m_secondarySubViewport.y(),
804 width: m_secondarySubViewport.width(),
805 height: m_secondarySubViewport.height());
806
807 // Set up projection matrix
808 QMatrix4x4 projectionMatrix;
809
810 GLfloat aspect = (GLfloat)m_secondarySubViewport.width()
811 / (GLfloat)m_secondarySubViewport.height();
812 GLfloat sliceUnitsScaled = sliceUnits / m_autoScaleAdjustment;
813 projectionMatrix.ortho(left: -sliceUnitsScaled * aspect, right: sliceUnitsScaled * aspect,
814 bottom: -sliceUnitsScaled, top: sliceUnitsScaled,
815 nearPlane: -1.0f, farPlane: 4.0f);
816
817 // Set view matrix
818 QMatrix4x4 viewMatrix;
819 viewMatrix.lookAt(eye: QVector3D(0.0f, 0.0f, 1.0f), center: zeroVector, up: upVector);
820
821 // Set light position
822 lightPos = QVector3D(0.0f, 0.0f, 2.0f);
823
824 QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
825
826 const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
827
828 bool rowMode = m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow);
829 AxisRenderCache &sliceCache = rowMode ? m_axisCacheX : m_axisCacheZ;
830
831 GLfloat scaleXBackground = 0.0f;
832 if (rowMode) {
833 // Don't use the regular margin for polar, as the graph is not going to be to scale anyway,
834 // and polar graphs often have quite a bit of margin, resulting in ugly slices.
835 if (m_polarGraph)
836 scaleXBackground = m_scaleX + 0.1f;
837 else
838 scaleXBackground = m_scaleXWithBackground;
839 } else {
840 if (m_polarGraph)
841 scaleXBackground = m_scaleZ + 0.1f;
842 else
843 scaleXBackground = m_scaleZWithBackground;
844 }
845
846 // Disable culling to avoid ugly conditionals with reversed axes and data
847 glDisable(GL_CULL_FACE);
848
849 if (!m_renderCacheList.isEmpty()) {
850 bool drawGrid = false;
851
852 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
853 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
854 if (cache->sliceSurfaceObject()->indexCount() && cache->renderable()) {
855 if (!drawGrid && cache->surfaceGridVisible()) {
856 glEnable(GL_POLYGON_OFFSET_FILL);
857 glPolygonOffset(factor: 0.5f, units: 1.0f);
858 drawGrid = true;
859 }
860
861 QMatrix4x4 MVPMatrix;
862 QMatrix4x4 modelMatrix;
863 QMatrix4x4 itModelMatrix;
864
865 QVector3D scaling(1.0f, 1.0f, sliceZScale);
866 modelMatrix.scale(vector: scaling);
867 itModelMatrix.scale(vector: scaling);
868
869 MVPMatrix = projectionViewMatrix * modelMatrix;
870 cache->setMVPMatrix(MVPMatrix);
871
872 if (cache->surfaceVisible()) {
873 ShaderHelper *surfaceShader = m_surfaceSliceSmoothShader;
874 if (cache->isFlatShadingEnabled())
875 surfaceShader = m_surfaceSliceFlatShader;
876
877 surfaceShader->bind();
878
879 GLuint colorTexture = cache->baseUniformTexture();
880 if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
881 colorTexture = cache->baseUniformTexture();
882 surfaceShader->setUniformValue(uniform: surfaceShader->gradientMin(), value: 0.0f);
883 surfaceShader->setUniformValue(uniform: surfaceShader->gradientHeight(), value: 0.0f);
884 } else {
885 colorTexture = cache->baseGradientTexture();
886 if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
887 float objMin = cache->surfaceObject()->minYValue();
888 float objMax = cache->surfaceObject()->maxYValue();
889 float objRange = objMax - objMin;
890 surfaceShader->setUniformValue(uniform: surfaceShader->gradientMin(),
891 value: -(objMin / objRange));
892 surfaceShader->setUniformValue(uniform: surfaceShader->gradientHeight(),
893 value: 1.0f / objRange);
894 } else {
895 surfaceShader->setUniformValue(uniform: surfaceShader->gradientMin(), value: 0.5f);
896 surfaceShader->setUniformValue(uniform: surfaceShader->gradientHeight(),
897 value: 1.0f / (m_scaleY * 2.0f));
898 }
899 }
900
901 // Set shader bindings
902 surfaceShader->setUniformValue(uniform: surfaceShader->lightP(), value: lightPos);
903 surfaceShader->setUniformValue(uniform: surfaceShader->view(), value: viewMatrix);
904 surfaceShader->setUniformValue(uniform: surfaceShader->model(), value: modelMatrix);
905 surfaceShader->setUniformValue(uniform: surfaceShader->nModel(),
906 value: itModelMatrix.inverted().transposed());
907 surfaceShader->setUniformValue(uniform: surfaceShader->MVP(), value: MVPMatrix);
908 surfaceShader->setUniformValue(uniform: surfaceShader->lightS(), value: 0.0f);
909 surfaceShader->setUniformValue(uniform: surfaceShader->ambientS(),
910 value: m_cachedTheme->ambientLightStrength()
911 + m_cachedTheme->lightStrength() / 10.0f);
912 surfaceShader->setUniformValue(uniform: surfaceShader->lightColor(), value: lightColor);
913
914 m_drawer->drawObject(shader: surfaceShader, object: cache->sliceSurfaceObject(), textureId: colorTexture);
915 }
916 }
917 }
918
919 // Draw surface grid
920 if (drawGrid) {
921 glDisable(GL_POLYGON_OFFSET_FILL);
922 m_surfaceGridShader->bind();
923 m_surfaceGridShader->setUniformValue(uniform: m_surfaceGridShader->color(),
924 value: Utils::vectorFromColor(color: m_cachedTheme->gridLineColor()));
925 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
926 SurfaceSeriesRenderCache *cache =
927 static_cast<SurfaceSeriesRenderCache *>(baseCache);
928 if (cache->sliceSurfaceObject()->indexCount() && cache->isVisible() &&
929 cache->surfaceGridVisible()) {
930 m_surfaceGridShader->setUniformValue(uniform: m_surfaceGridShader->MVP(),
931 value: cache->MVPMatrix());
932 m_drawer->drawSurfaceGrid(shader: m_surfaceGridShader, object: cache->sliceSurfaceObject());
933 }
934 }
935 }
936 }
937
938 glEnable(GL_CULL_FACE);
939 glCullFace(GL_BACK);
940
941 // Grid lines
942 if (m_cachedTheme->isGridEnabled()) {
943 ShaderHelper *lineShader;
944 if (m_isOpenGLES)
945 lineShader = m_selectionShader; // Plain color shader for GL_LINES
946 else
947 lineShader = m_backgroundShader;
948
949 // Bind line shader
950 lineShader->bind();
951
952 // Set unchanging shader bindings
953 QVector4D lineColor = Utils::vectorFromColor(color: m_cachedTheme->gridLineColor());
954 lineShader->setUniformValue(uniform: lineShader->lightP(), value: lightPos);
955 lineShader->setUniformValue(uniform: lineShader->view(), value: viewMatrix);
956 lineShader->setUniformValue(uniform: lineShader->color(), value: lineColor);
957 lineShader->setUniformValue(uniform: lineShader->ambientS(),
958 value: m_cachedTheme->ambientLightStrength()
959 + m_cachedTheme->lightStrength() / 10.0f);
960 lineShader->setUniformValue(uniform: lineShader->lightS(), value: 0.0f);
961 lineShader->setUniformValue(uniform: lineShader->lightColor(), value: lightColor);
962
963 // Horizontal lines
964 int gridLineCount = m_axisCacheY.gridLineCount();
965 if (m_axisCacheY.segmentCount() > 0) {
966 QVector3D gridLineScaleX(scaleXBackground, gridLineWidth, gridLineWidth);
967
968 for (int line = 0; line < gridLineCount; line++) {
969 QMatrix4x4 modelMatrix;
970 QMatrix4x4 MVPMatrix;
971 QMatrix4x4 itModelMatrix;
972
973 modelMatrix.translate(x: 0.0f, y: m_axisCacheY.gridLinePosition(index: line), z: -1.0f);
974
975 modelMatrix.scale(vector: gridLineScaleX);
976 itModelMatrix.scale(vector: gridLineScaleX);
977
978 MVPMatrix = projectionViewMatrix * modelMatrix;
979
980 // Set the rest of the shader bindings
981 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
982 lineShader->setUniformValue(uniform: lineShader->nModel(),
983 value: itModelMatrix.inverted().transposed());
984 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
985
986 // Draw the object
987 if (m_isOpenGLES)
988 m_drawer->drawLine(shader: lineShader);
989 else
990 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
991 }
992 }
993
994 // Vertical lines
995 QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
996
997 gridLineCount = sliceCache.gridLineCount();
998 for (int line = 0; line < gridLineCount; line++) {
999 QMatrix4x4 modelMatrix;
1000 QMatrix4x4 MVPMatrix;
1001 QMatrix4x4 itModelMatrix;
1002
1003 modelMatrix.translate(x: sliceCache.gridLinePosition(index: line), y: 0.0f, z: -1.0f);
1004 modelMatrix.scale(vector: gridLineScaleY);
1005 itModelMatrix.scale(vector: gridLineScaleY);
1006
1007 if (m_isOpenGLES) {
1008 modelMatrix.rotate(quaternion: m_zRightAngleRotation);
1009 itModelMatrix.rotate(quaternion: m_zRightAngleRotation);
1010 }
1011
1012 MVPMatrix = projectionViewMatrix * modelMatrix;
1013
1014 // Set the rest of the shader bindings
1015 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1016 lineShader->setUniformValue(uniform: lineShader->nModel(),
1017 value: itModelMatrix.inverted().transposed());
1018 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1019
1020 // Draw the object
1021 if (m_isOpenGLES)
1022 m_drawer->drawLine(shader: lineShader);
1023 else
1024 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1025 }
1026 }
1027
1028 // Draw labels
1029 m_labelShader->bind();
1030 glDisable(GL_DEPTH_TEST);
1031 glEnable(GL_BLEND);
1032 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1033
1034 // Y Labels to back wall
1035 int labelNbr = 0;
1036
1037 QVector3D positionComp(0.0f, 0.0f, 0.0f);
1038 QVector3D labelTrans = QVector3D(scaleXBackground + labelMargin, 0.0f, 0.0f);
1039 int labelCount = m_axisCacheY.labelCount();
1040 for (int label = 0; label < labelCount; label++) {
1041 if (m_axisCacheY.labelItems().size() > labelNbr) {
1042 labelTrans.setY(m_axisCacheY.labelPosition(index: label));
1043 const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i: labelNbr);
1044
1045 // Draw the label here
1046 m_dummyRenderItem.setTranslation(labelTrans);
1047 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
1048 positionComp, rotation: identityQuaternion, itemHeight: 0, mode: m_cachedSelectionMode,
1049 shader: m_labelShader, object: m_labelObj, camera: activeCamera, useDepth: true, rotateAlong: true,
1050 position: Drawer::LabelMid, alignment: Qt::AlignLeft, isSlicing: true);
1051 }
1052 labelNbr++;
1053 }
1054
1055 // X Labels to ground
1056 int countLabelItems = sliceCache.labelItems().size();
1057
1058 QVector3D rotation(0.0f, 0.0f, -45.0f);
1059 QQuaternion totalRotation = Utils::calculateRotation(xyzRotations: rotation);
1060
1061 labelNbr = 0;
1062 positionComp.setY(-0.1f);
1063 labelTrans.setY(-m_scaleYWithBackground);
1064 labelCount = sliceCache.labelCount();
1065 for (int label = 0; label < labelCount; label++) {
1066 if (countLabelItems > labelNbr) {
1067 // Draw the label here
1068 if (rowMode)
1069 labelTrans.setX(sliceCache.labelPosition(index: label));
1070 else
1071 labelTrans.setX(-sliceCache.labelPosition(index: label));
1072
1073 m_dummyRenderItem.setTranslation(labelTrans);
1074
1075 LabelItem *axisLabelItem;
1076 axisLabelItem = sliceCache.labelItems().at(i: labelNbr);
1077
1078 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: *axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
1079 positionComp, rotation: totalRotation, itemHeight: 0, mode: QAbstract3DGraph::SelectionRow,
1080 shader: m_labelShader, object: m_labelObj, camera: activeCamera,
1081 useDepth: false, rotateAlong: false, position: Drawer::LabelBelow,
1082 alignment: Qt::AlignLeft | Qt::AlignTop, isSlicing: true);
1083 }
1084 labelNbr++;
1085 }
1086
1087 // Draw labels for axes
1088 AbstractRenderItem *dummyItem(0);
1089 positionComp.setY(m_autoScaleAdjustment);
1090 m_drawer->drawLabel(item: *dummyItem, labelItem: sliceCache.titleItem(), viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
1091 positionComp, rotation: identityQuaternion, itemHeight: 0, mode: m_cachedSelectionMode, shader: m_labelShader,
1092 object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false, position: Drawer::LabelBottom,
1093 alignment: Qt::AlignCenter, isSlicing: true);
1094
1095 // Y-axis label
1096 rotation = QVector3D(0.0f, 0.0f, 90.0f);
1097 totalRotation = Utils::calculateRotation(xyzRotations: rotation);
1098 labelTrans = QVector3D(-scaleXBackground - labelMargin, 0.0f, 0.0f);
1099 m_dummyRenderItem.setTranslation(labelTrans);
1100 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: m_axisCacheY.titleItem(), viewmatrix: viewMatrix,
1101 projectionmatrix: projectionMatrix, positionComp: zeroVector, rotation: totalRotation, itemHeight: 0,
1102 mode: m_cachedSelectionMode, shader: m_labelShader, object: m_labelObj, camera: activeCamera,
1103 useDepth: false, rotateAlong: false, position: Drawer::LabelMid, alignment: Qt::AlignBottom);
1104
1105 glEnable(GL_DEPTH_TEST);
1106 glDisable(GL_BLEND);
1107
1108 // Release shader
1109 glUseProgram(program: 0);
1110}
1111
1112void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
1113{
1114 bool noShadows = true;
1115
1116 GLfloat backgroundRotation = 0;
1117 QVector4D lightColor = Utils::vectorFromColor(color: m_cachedTheme->lightColor());
1118
1119 glViewport(x: m_primarySubViewport.x(),
1120 y: m_primarySubViewport.y(),
1121 width: m_primarySubViewport.width(),
1122 height: m_primarySubViewport.height());
1123
1124 // Set up projection matrix
1125 QMatrix4x4 projectionMatrix;
1126 GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
1127 / (GLfloat)m_primarySubViewport.height();
1128 if (m_useOrthoProjection) {
1129 GLfloat orthoRatio = 2.0f;
1130 projectionMatrix.ortho(left: -viewPortRatio * orthoRatio, right: viewPortRatio * orthoRatio,
1131 bottom: -orthoRatio, top: orthoRatio,
1132 nearPlane: 0.0f, farPlane: 100.0f);
1133 } else {
1134 projectionMatrix.perspective(verticalAngle: 45.0f, aspectRatio: viewPortRatio, nearPlane: 0.1f, farPlane: 100.0f);
1135 }
1136
1137 const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
1138
1139 // Calculate view matrix
1140 QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
1141
1142 QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
1143
1144 // Calculate flipping indicators
1145 if (viewMatrix.row(index: 0).x() > 0)
1146 m_zFlipped = false;
1147 else
1148 m_zFlipped = true;
1149 if (viewMatrix.row(index: 0).z() <= 0)
1150 m_xFlipped = false;
1151 else
1152 m_xFlipped = true;
1153
1154 m_yFlippedForGrid = m_yFlipped;
1155 if (m_flipHorizontalGrid) {
1156 if (!m_useOrthoProjection) {
1157 // Need to determine if camera is below graph top
1158 float distanceToCenter = activeCamera->position().length()
1159 / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f;
1160 qreal cameraAngle = qDegreesToRadians(degrees: qreal(activeCamera->yRotation()));
1161 float cameraYPos = float(qSin(v: cameraAngle)) * distanceToCenter;
1162 m_yFlippedForGrid = cameraYPos < (m_scaleYWithBackground - m_oldCameraTarget.y());
1163 } else if (m_useOrthoProjection && activeCamera->yRotation() == 0.0f) {
1164 // With ortho we only need to flip at angle zero, to fix label autorotation angles
1165 m_yFlippedForGrid = !m_yFlipped;
1166 }
1167 }
1168
1169 // calculate background rotation based on view matrix rotation
1170 if (viewMatrix.row(index: 0).x() > 0 && viewMatrix.row(index: 0).z() <= 0)
1171 backgroundRotation = 270.0f;
1172 else if (viewMatrix.row(index: 0).x() > 0 && viewMatrix.row(index: 0).z() > 0)
1173 backgroundRotation = 180.0f;
1174 else if (viewMatrix.row(index: 0).x() <= 0 && viewMatrix.row(index: 0).z() > 0)
1175 backgroundRotation = 90.0f;
1176 else if (viewMatrix.row(index: 0).x() <= 0 && viewMatrix.row(index: 0).z() <= 0)
1177 backgroundRotation = 0.0f;
1178
1179 QVector3D lightPos = m_cachedScene->activeLight()->position();
1180
1181 QMatrix4x4 depthViewMatrix;
1182 QMatrix4x4 depthProjectionMatrix;
1183 QMatrix4x4 depthProjectionViewMatrix;
1184
1185 // Draw depth buffer
1186 GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
1187 if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone &&
1188 (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty())) {
1189 // Render scene into a depth texture for using with shadow mapping
1190 // Enable drawing to depth framebuffer
1191 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_depthFrameBuffer);
1192
1193 // Attach texture to depth attachment
1194 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
1195 texture: m_depthTexture, level: 0);
1196 glClear(GL_DEPTH_BUFFER_BIT);
1197
1198 // Bind depth shader
1199 m_depthShader->bind();
1200
1201 // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
1202 glViewport(x: 0, y: 0,
1203 width: m_primarySubViewport.width() * m_shadowQualityMultiplier,
1204 height: m_primarySubViewport.height() * m_shadowQualityMultiplier);
1205
1206 // Get the depth view matrix
1207 // It may be possible to hack lightPos here if we want to make some tweaks to shadow
1208 QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
1209 relativePosition: zeroVector, fixedRotation: 0.0f, distanceModifier: 4.0f / m_autoScaleAdjustment);
1210 depthViewMatrix.lookAt(eye: depthLightPos, center: zeroVector, up: upVector);
1211
1212 // Set the depth projection matrix
1213 depthProjectionMatrix.perspective(verticalAngle: 10.0f, aspectRatio: (GLfloat)m_primarySubViewport.width()
1214 / (GLfloat)m_primarySubViewport.height(), nearPlane: 3.0f, farPlane: 100.0f);
1215 depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
1216
1217 // Surface is not closed, so don't cull anything
1218 glDisable(GL_CULL_FACE);
1219
1220 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1221 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
1222 SurfaceObject *object = cache->surfaceObject();
1223 if (object->indexCount() && cache->surfaceVisible() && cache->isVisible()
1224 && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) {
1225 // No translation nor scaling for surfaces, therefore no modelMatrix
1226 // Use directly projectionViewMatrix
1227 m_depthShader->setUniformValue(uniform: m_depthShader->MVP(), value: depthProjectionViewMatrix);
1228
1229 // 1st attribute buffer : vertices
1230 glEnableVertexAttribArray(index: m_depthShader->posAtt());
1231 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->vertexBuf());
1232 glVertexAttribPointer(indx: m_depthShader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0,
1233 ptr: (void *)0);
1234
1235 // Index buffer
1236 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: object->elementBuf());
1237
1238 // Draw the triangles
1239 glDrawElements(GL_TRIANGLES, count: object->indexCount(), GL_UNSIGNED_INT, indices: (void *)0);
1240 }
1241 }
1242
1243 // Free buffers
1244 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
1245 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
1246
1247 glDisableVertexAttribArray(index: m_depthShader->posAtt());
1248
1249 glEnable(GL_CULL_FACE);
1250 glCullFace(GL_FRONT);
1251
1252 Abstract3DRenderer::drawCustomItems(state: RenderingDepth, regularShader: m_depthShader, viewMatrix,
1253 projectionViewMatrix,
1254 depthProjectionViewMatrix, depthTexture: m_depthTexture,
1255 shadowQuality: m_shadowQualityToShader);
1256
1257 // Disable drawing to depth framebuffer (= enable drawing to screen)
1258 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: defaultFboHandle);
1259
1260 // Revert to original viewport
1261 glViewport(x: m_primarySubViewport.x(),
1262 y: m_primarySubViewport.y(),
1263 width: m_primarySubViewport.width(),
1264 height: m_primarySubViewport.height());
1265
1266 // Reset culling to normal
1267 glEnable(GL_CULL_FACE);
1268 glCullFace(GL_BACK);
1269 }
1270
1271 // Do position mapping when necessary
1272 if (m_graphPositionQueryPending) {
1273 QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ);
1274 queriedGraphPosition(projectionViewMatrix, scaling: graphDimensions, defaultFboHandle);
1275 emit needRender();
1276 }
1277
1278 // Draw selection buffer
1279 if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty()
1280 || !m_customRenderCache.isEmpty())
1281 && m_selectionState == SelectOnScene
1282 && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
1283 && m_selectionResultTexture) {
1284 m_selectionShader->bind();
1285 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_selectionFrameBuffer);
1286 glViewport(x: 0,
1287 y: 0,
1288 width: m_primarySubViewport.width(),
1289 height: m_primarySubViewport.height());
1290
1291 glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used
1292 glClearColor(red: 0.0f, green: 0.0f, blue: 0.0f, alpha: 0.0f);
1293 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer
1294 glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled
1295
1296 glDisable(GL_CULL_FACE);
1297
1298 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1299 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
1300 if (cache->surfaceObject()->indexCount() && cache->renderable()) {
1301 m_selectionShader->setUniformValue(uniform: m_selectionShader->MVP(), value: projectionViewMatrix);
1302
1303 cache->surfaceObject()->activateSurfaceTexture(value: false);
1304
1305 m_drawer->drawObject(shader: m_selectionShader, object: cache->surfaceObject(),
1306 textureId: cache->selectionTexture());
1307 }
1308 }
1309 m_surfaceGridShader->bind();
1310 Abstract3DRenderer::drawCustomItems(state: RenderingSelection, regularShader: m_surfaceGridShader,
1311 viewMatrix,
1312 projectionViewMatrix, depthProjectionViewMatrix,
1313 depthTexture: m_depthTexture, shadowQuality: m_shadowQualityToShader);
1314 drawLabels(drawSelection: true, activeCamera, viewMatrix, projectionMatrix);
1315
1316 glEnable(GL_DITHER);
1317
1318 QVector4D clickedColor = Utils::getSelection(mousepos: m_inputPosition, height: m_viewport.height());
1319
1320 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: defaultFboHandle);
1321
1322 // Put the RGBA value back to uint
1323 uint selectionId = uint(clickedColor.x())
1324 + uint(clickedColor.y()) * greenMultiplier
1325 + uint(clickedColor.z()) * blueMultiplier
1326 + uint(clickedColor.w()) * alphaMultiplier;
1327
1328 m_clickedPosition = selectionIdToSurfacePoint(id: selectionId);
1329 m_clickResolved = true;
1330
1331 emit needRender();
1332
1333 // Revert to original viewport
1334 glViewport(x: m_primarySubViewport.x(),
1335 y: m_primarySubViewport.y(),
1336 width: m_primarySubViewport.width(),
1337 height: m_primarySubViewport.height());
1338 }
1339
1340 // Selection handling
1341 if (m_selectionDirty || m_selectionLabelDirty) {
1342 QPoint visiblePoint = Surface3DController::invalidSelectionPosition();
1343 if (m_selectedSeries) {
1344 SurfaceSeriesRenderCache *cache =
1345 static_cast<SurfaceSeriesRenderCache *>(
1346 m_renderCacheList.value(akey: const_cast<QSurface3DSeries *>(m_selectedSeries)));
1347 if (cache && m_selectedPoint != Surface3DController::invalidSelectionPosition()) {
1348 const QRect &sampleSpace = cache->sampleSpace();
1349 int x = m_selectedPoint.x() - sampleSpace.y();
1350 int y = m_selectedPoint.y() - sampleSpace.x();
1351 if (x >= 0 && y >= 0 && x < sampleSpace.height() && y < sampleSpace.width()
1352 && cache->dataArray().size()) {
1353 visiblePoint = QPoint(x, y);
1354 }
1355 }
1356 }
1357
1358 if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone
1359 || visiblePoint == Surface3DController::invalidSelectionPosition()) {
1360 m_selectionActive = false;
1361 } else {
1362 if (m_cachedIsSlicingActivated)
1363 updateSliceDataModel(point: visiblePoint);
1364 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionItem))
1365 surfacePointSelected(point: visiblePoint);
1366 m_selectionActive = true;
1367 }
1368
1369 m_selectionDirty = false;
1370 }
1371
1372 // Draw the surface
1373 if (!m_renderCacheList.isEmpty()) {
1374 // For surface we can see glimpses from underneath
1375 glDisable(GL_CULL_FACE);
1376
1377 bool drawGrid = false;
1378
1379 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1380 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
1381 QMatrix4x4 modelMatrix;
1382 QMatrix4x4 MVPMatrix;
1383 QMatrix4x4 itModelMatrix;
1384
1385#ifdef SHOW_DEPTH_TEXTURE_SCENE
1386 MVPMatrix = depthProjectionViewMatrix;
1387#else
1388 MVPMatrix = projectionViewMatrix;
1389#endif
1390 cache->setMVPMatrix(MVPMatrix);
1391
1392 const QRect &sampleSpace = cache->sampleSpace();
1393 if (cache->surfaceObject()->indexCount() && cache->isVisible() &&
1394 sampleSpace.width() >= 2 && sampleSpace.height() >= 2) {
1395 noShadows = false;
1396 if (!drawGrid && cache->surfaceGridVisible()) {
1397 glEnable(GL_POLYGON_OFFSET_FILL);
1398 glPolygonOffset(factor: 0.5f, units: 1.0f);
1399 drawGrid = true;
1400 }
1401
1402 if (cache->surfaceVisible()) {
1403 ShaderHelper *shader = m_surfaceFlatShader;
1404 if (cache->surfaceTexture())
1405 shader = m_surfaceTexturedFlatShader;
1406 if (!cache->isFlatShadingEnabled()) {
1407 shader = m_surfaceSmoothShader;
1408 if (cache->surfaceTexture())
1409 shader = m_surfaceTexturedSmoothShader;
1410 }
1411 shader->bind();
1412
1413 // Set shader bindings
1414 shader->setUniformValue(uniform: shader->lightP(), value: lightPos);
1415 shader->setUniformValue(uniform: shader->view(), value: viewMatrix);
1416 shader->setUniformValue(uniform: shader->model(), value: modelMatrix);
1417 shader->setUniformValue(uniform: shader->nModel(),
1418 value: itModelMatrix.inverted().transposed());
1419 shader->setUniformValue(uniform: shader->MVP(), value: MVPMatrix);
1420 shader->setUniformValue(uniform: shader->ambientS(),
1421 value: m_cachedTheme->ambientLightStrength());
1422 shader->setUniformValue(uniform: shader->lightColor(), value: lightColor);
1423
1424 // Set the surface texturing
1425 cache->surfaceObject()->activateSurfaceTexture(value: false);
1426 GLuint texture;
1427 if (cache->surfaceTexture()) {
1428 texture = cache->surfaceTexture();
1429 cache->surfaceObject()->activateSurfaceTexture(value: true);
1430 } else {
1431 if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
1432 texture = cache->baseUniformTexture();
1433 shader->setUniformValue(uniform: shader->gradientMin(), value: 0.0f);
1434 shader->setUniformValue(uniform: shader->gradientHeight(), value: 0.0f);
1435 } else {
1436 texture = cache->baseGradientTexture();
1437 if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
1438 float objMin = cache->surfaceObject()->minYValue();
1439 float objMax = cache->surfaceObject()->maxYValue();
1440 float objRange = objMax - objMin;
1441 shader->setUniformValue(uniform: shader->gradientMin(), value: -(objMin / objRange));
1442 shader->setUniformValue(uniform: shader->gradientHeight(), value: 1.0f / objRange);
1443 } else {
1444 shader->setUniformValue(uniform: shader->gradientMin(), value: 0.5f);
1445 shader->setUniformValue(uniform: shader->gradientHeight(),
1446 value: 1.0f / (m_scaleY * 2.0f));
1447 }
1448 }
1449 }
1450
1451 if (!m_isOpenGLES &&
1452 m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1453 // Set shadow shader bindings
1454 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1455 shader->setUniformValue(uniform: shader->shadowQ(), value: m_shadowQualityToShader);
1456 shader->setUniformValue(uniform: shader->depth(), value: depthMVPMatrix);
1457 shader->setUniformValue(uniform: shader->lightS(), value: adjustedLightStrength);
1458
1459 // Draw the objects
1460 m_drawer->drawObject(shader, object: cache->surfaceObject(), textureId: texture,
1461 depthTextureId: m_depthTexture);
1462 } else {
1463 // Set shadowless shader bindings
1464 shader->setUniformValue(uniform: shader->lightS(), value: m_cachedTheme->lightStrength());
1465 // Draw the objects
1466 m_drawer->drawObject(shader, object: cache->surfaceObject(), textureId: texture);
1467 }
1468 }
1469 }
1470 }
1471 glEnable(GL_CULL_FACE);
1472
1473 // Draw surface grid
1474 if (drawGrid) {
1475 glDisable(GL_POLYGON_OFFSET_FILL);
1476 m_surfaceGridShader->bind();
1477 m_surfaceGridShader->setUniformValue(uniform: m_surfaceGridShader->color(),
1478 value: Utils::vectorFromColor(
1479 color: m_cachedTheme->gridLineColor()));
1480 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1481 SurfaceSeriesRenderCache *cache =
1482 static_cast<SurfaceSeriesRenderCache *>(baseCache);
1483 m_surfaceGridShader->setUniformValue(uniform: m_surfaceGridShader->MVP(),
1484 value: cache->MVPMatrix());
1485
1486 const QRect &sampleSpace = cache->sampleSpace();
1487 if (cache->surfaceObject()->indexCount() && cache->surfaceGridVisible()
1488 && cache->isVisible() && sampleSpace.width() >= 2
1489 && sampleSpace.height() >= 2) {
1490 m_drawer->drawSurfaceGrid(shader: m_surfaceGridShader, object: cache->surfaceObject());
1491 }
1492 }
1493 }
1494 }
1495
1496 // Render selection ball
1497 if (m_selectionActive
1498 && m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionItem)) {
1499 for (SeriesRenderCache *baseCache: m_renderCacheList) {
1500 const SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
1501 if (cache->slicePointerActive() && cache->renderable() &&
1502 m_cachedIsSlicingActivated ) {
1503 cache->sliceSelectionPointer()->renderSelectionPointer(defaultFboHandle);
1504 }
1505 if (cache->mainPointerActive() && cache->renderable()) {
1506 cache->mainSelectionPointer()->renderSelectionPointer(defaultFboHandle,
1507 useOrtho: m_useOrthoProjection);
1508 }
1509 }
1510 }
1511
1512 // Bind background shader
1513 m_backgroundShader->bind();
1514 glCullFace(GL_BACK);
1515
1516 // Draw background
1517 if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
1518 QMatrix4x4 modelMatrix;
1519 QMatrix4x4 MVPMatrix;
1520 QMatrix4x4 itModelMatrix;
1521
1522 QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground, m_scaleZWithBackground);
1523 modelMatrix.scale(vector: bgScale);
1524
1525 // If we're viewing from below, background object must be flipped
1526 if (m_yFlipped) {
1527 modelMatrix.rotate(quaternion: m_xFlipRotation);
1528 modelMatrix.rotate(angle: 270.0f - backgroundRotation, x: 0.0f, y: 1.0f, z: 0.0f);
1529 } else {
1530 modelMatrix.rotate(angle: backgroundRotation, x: 0.0f, y: 1.0f, z: 0.0f);
1531 }
1532
1533 itModelMatrix = modelMatrix; // Only scaling and rotations, can be used directly
1534
1535#ifdef SHOW_DEPTH_TEXTURE_SCENE
1536 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1537#else
1538 MVPMatrix = projectionViewMatrix * modelMatrix;
1539#endif
1540
1541 bool blendEnabled = false;
1542 QVector4D backgroundColor = Utils::vectorFromColor(color: m_cachedTheme->backgroundColor());
1543 if (backgroundColor.w() < 1.0f) {
1544 blendEnabled = true;
1545 glEnable(GL_BLEND);
1546 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1547 }
1548
1549 // Set shader bindings
1550 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightP(), value: lightPos);
1551 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->view(), value: viewMatrix);
1552 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->model(), value: modelMatrix);
1553 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->nModel(),
1554 value: itModelMatrix.inverted().transposed());
1555 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->MVP(), value: MVPMatrix);
1556 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->color(), value: backgroundColor);
1557 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->ambientS(),
1558 value: m_cachedTheme->ambientLightStrength() * 2.0f);
1559 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightColor(), value: lightColor);
1560
1561 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1562 // Set shadow shader bindings
1563 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1564 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->shadowQ(),
1565 value: m_shadowQualityToShader);
1566 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->depth(), value: depthMVPMatrix);
1567 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightS(),
1568 value: adjustedLightStrength);
1569 // Draw the object
1570 if (noShadows && m_customRenderCache.isEmpty())
1571 m_drawer->drawObject(shader: m_backgroundShader, object: m_backgroundObj, textureId: 0, depthTextureId: m_noShadowTexture);
1572 else
1573 m_drawer->drawObject(shader: m_backgroundShader, object: m_backgroundObj, textureId: 0, depthTextureId: m_depthTexture);
1574 } else {
1575 // Set shadowless shader bindings
1576 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightS(),
1577 value: m_cachedTheme->lightStrength());
1578
1579 // Draw the object
1580 m_drawer->drawObject(shader: m_backgroundShader, object: m_backgroundObj);
1581 }
1582
1583 if (blendEnabled)
1584 glDisable(GL_BLEND);
1585 }
1586
1587 // Draw grid lines
1588 QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
1589 QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
1590 QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
1591
1592 if (m_cachedTheme->isGridEnabled()) {
1593 ShaderHelper *lineShader;
1594 if (m_isOpenGLES)
1595 lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES
1596 else
1597 lineShader = m_backgroundShader;
1598
1599 // Bind line shader
1600 lineShader->bind();
1601
1602 // Set unchanging shader bindings
1603 QVector4D lineColor = Utils::vectorFromColor(color: m_cachedTheme->gridLineColor());
1604 lineShader->setUniformValue(uniform: lineShader->lightP(), value: lightPos);
1605 lineShader->setUniformValue(uniform: lineShader->view(), value: viewMatrix);
1606 lineShader->setUniformValue(uniform: lineShader->color(), value: lineColor);
1607 lineShader->setUniformValue(uniform: lineShader->ambientS(), value: m_cachedTheme->ambientLightStrength());
1608 lineShader->setUniformValue(uniform: lineShader->lightColor(), value: lightColor);
1609 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1610 // Set shadowed shader bindings
1611 lineShader->setUniformValue(uniform: lineShader->shadowQ(), value: m_shadowQualityToShader);
1612 lineShader->setUniformValue(uniform: lineShader->lightS(),
1613 value: m_cachedTheme->lightStrength() / 20.0f);
1614 } else {
1615 // Set shadowless shader bindings
1616 lineShader->setUniformValue(uniform: lineShader->lightS(),
1617 value: m_cachedTheme->lightStrength() / 2.5f);
1618 }
1619
1620 QQuaternion lineYRotation;
1621 QQuaternion lineXRotation;
1622
1623 if (m_xFlipped)
1624 lineYRotation = m_yRightAngleRotationNeg;
1625 else
1626 lineYRotation = m_yRightAngleRotation;
1627
1628 if (m_yFlippedForGrid)
1629 lineXRotation = m_xRightAngleRotation;
1630 else
1631 lineXRotation = m_xRightAngleRotationNeg;
1632
1633 float yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset;
1634 if (m_yFlipped != m_flipHorizontalGrid)
1635 yFloorLinePosition = -yFloorLinePosition;
1636
1637 // Rows (= Z)
1638 if (m_axisCacheZ.segmentCount() > 0) {
1639 int gridLineCount = m_axisCacheZ.gridLineCount();
1640 // Floor lines
1641 if (m_polarGraph) {
1642 drawRadialGrid(shader: lineShader, yFloorLinePos: yFloorLinePosition, projectionViewMatrix,
1643 depthMatrix: depthProjectionViewMatrix);
1644 } else {
1645 for (int line = 0; line < gridLineCount; line++) {
1646 QMatrix4x4 modelMatrix;
1647 QMatrix4x4 MVPMatrix;
1648 QMatrix4x4 itModelMatrix;
1649
1650 modelMatrix.translate(x: 0.0f, y: yFloorLinePosition,
1651 z: m_axisCacheZ.gridLinePosition(index: line));
1652
1653 modelMatrix.scale(vector: gridLineScaleX);
1654 itModelMatrix.scale(vector: gridLineScaleX);
1655
1656 modelMatrix.rotate(quaternion: lineXRotation);
1657 itModelMatrix.rotate(quaternion: lineXRotation);
1658
1659 MVPMatrix = projectionViewMatrix * modelMatrix;
1660
1661 // Set the rest of the shader bindings
1662 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1663 lineShader->setUniformValue(uniform: lineShader->nModel(),
1664 value: itModelMatrix.inverted().transposed());
1665 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1666
1667 if (!m_isOpenGLES) {
1668 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1669 // Set shadow shader bindings
1670 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1671 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1672 // Draw the object
1673 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1674 } else {
1675 // Draw the object
1676 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1677 }
1678 } else {
1679 m_drawer->drawLine(shader: lineShader);
1680 }
1681 }
1682 // Side wall lines
1683 GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
1684
1685 if (!m_xFlipped)
1686 lineXTrans = -lineXTrans;
1687
1688 for (int line = 0; line < gridLineCount; line++) {
1689 QMatrix4x4 modelMatrix;
1690 QMatrix4x4 MVPMatrix;
1691 QMatrix4x4 itModelMatrix;
1692
1693 modelMatrix.translate(x: lineXTrans, y: 0.0f, z: m_axisCacheZ.gridLinePosition(index: line));
1694
1695 modelMatrix.scale(vector: gridLineScaleY);
1696 itModelMatrix.scale(vector: gridLineScaleY);
1697
1698 if (m_isOpenGLES) {
1699 modelMatrix.rotate(quaternion: m_zRightAngleRotation);
1700 itModelMatrix.rotate(quaternion: m_zRightAngleRotation);
1701 } else {
1702 modelMatrix.rotate(quaternion: lineYRotation);
1703 itModelMatrix.rotate(quaternion: lineYRotation);
1704 }
1705
1706 MVPMatrix = projectionViewMatrix * modelMatrix;
1707
1708 // Set the rest of the shader bindings
1709 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1710 lineShader->setUniformValue(uniform: lineShader->nModel(),
1711 value: itModelMatrix.inverted().transposed());
1712 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1713
1714 if (!m_isOpenGLES) {
1715 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1716 // Set shadow shader bindings
1717 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1718 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1719 // Draw the object
1720 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1721 } else {
1722 // Draw the object
1723 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1724 }
1725 } else {
1726 m_drawer->drawLine(shader: lineShader);
1727 }
1728 }
1729 }
1730 }
1731
1732 // Columns (= X)
1733 if (m_axisCacheX.segmentCount() > 0) {
1734 if (m_isOpenGLES)
1735 lineXRotation = m_yRightAngleRotation;
1736
1737 // Floor lines
1738 int gridLineCount = m_axisCacheX.gridLineCount();
1739
1740 if (m_polarGraph) {
1741 drawAngularGrid(shader: lineShader, yFloorLinePos: yFloorLinePosition, projectionViewMatrix,
1742 depthMatrix: depthProjectionViewMatrix);
1743 } else {
1744 for (int line = 0; line < gridLineCount; line++) {
1745 QMatrix4x4 modelMatrix;
1746 QMatrix4x4 MVPMatrix;
1747 QMatrix4x4 itModelMatrix;
1748
1749 modelMatrix.translate(x: m_axisCacheX.gridLinePosition(index: line), y: yFloorLinePosition,
1750 z: 0.0f);
1751
1752 modelMatrix.scale(vector: gridLineScaleZ);
1753 itModelMatrix.scale(vector: gridLineScaleZ);
1754
1755 modelMatrix.rotate(quaternion: lineXRotation);
1756 itModelMatrix.rotate(quaternion: lineXRotation);
1757
1758 MVPMatrix = projectionViewMatrix * modelMatrix;
1759
1760 // Set the rest of the shader bindings
1761 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1762 lineShader->setUniformValue(uniform: lineShader->nModel(),
1763 value: itModelMatrix.inverted().transposed());
1764 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1765
1766 if (!m_isOpenGLES) {
1767 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1768 // Set shadow shader bindings
1769 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1770 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1771 // Draw the object
1772 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1773 } else {
1774 // Draw the object
1775 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1776 }
1777 } else {
1778 m_drawer->drawLine(shader: lineShader);
1779 }
1780 }
1781
1782 // Back wall lines
1783 GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
1784
1785 if (!m_zFlipped)
1786 lineZTrans = -lineZTrans;
1787
1788 for (int line = 0; line < gridLineCount; line++) {
1789 QMatrix4x4 modelMatrix;
1790 QMatrix4x4 MVPMatrix;
1791 QMatrix4x4 itModelMatrix;
1792
1793 modelMatrix.translate(x: m_axisCacheX.gridLinePosition(index: line), y: 0.0f, z: lineZTrans);
1794
1795 modelMatrix.scale(vector: gridLineScaleY);
1796 itModelMatrix.scale(vector: gridLineScaleY);
1797
1798 if (m_isOpenGLES) {
1799 modelMatrix.rotate(quaternion: m_zRightAngleRotation);
1800 itModelMatrix.rotate(quaternion: m_zRightAngleRotation);
1801 } else if (m_zFlipped) {
1802 modelMatrix.rotate(quaternion: m_xFlipRotation);
1803 itModelMatrix.rotate(quaternion: m_xFlipRotation);
1804 }
1805
1806 MVPMatrix = projectionViewMatrix * modelMatrix;
1807
1808 // Set the rest of the shader bindings
1809 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1810 lineShader->setUniformValue(uniform: lineShader->nModel(),
1811 value: itModelMatrix.inverted().transposed());
1812 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1813
1814 if (!m_isOpenGLES) {
1815 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1816 // Set shadow shader bindings
1817 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1818 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1819 // Draw the object
1820 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1821 } else {
1822 // Draw the object
1823 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1824 }
1825 } else {
1826 m_drawer->drawLine(shader: lineShader);
1827 }
1828 }
1829 }
1830 }
1831
1832 // Horizontal wall lines
1833 if (m_axisCacheY.segmentCount() > 0) {
1834 // Back wall
1835 int gridLineCount = m_axisCacheY.gridLineCount();
1836
1837 GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
1838
1839 if (!m_zFlipped)
1840 lineZTrans = -lineZTrans;
1841
1842 for (int line = 0; line < gridLineCount; line++) {
1843 QMatrix4x4 modelMatrix;
1844 QMatrix4x4 MVPMatrix;
1845 QMatrix4x4 itModelMatrix;
1846
1847 modelMatrix.translate(x: 0.0f, y: m_axisCacheY.gridLinePosition(index: line), z: lineZTrans);
1848
1849 modelMatrix.scale(vector: gridLineScaleX);
1850 itModelMatrix.scale(vector: gridLineScaleX);
1851
1852 if (m_zFlipped) {
1853 modelMatrix.rotate(quaternion: m_xFlipRotation);
1854 itModelMatrix.rotate(quaternion: m_xFlipRotation);
1855 }
1856
1857 MVPMatrix = projectionViewMatrix * modelMatrix;
1858
1859 // Set the rest of the shader bindings
1860 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1861 lineShader->setUniformValue(uniform: lineShader->nModel(),
1862 value: itModelMatrix.inverted().transposed());
1863 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1864
1865 if (!m_isOpenGLES) {
1866 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1867 // Set shadow shader bindings
1868 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1869 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1870 // Draw the object
1871 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1872 } else {
1873 // Draw the object
1874 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1875 }
1876 } else {
1877 m_drawer->drawLine(shader: lineShader);
1878 }
1879 }
1880
1881 // Side wall
1882 GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
1883
1884 if (!m_xFlipped)
1885 lineXTrans = -lineXTrans;
1886
1887 for (int line = 0; line < gridLineCount; line++) {
1888 QMatrix4x4 modelMatrix;
1889 QMatrix4x4 MVPMatrix;
1890 QMatrix4x4 itModelMatrix;
1891
1892 modelMatrix.translate(x: lineXTrans, y: m_axisCacheY.gridLinePosition(index: line), z: 0.0f);
1893
1894 modelMatrix.scale(vector: gridLineScaleZ);
1895 itModelMatrix.scale(vector: gridLineScaleZ);
1896
1897 modelMatrix.rotate(quaternion: lineYRotation);
1898 itModelMatrix.rotate(quaternion: lineYRotation);
1899
1900 MVPMatrix = projectionViewMatrix * modelMatrix;
1901
1902 // Set the rest of the shader bindings
1903 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1904 lineShader->setUniformValue(uniform: lineShader->nModel(),
1905 value: itModelMatrix.inverted().transposed());
1906 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1907
1908 if (!m_isOpenGLES) {
1909 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1910 // Set shadow shader bindings
1911 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1912 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1913 // Draw the object
1914 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1915 } else {
1916 // Draw the object
1917 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1918 }
1919 } else {
1920 m_drawer->drawLine(shader: lineShader);
1921 }
1922 }
1923 }
1924 }
1925
1926 Abstract3DRenderer::drawCustomItems(state: RenderingNormal, regularShader: m_customItemShader, viewMatrix,
1927 projectionViewMatrix, depthProjectionViewMatrix,
1928 depthTexture: m_depthTexture, shadowQuality: m_shadowQualityToShader);
1929
1930 drawLabels(drawSelection: false, activeCamera, viewMatrix, projectionMatrix);
1931
1932 // Release shader
1933 glUseProgram(program: 0);
1934}
1935
1936void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
1937 const QMatrix4x4 &viewMatrix,
1938 const QMatrix4x4 &projectionMatrix)
1939{
1940 ShaderHelper *shader = 0;
1941 GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
1942 GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
1943 GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
1944 if (drawSelection) {
1945 shader = m_surfaceGridShader;
1946 } else {
1947 shader = m_labelShader;
1948 shader->bind();
1949
1950 glEnable(GL_BLEND);
1951 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1952 }
1953
1954 glEnable(GL_POLYGON_OFFSET_FILL);
1955
1956 float labelAutoAngle = m_axisCacheZ.labelAutoRotation();
1957 float labelAngleFraction = labelAutoAngle / 90.0f;
1958 float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
1959 float fractionCamX = activeCamera->xRotation() * labelAngleFraction;
1960 float labelsMaxWidth = 0.0f;
1961
1962 int startIndex;
1963 int endIndex;
1964 int indexStep;
1965
1966 // Z Labels
1967 QVector3D positionZComp(0.0f, 0.0f, 0.0f);
1968 if (m_axisCacheZ.segmentCount() > 0) {
1969 int labelCount = m_axisCacheZ.labelCount();
1970 float labelXTrans = m_scaleXWithBackground + labelMargin;
1971 float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground;
1972 if (m_polarGraph) {
1973 labelXTrans *= m_radialLabelOffset;
1974 // YTrans up only if over background
1975 if (m_radialLabelOffset < 1.0f)
1976 labelYTrans += gridLineOffset + gridLineWidth;
1977 }
1978 Qt::Alignment alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
1979 QVector3D labelRotation;
1980 if (m_xFlipped)
1981 labelXTrans = -labelXTrans;
1982 if (m_yFlipped)
1983 labelYTrans = -labelYTrans;
1984 if (labelAutoAngle == 0.0f) {
1985 if (m_zFlipped)
1986 labelRotation.setY(180.0f);
1987 if (m_yFlippedForGrid) {
1988 if (m_zFlipped)
1989 labelRotation.setY(180.0f);
1990 else
1991 labelRotation.setY(0.0f);
1992 labelRotation.setX(90.0f);
1993 } else {
1994 labelRotation.setX(-90.0f);
1995 }
1996 } else {
1997 if (m_zFlipped)
1998 labelRotation.setY(180.0f);
1999 if (m_yFlippedForGrid) {
2000 if (m_zFlipped) {
2001 if (m_xFlipped) {
2002 labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
2003 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
2004 labelRotation.setZ(labelAutoAngle + fractionCamY);
2005 } else {
2006 labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
2007 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2008 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2009 }
2010 } else {
2011 if (m_xFlipped) {
2012 labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
2013 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
2014 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2015 } else {
2016 labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
2017 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2018 labelRotation.setZ(labelAutoAngle + fractionCamY);
2019 }
2020 }
2021 } else {
2022 if (m_zFlipped) {
2023 if (m_xFlipped) {
2024 labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
2025 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2026 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2027 } else {
2028 labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
2029 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2030 labelRotation.setZ(labelAutoAngle - fractionCamY);
2031 }
2032 } else {
2033 if (m_xFlipped) {
2034 labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
2035 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2036 labelRotation.setZ(labelAutoAngle - fractionCamY);
2037 } else {
2038 labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
2039 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2040 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2041 }
2042 }
2043 }
2044 }
2045
2046 QQuaternion totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
2047
2048 QVector3D labelTrans = QVector3D(labelXTrans,
2049 labelYTrans,
2050 0.0f);
2051
2052 if (m_zFlipped) {
2053 startIndex = 0;
2054 endIndex = labelCount;
2055 indexStep = 1;
2056 } else {
2057 startIndex = labelCount - 1;
2058 endIndex = -1;
2059 indexStep = -1;
2060 }
2061 float offsetValue = 0.0f;
2062 for (int label = startIndex; label != endIndex; label = label + indexStep) {
2063 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
2064 const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(i: label);
2065 // Draw the label here
2066 if (m_polarGraph) {
2067 float direction = m_zFlipped ? -1.0f : 1.0f;
2068 labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(i: label)
2069 * -m_polarRadius
2070 + m_drawer->scaledFontSize() + gridLineWidth) * direction);
2071 } else {
2072 labelTrans.setZ(m_axisCacheZ.labelPosition(index: label));
2073 }
2074 if (label == 0 || label == (labelCount - 1)) {
2075 // If the margin is small, adjust the position of the edge labels to avoid overlapping
2076 // with labels of the other axes.
2077 float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
2078 float labelOverlap = qAbs(t: labelTrans.z())
2079 + (scaleFactor * axisLabelItem.size().height() / 2.0f)
2080 - m_scaleZWithBackground + labelMargin;
2081 // No need to adjust quite as much on the front edges
2082 if (label != startIndex)
2083 labelOverlap /= 2.0f;
2084 if (labelOverlap > 0.0f) {
2085 if (label == 0)
2086 labelTrans.setZ(labelTrans.z() - labelOverlap);
2087 else
2088 labelTrans.setZ(labelTrans.z() + labelOverlap);
2089 }
2090 }
2091 m_dummyRenderItem.setTranslation(labelTrans);
2092
2093 if (drawSelection) {
2094 QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
2095 alphaForRowSelection);
2096 shader->setUniformValue(uniform: shader->color(), value: labelColor);
2097 }
2098
2099 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2100 positionComp: positionZComp, rotation: totalRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2101 shader, object: m_labelObj, camera: activeCamera,
2102 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment, isSlicing: false, isSelecting: drawSelection);
2103 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
2104 }
2105 if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
2106 if (m_polarGraph) {
2107 float titleZ = -m_polarRadius / 2.0f;
2108 if (m_zFlipped)
2109 titleZ = -titleZ;
2110 labelTrans.setZ(titleZ);
2111 } else {
2112 labelTrans.setZ(0.0f);
2113 }
2114 drawAxisTitleZ(labelRotation, labelTrans, totalRotation, dummyItem&: m_dummyRenderItem,
2115 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
2116 }
2117 }
2118 // X Labels
2119 if (m_axisCacheX.segmentCount() > 0) {
2120 labelsMaxWidth = 0.0f;
2121 labelAutoAngle = m_axisCacheX.labelAutoRotation();
2122 labelAngleFraction = labelAutoAngle / 90.0f;
2123 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2124 fractionCamX = activeCamera->xRotation() * labelAngleFraction;
2125 int labelCount = m_axisCacheX.labelCount();
2126
2127 float labelZTrans = 0.0f;
2128 float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground;
2129 if (m_polarGraph)
2130 labelYTrans += gridLineOffset + gridLineWidth;
2131 else
2132 labelZTrans = m_scaleZWithBackground + labelMargin;
2133
2134 Qt::Alignment alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2135 QVector3D labelRotation;
2136 if (m_zFlipped)
2137 labelZTrans = -labelZTrans;
2138 if (m_yFlipped)
2139 labelYTrans = -labelYTrans;
2140 if (labelAutoAngle == 0.0f) {
2141 labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
2142 if (m_xFlipped)
2143 labelRotation.setY(-90.0f);
2144 if (m_yFlippedForGrid) {
2145 if (m_xFlipped)
2146 labelRotation.setY(-90.0f);
2147 else
2148 labelRotation.setY(90.0f);
2149 labelRotation.setX(90.0f);
2150 }
2151 } else {
2152 if (m_xFlipped)
2153 labelRotation.setY(-90.0f);
2154 else
2155 labelRotation.setY(90.0f);
2156 if (m_yFlippedForGrid) {
2157 if (m_zFlipped) {
2158 if (m_xFlipped) {
2159 labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
2160 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2161 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2162 } else {
2163 labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
2164 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2165 labelRotation.setZ(labelAutoAngle + fractionCamY);
2166 }
2167 } else {
2168 if (m_xFlipped) {
2169 labelRotation.setX(90.0f + fractionCamX
2170 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
2171 labelRotation.setZ(labelAutoAngle + fractionCamY);
2172 } else {
2173 labelRotation.setX(90.0f - fractionCamX
2174 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
2175 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2176 }
2177 }
2178 } else {
2179 if (m_zFlipped) {
2180 if (m_xFlipped) {
2181 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
2182 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2183 labelRotation.setZ(labelAutoAngle - fractionCamY);
2184 } else {
2185 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
2186 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2187 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2188 }
2189 } else {
2190 if (m_xFlipped) {
2191 labelRotation.setX(-90.0f - fractionCamX
2192 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2193 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2194 } else {
2195 labelRotation.setX(-90.0f + fractionCamX
2196 * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
2197 labelRotation.setZ(labelAutoAngle - fractionCamY);
2198 }
2199 }
2200 }
2201 }
2202
2203 QQuaternion totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
2204 if (m_polarGraph) {
2205 if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped))
2206 || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) {
2207 totalRotation *= m_zRightAngleRotation;
2208 } else {
2209 totalRotation *= m_zRightAngleRotationNeg;
2210 }
2211 }
2212
2213 QVector3D labelTrans = QVector3D(0.0f,
2214 labelYTrans,
2215 labelZTrans);
2216
2217 if (m_xFlipped) {
2218 startIndex = labelCount - 1;
2219 endIndex = -1;
2220 indexStep = -1;
2221 } else {
2222 startIndex = 0;
2223 endIndex = labelCount;
2224 indexStep = 1;
2225 }
2226 float offsetValue = 0.0f;
2227 bool showLastLabel = false;
2228 QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
2229 int lastLabelPosIndex = labelPositions.size() - 1;
2230 if (labelPositions.size()
2231 && (labelPositions.at(i: lastLabelPosIndex) != 1.0f || labelPositions.at(i: 0) != 0.0f)) {
2232 // Avoid overlapping first and last label if they would get on same position
2233 showLastLabel = true;
2234 }
2235
2236 for (int label = startIndex; label != endIndex; label = label + indexStep) {
2237 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
2238 // Draw the label here
2239 if (m_polarGraph) {
2240 // Calculate angular position
2241 if (label == lastLabelPosIndex && !showLastLabel)
2242 continue;
2243 float labelPosition = labelPositions.at(i: label);
2244 qreal angle = labelPosition * M_PI * 2.0;
2245 labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(v: angle)));
2246 labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(v: angle)));
2247 // Alignment depends on label angular position, as well as flips
2248 Qt::AlignmentFlag vAlignment = Qt::AlignCenter;
2249 Qt::AlignmentFlag hAlignment = Qt::AlignCenter;
2250 const float centerMargin = 0.005f;
2251 if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin)
2252 vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom;
2253 else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin)
2254 vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop;
2255
2256 if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin)
2257 hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
2258 else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin)
2259 hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight;
2260 if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter)
2261 vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop;
2262 alignment = vAlignment | hAlignment;
2263 } else {
2264 labelTrans.setX(m_axisCacheX.labelPosition(index: label));
2265 }
2266 const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(i: label);
2267 if (label == 0 || label == (labelCount - 1)) {
2268 // If the margin is small, adjust the position of the edge labels to avoid overlapping
2269 // with labels of the other axes.
2270 float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
2271 float labelOverlap = qAbs(t: labelTrans.x())
2272 + (scaleFactor * axisLabelItem.size().height() / 2.0f)
2273 - m_scaleXWithBackground + labelMargin;
2274 // No need to adjust quite as much on the front edges
2275 if (label != startIndex)
2276 labelOverlap /= 2.0f;
2277 if (labelOverlap > 0.0f) {
2278 if (label == 0)
2279 labelTrans.setX(labelTrans.x() + labelOverlap);
2280 else
2281 labelTrans.setX(labelTrans.x() - labelOverlap);
2282 }
2283 }
2284 m_dummyRenderItem.setTranslation(labelTrans);
2285
2286 if (drawSelection) {
2287 QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
2288 alphaForColumnSelection);
2289 shader->setUniformValue(uniform: shader->color(), value: labelColor);
2290 }
2291
2292 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2293 positionComp: positionZComp, rotation: totalRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2294 shader, object: m_labelObj, camera: activeCamera,
2295 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment, isSlicing: false, isSelecting: drawSelection);
2296 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
2297 }
2298 if (!drawSelection && m_axisCacheX.isTitleVisible()) {
2299 labelTrans.setX(0.0f);
2300 bool radial = false;
2301 if (m_polarGraph) {
2302 if (m_xFlipped == m_zFlipped)
2303 totalRotation *= m_zRightAngleRotation;
2304 else
2305 totalRotation *= m_zRightAngleRotationNeg;
2306 if (m_yFlippedForGrid)
2307 totalRotation *= QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: -180.0f);
2308 labelTrans.setZ(-m_polarRadius);
2309 radial = true;
2310 }
2311 drawAxisTitleX(labelRotation, labelTrans, totalRotation, dummyItem&: m_dummyRenderItem,
2312 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader,
2313 radial);
2314 }
2315 }
2316 // Y Labels
2317 if (m_axisCacheY.segmentCount() > 0) {
2318 labelsMaxWidth = 0.0f;
2319 labelAutoAngle = m_axisCacheY.labelAutoRotation();
2320 labelAngleFraction = labelAutoAngle / 90.0f;
2321 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2322 fractionCamX = activeCamera->xRotation() * labelAngleFraction;
2323 int labelCount = m_axisCacheY.labelCount();
2324
2325 float labelXTrans = m_scaleXWithBackground;
2326 float labelZTrans = m_scaleZWithBackground;
2327
2328 // Back & side wall
2329 float labelMarginXTrans = labelMargin;
2330 float labelMarginZTrans = labelMargin;
2331 QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
2332 QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
2333 Qt::AlignmentFlag backAlignment =
2334 (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2335 Qt::AlignmentFlag sideAlignment =
2336 (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2337 if (!m_xFlipped) {
2338 labelXTrans = -labelXTrans;
2339 labelMarginXTrans = -labelMargin;
2340 }
2341 if (m_zFlipped) {
2342 labelZTrans = -labelZTrans;
2343 labelMarginZTrans = -labelMargin;
2344 }
2345 if (labelAutoAngle == 0.0f) {
2346 if (!m_xFlipped)
2347 backLabelRotation.setY(90.0f);
2348 if (m_zFlipped)
2349 sideLabelRotation.setY(180.f);
2350 } else {
2351 // Orient side labels somewhat towards the camera
2352 if (m_xFlipped) {
2353 if (m_zFlipped)
2354 sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
2355 else
2356 sideLabelRotation.setY(-fractionCamX);
2357 backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
2358 } else {
2359 if (m_zFlipped)
2360 sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
2361 else
2362 sideLabelRotation.setY(-fractionCamX);
2363 backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
2364 }
2365 }
2366 sideLabelRotation.setX(-fractionCamY);
2367 backLabelRotation.setX(-fractionCamY);
2368
2369 QQuaternion totalSideRotation = Utils::calculateRotation(xyzRotations: sideLabelRotation);
2370 QQuaternion totalBackRotation = Utils::calculateRotation(xyzRotations: backLabelRotation);
2371
2372 QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans);
2373 QVector3D labelTransSide(-labelXTrans - labelMarginXTrans, 0.0f, -labelZTrans);
2374
2375 if (m_yFlipped) {
2376 startIndex = labelCount - 1;
2377 endIndex = -1;
2378 indexStep = -1;
2379 } else {
2380 startIndex = 0;
2381 endIndex = labelCount;
2382 indexStep = 1;
2383 }
2384 float offsetValue = 0.0f;
2385 for (int label = startIndex; label != endIndex; label = label + indexStep) {
2386 const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i: label);
2387 float labelYTrans = m_axisCacheY.labelPosition(index: label);
2388
2389 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
2390
2391 if (drawSelection) {
2392 QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f,
2393 alphaForValueSelection);
2394 shader->setUniformValue(uniform: shader->color(), value: labelColor);
2395 }
2396
2397 if (label == startIndex) {
2398 // If the margin is small, adjust the position of the edge label to avoid
2399 // overlapping with labels of the other axes.
2400 float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
2401 float labelOverlap = qAbs(t: labelYTrans)
2402 + (scaleFactor * axisLabelItem.size().height() / 2.0f)
2403 - m_scaleYWithBackground + labelMargin;
2404 if (labelOverlap > 0.0f) {
2405 if (label == 0)
2406 labelYTrans += labelOverlap;
2407 else
2408 labelYTrans -= labelOverlap;
2409 }
2410 }
2411
2412 // Back wall
2413 labelTransBack.setY(labelYTrans);
2414 m_dummyRenderItem.setTranslation(labelTransBack);
2415 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2416 positionComp: positionZComp, rotation: totalBackRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2417 shader, object: m_labelObj, camera: activeCamera,
2418 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment: backAlignment, isSlicing: false,
2419 isSelecting: drawSelection);
2420
2421 // Side wall
2422 labelTransSide.setY(labelYTrans);
2423 m_dummyRenderItem.setTranslation(labelTransSide);
2424 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2425 positionComp: positionZComp, rotation: totalSideRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2426 shader, object: m_labelObj, camera: activeCamera,
2427 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment: sideAlignment, isSlicing: false,
2428 isSelecting: drawSelection);
2429 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
2430 }
2431 if (!drawSelection && m_axisCacheY.isTitleVisible()) {
2432 labelTransSide.setY(0.0f);
2433 labelTransBack.setY(0.0f);
2434 drawAxisTitleY(sideLabelRotation, backLabelRotation, sideLabelTrans: labelTransSide, backLabelTrans: labelTransBack,
2435 totalSideRotation, totalBackRotation, dummyItem&: m_dummyRenderItem, activeCamera,
2436 labelsMaxWidth, viewMatrix, projectionMatrix,
2437 shader);
2438 }
2439 }
2440 glDisable(GL_POLYGON_OFFSET_FILL);
2441
2442 if (!drawSelection)
2443 glDisable(GL_BLEND);
2444}
2445
2446void Surface3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
2447{
2448 Abstract3DRenderer::updateSelectionMode(newMode: mode);
2449
2450 if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone)
2451 updateSelectionTextures();
2452}
2453
2454void Surface3DRenderer::updateSelectionTextures()
2455{
2456 uint lastSelectionId = 1;
2457
2458 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2459 SurfaceSeriesRenderCache *cache =
2460 static_cast<SurfaceSeriesRenderCache *>(baseCache);
2461 GLuint texture = cache->selectionTexture();
2462 m_textureHelper->deleteTexture(texture: &texture);
2463 createSelectionTexture(cache, lastSelectionId);
2464 }
2465 m_selectionTexturesDirty = false;
2466}
2467
2468void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache,
2469 uint &lastSelectionId)
2470{
2471 // Create the selection ID image. Each grid corner gets 1 pixel area of
2472 // ID color so that each vertex (data point) has 2x2 pixel area of ID color,
2473 // except the vertices on the edges.
2474 const QRect &sampleSpace = cache->sampleSpace();
2475 int idImageWidth = (sampleSpace.width() - 1) * 2;
2476 int idImageHeight = (sampleSpace.height() - 1) * 2;
2477
2478 if (idImageHeight <= 0 || idImageWidth <= 0) {
2479 cache->setSelectionIdRange(start: ~0U, end: ~0U);
2480 cache->setSelectionTexture(0);
2481 return;
2482 }
2483
2484 int stride = idImageWidth * 4 * sizeof(uchar); // 4 = number of color components (rgba)
2485
2486 uint idStart = lastSelectionId;
2487 uchar *bits = new uchar[idImageWidth * idImageHeight * 4 * sizeof(uchar)];
2488 for (int i = 0; i < idImageHeight; i += 2) {
2489 for (int j = 0; j < idImageWidth; j += 2) {
2490 int p = (i * idImageWidth + j) * 4;
2491 uchar r, g, b, a;
2492 idToRGBA(id: lastSelectionId, r: &r, g: &g, b: &b, a: &a);
2493 fillIdCorner(p: &bits[p], r, g, b, a);
2494
2495 idToRGBA(id: lastSelectionId + 1, r: &r, g: &g, b: &b, a: &a);
2496 fillIdCorner(p: &bits[p + 4], r, g, b, a);
2497
2498 idToRGBA(id: lastSelectionId + sampleSpace.width(), r: &r, g: &g, b: &b, a: &a);
2499 fillIdCorner(p: &bits[p + stride], r, g, b, a);
2500
2501 idToRGBA(id: lastSelectionId + sampleSpace.width() + 1, r: &r, g: &g, b: &b, a: &a);
2502 fillIdCorner(p: &bits[p + stride + 4], r, g, b, a);
2503
2504 lastSelectionId++;
2505 }
2506 lastSelectionId++;
2507 }
2508 lastSelectionId += sampleSpace.width();
2509 cache->setSelectionIdRange(start: idStart, end: lastSelectionId - 1);
2510
2511 // Move the ID image (bits) to the texture
2512 QImage image = QImage(bits, idImageWidth, idImageHeight, QImage::Format_RGB32);
2513 GLuint selectionTexture = m_textureHelper->create2DTexture(image, useTrilinearFiltering: false, convert: false, smoothScale: false);
2514 cache->setSelectionTexture(selectionTexture);
2515
2516 // Release the temp bits allocation
2517 delete[] bits;
2518}
2519
2520void Surface3DRenderer::initSelectionBuffer()
2521{
2522 // Create the result selection texture and buffers
2523 m_textureHelper->deleteTexture(texture: &m_selectionResultTexture);
2524
2525 m_selectionResultTexture = m_textureHelper->createSelectionTexture(size: m_primarySubViewport.size(),
2526 frameBuffer&: m_selectionFrameBuffer,
2527 depthBuffer&: m_selectionDepthBuffer);
2528}
2529
2530void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a)
2531{
2532 p[0] = r;
2533 p[1] = g;
2534 p[2] = b;
2535 p[3] = a;
2536}
2537
2538void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a)
2539{
2540 *r = id & ID_TO_RGBA_MASK;
2541 *g = (id >> 8) & ID_TO_RGBA_MASK;
2542 *b = (id >> 16) & ID_TO_RGBA_MASK;
2543 *a = (id >> 24) & ID_TO_RGBA_MASK;
2544}
2545
2546void Surface3DRenderer::calculateSceneScalingFactors()
2547{
2548 // Margin for background (the default 0.10 makes it 10% larger to avoid
2549 // selection ball being drawn inside background)
2550 if (m_requestedMargin < 0.0f) {
2551 m_hBackgroundMargin = 0.1f;
2552 m_vBackgroundMargin = 0.1f;
2553 } else {
2554 m_hBackgroundMargin = m_requestedMargin;
2555 m_vBackgroundMargin = m_requestedMargin;
2556 }
2557 if (m_polarGraph) {
2558 float polarMargin = calculatePolarBackgroundMargin();
2559 m_hBackgroundMargin = qMax(a: m_hBackgroundMargin, b: polarMargin);
2560 }
2561
2562 // Calculate scene scaling and translation factors
2563 m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min());
2564
2565 float horizontalAspectRatio;
2566 if (m_polarGraph)
2567 horizontalAspectRatio = 1.0f;
2568 else
2569 horizontalAspectRatio = m_graphHorizontalAspectRatio;
2570
2571 QSizeF areaSize;
2572 if (horizontalAspectRatio == 0.0f) {
2573 areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
2574 areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
2575 } else {
2576 areaSize.setHeight(1.0f);
2577 areaSize.setWidth(horizontalAspectRatio);
2578 }
2579
2580 float horizontalMaxDimension;
2581 if (m_graphAspectRatio > 2.0f) {
2582 horizontalMaxDimension = 2.0f;
2583 m_scaleY = 2.0f / m_graphAspectRatio;
2584 } else {
2585 horizontalMaxDimension = m_graphAspectRatio;
2586 m_scaleY = 1.0f;
2587 }
2588 if (m_polarGraph)
2589 m_polarRadius = horizontalMaxDimension;
2590
2591 float scaleFactor = qMax(a: areaSize.width(), b: areaSize.height());
2592 m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
2593 m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
2594
2595 m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin;
2596 m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin;
2597 m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin;
2598
2599 m_axisCacheX.setScale(m_scaleX * 2.0f);
2600 m_axisCacheY.setScale(m_scaleY * 2.0f);
2601 m_axisCacheZ.setScale(-m_scaleZ * 2.0f);
2602 m_axisCacheX.setTranslate(-m_scaleX);
2603 m_axisCacheY.setTranslate(-m_scaleY);
2604 m_axisCacheZ.setTranslate(m_scaleZ);
2605
2606 updateCameraViewport();
2607 updateCustomItemPositions();
2608}
2609
2610void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache)
2611{
2612 bool flatEnable = cache->isFlatShadingEnabled();
2613 if (flatEnable && !m_flatSupported) {
2614 qWarning() << "Warning: Flat qualifier not supported on your platform's GLSL language."
2615 " Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.";
2616 cache->setFlatShadingEnabled(false);
2617 cache->setFlatChangeAllowed(false);
2618 }
2619}
2620
2621void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dimensionChanged)
2622{
2623 QSurfaceDataArray &dataArray = cache->dataArray();
2624 const QRect &sampleSpace = cache->sampleSpace();
2625
2626 const QSurface3DSeries *currentSeries = cache->series();
2627 QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
2628 const QSurfaceDataArray &array = *dataProxy->array();
2629
2630 if (cache->isFlatShadingEnabled()) {
2631 cache->surfaceObject()->setUpData(dataArray, space: sampleSpace, changeGeometry: dimensionChanged, polar: m_polarGraph);
2632 if (cache->surfaceTexture())
2633 cache->surfaceObject()->coarseUVs(dataArray: array, modelArray: dataArray);
2634 } else {
2635 cache->surfaceObject()->setUpSmoothData(dataArray, space: sampleSpace, changeGeometry: dimensionChanged,
2636 polar: m_polarGraph);
2637 if (cache->surfaceTexture())
2638 cache->surfaceObject()->smoothUVs(dataArray: array, modelArray: dataArray);
2639 }
2640}
2641
2642void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series)
2643{
2644 m_selectedPoint = position;
2645 m_selectedSeries = series;
2646 m_selectionDirty = true;
2647}
2648
2649void Surface3DRenderer::updateFlipHorizontalGrid(bool flip)
2650{
2651 m_flipHorizontalGrid = flip;
2652}
2653
2654void Surface3DRenderer::resetClickedStatus()
2655{
2656 m_clickedPosition = Surface3DController::invalidSelectionPosition();
2657 m_clickedSeries = 0;
2658}
2659
2660void Surface3DRenderer::loadBackgroundMesh()
2661{
2662 ObjectHelper::resetObjectHelper(cacheId: this, obj&: m_backgroundObj,
2663 QStringLiteral(":/defaultMeshes/background"));
2664}
2665
2666void Surface3DRenderer::surfacePointSelected(const QPoint &point)
2667{
2668 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2669 SurfaceSeriesRenderCache *cache =
2670 static_cast<SurfaceSeriesRenderCache *>(baseCache);
2671 cache->setSlicePointerActivity(false);
2672 cache->setMainPointerActivity(false);
2673 }
2674
2675 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries)) {
2676 // Find axis coordinates for the selected point
2677 SurfaceSeriesRenderCache *selectedCache =
2678 static_cast<SurfaceSeriesRenderCache *>(
2679 m_renderCacheList.value(akey: const_cast<QSurface3DSeries *>(m_selectedSeries)));
2680 QSurfaceDataArray &dataArray = selectedCache->dataArray();
2681 QSurfaceDataItem item = dataArray.at(i: point.x())->at(i: point.y());
2682 QPointF coords(item.x(), item.z());
2683
2684 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2685 SurfaceSeriesRenderCache *cache =
2686 static_cast<SurfaceSeriesRenderCache *>(baseCache);
2687 if (cache->series() != m_selectedSeries) {
2688 QPoint mappedPoint = mapCoordsToSampleSpace(cache, coords);
2689 updateSelectionPoint(cache, point: mappedPoint, label: false);
2690 } else {
2691 updateSelectionPoint(cache, point, label: true);
2692 }
2693 }
2694 } else {
2695 if (m_selectedSeries) {
2696 SurfaceSeriesRenderCache *cache =
2697 static_cast<SurfaceSeriesRenderCache *>(
2698 m_renderCacheList.value(akey: const_cast<QSurface3DSeries *>(m_selectedSeries)));
2699 if (cache)
2700 updateSelectionPoint(cache, point, label: true);
2701 }
2702 }
2703}
2704
2705void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point,
2706 bool label)
2707{
2708 int row = point.x();
2709 int column = point.y();
2710
2711 if (column < 0 || row < 0)
2712 return;
2713
2714 SelectionPointer *slicePointer = cache->sliceSelectionPointer();
2715 if (!slicePointer && m_cachedIsSlicingActivated) {
2716 slicePointer = new SelectionPointer(m_drawer);
2717 cache->setSliceSelectionPointer(slicePointer);
2718 }
2719 SelectionPointer *mainPointer = cache->mainSelectionPointer();
2720 if (!mainPointer) {
2721 mainPointer = new SelectionPointer(m_drawer);
2722 cache->setMainSelectionPointer(mainPointer);
2723 }
2724
2725 QString selectionLabel;
2726 if (label) {
2727 m_selectionLabelDirty = false;
2728 selectionLabel = cache->itemLabel();
2729 }
2730
2731 if (m_cachedIsSlicingActivated) {
2732 QVector3D subPosFront;
2733 QVector3D subPosBack;
2734 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)) {
2735 subPosFront = cache->sliceSurfaceObject()->vertexAt(column, row: 0);
2736 subPosBack = cache->sliceSurfaceObject()->vertexAt(column, row: 1);
2737 } else if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) {
2738 subPosFront = cache->sliceSurfaceObject()->vertexAt(column: row, row: 0);
2739 subPosBack = cache->sliceSurfaceObject()->vertexAt(column: row, row: 1);
2740 }
2741 slicePointer->updateBoundingRect(rect: m_secondarySubViewport);
2742 slicePointer->updateSliceData(sliceActivated: true, autoScaleAdjustment: m_autoScaleAdjustment);
2743 slicePointer->setPosition((subPosFront + subPosBack) / 2.0f);
2744 slicePointer->setLabel(label: selectionLabel);
2745 slicePointer->setPointerObject(cache->object());
2746 slicePointer->setLabelObject(m_labelObj);
2747 slicePointer->setHighlightColor(cache->singleHighlightColor());
2748 slicePointer->updateScene(scene: m_cachedScene);
2749 slicePointer->setRotation(cache->meshRotation());
2750 cache->setSlicePointerActivity(true);
2751 }
2752
2753 QVector3D mainPos;
2754 mainPos = cache->surfaceObject()->vertexAt(column, row);
2755 mainPointer->updateBoundingRect(rect: m_primarySubViewport);
2756 mainPointer->updateSliceData(sliceActivated: false, autoScaleAdjustment: m_autoScaleAdjustment);
2757 mainPointer->setPosition(mainPos);
2758 mainPointer->setLabel(label: selectionLabel);
2759 mainPointer->setPointerObject(cache->object());
2760 mainPointer->setLabelObject(m_labelObj);
2761 mainPointer->setHighlightColor(cache->singleHighlightColor());
2762 mainPointer->updateScene(scene: m_cachedScene);
2763 mainPointer->setRotation(cache->meshRotation());
2764 cache->setMainPointerActivity(true);
2765}
2766
2767// Maps selection Id to surface point in data array
2768QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id)
2769{
2770 m_clickedType = QAbstract3DGraph::ElementNone;
2771 m_selectedLabelIndex = -1;
2772 m_selectedCustomItemIndex = -1;
2773 // Check for label and custom item selection
2774 if (id / alphaMultiplier == labelRowAlpha) {
2775 m_selectedLabelIndex = id - (alphaMultiplier * uint(labelRowAlpha));
2776 m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
2777 return Surface3DController::invalidSelectionPosition();
2778 } else if (id / alphaMultiplier == labelColumnAlpha) {
2779 m_selectedLabelIndex = (id - (alphaMultiplier * uint(labelColumnAlpha))) / greenMultiplier;
2780 m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
2781 return Surface3DController::invalidSelectionPosition();
2782 } else if (id / alphaMultiplier == labelValueAlpha) {
2783 m_selectedLabelIndex = (id - (alphaMultiplier * uint(labelValueAlpha))) / blueMultiplier;
2784 m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
2785 return Surface3DController::invalidSelectionPosition();
2786 } else if (id / alphaMultiplier == customItemAlpha) {
2787 // Custom item selection
2788 m_clickedType = QAbstract3DGraph::ElementCustomItem;
2789 m_selectedCustomItemIndex = id - (alphaMultiplier * uint(customItemAlpha));
2790 return Surface3DController::invalidSelectionPosition();
2791 }
2792
2793 // Not a label selection
2794 SurfaceSeriesRenderCache *selectedCache = 0;
2795 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2796 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
2797 if (cache->isWithinIdRange(selection: id)) {
2798 selectedCache = cache;
2799 break;
2800 }
2801 }
2802 if (!selectedCache) {
2803 m_clickedSeries = 0;
2804 return Surface3DController::invalidSelectionPosition();
2805 }
2806
2807 uint idInSeries = id - selectedCache->selectionIdStart() + 1;
2808 const QRect &sampleSpace = selectedCache->sampleSpace();
2809 int column = ((idInSeries - 1) % sampleSpace.width()) + sampleSpace.x();
2810 int row = ((idInSeries - 1) / sampleSpace.width()) + sampleSpace.y();
2811
2812 m_clickedSeries = selectedCache->series();
2813 m_clickedType = QAbstract3DGraph::ElementSeries;
2814 return QPoint(row, column);
2815}
2816
2817void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)
2818{
2819 m_cachedShadowQuality = quality;
2820
2821 switch (quality) {
2822 case QAbstract3DGraph::ShadowQualityLow:
2823 m_shadowQualityToShader = 33.3f;
2824 m_shadowQualityMultiplier = 1;
2825 break;
2826 case QAbstract3DGraph::ShadowQualityMedium:
2827 m_shadowQualityToShader = 100.0f;
2828 m_shadowQualityMultiplier = 3;
2829 break;
2830 case QAbstract3DGraph::ShadowQualityHigh:
2831 m_shadowQualityToShader = 200.0f;
2832 m_shadowQualityMultiplier = 5;
2833 break;
2834 case QAbstract3DGraph::ShadowQualitySoftLow:
2835 m_shadowQualityToShader = 5.0f;
2836 m_shadowQualityMultiplier = 1;
2837 break;
2838 case QAbstract3DGraph::ShadowQualitySoftMedium:
2839 m_shadowQualityToShader = 10.0f;
2840 m_shadowQualityMultiplier = 3;
2841 break;
2842 case QAbstract3DGraph::ShadowQualitySoftHigh:
2843 m_shadowQualityToShader = 15.0f;
2844 m_shadowQualityMultiplier = 4;
2845 break;
2846 default:
2847 m_shadowQualityToShader = 0.0f;
2848 m_shadowQualityMultiplier = 1;
2849 break;
2850 }
2851
2852 handleShadowQualityChange();
2853
2854 updateDepthBuffer();
2855}
2856
2857void Surface3DRenderer::updateTextures()
2858{
2859 Abstract3DRenderer::updateTextures();
2860
2861 if (m_polarGraph)
2862 calculateSceneScalingFactors();
2863}
2864
2865void Surface3DRenderer::updateSlicingActive(bool isSlicing)
2866{
2867 if (m_cachedIsSlicingActivated == isSlicing)
2868 return;
2869
2870 m_cachedIsSlicingActivated = isSlicing;
2871
2872 if (!m_cachedIsSlicingActivated) {
2873 // We need to re-init selection buffer in case there has been a resize
2874 initSelectionBuffer();
2875 initCursorPositionBuffer();
2876 }
2877
2878 updateDepthBuffer(); // Re-init depth buffer as well
2879
2880 m_selectionDirty = true;
2881
2882 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2883 SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
2884 if (cache->mainSelectionPointer())
2885 cache->mainSelectionPointer()->updateBoundingRect(rect: m_primarySubViewport);
2886 }
2887}
2888
2889void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
2890{
2891 Q_UNUSED(vertexShader);
2892 Q_UNUSED(fragmentShader);
2893
2894 delete m_surfaceFlatShader;
2895 delete m_surfaceSmoothShader;
2896 delete m_surfaceTexturedSmoothShader;
2897 delete m_surfaceTexturedFlatShader;
2898 delete m_surfaceSliceFlatShader;
2899 delete m_surfaceSliceSmoothShader;
2900
2901 if (!m_isOpenGLES) {
2902 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
2903 m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
2904 QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex"));
2905 m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
2906 QStringLiteral(":/shaders/fragmentTexturedSurfaceShadow"));
2907 } else {
2908 m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
2909 QStringLiteral(":/shaders/fragmentSurface"));
2910 m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
2911 QStringLiteral(":/shaders/fragmentTexture"));
2912 }
2913 m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
2914 QStringLiteral(":/shaders/fragmentSurface"));
2915 if (m_flatSupported) {
2916 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
2917 m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
2918 QStringLiteral(":/shaders/fragmentSurfaceShadowFlat"));
2919 m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
2920 QStringLiteral(":/shaders/fragmentTexturedSurfaceShadowFlat"));
2921 } else {
2922 m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
2923 QStringLiteral(":/shaders/fragmentSurfaceFlat"));
2924 m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
2925 QStringLiteral(":/shaders/fragmentSurfaceTexturedFlat"));
2926 }
2927 m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
2928 QStringLiteral(":/shaders/fragmentSurfaceFlat"));
2929 } else {
2930 m_surfaceFlatShader = 0;
2931 m_surfaceSliceFlatShader = 0;
2932 m_surfaceTexturedFlatShader = 0;
2933 }
2934 } else {
2935 m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
2936 QStringLiteral(":/shaders/fragmentSurfaceES2"));
2937 m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
2938 QStringLiteral(":/shaders/fragmentSurfaceES2"));
2939 m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
2940 QStringLiteral(":/shaders/fragmentTextureES2"));
2941 m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
2942 QStringLiteral(":/shaders/fragmentTextureES2"));
2943 m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
2944 QStringLiteral(":/shaders/fragmentSurfaceES2"));
2945 m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
2946 QStringLiteral(":/shaders/fragmentSurfaceES2"));
2947 }
2948
2949 m_surfaceSmoothShader->initialize();
2950 m_surfaceSliceSmoothShader->initialize();
2951 m_surfaceTexturedSmoothShader->initialize();
2952 if (m_flatSupported) {
2953 m_surfaceFlatShader->initialize();
2954 m_surfaceSliceFlatShader->initialize();
2955 m_surfaceTexturedFlatShader->initialize();
2956 }
2957}
2958
2959void Surface3DRenderer::initBackgroundShaders(const QString &vertexShader,
2960 const QString &fragmentShader)
2961{
2962 if (m_backgroundShader)
2963 delete m_backgroundShader;
2964 m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader);
2965 m_backgroundShader->initialize();
2966}
2967
2968void Surface3DRenderer::initSelectionShaders()
2969{
2970 if (m_selectionShader)
2971 delete m_selectionShader;
2972 m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexLabel"),
2973 QStringLiteral(":/shaders/fragmentLabel"));
2974 m_selectionShader->initialize();
2975}
2976
2977void Surface3DRenderer::initSurfaceShaders()
2978{
2979 // Gridline shader
2980 if (m_surfaceGridShader)
2981 delete m_surfaceGridShader;
2982 m_surfaceGridShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
2983 QStringLiteral(":/shaders/fragmentPlainColor"));
2984 m_surfaceGridShader->initialize();
2985
2986 // Triggers surface shader selection by shadow setting
2987 handleShadowQualityChange();
2988}
2989
2990void Surface3DRenderer::initDepthShader()
2991{
2992 if (!m_isOpenGLES) {
2993 delete m_depthShader;
2994 m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
2995 QStringLiteral(":/shaders/fragmentDepth"));
2996 m_depthShader->initialize();
2997 }
2998}
2999
3000void Surface3DRenderer::updateDepthBuffer()
3001{
3002 if (!m_isOpenGLES) {
3003 m_textureHelper->deleteTexture(texture: &m_depthTexture);
3004
3005 if (m_primarySubViewport.size().isEmpty())
3006 return;
3007
3008 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
3009 m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(size: m_primarySubViewport.size(),
3010 frameBuffer&: m_depthFrameBuffer,
3011 textureSize: m_shadowQualityMultiplier);
3012 if (!m_depthTexture)
3013 lowerShadowQuality();
3014 }
3015 }
3016}
3017
3018QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &position,
3019 bool isAbsolute)
3020{
3021 float xTrans = 0.0f;
3022 float yTrans = 0.0f;
3023 float zTrans = 0.0f;
3024 if (!isAbsolute) {
3025 if (m_polarGraph) {
3026 calculatePolarXZ(dataPos: position, x&: xTrans, z&: zTrans);
3027 } else {
3028 xTrans = m_axisCacheX.positionAt(value: position.x());
3029 zTrans = m_axisCacheZ.positionAt(value: position.z());
3030 }
3031 yTrans = m_axisCacheY.positionAt(value: position.y());
3032 } else {
3033 xTrans = position.x() * m_scaleX;
3034 yTrans = position.y() * m_scaleY;
3035 zTrans = position.z() * -m_scaleZ;
3036 }
3037 return QVector3D(xTrans, yTrans, zTrans);
3038}
3039
3040void Surface3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
3041 const QStringList &labels)
3042{
3043 Abstract3DRenderer::updateAxisLabels(orientation, labels);
3044
3045 // Angular axis label dimensions affect the chart dimensions
3046 if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
3047 calculateSceneScalingFactors();
3048}
3049
3050void Surface3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, bool visible)
3051{
3052 Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible);
3053
3054 // Angular axis title existence affects the chart dimensions
3055 if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
3056 calculateSceneScalingFactors();
3057}
3058
3059void Surface3DRenderer::updateMargin(float margin)
3060{
3061 Abstract3DRenderer::updateMargin(margin);
3062 calculateSceneScalingFactors();
3063}
3064
3065QT_END_NAMESPACE_DATAVISUALIZATION
3066

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