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

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