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 
38 static const int ID_TO_RGBA_MASK = 0xff;
39 
40 QT_BEGIN_NAMESPACE_DATAVISUALIZATION
41 
42 //#define SHOW_DEPTH_TEXTURE_SCENE
43 
44 const GLfloat sliceZScale = 0.1f;
45 const GLfloat sliceUnits = 2.5f;
46 const uint greenMultiplier = 256;
47 const uint blueMultiplier = 65536;
48 const uint alphaMultiplier = 16777216;
49 
Surface3DRenderer(Surface3DController * controller)50 Surface3DRenderer::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(this, &Surface3DRenderer::flatShadingSupportedChanged,
87                 controller, &Surface3DController::handleFlatShadingSupportedChange);
88         emit flatShadingSupportedChanged(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 
~Surface3DRenderer()96 Surface3DRenderer::~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 
contextCleanup()111 void Surface3DRenderer::contextCleanup()
112 {
113     if (QOpenGLContext::currentContext()) {
114         m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer);
115         m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
116         m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
117 
118         m_textureHelper->deleteTexture(&m_noShadowTexture);
119         m_textureHelper->deleteTexture(&m_depthTexture);
120         m_textureHelper->deleteTexture(&m_selectionResultTexture);
121     }
122 }
123 
initializeOpenGL()124 void 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(Qt::white);
149     m_noShadowTexture = m_textureHelper->create2DTexture(image, false, true, false, true);
150 }
151 
fixCameraTarget(QVector3D & target)152 void 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 
getVisibleItemBounds(QVector3D & minBounds,QVector3D & maxBounds)159 void 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(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(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(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(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(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(maxBounds.z() - m_scaleZ) / itemRangeZ)));
195     else
196         maxBounds.setZ(-1.0f);
197 }
198 
updateData()199 void 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(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(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 + sampleSpace.y())->at(
238                                     j + sampleSpace.x());
239                     }
240                 }
241 
242                 checkFlatSupport(cache);
243                 updateObjects(cache, 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(m_selectedPoint, m_selectedSeries);
256 }
257 
updateSeries(const QList<QAbstract3DSeries * > & seriesList)258 void 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(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, true);
277             cache->setFlatStatusDirty(false);
278         }
279     }
280 
281     if (noSelection && !selectionLabel().isEmpty()) {
282         m_selectionLabelDirty = true;
283         updateSelectedPoint(Surface3DController::invalidSelectionPosition(), 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(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 
updateSurfaceTextures(QVector<QSurface3DSeries * > seriesList)308 void Surface3DRenderer::updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList)
309 {
310     foreach (QSurface3DSeries *series, seriesList) {
311         SurfaceSeriesRenderCache *cache =
312                 static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(series));
313         if (cache) {
314             GLuint oldTexture = cache->surfaceTexture();
315             m_textureHelper->deleteTexture(&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(series->texture(),
324                                                                 true, true, true, true);
325                 glBindTexture(GL_TEXTURE_2D, texId);
326                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
327                 glBindTexture(GL_TEXTURE_2D, 0);
328                 cache->setSurfaceTexture(texId);
329 
330                 if (cache->isFlatShadingEnabled())
331                     cache->surfaceObject()->coarseUVs(array, cache->dataArray());
332                 else
333                     cache->surfaceObject()->smoothUVs(array, cache->dataArray());
334             }
335         }
336     }
337 }
338 
createNewCache(QAbstract3DSeries * series)339 SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series)
340 {
341     m_selectionTexturesDirty = true;
342     return new SurfaceSeriesRenderCache(series, this);
343 }
344 
cleanCache(SeriesRenderCache * cache)345 void Surface3DRenderer::cleanCache(SeriesRenderCache *cache)
346 {
347     Abstract3DRenderer::cleanCache(cache);
348     m_selectionTexturesDirty = true;
349 }
350 
updateRows(const QVector<Surface3DController::ChangeRow> & rows)351 void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow> &rows)
352 {
353     foreach (Surface3DController::ChangeRow item, rows) {
354         SurfaceSeriesRenderCache *cache =
355                 static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(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(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(row - sampleSpace.y())))[j] =
373                             srcArray->at(row)->at(j + sampleSpace.x());
374                 }
375 
376                 if (cache->isFlatShadingEnabled()) {
377                     cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y(),
378                                                             m_polarGraph);
379                 } else {
380                     cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y(),
381                                                             m_polarGraph);
382                 }
383             }
384             if (updateBuffers)
385                 cache->surfaceObject()->uploadBuffers();
386         }
387     }
388 
389     updateSelectedPoint(m_selectedPoint, m_selectedSeries);
390 }
391 
updateItems(const QVector<Surface3DController::ChangeItem> & points)392 void Surface3DRenderer::updateItems(const QVector<Surface3DController::ChangeItem> &points)
393 {
394     foreach (Surface3DController::ChangeItem item, points) {
395         SurfaceSeriesRenderCache *cache =
396                 static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(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(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(y)))[x] = srcArray->at(point.x())->at(point.y());
419 
420                 if (cache->isFlatShadingEnabled())
421                     cache->surfaceObject()->updateCoarseItem(dstArray, y, x, m_polarGraph);
422                 else
423                     cache->surfaceObject()->updateSmoothItem(dstArray, y, x, m_polarGraph);
424             }
425             if (updateBuffers)
426                 cache->surfaceObject()->uploadBuffers();
427         }
428 
429     }
430 
431     updateSelectedPoint(m_selectedPoint, m_selectedSeries);
432 }
433 
updateSliceDataModel(const QPoint & point)434 void 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(QAbstract3DGraph::SelectionMultiSeries)) {
440         // Find axis coordinates for the selected point
441         SeriesRenderCache *selectedCache =
442                 m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries));
443         QSurfaceDataArray &dataArray =
444                 static_cast<SurfaceSeriesRenderCache *>(selectedCache)->dataArray();
445         QSurfaceDataItem item = dataArray.at(point.x())->at(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, 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(m_selectedSeries));
462             if (cache)
463                 updateSliceObject(static_cast<SurfaceSeriesRenderCache *>(cache), point);
464         }
465     }
466 }
467 
mapCoordsToSampleSpace(SurfaceSeriesRenderCache * cache,const QPointF & coords)468 QPoint 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(top)->size() - 1;
476     QSurfaceDataItem itemBottomLeft = dataArray.at(0)->at(0);
477     QSurfaceDataItem itemTopRight = dataArray.at(top)->at(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(0)->at(sampleX);
486         if (!::qFuzzyCompare(float(coords.x()), item.x())) {
487             int direction = 1;
488             if (item.x() > coords.x())
489                 direction = -1;
490 
491             findMatchingColumn(coords.x(), 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(sampleY)->at(0);
505         if (!::qFuzzyCompare(float(coords.y()), item.z())) {
506             int direction = 1;
507             if (item.z() > coords.y())
508                 direction = -1;
509 
510             findMatchingRow(coords.y(), sampleY, direction, dataArray);
511         }
512 
513         if (sampleY >= 0 && sampleY <= top)
514             point.setX(sampleY);
515     }
516 
517     return point;
518 }
519 
findMatchingRow(float z,int & sample,int direction,QSurfaceDataArray & dataArray)520 void Surface3DRenderer::findMatchingRow(float z, int &sample, int direction,
521                                         QSurfaceDataArray &dataArray)
522 {
523     int maxZ = dataArray.size() - 1;
524     QSurfaceDataItem item = dataArray.at(sample)->at(0);
525     float distance = qAbs(z - item.z());
526     int newSample = sample + direction;
527     while (newSample >= 0 && newSample <= maxZ) {
528         item = dataArray.at(newSample)->at(0);
529         float newDist = qAbs(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 
findMatchingColumn(float x,int & sample,int direction,QSurfaceDataArray & dataArray)540 void Surface3DRenderer::findMatchingColumn(float x, int &sample, int direction,
541                                            QSurfaceDataArray &dataArray)
542 {
543     int maxX = dataArray.at(0)->size() - 1;
544     QSurfaceDataItem item = dataArray.at(0)->at(sample);
545     float distance = qAbs(x - item.x());
546     int newSample = sample + direction;
547     while (newSample >= 0 && newSample <= maxX) {
548         item = dataArray.at(0)->at(newSample);
549         float newDist = qAbs(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 
updateSliceObject(SurfaceSeriesRenderCache * cache,const QPoint & point)560 void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const QPoint &point)
561 {
562     int column = point.y();
563     int row = point.x();
564 
565     if ((m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow) && row == -1) ||
566             (m_cachedSelectionMode.testFlag(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(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(QAbstract3DGraph::SelectionRow)) {
585         QSurfaceDataRow *src = dataArray.at(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(column).z(),
599                                                  dataArray.at(i)->at(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(sliceDataArray, sliceRect, true, false, flipZX);
618         } else {
619             cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, false,
620                                                          flipZX);
621         }
622     }
623 }
624 
getDataValue(const QSurfaceDataArray & array,bool searchRow,int index)625 inline static float getDataValue(const QSurfaceDataArray &array, bool searchRow, int index)
626 {
627     if (searchRow)
628         return array.at(0)->at(index).x();
629     else
630         return array.at(index)->at(0).z();
631 }
632 
binarySearchArray(const QSurfaceDataArray & array,int maxIdx,float limitValue,bool searchRow,bool lowBound,bool ascending)633 inline 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, 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, retVal) < limitValue)
675             retVal = -1;
676     } else {
677         if (getDataValue(array, searchRow, retVal) > limitValue)
678             retVal = -1;
679     }
680     return retVal;
681 }
682 
calculateSampleRect(const QSurfaceDataArray & array)683 QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array)
684 {
685     QRect sampleSpace;
686 
687     const int maxRow = array.size() - 1;
688     const int maxColumn = array.at(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(0)->at(0).x() < array.at(0)->at(maxColumn).x();
693     const bool ascendingZ = array.at(0)->at(0).z() < array.at(maxRow)->at(0).z();
694 
695     int idx = binarySearchArray(array, maxColumn, m_axisCacheX.min(), true, true, 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, maxColumn, m_axisCacheX.max(), true, false, 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, maxRow, m_axisCacheZ.min(), false, true, 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, maxRow, m_axisCacheZ.max(), false, false, 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 
updateScene(Q3DScene * scene)742 void Surface3DRenderer::updateScene(Q3DScene *scene)
743 {
744     Abstract3DRenderer::updateScene(scene);
745 
746     if (m_selectionActive
747             && m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem)) {
748         m_selectionDirty = true; // Ball may need repositioning if scene changes
749     }
750 
751     updateSlicingActive(scene->isSlicingActive());
752 }
753 
render(GLuint defaultFboHandle)754 void 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(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                                                                     m_useOrthoProjection);
782             }
783         }
784     }
785 }
786 
drawSlicedScene()787 void Surface3DRenderer::drawSlicedScene()
788 {
789     if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)
790             == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
791         qWarning("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(m_cachedTheme->lightColor());
800 
801     // Specify viewport
802     glViewport(m_secondarySubViewport.x(),
803                m_secondarySubViewport.y(),
804                m_secondarySubViewport.width(),
805                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(-sliceUnitsScaled * aspect, sliceUnitsScaled * aspect,
814                            -sliceUnitsScaled, sliceUnitsScaled,
815                            -1.0f, 4.0f);
816 
817     // Set view matrix
818     QMatrix4x4 viewMatrix;
819     viewMatrix.lookAt(QVector3D(0.0f, 0.0f, 1.0f), zeroVector, 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(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(0.5f, 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(scaling);
867                 itModelMatrix.scale(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(surfaceShader->gradientMin(), 0.0f);
883                         surfaceShader->setUniformValue(surfaceShader->gradientHeight(), 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(surfaceShader->gradientMin(),
891                                                            -(objMin / objRange));
892                             surfaceShader->setUniformValue(surfaceShader->gradientHeight(),
893                                                            1.0f / objRange);
894                         } else {
895                             surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.5f);
896                             surfaceShader->setUniformValue(surfaceShader->gradientHeight(),
897                                                            1.0f / (m_scaleY * 2.0f));
898                         }
899                     }
900 
901                     // Set shader bindings
902                     surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos);
903                     surfaceShader->setUniformValue(surfaceShader->view(), viewMatrix);
904                     surfaceShader->setUniformValue(surfaceShader->model(), modelMatrix);
905                     surfaceShader->setUniformValue(surfaceShader->nModel(),
906                                                    itModelMatrix.inverted().transposed());
907                     surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix);
908                     surfaceShader->setUniformValue(surfaceShader->lightS(), 0.0f);
909                     surfaceShader->setUniformValue(surfaceShader->ambientS(),
910                                                    m_cachedTheme->ambientLightStrength()
911                                                    + m_cachedTheme->lightStrength() / 10.0f);
912                     surfaceShader->setUniformValue(surfaceShader->lightColor(), lightColor);
913 
914                     m_drawer->drawObject(surfaceShader, cache->sliceSurfaceObject(), 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(m_surfaceGridShader->color(),
924                                                  Utils::vectorFromColor(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(m_surfaceGridShader->MVP(),
931                                                          cache->MVPMatrix());
932                     m_drawer->drawSurfaceGrid(m_surfaceGridShader, 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(m_cachedTheme->gridLineColor());
954         lineShader->setUniformValue(lineShader->lightP(), lightPos);
955         lineShader->setUniformValue(lineShader->view(), viewMatrix);
956         lineShader->setUniformValue(lineShader->color(), lineColor);
957         lineShader->setUniformValue(lineShader->ambientS(),
958                                     m_cachedTheme->ambientLightStrength()
959                                     + m_cachedTheme->lightStrength() / 10.0f);
960         lineShader->setUniformValue(lineShader->lightS(), 0.0f);
961         lineShader->setUniformValue(lineShader->lightColor(), 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(0.0f, m_axisCacheY.gridLinePosition(line), -1.0f);
974 
975                 modelMatrix.scale(gridLineScaleX);
976                 itModelMatrix.scale(gridLineScaleX);
977 
978                 MVPMatrix = projectionViewMatrix * modelMatrix;
979 
980                 // Set the rest of the shader bindings
981                 lineShader->setUniformValue(lineShader->model(), modelMatrix);
982                 lineShader->setUniformValue(lineShader->nModel(),
983                                             itModelMatrix.inverted().transposed());
984                 lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
985 
986                 // Draw the object
987                 if (m_isOpenGLES)
988                     m_drawer->drawLine(lineShader);
989                 else
990                     m_drawer->drawObject(lineShader, 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(sliceCache.gridLinePosition(line), 0.0f, -1.0f);
1004             modelMatrix.scale(gridLineScaleY);
1005             itModelMatrix.scale(gridLineScaleY);
1006 
1007             if (m_isOpenGLES) {
1008                 modelMatrix.rotate(m_zRightAngleRotation);
1009                 itModelMatrix.rotate(m_zRightAngleRotation);
1010             }
1011 
1012             MVPMatrix = projectionViewMatrix * modelMatrix;
1013 
1014             // Set the rest of the shader bindings
1015             lineShader->setUniformValue(lineShader->model(), modelMatrix);
1016             lineShader->setUniformValue(lineShader->nModel(),
1017                                         itModelMatrix.inverted().transposed());
1018             lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
1019 
1020             // Draw the object
1021             if (m_isOpenGLES)
1022                 m_drawer->drawLine(lineShader);
1023             else
1024                 m_drawer->drawObject(lineShader, 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(label));
1043             const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
1044 
1045             // Draw the label here
1046             m_dummyRenderItem.setTranslation(labelTrans);
1047             m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
1048                                 positionComp, identityQuaternion, 0, m_cachedSelectionMode,
1049                                 m_labelShader, m_labelObj, activeCamera, true, true,
1050                                 Drawer::LabelMid, Qt::AlignLeft, 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(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(label));
1070             else
1071                 labelTrans.setX(-sliceCache.labelPosition(label));
1072 
1073             m_dummyRenderItem.setTranslation(labelTrans);
1074 
1075             LabelItem *axisLabelItem;
1076             axisLabelItem = sliceCache.labelItems().at(labelNbr);
1077 
1078             m_drawer->drawLabel(m_dummyRenderItem, *axisLabelItem, viewMatrix, projectionMatrix,
1079                                 positionComp, totalRotation, 0, QAbstract3DGraph::SelectionRow,
1080                                 m_labelShader, m_labelObj, activeCamera,
1081                                 false, false, Drawer::LabelBelow,
1082                                 Qt::AlignLeft | Qt::AlignTop, true);
1083         }
1084         labelNbr++;
1085     }
1086 
1087     // Draw labels for axes
1088     AbstractRenderItem *dummyItem(0);
1089     positionComp.setY(m_autoScaleAdjustment);
1090     m_drawer->drawLabel(*dummyItem, sliceCache.titleItem(), viewMatrix, projectionMatrix,
1091                         positionComp, identityQuaternion, 0, m_cachedSelectionMode, m_labelShader,
1092                         m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
1093                         Qt::AlignCenter, true);
1094 
1095     // Y-axis label
1096     rotation = QVector3D(0.0f, 0.0f, 90.0f);
1097     totalRotation = Utils::calculateRotation(rotation);
1098     labelTrans = QVector3D(-scaleXBackground - labelMargin, 0.0f, 0.0f);
1099     m_dummyRenderItem.setTranslation(labelTrans);
1100     m_drawer->drawLabel(m_dummyRenderItem, m_axisCacheY.titleItem(), viewMatrix,
1101                         projectionMatrix, zeroVector, totalRotation, 0,
1102                         m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
1103                         false, false, Drawer::LabelMid, Qt::AlignBottom);
1104 
1105     glEnable(GL_DEPTH_TEST);
1106     glDisable(GL_BLEND);
1107 
1108     // Release shader
1109     glUseProgram(0);
1110 }
1111 
drawScene(GLuint defaultFboHandle)1112 void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
1113 {
1114     bool noShadows = true;
1115 
1116     GLfloat backgroundRotation = 0;
1117     QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
1118 
1119     glViewport(m_primarySubViewport.x(),
1120                m_primarySubViewport.y(),
1121                m_primarySubViewport.width(),
1122                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(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
1131                                -orthoRatio, orthoRatio,
1132                                0.0f, 100.0f);
1133     } else {
1134         projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 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(0).x() > 0)
1146         m_zFlipped = false;
1147     else
1148         m_zFlipped = true;
1149     if (viewMatrix.row(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(qreal(activeCamera->yRotation()));
1161             float cameraYPos = float(qSin(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(0).x() > 0 && viewMatrix.row(0).z() <= 0)
1171         backgroundRotation = 270.0f;
1172     else if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() > 0)
1173         backgroundRotation = 180.0f;
1174     else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() > 0)
1175         backgroundRotation = 90.0f;
1176     else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(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, m_depthFrameBuffer);
1192 
1193         // Attach texture to depth attachment
1194         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
1195                                m_depthTexture, 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(0, 0,
1203                    m_primarySubViewport.width() * m_shadowQualityMultiplier,
1204                    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                     zeroVector, 0.0f, 4.0f / m_autoScaleAdjustment);
1210         depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
1211 
1212         // Set the depth projection matrix
1213         depthProjectionMatrix.perspective(10.0f, (GLfloat)m_primarySubViewport.width()
1214                                           / (GLfloat)m_primarySubViewport.height(), 3.0f, 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(m_depthShader->MVP(), depthProjectionViewMatrix);
1228 
1229                 // 1st attribute buffer : vertices
1230                 glEnableVertexAttribArray(m_depthShader->posAtt());
1231                 glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
1232                 glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
1233                                       (void *)0);
1234 
1235                 // Index buffer
1236                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
1237 
1238                 // Draw the triangles
1239                 glDrawElements(GL_TRIANGLES, object->indexCount(), GL_UNSIGNED_INT, (void *)0);
1240             }
1241         }
1242 
1243         // Free buffers
1244         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1245         glBindBuffer(GL_ARRAY_BUFFER, 0);
1246 
1247         glDisableVertexAttribArray(m_depthShader->posAtt());
1248 
1249         glEnable(GL_CULL_FACE);
1250         glCullFace(GL_FRONT);
1251 
1252         Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
1253                                             projectionViewMatrix,
1254                                             depthProjectionViewMatrix, m_depthTexture,
1255                                             m_shadowQualityToShader);
1256 
1257         // Disable drawing to depth framebuffer (= enable drawing to screen)
1258         glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
1259 
1260         // Revert to original viewport
1261         glViewport(m_primarySubViewport.x(),
1262                    m_primarySubViewport.y(),
1263                    m_primarySubViewport.width(),
1264                    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, 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, m_selectionFrameBuffer);
1286         glViewport(0,
1287                    0,
1288                    m_primarySubViewport.width(),
1289                    m_primarySubViewport.height());
1290 
1291         glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used
1292         glClearColor(0.0f, 0.0f, 0.0f, 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(m_selectionShader->MVP(), projectionViewMatrix);
1302 
1303                 cache->surfaceObject()->activateSurfaceTexture(false);
1304 
1305                 m_drawer->drawObject(m_selectionShader, cache->surfaceObject(),
1306                                      cache->selectionTexture());
1307             }
1308         }
1309         m_surfaceGridShader->bind();
1310         Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader,
1311                                             viewMatrix,
1312                                             projectionViewMatrix, depthProjectionViewMatrix,
1313                                             m_depthTexture, m_shadowQualityToShader);
1314         drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
1315 
1316         glEnable(GL_DITHER);
1317 
1318         QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height());
1319 
1320         glBindFramebuffer(GL_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(selectionId);
1329         m_clickResolved = true;
1330 
1331         emit needRender();
1332 
1333         // Revert to original viewport
1334         glViewport(m_primarySubViewport.x(),
1335                    m_primarySubViewport.y(),
1336                    m_primarySubViewport.width(),
1337                    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(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(visiblePoint);
1364             if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem))
1365                 surfacePointSelected(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(0.5f, 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(shader->lightP(), lightPos);
1415                     shader->setUniformValue(shader->view(), viewMatrix);
1416                     shader->setUniformValue(shader->model(), modelMatrix);
1417                     shader->setUniformValue(shader->nModel(),
1418                                             itModelMatrix.inverted().transposed());
1419                     shader->setUniformValue(shader->MVP(), MVPMatrix);
1420                     shader->setUniformValue(shader->ambientS(),
1421                                             m_cachedTheme->ambientLightStrength());
1422                     shader->setUniformValue(shader->lightColor(), lightColor);
1423 
1424                     // Set the surface texturing
1425                     cache->surfaceObject()->activateSurfaceTexture(false);
1426                     GLuint texture;
1427                     if (cache->surfaceTexture()) {
1428                         texture = cache->surfaceTexture();
1429                         cache->surfaceObject()->activateSurfaceTexture(true);
1430                     } else {
1431                         if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
1432                             texture = cache->baseUniformTexture();
1433                             shader->setUniformValue(shader->gradientMin(), 0.0f);
1434                             shader->setUniformValue(shader->gradientHeight(), 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(shader->gradientMin(), -(objMin / objRange));
1442                                 shader->setUniformValue(shader->gradientHeight(), 1.0f / objRange);
1443                             } else {
1444                                 shader->setUniformValue(shader->gradientMin(), 0.5f);
1445                                 shader->setUniformValue(shader->gradientHeight(),
1446                                                         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(shader->shadowQ(), m_shadowQualityToShader);
1456                         shader->setUniformValue(shader->depth(), depthMVPMatrix);
1457                         shader->setUniformValue(shader->lightS(), adjustedLightStrength);
1458 
1459                         // Draw the objects
1460                         m_drawer->drawObject(shader, cache->surfaceObject(), texture,
1461                                              m_depthTexture);
1462                     } else {
1463                         // Set shadowless shader bindings
1464                         shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
1465                         // Draw the objects
1466                         m_drawer->drawObject(shader, cache->surfaceObject(), 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(m_surfaceGridShader->color(),
1478                                                  Utils::vectorFromColor(
1479                                                      m_cachedTheme->gridLineColor()));
1480             foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1481                 SurfaceSeriesRenderCache *cache =
1482                         static_cast<SurfaceSeriesRenderCache *>(baseCache);
1483                 m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(),
1484                                                      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(m_surfaceGridShader, cache->surfaceObject());
1491                 }
1492             }
1493         }
1494     }
1495 
1496     // Render selection ball
1497     if (m_selectionActive
1498             && m_cachedSelectionMode.testFlag(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                                                                       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(bgScale);
1524 
1525         // If we're viewing from below, background object must be flipped
1526         if (m_yFlipped) {
1527             modelMatrix.rotate(m_xFlipRotation);
1528             modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f);
1529         } else {
1530             modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 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(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(m_backgroundShader->lightP(), lightPos);
1551         m_backgroundShader->setUniformValue(m_backgroundShader->view(), viewMatrix);
1552         m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix);
1553         m_backgroundShader->setUniformValue(m_backgroundShader->nModel(),
1554                                             itModelMatrix.inverted().transposed());
1555         m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix);
1556         m_backgroundShader->setUniformValue(m_backgroundShader->color(), backgroundColor);
1557         m_backgroundShader->setUniformValue(m_backgroundShader->ambientS(),
1558                                             m_cachedTheme->ambientLightStrength() * 2.0f);
1559         m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
1560 
1561         if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1562             // Set shadow shader bindings
1563             QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1564             m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
1565                                                 m_shadowQualityToShader);
1566             m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix);
1567             m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
1568                                                 adjustedLightStrength);
1569             // Draw the object
1570             if (noShadows && m_customRenderCache.isEmpty())
1571                 m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_noShadowTexture);
1572             else
1573                 m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
1574         } else {
1575             // Set shadowless shader bindings
1576             m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
1577                                                 m_cachedTheme->lightStrength());
1578 
1579             // Draw the object
1580             m_drawer->drawObject(m_backgroundShader, 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(m_cachedTheme->gridLineColor());
1604         lineShader->setUniformValue(lineShader->lightP(), lightPos);
1605         lineShader->setUniformValue(lineShader->view(), viewMatrix);
1606         lineShader->setUniformValue(lineShader->color(), lineColor);
1607         lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
1608         lineShader->setUniformValue(lineShader->lightColor(), lightColor);
1609         if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1610             // Set shadowed shader bindings
1611             lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
1612             lineShader->setUniformValue(lineShader->lightS(),
1613                                         m_cachedTheme->lightStrength() / 20.0f);
1614         } else {
1615             // Set shadowless shader bindings
1616             lineShader->setUniformValue(lineShader->lightS(),
1617                                         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(lineShader, yFloorLinePosition, projectionViewMatrix,
1643                                depthProjectionViewMatrix);
1644             } else {
1645                 for (int line = 0; line < gridLineCount; line++) {
1646                     QMatrix4x4 modelMatrix;
1647                     QMatrix4x4 MVPMatrix;
1648                     QMatrix4x4 itModelMatrix;
1649 
1650                     modelMatrix.translate(0.0f, yFloorLinePosition,
1651                                           m_axisCacheZ.gridLinePosition(line));
1652 
1653                     modelMatrix.scale(gridLineScaleX);
1654                     itModelMatrix.scale(gridLineScaleX);
1655 
1656                     modelMatrix.rotate(lineXRotation);
1657                     itModelMatrix.rotate(lineXRotation);
1658 
1659                     MVPMatrix = projectionViewMatrix * modelMatrix;
1660 
1661                     // Set the rest of the shader bindings
1662                     lineShader->setUniformValue(lineShader->model(), modelMatrix);
1663                     lineShader->setUniformValue(lineShader->nModel(),
1664                                                 itModelMatrix.inverted().transposed());
1665                     lineShader->setUniformValue(lineShader->MVP(), 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(lineShader->depth(), depthMVPMatrix);
1672                             // Draw the object
1673                             m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1674                         } else {
1675                             // Draw the object
1676                             m_drawer->drawObject(lineShader, m_gridLineObj);
1677                         }
1678                     } else {
1679                         m_drawer->drawLine(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(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
1694 
1695                     modelMatrix.scale(gridLineScaleY);
1696                     itModelMatrix.scale(gridLineScaleY);
1697 
1698                     if (m_isOpenGLES) {
1699                         modelMatrix.rotate(m_zRightAngleRotation);
1700                         itModelMatrix.rotate(m_zRightAngleRotation);
1701                     } else {
1702                         modelMatrix.rotate(lineYRotation);
1703                         itModelMatrix.rotate(lineYRotation);
1704                     }
1705 
1706                     MVPMatrix = projectionViewMatrix * modelMatrix;
1707 
1708                     // Set the rest of the shader bindings
1709                     lineShader->setUniformValue(lineShader->model(), modelMatrix);
1710                     lineShader->setUniformValue(lineShader->nModel(),
1711                                                 itModelMatrix.inverted().transposed());
1712                     lineShader->setUniformValue(lineShader->MVP(), 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(lineShader->depth(), depthMVPMatrix);
1719                             // Draw the object
1720                             m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1721                         } else {
1722                             // Draw the object
1723                             m_drawer->drawObject(lineShader, m_gridLineObj);
1724                         }
1725                     } else {
1726                         m_drawer->drawLine(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(lineShader, yFloorLinePosition, projectionViewMatrix,
1742                                 depthProjectionViewMatrix);
1743             } else {
1744                 for (int line = 0; line < gridLineCount; line++) {
1745                     QMatrix4x4 modelMatrix;
1746                     QMatrix4x4 MVPMatrix;
1747                     QMatrix4x4 itModelMatrix;
1748 
1749                     modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
1750                                           0.0f);
1751 
1752                     modelMatrix.scale(gridLineScaleZ);
1753                     itModelMatrix.scale(gridLineScaleZ);
1754 
1755                     modelMatrix.rotate(lineXRotation);
1756                     itModelMatrix.rotate(lineXRotation);
1757 
1758                     MVPMatrix = projectionViewMatrix * modelMatrix;
1759 
1760                     // Set the rest of the shader bindings
1761                     lineShader->setUniformValue(lineShader->model(), modelMatrix);
1762                     lineShader->setUniformValue(lineShader->nModel(),
1763                                                 itModelMatrix.inverted().transposed());
1764                     lineShader->setUniformValue(lineShader->MVP(), 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(lineShader->depth(), depthMVPMatrix);
1771                             // Draw the object
1772                             m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1773                         } else {
1774                             // Draw the object
1775                             m_drawer->drawObject(lineShader, m_gridLineObj);
1776                         }
1777                     } else {
1778                         m_drawer->drawLine(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(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
1794 
1795                     modelMatrix.scale(gridLineScaleY);
1796                     itModelMatrix.scale(gridLineScaleY);
1797 
1798                     if (m_isOpenGLES) {
1799                         modelMatrix.rotate(m_zRightAngleRotation);
1800                         itModelMatrix.rotate(m_zRightAngleRotation);
1801                     } else if (m_zFlipped) {
1802                         modelMatrix.rotate(m_xFlipRotation);
1803                         itModelMatrix.rotate(m_xFlipRotation);
1804                     }
1805 
1806                     MVPMatrix = projectionViewMatrix * modelMatrix;
1807 
1808                     // Set the rest of the shader bindings
1809                     lineShader->setUniformValue(lineShader->model(), modelMatrix);
1810                     lineShader->setUniformValue(lineShader->nModel(),
1811                                                 itModelMatrix.inverted().transposed());
1812                     lineShader->setUniformValue(lineShader->MVP(), 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(lineShader->depth(), depthMVPMatrix);
1819                             // Draw the object
1820                             m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1821                         } else {
1822                             // Draw the object
1823                             m_drawer->drawObject(lineShader, m_gridLineObj);
1824                         }
1825                     } else {
1826                         m_drawer->drawLine(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(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans);
1848 
1849                 modelMatrix.scale(gridLineScaleX);
1850                 itModelMatrix.scale(gridLineScaleX);
1851 
1852                 if (m_zFlipped) {
1853                     modelMatrix.rotate(m_xFlipRotation);
1854                     itModelMatrix.rotate(m_xFlipRotation);
1855                 }
1856 
1857                 MVPMatrix = projectionViewMatrix * modelMatrix;
1858 
1859                 // Set the rest of the shader bindings
1860                 lineShader->setUniformValue(lineShader->model(), modelMatrix);
1861                 lineShader->setUniformValue(lineShader->nModel(),
1862                                             itModelMatrix.inverted().transposed());
1863                 lineShader->setUniformValue(lineShader->MVP(), 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(lineShader->depth(), depthMVPMatrix);
1870                         // Draw the object
1871                         m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1872                     } else {
1873                         // Draw the object
1874                         m_drawer->drawObject(lineShader, m_gridLineObj);
1875                     }
1876                 } else {
1877                     m_drawer->drawLine(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(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f);
1893 
1894                 modelMatrix.scale(gridLineScaleZ);
1895                 itModelMatrix.scale(gridLineScaleZ);
1896 
1897                 modelMatrix.rotate(lineYRotation);
1898                 itModelMatrix.rotate(lineYRotation);
1899 
1900                 MVPMatrix = projectionViewMatrix * modelMatrix;
1901 
1902                 // Set the rest of the shader bindings
1903                 lineShader->setUniformValue(lineShader->model(), modelMatrix);
1904                 lineShader->setUniformValue(lineShader->nModel(),
1905                                             itModelMatrix.inverted().transposed());
1906                 lineShader->setUniformValue(lineShader->MVP(), 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(lineShader->depth(), depthMVPMatrix);
1913                         // Draw the object
1914                         m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1915                     } else {
1916                         // Draw the object
1917                         m_drawer->drawObject(lineShader, m_gridLineObj);
1918                     }
1919                 } else {
1920                     m_drawer->drawLine(lineShader);
1921                 }
1922             }
1923         }
1924     }
1925 
1926     Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
1927                                         projectionViewMatrix, depthProjectionViewMatrix,
1928                                         m_depthTexture, m_shadowQualityToShader);
1929 
1930     drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
1931 
1932     // Release shader
1933     glUseProgram(0);
1934 }
1935 
drawLabels(bool drawSelection,const Q3DCamera * activeCamera,const QMatrix4x4 & viewMatrix,const QMatrix4x4 & projectionMatrix)1936 void 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(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(offsetValue++ / -10.0f, 1.0f);
2064             const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(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(label)
2069                                  * -m_polarRadius
2070                                  + m_drawer->scaledFontSize() + gridLineWidth) * direction);
2071             } else {
2072                 labelTrans.setZ(m_axisCacheZ.labelPosition(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(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(shader->color(), labelColor);
2097             }
2098 
2099             m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2100                                 positionZComp, totalRotation, 0, m_cachedSelectionMode,
2101                                 shader, m_labelObj, activeCamera,
2102                                 true, true, Drawer::LabelMid, alignment, false, drawSelection);
2103             labelsMaxWidth = qMax(labelsMaxWidth, 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, 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(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(lastLabelPosIndex) != 1.0f || labelPositions.at(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(offsetValue++ / -10.0f, 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(label);
2244                 qreal angle = labelPosition * M_PI * 2.0;
2245                 labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle)));
2246                 labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(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(label));
2265             }
2266             const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(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(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(shader->color(), labelColor);
2290             }
2291 
2292             m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2293                                 positionZComp, totalRotation, 0, m_cachedSelectionMode,
2294                                 shader, m_labelObj, activeCamera,
2295                                 true, true, Drawer::LabelMid, alignment, false, drawSelection);
2296             labelsMaxWidth = qMax(labelsMaxWidth, 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(0.0f, 0.0f, 1.0f, -180.0f);
2308                 labelTrans.setZ(-m_polarRadius);
2309                 radial = true;
2310             }
2311             drawAxisTitleX(labelRotation, labelTrans, totalRotation, 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(sideLabelRotation);
2370         QQuaternion totalBackRotation = Utils::calculateRotation(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(label);
2387             float labelYTrans = m_axisCacheY.labelPosition(label);
2388 
2389             glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
2390 
2391             if (drawSelection) {
2392                 QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f,
2393                                                  alphaForValueSelection);
2394                 shader->setUniformValue(shader->color(), 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(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(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2416                                 positionZComp, totalBackRotation, 0, m_cachedSelectionMode,
2417                                 shader, m_labelObj, activeCamera,
2418                                 true, true, Drawer::LabelMid, backAlignment, false,
2419                                 drawSelection);
2420 
2421             // Side wall
2422             labelTransSide.setY(labelYTrans);
2423             m_dummyRenderItem.setTranslation(labelTransSide);
2424             m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2425                                 positionZComp, totalSideRotation, 0, m_cachedSelectionMode,
2426                                 shader, m_labelObj, activeCamera,
2427                                 true, true, Drawer::LabelMid, sideAlignment, false,
2428                                 drawSelection);
2429             labelsMaxWidth = qMax(labelsMaxWidth, 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, labelTransSide, labelTransBack,
2435                            totalSideRotation, totalBackRotation, 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 
updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)2446 void Surface3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
2447 {
2448     Abstract3DRenderer::updateSelectionMode(mode);
2449 
2450     if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone)
2451         updateSelectionTextures();
2452 }
2453 
updateSelectionTextures()2454 void 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);
2463         createSelectionTexture(cache, lastSelectionId);
2464     }
2465     m_selectionTexturesDirty = false;
2466 }
2467 
createSelectionTexture(SurfaceSeriesRenderCache * cache,uint & lastSelectionId)2468 void 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(~0U, ~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(lastSelectionId, &r, &g, &b, &a);
2493             fillIdCorner(&bits[p], r, g, b, a);
2494 
2495             idToRGBA(lastSelectionId + 1, &r, &g, &b, &a);
2496             fillIdCorner(&bits[p + 4], r, g, b, a);
2497 
2498             idToRGBA(lastSelectionId + sampleSpace.width(), &r, &g, &b, &a);
2499             fillIdCorner(&bits[p + stride], r, g, b, a);
2500 
2501             idToRGBA(lastSelectionId + sampleSpace.width() + 1, &r, &g, &b, &a);
2502             fillIdCorner(&bits[p + stride + 4], r, g, b, a);
2503 
2504             lastSelectionId++;
2505         }
2506         lastSelectionId++;
2507     }
2508     lastSelectionId += sampleSpace.width();
2509     cache->setSelectionIdRange(idStart, 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, false, false, false);
2514     cache->setSelectionTexture(selectionTexture);
2515 
2516     // Release the temp bits allocation
2517     delete[] bits;
2518 }
2519 
initSelectionBuffer()2520 void Surface3DRenderer::initSelectionBuffer()
2521 {
2522     // Create the result selection texture and buffers
2523     m_textureHelper->deleteTexture(&m_selectionResultTexture);
2524 
2525     m_selectionResultTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(),
2526                                                                        m_selectionFrameBuffer,
2527                                                                        m_selectionDepthBuffer);
2528 }
2529 
fillIdCorner(uchar * p,uchar r,uchar g,uchar b,uchar a)2530 void 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 
idToRGBA(uint id,uchar * r,uchar * g,uchar * b,uchar * a)2538 void 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 
calculateSceneScalingFactors()2546 void 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(m_hBackgroundMargin, 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(areaSize.width(), 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 
checkFlatSupport(SurfaceSeriesRenderCache * cache)2610 void 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 
updateObjects(SurfaceSeriesRenderCache * cache,bool dimensionChanged)2621 void 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, sampleSpace, dimensionChanged, m_polarGraph);
2632         if (cache->surfaceTexture())
2633             cache->surfaceObject()->coarseUVs(array, dataArray);
2634     } else {
2635         cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged,
2636                                                 m_polarGraph);
2637         if (cache->surfaceTexture())
2638             cache->surfaceObject()->smoothUVs(array, dataArray);
2639     }
2640 }
2641 
updateSelectedPoint(const QPoint & position,QSurface3DSeries * series)2642 void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series)
2643 {
2644     m_selectedPoint = position;
2645     m_selectedSeries = series;
2646     m_selectionDirty = true;
2647 }
2648 
updateFlipHorizontalGrid(bool flip)2649 void Surface3DRenderer::updateFlipHorizontalGrid(bool flip)
2650 {
2651     m_flipHorizontalGrid = flip;
2652 }
2653 
resetClickedStatus()2654 void Surface3DRenderer::resetClickedStatus()
2655 {
2656     m_clickedPosition = Surface3DController::invalidSelectionPosition();
2657     m_clickedSeries = 0;
2658 }
2659 
loadBackgroundMesh()2660 void Surface3DRenderer::loadBackgroundMesh()
2661 {
2662     ObjectHelper::resetObjectHelper(this, m_backgroundObj,
2663                                     QStringLiteral(":/defaultMeshes/background"));
2664 }
2665 
surfacePointSelected(const QPoint & point)2666 void 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(QAbstract3DGraph::SelectionMultiSeries)) {
2676         // Find axis coordinates for the selected point
2677         SurfaceSeriesRenderCache *selectedCache =
2678                 static_cast<SurfaceSeriesRenderCache *>(
2679                     m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)));
2680         QSurfaceDataArray &dataArray = selectedCache->dataArray();
2681         QSurfaceDataItem item = dataArray.at(point.x())->at(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, mappedPoint, false);
2690             } else {
2691                 updateSelectionPoint(cache, point, true);
2692             }
2693         }
2694     } else {
2695         if (m_selectedSeries) {
2696             SurfaceSeriesRenderCache *cache =
2697                     static_cast<SurfaceSeriesRenderCache *>(
2698                         m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)));
2699             if (cache)
2700                 updateSelectionPoint(cache, point, true);
2701         }
2702     }
2703 }
2704 
updateSelectionPoint(SurfaceSeriesRenderCache * cache,const QPoint & point,bool label)2705 void 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(QAbstract3DGraph::SelectionRow)) {
2735             subPosFront = cache->sliceSurfaceObject()->vertexAt(column, 0);
2736             subPosBack = cache->sliceSurfaceObject()->vertexAt(column, 1);
2737         } else if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
2738             subPosFront = cache->sliceSurfaceObject()->vertexAt(row, 0);
2739             subPosBack = cache->sliceSurfaceObject()->vertexAt(row, 1);
2740         }
2741         slicePointer->updateBoundingRect(m_secondarySubViewport);
2742         slicePointer->updateSliceData(true, m_autoScaleAdjustment);
2743         slicePointer->setPosition((subPosFront + subPosBack) / 2.0f);
2744         slicePointer->setLabel(selectionLabel);
2745         slicePointer->setPointerObject(cache->object());
2746         slicePointer->setLabelObject(m_labelObj);
2747         slicePointer->setHighlightColor(cache->singleHighlightColor());
2748         slicePointer->updateScene(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(m_primarySubViewport);
2756     mainPointer->updateSliceData(false, m_autoScaleAdjustment);
2757     mainPointer->setPosition(mainPos);
2758     mainPointer->setLabel(selectionLabel);
2759     mainPointer->setPointerObject(cache->object());
2760     mainPointer->setLabelObject(m_labelObj);
2761     mainPointer->setHighlightColor(cache->singleHighlightColor());
2762     mainPointer->updateScene(m_cachedScene);
2763     mainPointer->setRotation(cache->meshRotation());
2764     cache->setMainPointerActivity(true);
2765 }
2766 
2767 // Maps selection Id to surface point in data array
selectionIdToSurfacePoint(uint id)2768 QPoint 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(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 
updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)2817 void 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 
updateTextures()2857 void Surface3DRenderer::updateTextures()
2858 {
2859     Abstract3DRenderer::updateTextures();
2860 
2861     if (m_polarGraph)
2862         calculateSceneScalingFactors();
2863 }
2864 
updateSlicingActive(bool isSlicing)2865 void 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(m_primarySubViewport);
2886     }
2887 }
2888 
initShaders(const QString & vertexShader,const QString & fragmentShader)2889 void 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 
initBackgroundShaders(const QString & vertexShader,const QString & fragmentShader)2959 void 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 
initSelectionShaders()2968 void 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 
initSurfaceShaders()2977 void 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 
initDepthShader()2990 void 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 
updateDepthBuffer()3000 void Surface3DRenderer::updateDepthBuffer()
3001 {
3002     if (!m_isOpenGLES) {
3003         m_textureHelper->deleteTexture(&m_depthTexture);
3004 
3005         if (m_primarySubViewport.size().isEmpty())
3006             return;
3007 
3008         if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
3009             m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
3010                                                                             m_depthFrameBuffer,
3011                                                                             m_shadowQualityMultiplier);
3012             if (!m_depthTexture)
3013                 lowerShadowQuality();
3014         }
3015     }
3016 }
3017 
convertPositionToTranslation(const QVector3D & position,bool isAbsolute)3018 QVector3D 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(position, xTrans, zTrans);
3027         } else {
3028             xTrans = m_axisCacheX.positionAt(position.x());
3029             zTrans = m_axisCacheZ.positionAt(position.z());
3030         }
3031         yTrans = m_axisCacheY.positionAt(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 
updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,const QStringList & labels)3040 void 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 
updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,bool visible)3050 void 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 
updateMargin(float margin)3059 void Surface3DRenderer::updateMargin(float margin)
3060 {
3061     Abstract3DRenderer::updateMargin(margin);
3062     calculateSceneScalingFactors();
3063 }
3064 
3065 QT_END_NAMESPACE_DATAVISUALIZATION
3066