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

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