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

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