1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Data Visualization module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29
30 #include "bars3drenderer_p.h"
31 #include "q3dcamera_p.h"
32 #include "shaderhelper_p.h"
33 #include "texturehelper_p.h"
34 #include "utils_p.h"
35 #include "barseriesrendercache_p.h"
36
37 #include <QtCore/qmath.h>
38
39 // You can verify that depth buffer drawing works correctly by uncommenting this.
40 // You should see the scene from where the light is
41 //#define SHOW_DEPTH_TEXTURE_SCENE
42
43 QT_BEGIN_NAMESPACE_DATAVISUALIZATION
44
45 const bool sliceGridLabels = true;
46
Bars3DRenderer(Bars3DController * controller)47 Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
48 : Abstract3DRenderer(controller),
49 m_cachedIsSlicingActivated(false),
50 m_cachedRowCount(0),
51 m_cachedColumnCount(0),
52 m_selectedBar(0),
53 m_sliceCache(0),
54 m_sliceTitleItem(0),
55 m_updateLabels(false),
56 m_barShader(0),
57 m_barGradientShader(0),
58 m_depthShader(0),
59 m_selectionShader(0),
60 m_backgroundShader(0),
61 m_bgrTexture(0),
62 m_selectionTexture(0),
63 m_depthFrameBuffer(0),
64 m_selectionFrameBuffer(0),
65 m_selectionDepthBuffer(0),
66 m_shadowQualityToShader(100.0f),
67 m_shadowQualityMultiplier(3),
68 m_heightNormalizer(1.0f),
69 m_backgroundAdjustment(0.0f),
70 m_rowWidth(0),
71 m_columnDepth(0),
72 m_maxDimension(0),
73 m_scaleX(0),
74 m_scaleZ(0),
75 m_scaleFactor(0),
76 m_maxSceneSize(40.0f),
77 m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()),
78 m_selectedBarPos(Bars3DController::invalidSelectionPosition()),
79 m_selectedSeriesCache(0),
80 m_noZeroInRange(false),
81 m_seriesScaleX(0.0f),
82 m_seriesScaleZ(0.0f),
83 m_seriesStep(0.0f),
84 m_seriesStart(0.0f),
85 m_clickedPosition(Bars3DController::invalidSelectionPosition()),
86 m_keepSeriesUniform(false),
87 m_haveUniformColorSeries(false),
88 m_haveGradientSeries(false),
89 m_zeroPosition(0.0f),
90 m_xScaleFactor(1.0f),
91 m_zScaleFactor(1.0f),
92 m_floorLevel(0.0f),
93 m_actualFloorLevel(0.0f)
94 {
95 m_axisCacheY.setScale(2.0f);
96 m_axisCacheY.setTranslate(-1.0f);
97
98 initializeOpenGL();
99 }
100
~Bars3DRenderer()101 Bars3DRenderer::~Bars3DRenderer()
102 {
103 contextCleanup();
104 delete m_barShader;
105 delete m_barGradientShader;
106 delete m_depthShader;
107 delete m_selectionShader;
108 delete m_backgroundShader;
109 }
110
contextCleanup()111 void Bars3DRenderer::contextCleanup()
112 {
113 if (QOpenGLContext::currentContext()) {
114 m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
115 m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
116 m_textureHelper->deleteTexture(&m_selectionTexture);
117 m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer);
118 m_textureHelper->deleteTexture(&m_bgrTexture);
119 }
120 }
121
initializeOpenGL()122 void Bars3DRenderer::initializeOpenGL()
123 {
124 Abstract3DRenderer::initializeOpenGL();
125
126 // Initialize shaders
127
128 // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
129 initDepthShader();
130
131 // Init selection shader
132 initSelectionShader();
133
134 // Load grid line mesh
135 loadGridLineMesh();
136
137 // Load background mesh (we need to be initialized first)
138 loadBackgroundMesh();
139 }
140
fixCameraTarget(QVector3D & target)141 void Bars3DRenderer::fixCameraTarget(QVector3D &target)
142 {
143 target.setX(target.x() * m_xScaleFactor);
144 target.setY(0.0f);
145 target.setZ(target.z() * -m_zScaleFactor);
146 }
147
getVisibleItemBounds(QVector3D & minBounds,QVector3D & maxBounds)148 void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
149 {
150 // The inputs are the item bounds in OpenGL coordinates.
151 // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
152 // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
153 float itemRangeX = (maxBounds.x() - minBounds.x());
154 float itemRangeY = (maxBounds.y() - minBounds.y());
155 float itemRangeZ = (maxBounds.z() - minBounds.z());
156
157 if (minBounds.x() < -m_xScaleFactor)
158 minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX));
159 else
160 minBounds.setX(-1.0f);
161
162 if (minBounds.y() < -1.0f + m_backgroundAdjustment)
163 minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY)));
164 else
165 minBounds.setY(1.0f);
166
167 if (minBounds.z() < -m_zScaleFactor)
168 minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ)));
169 else
170 minBounds.setZ(1.0f);
171
172 if (maxBounds.x() > m_xScaleFactor)
173 maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX));
174 else
175 maxBounds.setX(1.0f);
176
177 if (maxBounds.y() > 1.0f + m_backgroundAdjustment)
178 maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY)));
179 else
180 maxBounds.setY(-1.0f);
181
182 if (maxBounds.z() > m_zScaleFactor)
183 maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ)));
184 else
185 maxBounds.setZ(-1.0f);
186 }
187
updateData()188 void Bars3DRenderer::updateData()
189 {
190 int minRow = m_axisCacheZ.min();
191 int maxRow = m_axisCacheZ.max();
192 int minCol = m_axisCacheX.min();
193 int maxCol = m_axisCacheX.max();
194 int newRows = maxRow - minRow + 1;
195 int newColumns = maxCol - minCol + 1;
196 int dataRowCount = 0;
197 int maxDataRowCount = 0;
198
199 m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
200 m_seriesStep = 1.0f / float(m_visibleSeriesCount);
201 m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) / 2.0f) * m_seriesStep;
202
203 if (m_keepSeriesUniform)
204 m_seriesScaleZ = m_seriesScaleX;
205 else
206 m_seriesScaleZ = 1.0f;
207
208 if (m_cachedRowCount != newRows || m_cachedColumnCount != newColumns) {
209 // Force update for selection related items
210 m_sliceCache = 0;
211 m_sliceTitleItem = 0;
212
213 m_cachedColumnCount = newColumns;
214 m_cachedRowCount = newRows;
215 // Calculate max scene size
216 GLfloat sceneRatio = qMin(GLfloat(newColumns) / GLfloat(newRows),
217 GLfloat(newRows) / GLfloat(newColumns));
218 m_maxSceneSize = 2.0f * qSqrt(sceneRatio * newColumns * newRows);
219 }
220
221 calculateSceneScalingFactors();
222
223 m_zeroPosition = m_axisCacheY.formatter()->positionAt(m_actualFloorLevel);
224
225 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
226 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
227 if (cache->isVisible()) {
228 const QBar3DSeries *currentSeries = cache->series();
229 BarRenderItemArray &renderArray = cache->renderArray();
230 bool dimensionsChanged = false;
231 if (newRows != renderArray.size()
232 || newColumns != renderArray.at(0).size()) {
233 // Destroy old render items and reallocate new array
234 dimensionsChanged = true;
235 renderArray.resize(newRows);
236 for (int i = 0; i < newRows; i++)
237 renderArray[i].resize(newColumns);
238 cache->sliceArray().clear();
239 }
240
241 if (cache->dataDirty() || dimensionsChanged) {
242 QBarDataProxy *dataProxy = currentSeries->dataProxy();
243 dataRowCount = dataProxy->rowCount();
244 if (maxDataRowCount < dataRowCount)
245 maxDataRowCount = qMin(dataRowCount, newRows);
246 int dataRowIndex = minRow;
247 for (int i = 0; i < newRows; i++) {
248 BarRenderItemRow &renderRow = renderArray[i];
249 const QBarDataRow *dataRow = 0;
250 if (dataRowIndex < dataRowCount)
251 dataRow = dataProxy->rowAt(dataRowIndex);
252 updateRenderRow(dataRow, renderRow);
253 dataRowIndex++;
254 }
255 cache->setDataDirty(false);
256 }
257 }
258 }
259
260 // Reset selected bar to update selection
261 updateSelectedBar(m_selectedBarPos,
262 m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0);
263 }
264
updateRenderRow(const QBarDataRow * dataRow,BarRenderItemRow & renderRow)265 void Bars3DRenderer::updateRenderRow(const QBarDataRow *dataRow, BarRenderItemRow &renderRow)
266 {
267 int j = 0;
268 int renderRowSize = renderRow.size();
269 int startIndex = m_axisCacheX.min();
270
271 if (dataRow) {
272 int updateSize = qMin((dataRow->size() - startIndex), renderRowSize);
273 int dataColIndex = startIndex;
274 for (; j < updateSize ; j++) {
275 updateRenderItem(dataRow->at(dataColIndex), renderRow[j]);
276 dataColIndex++;
277 }
278 }
279 for (; j < renderRowSize; j++) {
280 renderRow[j].setValue(0.0f);
281 renderRow[j].setHeight(0.0f);
282 renderRow[j].setRotation(identityQuaternion);
283 }
284 }
285
updateRenderItem(const QBarDataItem & dataItem,BarRenderItem & renderItem)286 void Bars3DRenderer::updateRenderItem(const QBarDataItem &dataItem, BarRenderItem &renderItem)
287 {
288 float value = dataItem.value();
289 float heightValue = m_axisCacheY.formatter()->positionAt(value);
290 if (m_noZeroInRange) {
291 if (m_hasNegativeValues) {
292 heightValue = -1.0f + heightValue;
293 if (heightValue > 0.0f)
294 heightValue = 0.0f;
295 } else {
296 if (heightValue < 0.0f)
297 heightValue = 0.0f;
298 }
299 } else {
300 heightValue -= m_zeroPosition;
301 }
302 if (m_axisCacheY.reversed())
303 heightValue = -heightValue;
304
305 renderItem.setValue(value);
306 renderItem.setHeight(heightValue);
307
308 float angle = dataItem.rotation();
309 if (angle) {
310 renderItem.setRotation(
311 QQuaternion::fromAxisAndAngle(
312 upVector, angle));
313 } else {
314 renderItem.setRotation(identityQuaternion);
315 }
316 }
317
updateSeries(const QList<QAbstract3DSeries * > & seriesList)318 void Bars3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
319 {
320 Abstract3DRenderer::updateSeries(seriesList);
321
322 bool noSelection = true;
323 int seriesCount = seriesList.size();
324 int visualIndex = 0;
325 m_haveUniformColorSeries = false;
326 m_haveGradientSeries = false;
327 for (int i = 0; i < seriesCount; i++) {
328 QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(seriesList[i]);
329 BarSeriesRenderCache *cache =
330 static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(barSeries));
331 if (barSeries->isVisible()) {
332 if (noSelection
333 && barSeries->selectedBar() != QBar3DSeries::invalidSelectionPosition()) {
334 if (selectionLabel() != cache->itemLabel())
335 m_selectionLabelDirty = true;
336 noSelection = false;
337 }
338 cache->setVisualIndex(visualIndex++);
339 if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
340 m_haveUniformColorSeries = true;
341 else
342 m_haveGradientSeries = true;
343 } else {
344 cache->setVisualIndex(-1);
345 }
346
347 }
348 if (noSelection) {
349 if (!selectionLabel().isEmpty())
350 m_selectionLabelDirty = true;
351 m_selectedSeriesCache = 0;
352 }
353 }
354
createNewCache(QAbstract3DSeries * series)355 SeriesRenderCache *Bars3DRenderer::createNewCache(QAbstract3DSeries *series)
356 {
357 return new BarSeriesRenderCache(series, this);
358 }
359
updateRows(const QVector<Bars3DController::ChangeRow> & rows)360 void Bars3DRenderer::updateRows(const QVector<Bars3DController::ChangeRow> &rows)
361 {
362 int minRow = m_axisCacheZ.min();
363 int maxRow = m_axisCacheZ.max();
364 BarSeriesRenderCache *cache = 0;
365 const QBar3DSeries *prevSeries = 0;
366 const QBarDataArray *dataArray = 0;
367
368 foreach (Bars3DController::ChangeRow item, rows) {
369 const int row = item.row;
370 if (row < minRow || row > maxRow)
371 continue;
372 QBar3DSeries *currentSeries = item.series;
373 if (currentSeries != prevSeries) {
374 cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries));
375 prevSeries = currentSeries;
376 dataArray = item.series->dataProxy()->array();
377 // Invisible series render caches are not updated, but instead just marked dirty, so that
378 // they can be completely recalculated when they are turned visible.
379 if (!cache->isVisible() && !cache->dataDirty())
380 cache->setDataDirty(true);
381 }
382 if (cache->isVisible()) {
383 updateRenderRow(dataArray->at(row), cache->renderArray()[row - minRow]);
384 if (m_cachedIsSlicingActivated
385 && cache == m_selectedSeriesCache
386 && m_selectedBarPos.x() == row) {
387 m_selectionDirty = true; // Need to update slice view
388 }
389 }
390 }
391 }
392
updateItems(const QVector<Bars3DController::ChangeItem> & items)393 void Bars3DRenderer::updateItems(const QVector<Bars3DController::ChangeItem> &items)
394 {
395 int minRow = m_axisCacheZ.min();
396 int maxRow = m_axisCacheZ.max();
397 int minCol = m_axisCacheX.min();
398 int maxCol = m_axisCacheX.max();
399 BarSeriesRenderCache *cache = 0;
400 const QBar3DSeries *prevSeries = 0;
401 const QBarDataArray *dataArray = 0;
402
403 foreach (Bars3DController::ChangeItem item, items) {
404 const int row = item.point.x();
405 const int col = item.point.y();
406 if (row < minRow || row > maxRow || col < minCol || col > maxCol)
407 continue;
408 QBar3DSeries *currentSeries = item.series;
409 if (currentSeries != prevSeries) {
410 cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries));
411 prevSeries = currentSeries;
412 dataArray = item.series->dataProxy()->array();
413 // Invisible series render caches are not updated, but instead just marked dirty, so that
414 // they can be completely recalculated when they are turned visible.
415 if (!cache->isVisible() && !cache->dataDirty())
416 cache->setDataDirty(true);
417 }
418 if (cache->isVisible()) {
419 updateRenderItem(dataArray->at(row)->at(col),
420 cache->renderArray()[row - minRow][col - minCol]);
421 if (m_cachedIsSlicingActivated
422 && cache == m_selectedSeriesCache
423 && m_selectedBarPos == QPoint(row, col)) {
424 m_selectionDirty = true; // Need to update slice view
425 }
426 }
427 }
428 }
429
updateScene(Q3DScene * scene)430 void Bars3DRenderer::updateScene(Q3DScene *scene)
431 {
432 if (!m_noZeroInRange) {
433 scene->activeCamera()->d_ptr->setMinYRotation(-90.0);
434 scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
435 } else {
436 if ((m_hasNegativeValues && !m_axisCacheY.reversed())
437 || (!m_hasNegativeValues && m_axisCacheY.reversed())) {
438 scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
439 scene->activeCamera()->d_ptr->setMaxYRotation(0.0);
440 } else {
441 scene->activeCamera()->d_ptr->setMinYRotation(0.0f);
442 scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
443 }
444 }
445
446 Abstract3DRenderer::updateScene(scene);
447
448 updateSlicingActive(scene->isSlicingActive());
449 }
450
render(GLuint defaultFboHandle)451 void Bars3DRenderer::render(GLuint defaultFboHandle)
452 {
453 // Handle GL state setup for FBO buffers and clearing of the render surface
454 Abstract3DRenderer::render(defaultFboHandle);
455
456 if (m_axisCacheY.positionsDirty())
457 m_axisCacheY.updateAllPositions();
458
459 drawScene(defaultFboHandle);
460 if (m_cachedIsSlicingActivated)
461 drawSlicedScene();
462 }
463
drawSlicedScene()464 void Bars3DRenderer::drawSlicedScene()
465 {
466 if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)
467 == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
468 qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
469 " QAbstract3DGraph::SelectionColumn must be set before calling"
470 " setSlicingActive(true).");
471 return;
472 }
473
474 GLfloat barPosX = 0;
475 QVector3D lightPos;
476 QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
477
478 // Specify viewport
479 glViewport(m_secondarySubViewport.x(),
480 m_secondarySubViewport.y(),
481 m_secondarySubViewport.width(),
482 m_secondarySubViewport.height());
483
484 // Set up projection matrix
485 QMatrix4x4 projectionMatrix;
486 GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
487 / (GLfloat)m_primarySubViewport.height();
488 if (m_useOrthoProjection) {
489 GLfloat orthoRatio = 2.0f / m_autoScaleAdjustment;
490 projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
491 -orthoRatio, orthoRatio,
492 0.0f, 100.0f);
493 } else {
494 projectionMatrix.perspective(35.0f, viewPortRatio, 0.1f, 100.0f);
495 }
496
497 // Set view matrix
498 QMatrix4x4 viewMatrix;
499
500 // Adjust scaling (zoom rate based on aspect ratio)
501 GLfloat camZPosSliced = cameraDistance / m_autoScaleAdjustment;
502
503 viewMatrix.lookAt(QVector3D(0.0f, 0.0f, camZPosSliced), zeroVector, upVector);
504
505 // Set light position
506 lightPos = QVector3D(0.0f, 0.0f, camZPosSliced * 2.0f);
507
508 const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
509
510 // Draw the selected row / column
511 QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
512 bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
513 bool itemMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem);
514
515 GLfloat barPosYAdjustment = -0.8f; // Translate to -1.0 + 0.2 for row/column labels
516 GLfloat gridAdjustment = 1.0f + barPosYAdjustment - m_backgroundAdjustment;
517 GLfloat scaleFactor = 0.0f;
518 if (rowMode)
519 scaleFactor = (1.1f * m_rowWidth) / m_scaleFactor;
520 else
521 scaleFactor = (1.1f * m_columnDepth) / m_scaleFactor;
522 GLfloat barLabelYPos = barPosYAdjustment - labelMargin;
523 GLfloat zeroPosAdjustment = 0.0f;
524 GLfloat directionMultiplier = 2.0f;
525 GLfloat directionBase = 0.0f;
526 if (m_axisCacheY.reversed()) {
527 directionMultiplier = -2.0f;
528 directionBase = -2.0f;
529 }
530 zeroPosAdjustment = directionBase +
531 directionMultiplier * m_axisCacheY.min() / m_heightNormalizer;
532 zeroPosAdjustment = qBound(-2.0f, zeroPosAdjustment, 0.0f);
533
534 // Draw grid lines
535 if (m_cachedTheme->isGridEnabled()) {
536 glDisable(GL_DEPTH_TEST);
537 ShaderHelper *lineShader;
538 if (m_isOpenGLES)
539 lineShader = m_selectionShader; // Plain color shader for GL_LINES
540 else
541 lineShader = m_backgroundShader;
542
543 // Bind line shader
544 lineShader->bind();
545
546 // Set unchanging shader bindings
547 QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
548 lineShader->setUniformValue(lineShader->lightP(), lightPos);
549 lineShader->setUniformValue(lineShader->view(), viewMatrix);
550 lineShader->setUniformValue(lineShader->color(), lineColor);
551 lineShader->setUniformValue(lineShader->ambientS(),
552 m_cachedTheme->ambientLightStrength()
553 + m_cachedTheme->lightStrength() / 7.0f);
554 lineShader->setUniformValue(lineShader->lightS(), 0.0f);
555 lineShader->setUniformValue(lineShader->lightColor(), lightColor);
556
557 // Horizontal lines
558 if (m_axisCacheY.segmentCount() > 0) {
559 int gridLineCount = m_axisCacheY.gridLineCount();
560
561 QVector3D gridLineScale(scaleFactor, gridLineWidth, gridLineWidth);
562 bool noZero = true;
563 QMatrix4x4 MVPMatrix;
564 QMatrix4x4 itModelMatrix;
565
566 for (int line = 0; line < gridLineCount; line++) {
567 QMatrix4x4 modelMatrix;
568 GLfloat gridPos = m_axisCacheY.gridLinePosition(line) + gridAdjustment;
569 modelMatrix.translate(0.0f, gridPos, 0.0f);
570 modelMatrix.scale(gridLineScale);
571 itModelMatrix = modelMatrix;
572 MVPMatrix = projectionViewMatrix * modelMatrix;
573
574 // Set the rest of the shader bindings
575 lineShader->setUniformValue(lineShader->model(), modelMatrix);
576 lineShader->setUniformValue(lineShader->nModel(),
577 itModelMatrix.inverted().transposed());
578 lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
579
580 // Draw the object
581 if (m_isOpenGLES)
582 m_drawer->drawLine(lineShader);
583 else
584 m_drawer->drawObject(lineShader, m_gridLineObj);
585
586 // Check if we have a line at zero position already
587 if (gridPos == (barPosYAdjustment + zeroPosAdjustment))
588 noZero = false;
589 }
590
591 // Draw a line at zero, if none exists
592 if (!m_noZeroInRange && noZero) {
593 QMatrix4x4 modelMatrix;
594 modelMatrix.translate(0.0f, barPosYAdjustment - zeroPosAdjustment, 0.0f);
595 modelMatrix.scale(gridLineScale);
596 itModelMatrix = modelMatrix;
597 MVPMatrix = projectionViewMatrix * modelMatrix;
598
599 // Set the rest of the shader bindings
600 lineShader->setUniformValue(lineShader->model(), modelMatrix);
601 lineShader->setUniformValue(lineShader->nModel(),
602 itModelMatrix.inverted().transposed());
603 lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
604 lineShader->setUniformValue(lineShader->color(),
605 Utils::vectorFromColor(
606 m_cachedTheme->labelTextColor()));
607
608 // Draw the object
609 if (m_isOpenGLES)
610 m_drawer->drawLine(lineShader);
611 else
612 m_drawer->drawObject(lineShader, m_gridLineObj);
613 }
614 }
615
616 if (sliceGridLabels) {
617 // Bind label shader
618 m_labelShader->bind();
619 glCullFace(GL_BACK);
620 glEnable(GL_BLEND);
621 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
622
623 // Draw grid labels
624 int labelNbr = 0;
625 int labelCount = m_axisCacheY.labelCount();
626 QVector3D labelTrans = QVector3D(scaleFactor + labelMargin, 0.0f, 0.0f);
627
628 for (int i = 0; i < labelCount; i++) {
629 if (m_axisCacheY.labelItems().size() > labelNbr) {
630 const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
631 GLfloat gridPos = m_axisCacheY.labelPosition(i) + gridAdjustment;
632 labelTrans.setY(gridPos);
633 m_dummyBarRenderItem.setTranslation(labelTrans);
634 m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix,
635 projectionMatrix, zeroVector, identityQuaternion, 0,
636 m_cachedSelectionMode, m_labelShader, m_labelObj,
637 activeCamera, true, true, Drawer::LabelMid, Qt::AlignLeft);
638 }
639 labelNbr++;
640 }
641 glDisable(GL_BLEND);
642 glEnable(GL_DEPTH_TEST);
643 }
644 }
645
646 // Draw bars
647 QVector3D modelMatrixScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ);
648 if (!rowMode) {
649 modelMatrixScaler.setX(m_scaleZ * m_seriesScaleZ);
650 modelMatrixScaler.setZ(m_scaleX * m_seriesScaleX);
651 }
652
653 // Set common bar shader bindings
654 m_barShader->bind();
655 m_barShader->setUniformValue(m_barShader->lightP(), lightPos);
656 m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
657 m_barShader->setUniformValue(m_barShader->lightS(), 0.15f);
658 m_barShader->setUniformValue(m_barShader->ambientS(),
659 m_cachedTheme->ambientLightStrength()
660 + m_cachedTheme->lightStrength() / 7.0f);
661 m_barShader->setUniformValue(m_barShader->lightColor(), lightColor);
662 m_barGradientShader->bind();
663 m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos);
664 m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix);
665 m_barGradientShader->setUniformValue(m_barGradientShader->lightS(), 0.15f);
666 m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(),
667 m_cachedTheme->ambientLightStrength()
668 + m_cachedTheme->lightStrength() / 7.0f);
669 m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f);
670 m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor);
671
672 // Default to uniform shader
673 ShaderHelper *barShader = m_barShader;
674 barShader->bind();
675
676 Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
677 Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform;
678 ObjectHelper *barObj = 0;
679 QVector4D highlightColor;
680 QVector4D baseColor;
681 GLuint highlightGradientTexture = 0;
682 GLuint baseGradientTexture = 0;
683 bool colorStyleIsUniform = true;
684 int firstVisualIndex = m_renderCacheList.size();
685 QVector<BarRenderSliceItem> *firstVisualSliceArray = 0;
686 BarRenderSliceItem *selectedItem = 0;
687
688 QQuaternion seriesRotation;
689 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
690 if (baseCache->isVisible()
691 && (baseCache == m_selectedSeriesCache
692 || m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries))) {
693 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
694 QVector<BarRenderSliceItem> &sliceArray = cache->sliceArray();
695 int sliceCount = sliceArray.size();
696 if (firstVisualIndex > cache->visualIndex()) {
697 firstVisualIndex = cache->visualIndex();
698 firstVisualSliceArray = &sliceArray;
699 }
700
701 barObj = cache->object();
702 colorStyle = cache->colorStyle();
703 colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
704 if (colorStyleIsUniform) {
705 highlightColor = cache->singleHighlightColor();
706 baseColor = cache->baseColor();
707 } else {
708 highlightGradientTexture = cache->singleHighlightGradientTexture();
709 baseGradientTexture = cache->baseGradientTexture();
710 }
711
712 // Rebind shader if it has changed
713 if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
714 if (colorStyleIsUniform)
715 barShader = m_barShader;
716 else
717 barShader = m_barGradientShader;
718 barShader->bind();
719 }
720
721 if (!colorStyleIsUniform && (previousColorStyle != colorStyle)
722 && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
723 m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f);
724 }
725
726 previousColorStyle = colorStyle;
727 seriesRotation = cache->meshRotation();
728 bool selectedSeries = (cache == m_selectedSeriesCache);
729
730 for (int bar = 0; bar < sliceCount; bar++) {
731 BarRenderSliceItem &item = cache->sliceArray()[bar];
732 if (selectedSeries && itemMode && sliceGridLabels
733 && m_visualSelectedBarPos.x() == item.position().x()
734 && m_visualSelectedBarPos.y() == item.position().y()) {
735 selectedItem = &item;
736 }
737 if (!item.value())
738 continue;
739
740 if (item.height() < 0)
741 glCullFace(GL_FRONT);
742 else
743 glCullFace(GL_BACK);
744
745 QMatrix4x4 MVPMatrix;
746 QMatrix4x4 modelMatrix;
747 QMatrix4x4 itModelMatrix;
748 QQuaternion barRotation = item.rotation();
749 GLfloat barPosY = item.translation().y() + barPosYAdjustment - zeroPosAdjustment;
750
751 if (rowMode) {
752 barPosX = item.translation().x();
753 } else {
754 barPosX = -(item.translation().z()); // flip z; frontmost bar to the left
755 barRotation *= m_yRightAngleRotation;
756 }
757
758 modelMatrix.translate(barPosX, barPosY, 0.0f);
759 modelMatrixScaler.setY(item.height());
760
761 if (!seriesRotation.isIdentity())
762 barRotation *= seriesRotation;
763
764 if (!barRotation.isIdentity()) {
765 modelMatrix.rotate(barRotation);
766 itModelMatrix.rotate(barRotation);
767 }
768
769 modelMatrix.scale(modelMatrixScaler);
770 itModelMatrix.scale(modelMatrixScaler);
771
772 MVPMatrix = projectionViewMatrix * modelMatrix;
773
774 QVector4D barColor;
775 GLuint gradientTexture = 0;
776
777 if (itemMode && m_visualSelectedBarPos.x() == item.position().x()
778 && m_visualSelectedBarPos.y() == item.position().y()) {
779 if (colorStyleIsUniform)
780 barColor = highlightColor;
781 else
782 gradientTexture = highlightGradientTexture;
783 } else {
784 if (colorStyleIsUniform)
785 barColor = baseColor;
786 else
787 gradientTexture = baseGradientTexture;
788 }
789
790 if (item.height() != 0) {
791 // Set shader bindings
792 barShader->setUniformValue(barShader->model(), modelMatrix);
793 barShader->setUniformValue(barShader->nModel(),
794 itModelMatrix.inverted().transposed());
795 barShader->setUniformValue(barShader->MVP(), MVPMatrix);
796 if (colorStyleIsUniform) {
797 barShader->setUniformValue(barShader->color(), barColor);
798 } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
799 barShader->setUniformValue(barShader->gradientHeight(),
800 (qAbs(item.height()) / m_gradientFraction));
801 }
802
803 // Draw the object
804 m_drawer->drawObject(barShader,
805 barObj,
806 gradientTexture);
807 }
808 }
809 }
810 }
811
812 // Draw labels
813 m_labelShader->bind();
814 glDisable(GL_DEPTH_TEST);
815 glCullFace(GL_BACK);
816 glEnable(GL_BLEND);
817 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
818
819 BarRenderItem *dummyItem(0);
820 const LabelItem &sliceSelectionLabel = *m_sliceTitleItem;
821 QVector3D positionComp(0.0f, m_autoScaleAdjustment, 0.0f);
822
823 // Draw labels for bars
824 QVector3D sliceValueRotation(0.0f, 0.0f, 90.0f);
825 QVector3D sliceLabelRotation(0.0f, 0.0f, -45.0f);
826 QQuaternion totalSliceValueRotation = Utils::calculateRotation(sliceValueRotation);
827 QQuaternion totalSliceLabelRotation = Utils::calculateRotation(sliceLabelRotation);
828
829 int labelCount = m_sliceCache->labelItems().size();
830
831 for (int labelNo = 0; labelNo < labelCount; labelNo++) {
832 // Check for invalid usage (no selection when setting slicing active)
833 if (!firstVisualSliceArray) {
834 qWarning("No slice data found. Make sure there is a valid selection.");
835 continue;
836 }
837
838 // Get labels from first series only
839 const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo);
840 m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
841 barLabelYPos,
842 item.translation().z()));
843
844 // Draw labels
845 m_drawer->drawLabel(m_dummyBarRenderItem, *m_sliceCache->labelItems().at(labelNo),
846 viewMatrix, projectionMatrix, positionComp, totalSliceLabelRotation,
847 0, m_cachedSelectionMode, m_labelShader,
848 m_labelObj, activeCamera, false, false, Drawer::LabelMid,
849 Qt::AlignLeft | Qt::AlignTop, true);
850 }
851
852 if (!sliceGridLabels) {
853 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
854 if (baseCache->isVisible()) {
855 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
856 QVector<BarRenderSliceItem> &sliceArray = cache->sliceArray();
857 int sliceCount = sliceArray.size();
858 for (int col = 0; col < sliceCount; col++) {
859 BarRenderSliceItem &item = sliceArray[col];
860
861 // Draw values
862 if (item.height() != 0.0f || (!m_noZeroInRange && item.value() == 0.0f)) {
863 // Create label texture if we need it
864 if (item.sliceLabel().isNull() || m_updateLabels) {
865 QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
866 qreal(item.value()), m_axisCacheY.labelFormat());
867 item.setSliceLabel(valueLabelText);
868 m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel());
869 m_updateLabels = false;
870 }
871 Qt::AlignmentFlag alignment =
872 (item.height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
873 Drawer::LabelPosition labelPos =
874 (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
875 m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
876 barPosYAdjustment
877 - zeroPosAdjustment
878 + item.height(),
879 item.translation().z()));
880
881 m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix,
882 projectionMatrix, zeroVector, totalSliceValueRotation,
883 item.height(), m_cachedSelectionMode, m_labelShader,
884 m_labelObj, activeCamera, false, false, labelPos,
885 alignment, true);
886 }
887 }
888 }
889 }
890 } else if (selectedItem) {
891 // Only draw value for selected item when grid labels are on
892 // Create label texture if we need it
893 if (selectedItem->sliceLabel().isNull() || m_updateLabels) {
894 QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
895 qreal(selectedItem->value()), m_axisCacheY.labelFormat());
896 selectedItem->setSliceLabel(valueLabelText);
897 m_drawer->generateLabelItem(selectedItem->sliceLabelItem(), selectedItem->sliceLabel());
898 m_updateLabels = false;
899 }
900 Qt::AlignmentFlag alignment = (selectedItem->height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
901 Drawer::LabelPosition labelPos =
902 (selectedItem->height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
903 m_dummyBarRenderItem.setTranslation(QVector3D(selectedItem->translation().x(),
904 barPosYAdjustment - zeroPosAdjustment
905 + selectedItem->height(),
906 selectedItem->translation().z()));
907
908 m_drawer->drawLabel(m_dummyBarRenderItem, selectedItem->sliceLabelItem(), viewMatrix,
909 projectionMatrix, zeroVector, totalSliceValueRotation,
910 selectedItem->height(), m_cachedSelectionMode, m_labelShader,
911 m_labelObj, activeCamera, false, false, labelPos,
912 alignment, true);
913 }
914
915 // Draw labels for axes
916 if (rowMode) {
917 if (m_sliceTitleItem) {
918 m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix,
919 positionComp, identityQuaternion, 0, m_cachedSelectionMode,
920 m_labelShader, m_labelObj, activeCamera, false, false,
921 Drawer::LabelTop, Qt::AlignCenter, true);
922 }
923 m_drawer->drawLabel(*dummyItem, m_axisCacheX.titleItem(), viewMatrix, projectionMatrix,
924 positionComp, identityQuaternion, 0, m_cachedSelectionMode,
925 m_labelShader, m_labelObj, activeCamera, false, false,
926 Drawer::LabelBottom, Qt::AlignCenter, true);
927 } else {
928 m_drawer->drawLabel(*dummyItem, m_axisCacheZ.titleItem(), viewMatrix, projectionMatrix,
929 positionComp, identityQuaternion, 0, m_cachedSelectionMode,
930 m_labelShader,
931 m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
932 Qt::AlignCenter, true);
933 if (m_sliceTitleItem) {
934 m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix,
935 positionComp, identityQuaternion, 0, m_cachedSelectionMode,
936 m_labelShader,
937 m_labelObj, activeCamera, false, false, Drawer::LabelTop,
938 Qt::AlignCenter, true);
939 }
940 }
941 // Y-axis label
942 QVector3D labelTrans = QVector3D(-scaleFactor - labelMargin, 0.2f, 0.0f); // y = 0.2 for row/column labels (see barPosYAdjustment)
943 m_dummyBarRenderItem.setTranslation(labelTrans);
944 m_drawer->drawLabel(m_dummyBarRenderItem, m_axisCacheY.titleItem(), viewMatrix,
945 projectionMatrix, zeroVector, totalSliceValueRotation, 0,
946 m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
947 false, false, Drawer::LabelMid, Qt::AlignBottom);
948
949 glDisable(GL_BLEND);
950 glEnable(GL_DEPTH_TEST);
951
952 // Release shader
953 glUseProgram(0);
954 }
955
drawScene(GLuint defaultFboHandle)956 void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
957 {
958 GLint startBar = 0;
959 GLint stopBar = 0;
960 GLint stepBar = 0;
961
962 GLint startRow = 0;
963 GLint stopRow = 0;
964 GLint stepRow = 0;
965
966 GLfloat backgroundRotation = 0;
967
968 GLfloat colPos = 0;
969 GLfloat rowPos = 0;
970
971 const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
972
973 glViewport(m_primarySubViewport.x(),
974 m_primarySubViewport.y(),
975 m_primarySubViewport.width(),
976 m_primarySubViewport.height());
977
978 // Set up projection matrix
979 QMatrix4x4 projectionMatrix;
980 GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
981 / (GLfloat)m_primarySubViewport.height();
982 if (m_useOrthoProjection) {
983 GLfloat orthoRatio = 2.0f;
984 projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
985 -orthoRatio, orthoRatio,
986 0.0f, 100.0f);
987 } else {
988 projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
989 }
990
991 // Get the view matrix
992 QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
993
994 // Calculate drawing order
995 // Draw order is reversed to optimize amount of drawing (ie. draw front objects first,
996 // depth test handles not needing to draw objects behind them)
997 if (viewMatrix.row(0).x() > 0) {
998 startRow = 0;
999 stopRow = m_cachedRowCount;
1000 stepRow = 1;
1001 m_zFlipped = false;
1002 } else {
1003 startRow = m_cachedRowCount - 1;
1004 stopRow = -1;
1005 stepRow = -1;
1006 m_zFlipped = true;
1007 }
1008 if (viewMatrix.row(0).z() <= 0) {
1009 startBar = 0;
1010 stopBar = m_cachedColumnCount;
1011 stepBar = 1;
1012 m_xFlipped = false;
1013 } else {
1014 startBar = m_cachedColumnCount - 1;
1015 stopBar = -1;
1016 stepBar = -1;
1017 m_xFlipped = true;
1018 }
1019
1020 // Check if we're viewing the scene from below
1021 if (viewMatrix.row(2).y() < 0)
1022 m_yFlipped = true;
1023 else
1024 m_yFlipped = false;
1025
1026 // calculate background rotation based on view matrix rotation
1027 if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0)
1028 backgroundRotation = 270.0f;
1029 else if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() > 0)
1030 backgroundRotation = 180.0f;
1031 else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() > 0)
1032 backgroundRotation = 90.0f;
1033 else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() <= 0)
1034 backgroundRotation = 0.0f;
1035
1036 // Get light position from the scene
1037 QVector3D lightPos = m_cachedScene->activeLight()->position();
1038
1039 // Skip depth rendering if we're in slice mode
1040 // Introduce regardless of shadow quality to simplify logic
1041 QMatrix4x4 depthViewMatrix;
1042 QMatrix4x4 depthProjectionMatrix;
1043 QMatrix4x4 depthProjectionViewMatrix;
1044
1045 QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
1046
1047 BarRenderItem *selectedBar(0);
1048
1049 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1050 // Render scene into a depth texture for using with shadow mapping
1051 // Enable drawing to depth framebuffer
1052 glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
1053 glClear(GL_DEPTH_BUFFER_BIT);
1054
1055 // Bind depth shader
1056 m_depthShader->bind();
1057
1058 // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
1059 // Depth viewport must always start from 0, 0, as it is rendered into a texture, not screen
1060 glViewport(0, 0,
1061 m_primarySubViewport.width() * m_shadowQualityMultiplier,
1062 m_primarySubViewport.height() * m_shadowQualityMultiplier);
1063
1064 // Get the depth view matrix
1065 // It may be possible to hack lightPos here if we want to make some tweaks to shadow
1066 QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
1067 zeroVector, 0.0f, 3.5f / m_autoScaleAdjustment);
1068 depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
1069
1070 // Set the depth projection matrix
1071 depthProjectionMatrix.perspective(10.0f, viewPortRatio, 3.0f, 100.0f);
1072 depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
1073
1074 // Draw bars to depth buffer
1075 QVector3D shadowScaler(m_scaleX * m_seriesScaleX * 0.9f, 0.0f,
1076 m_scaleZ * m_seriesScaleZ * 0.9f);
1077 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1078 if (baseCache->isVisible()) {
1079 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
1080 float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f;
1081 ObjectHelper *barObj = cache->object();
1082 QQuaternion seriesRotation(cache->meshRotation());
1083 const BarRenderItemArray &renderArray = cache->renderArray();
1084 for (int row = startRow; row != stopRow; row += stepRow) {
1085 const BarRenderItemRow &renderRow = renderArray.at(row);
1086 for (int bar = startBar; bar != stopBar; bar += stepBar) {
1087 const BarRenderItem &item = renderRow.at(bar);
1088 if (!item.value())
1089 continue;
1090 GLfloat shadowOffset = 0.0f;
1091 // Set front face culling for negative valued bars and back face culling
1092 // for positive valued bars to remove peter-panning issues
1093 if (item.height() > 0) {
1094 glCullFace(GL_BACK);
1095 if (m_yFlipped)
1096 shadowOffset = 0.015f;
1097 } else {
1098 glCullFace(GL_FRONT);
1099 if (!m_yFlipped)
1100 shadowOffset = -0.015f;
1101 }
1102
1103 if (m_cachedTheme->isBackgroundEnabled() && m_reflectionEnabled
1104 && ((m_yFlipped && item.height() > 0.0)
1105 || (!m_yFlipped && item.height() < 0.0))) {
1106 continue;
1107 }
1108
1109 QMatrix4x4 modelMatrix;
1110 QMatrix4x4 MVPMatrix;
1111
1112 colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
1113 rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1114
1115 // Draw shadows for bars "on the other side" a bit off ground to avoid
1116 // seeing shadows through the ground
1117 modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
1118 item.height() + shadowOffset,
1119 (m_columnDepth - rowPos) / m_scaleFactor);
1120 // Scale the bars down in X and Z to reduce self-shadowing issues
1121 shadowScaler.setY(item.height());
1122 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
1123 modelMatrix.rotate(seriesRotation * item.rotation());
1124 modelMatrix.scale(shadowScaler);
1125
1126 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1127
1128 m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
1129
1130 // 1st attribute buffer : vertices
1131 glEnableVertexAttribArray(m_depthShader->posAtt());
1132 glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf());
1133 glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
1134 (void *)0);
1135
1136 // Index buffer
1137 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf());
1138
1139 // Draw the triangles
1140 glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_INT,
1141 (void *)0);
1142
1143 // Free buffers
1144 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1145 glBindBuffer(GL_ARRAY_BUFFER, 0);
1146
1147 glDisableVertexAttribArray(m_depthShader->posAtt());
1148 }
1149 }
1150 }
1151 }
1152
1153 Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
1154 projectionViewMatrix,
1155 depthProjectionViewMatrix, m_depthTexture,
1156 m_shadowQualityToShader);
1157
1158 // Disable drawing to depth framebuffer (= enable drawing to screen)
1159 glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
1160
1161 // Reset culling to normal
1162 glCullFace(GL_BACK);
1163
1164 // Revert to original viewport
1165 glViewport(m_primarySubViewport.x(),
1166 m_primarySubViewport.y(),
1167 m_primarySubViewport.width(),
1168 m_primarySubViewport.height());
1169 }
1170
1171 // Do position mapping when necessary
1172 if (m_graphPositionQueryPending) {
1173 QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor);
1174 queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
1175
1176 // Y is always at floor level
1177 m_queriedGraphPosition.setY(0.0f);
1178 emit needRender();
1179 }
1180
1181 // Skip selection mode drawing if we're slicing or have no selection mode
1182 if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
1183 && m_selectionState == SelectOnScene
1184 && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
1185 && m_selectionTexture) {
1186 // Bind selection shader
1187 m_selectionShader->bind();
1188
1189 // Draw bars to selection buffer
1190 glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
1191 glViewport(0, 0,
1192 m_primarySubViewport.width(),
1193 m_primarySubViewport.height());
1194
1195 glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used
1196 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Set clear color to white (= selectionSkipColor)
1197 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer
1198 glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled
1199 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1200 if (baseCache->isVisible()) {
1201 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
1202 float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f;
1203 ObjectHelper *barObj = cache->object();
1204 QQuaternion seriesRotation(cache->meshRotation());
1205 const BarRenderItemArray &renderArray = cache->renderArray();
1206 for (int row = startRow; row != stopRow; row += stepRow) {
1207 const BarRenderItemRow &renderRow = renderArray.at(row);
1208 for (int bar = startBar; bar != stopBar; bar += stepBar) {
1209 const BarRenderItem &item = renderRow.at(bar);
1210 if (!item.value())
1211 continue;
1212
1213 if (item.height() < 0)
1214 glCullFace(GL_FRONT);
1215 else
1216 glCullFace(GL_BACK);
1217
1218 QMatrix4x4 modelMatrix;
1219 QMatrix4x4 MVPMatrix;
1220
1221 colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
1222 rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1223
1224 modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
1225 item.height(),
1226 (m_columnDepth - rowPos) / m_scaleFactor);
1227 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
1228 modelMatrix.rotate(seriesRotation * item.rotation());
1229 modelMatrix.scale(QVector3D(m_scaleX * m_seriesScaleX,
1230 item.height(),
1231 m_scaleZ * m_seriesScaleZ));
1232
1233 MVPMatrix = projectionViewMatrix * modelMatrix;
1234
1235 QVector4D barColor = QVector4D(GLfloat(row) / 255.0f,
1236 GLfloat(bar) / 255.0f,
1237 GLfloat(cache->visualIndex()) / 255.0f,
1238 itemAlpha);
1239
1240 m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
1241 m_selectionShader->setUniformValue(m_selectionShader->color(), barColor);
1242
1243 m_drawer->drawSelectionObject(m_selectionShader, barObj);
1244 }
1245 }
1246 }
1247 }
1248 glCullFace(GL_BACK);
1249 Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
1250 viewMatrix,
1251 projectionViewMatrix, depthProjectionViewMatrix,
1252 m_depthTexture, m_shadowQualityToShader);
1253 drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
1254 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1255 viewMatrix, false, true);
1256 glEnable(GL_DITHER);
1257
1258 // Read color under cursor
1259 QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height());
1260 m_clickedPosition = selectionColorToArrayPosition(clickedColor);
1261 m_clickedSeries = selectionColorToSeries(clickedColor);
1262 m_clickResolved = true;
1263
1264 emit needRender();
1265
1266 // Revert to original render target and viewport
1267 glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
1268 glViewport(m_primarySubViewport.x(),
1269 m_primarySubViewport.y(),
1270 m_primarySubViewport.width(),
1271 m_primarySubViewport.height());
1272 }
1273
1274 if (m_reflectionEnabled) {
1275 //
1276 // Draw reflections
1277 //
1278 glDisable(GL_DEPTH_TEST);
1279 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1280 glEnable(GL_STENCIL_TEST);
1281 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
1282 glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
1283
1284 // Draw background stencil
1285 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1286 viewMatrix);
1287
1288 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1289 glEnable(GL_DEPTH_TEST);
1290
1291 glStencilFunc(GL_EQUAL, 1, 0xffffffff);
1292 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1293
1294 // Set light
1295 QVector3D reflectionLightPos = lightPos;
1296 reflectionLightPos.setY(-(lightPos.y()));
1297 m_cachedScene->activeLight()->setPosition(reflectionLightPos);
1298
1299 // Draw bar reflections
1300 (void)drawBars(&selectedBar, depthProjectionViewMatrix,
1301 projectionViewMatrix, viewMatrix,
1302 startRow, stopRow, stepRow,
1303 startBar, stopBar, stepBar, -1.0f);
1304
1305 Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader,
1306 viewMatrix, projectionViewMatrix,
1307 depthProjectionViewMatrix, m_depthTexture,
1308 m_shadowQualityToShader, -1.0f);
1309
1310 // Reset light
1311 m_cachedScene->activeLight()->setPosition(lightPos);
1312
1313 glDisable(GL_STENCIL_TEST);
1314
1315 glCullFace(GL_BACK);
1316 }
1317
1318 //
1319 // Draw the real scene
1320 //
1321 // Draw background
1322 if (m_reflectionEnabled) {
1323 glEnable(GL_BLEND);
1324 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1325 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1326 viewMatrix, true);
1327 glDisable(GL_BLEND);
1328 } else {
1329 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1330 viewMatrix);
1331 }
1332
1333 // Draw bars
1334 bool barSelectionFound = drawBars(&selectedBar, depthProjectionViewMatrix,
1335 projectionViewMatrix, viewMatrix,
1336 startRow, stopRow, stepRow,
1337 startBar, stopBar, stepBar);
1338
1339 // Draw grid lines
1340 drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix);
1341
1342 // Draw custom items
1343 Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
1344 projectionViewMatrix, depthProjectionViewMatrix,
1345 m_depthTexture, m_shadowQualityToShader);
1346
1347 // Draw labels
1348 drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
1349
1350 // Handle selected bar label generation
1351 if (barSelectionFound) {
1352 // Print value of selected bar
1353 glDisable(GL_DEPTH_TEST);
1354 // Draw the selection label
1355 LabelItem &labelItem = selectionLabelItem();
1356 if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId()
1357 || m_selectionLabelDirty) {
1358 QString labelText = selectionLabel();
1359 if (labelText.isNull() || m_selectionLabelDirty) {
1360 labelText = m_selectedSeriesCache->itemLabel();
1361 setSelectionLabel(labelText);
1362 m_selectionLabelDirty = false;
1363 }
1364 m_drawer->generateLabelItem(labelItem, labelText);
1365 m_selectedBar = selectedBar;
1366 }
1367
1368 Drawer::LabelPosition position =
1369 m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow;
1370
1371 m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix,
1372 zeroVector, identityQuaternion, selectedBar->height(),
1373 m_cachedSelectionMode, m_labelShader,
1374 m_labelObj, activeCamera, true, false, position);
1375
1376 // Reset label update flag; they should have been updated when we get here
1377 m_updateLabels = false;
1378
1379 glEnable(GL_DEPTH_TEST);
1380 } else {
1381 m_selectedBar = 0;
1382 }
1383
1384 glDisable(GL_BLEND);
1385
1386 // Release shader
1387 glUseProgram(0);
1388 m_selectionDirty = false;
1389 }
1390
drawBars(BarRenderItem ** selectedBar,const QMatrix4x4 & depthProjectionViewMatrix,const QMatrix4x4 & projectionViewMatrix,const QMatrix4x4 & viewMatrix,GLint startRow,GLint stopRow,GLint stepRow,GLint startBar,GLint stopBar,GLint stepBar,GLfloat reflection)1391 bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar,
1392 const QMatrix4x4 &depthProjectionViewMatrix,
1393 const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
1394 GLint startRow, GLint stopRow, GLint stepRow,
1395 GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection)
1396 {
1397 QVector3D lightPos = m_cachedScene->activeLight()->position();
1398 QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
1399
1400 bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
1401
1402 ShaderHelper *barShader = 0;
1403 GLuint gradientTexture = 0;
1404 Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
1405
1406 // Set unchanging shader bindings
1407 if (m_haveGradientSeries) {
1408 m_barGradientShader->bind();
1409 m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos);
1410 m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix);
1411 m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(),
1412 m_cachedTheme->ambientLightStrength());
1413 m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f);
1414 m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor);
1415 }
1416
1417 if (m_haveUniformColorSeries) {
1418 m_barShader->bind();
1419 m_barShader->setUniformValue(m_barShader->lightP(), lightPos);
1420 m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
1421 m_barShader->setUniformValue(m_barShader->ambientS(),
1422 m_cachedTheme->ambientLightStrength());
1423 m_barShader->setUniformValue(m_barShader->lightColor(), lightColor);
1424 barShader = m_barShader;
1425 } else {
1426 barShader = m_barGradientShader;
1427 previousColorStyle = Q3DTheme::ColorStyleRangeGradient;
1428 }
1429
1430 int sliceReserveAmount = 0;
1431 if (m_selectionDirty && m_cachedIsSlicingActivated) {
1432 // Slice doesn't own its items, no need to delete them - just clear
1433 if (rowMode)
1434 sliceReserveAmount = m_cachedColumnCount;
1435 else
1436 sliceReserveAmount = m_cachedRowCount;
1437
1438 // Set slice cache, i.e. axis cache from where slice labels are taken
1439 if (rowMode)
1440 m_sliceCache = &m_axisCacheX;
1441 else
1442 m_sliceCache = &m_axisCacheZ;
1443 m_sliceTitleItem = 0;
1444 }
1445
1446 glEnable(GL_POLYGON_OFFSET_FILL);
1447 glPolygonOffset(0.5f, 1.0f);
1448
1449 GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
1450 GLfloat adjustedHighlightStrength = m_cachedTheme->highlightLightStrength() / 10.0f;
1451
1452 bool barSelectionFound = false;
1453
1454 QVector4D baseColor;
1455 QVector4D barColor;
1456 QVector3D modelScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ);
1457 bool somethingSelected =
1458 (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition());
1459 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1460 if (baseCache->isVisible()) {
1461 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
1462 float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f;
1463 ObjectHelper *barObj = cache->object();
1464 QQuaternion seriesRotation(cache->meshRotation());
1465 Q3DTheme::ColorStyle colorStyle = cache->colorStyle();
1466 BarRenderItemArray &renderArray = cache->renderArray();
1467 bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
1468 if (sliceReserveAmount)
1469 cache->sliceArray().resize(sliceReserveAmount);
1470
1471 // Rebind shader if it has changed
1472 if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
1473 if (colorStyleIsUniform)
1474 barShader = m_barShader;
1475 else
1476 barShader = m_barGradientShader;
1477 barShader->bind();
1478 }
1479
1480 if (colorStyleIsUniform) {
1481 baseColor = cache->baseColor();
1482 } else if ((previousColorStyle != colorStyle)
1483 && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
1484 m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f);
1485 }
1486
1487 // Always use base color when no selection mode
1488 if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone) {
1489 if (colorStyleIsUniform)
1490 barColor = baseColor;
1491 else
1492 gradientTexture = cache->baseGradientTexture();
1493 }
1494
1495 previousColorStyle = colorStyle;
1496 for (int row = startRow; row != stopRow; row += stepRow) {
1497 BarRenderItemRow &renderRow = renderArray[row];
1498 for (int bar = startBar; bar != stopBar; bar += stepBar) {
1499 BarRenderItem &item = renderRow[bar];
1500 float adjustedHeight = reflection * item.height();
1501 if (adjustedHeight < 0)
1502 glCullFace(GL_FRONT);
1503 else
1504 glCullFace(GL_BACK);
1505
1506 QMatrix4x4 modelMatrix;
1507 QMatrix4x4 itModelMatrix;
1508 QMatrix4x4 MVPMatrix;
1509
1510 GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
1511 GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1512
1513 modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
1514 adjustedHeight,
1515 (m_columnDepth - rowPos) / m_scaleFactor);
1516 modelScaler.setY(adjustedHeight);
1517 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
1518 QQuaternion totalRotation = seriesRotation * item.rotation();
1519 modelMatrix.rotate(totalRotation);
1520 itModelMatrix.rotate(totalRotation);
1521 }
1522 modelMatrix.scale(modelScaler);
1523 itModelMatrix.scale(modelScaler);
1524 #ifdef SHOW_DEPTH_TEXTURE_SCENE
1525 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1526 #else
1527 MVPMatrix = projectionViewMatrix * modelMatrix;
1528 #endif
1529 GLfloat lightStrength = m_cachedTheme->lightStrength();
1530 GLfloat shadowLightStrength = adjustedLightStrength;
1531
1532 if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
1533 Bars3DController::SelectionType selectionType =
1534 Bars3DController::SelectionNone;
1535 if (somethingSelected)
1536 selectionType = isSelected(row, bar, cache);
1537
1538 switch (selectionType) {
1539 case Bars3DController::SelectionItem: {
1540 if (colorStyleIsUniform)
1541 barColor = cache->singleHighlightColor();
1542 else
1543 gradientTexture = cache->singleHighlightGradientTexture();
1544
1545 lightStrength = m_cachedTheme->highlightLightStrength();
1546 shadowLightStrength = adjustedHighlightStrength;
1547 // Insert position data into render item
1548 // We have no ownership, don't delete the previous one
1549 if (!m_cachedIsSlicingActivated
1550 && m_selectedSeriesCache == cache) {
1551 *selectedBar = &item;
1552 (*selectedBar)->setPosition(QPoint(row, bar));
1553 item.setTranslation(modelMatrix.column(3).toVector3D());
1554 barSelectionFound = true;
1555 }
1556 if (m_selectionDirty && m_cachedIsSlicingActivated) {
1557 QVector3D translation = modelMatrix.column(3).toVector3D();
1558 if (m_cachedSelectionMode & QAbstract3DGraph::SelectionColumn
1559 && m_visibleSeriesCount > 1) {
1560 translation.setZ((m_columnDepth
1561 - ((row + seriesPos)
1562 * (m_cachedBarSpacing.height())))
1563 / m_scaleFactor);
1564 }
1565 item.setTranslation(translation);
1566 item.setPosition(QPoint(row, bar));
1567 if (rowMode)
1568 cache->sliceArray()[bar].setItem(item);
1569 else
1570 cache->sliceArray()[row].setItem(item);
1571 }
1572 break;
1573 }
1574 case Bars3DController::SelectionRow: {
1575 // Current bar is on the same row as the selected bar
1576 if (colorStyleIsUniform)
1577 barColor = cache->multiHighlightColor();
1578 else
1579 gradientTexture = cache->multiHighlightGradientTexture();
1580
1581 lightStrength = m_cachedTheme->highlightLightStrength();
1582 shadowLightStrength = adjustedHighlightStrength;
1583 if (m_cachedIsSlicingActivated) {
1584 item.setTranslation(modelMatrix.column(3).toVector3D());
1585 item.setPosition(QPoint(row, bar));
1586 if (m_selectionDirty) {
1587 if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row)
1588 m_sliceTitleItem = m_axisCacheZ.labelItems().at(row);
1589 cache->sliceArray()[bar].setItem(item);
1590 }
1591 }
1592 break;
1593 }
1594 case Bars3DController::SelectionColumn: {
1595 // Current bar is on the same column as the selected bar
1596 if (colorStyleIsUniform)
1597 barColor = cache->multiHighlightColor();
1598 else
1599 gradientTexture = cache->multiHighlightGradientTexture();
1600
1601 lightStrength = m_cachedTheme->highlightLightStrength();
1602 shadowLightStrength = adjustedHighlightStrength;
1603 if (m_cachedIsSlicingActivated) {
1604 QVector3D translation = modelMatrix.column(3).toVector3D();
1605 if (m_visibleSeriesCount > 1) {
1606 translation.setZ((m_columnDepth
1607 - ((row + seriesPos)
1608 * (m_cachedBarSpacing.height())))
1609 / m_scaleFactor);
1610 }
1611 item.setTranslation(translation);
1612 item.setPosition(QPoint(row, bar));
1613 if (m_selectionDirty) {
1614 if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar)
1615 m_sliceTitleItem = m_axisCacheX.labelItems().at(bar);
1616 cache->sliceArray()[row].setItem(item);
1617 }
1618 }
1619 break;
1620 }
1621 case Bars3DController::SelectionNone: {
1622 // Current bar is not selected, nor on a row or column
1623 if (colorStyleIsUniform)
1624 barColor = baseColor;
1625 else
1626 gradientTexture = cache->baseGradientTexture();
1627 break;
1628 }
1629 }
1630 }
1631
1632 if (item.height() == 0) {
1633 continue;
1634 } else if ((m_reflectionEnabled
1635 && (reflection == 1.0f
1636 || (reflection != 1.0f
1637 && ((m_yFlipped && item.height() < 0.0)
1638 || (!m_yFlipped && item.height() > 0.0)))))
1639 || !m_reflectionEnabled) {
1640 // Skip drawing of 0-height bars and reflections of bars on the "wrong side"
1641 // Set shader bindings
1642 barShader->setUniformValue(barShader->model(), modelMatrix);
1643 barShader->setUniformValue(barShader->nModel(),
1644 itModelMatrix.transposed().inverted());
1645 barShader->setUniformValue(barShader->MVP(), MVPMatrix);
1646 if (colorStyleIsUniform) {
1647 barShader->setUniformValue(barShader->color(), barColor);
1648 } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
1649 barShader->setUniformValue(barShader->gradientHeight(),
1650 qAbs(item.height()) / m_gradientFraction);
1651 }
1652
1653 if (((m_reflectionEnabled && reflection == 1.0f
1654 && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
1655 || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
1656 && !m_isOpenGLES) {
1657 // Set shadow shader bindings
1658 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1659 barShader->setUniformValue(barShader->shadowQ(),
1660 m_shadowQualityToShader);
1661 barShader->setUniformValue(barShader->depth(), depthMVPMatrix);
1662 barShader->setUniformValue(barShader->lightS(), shadowLightStrength);
1663 barShader->setUniformValue(barShader->lightColor(), lightColor);
1664
1665 // Draw the object
1666 m_drawer->drawObject(barShader, barObj, gradientTexture,
1667 m_depthTexture);
1668 } else {
1669 // Set shadowless shader bindings
1670 if (m_reflectionEnabled && reflection != 1.0f
1671 && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1672 barShader->setUniformValue(barShader->lightS(),
1673 adjustedLightStrength);
1674 } else {
1675 barShader->setUniformValue(barShader->lightS(), lightStrength);
1676 }
1677
1678 // Draw the object
1679 m_drawer->drawObject(barShader, barObj, gradientTexture);
1680 }
1681 }
1682 }
1683 }
1684 }
1685 }
1686 glDisable(GL_POLYGON_OFFSET_FILL);
1687
1688 // Reset culling
1689 glCullFace(GL_BACK);
1690
1691 return barSelectionFound;
1692 }
1693
drawBackground(GLfloat backgroundRotation,const QMatrix4x4 & depthProjectionViewMatrix,const QMatrix4x4 & projectionViewMatrix,const QMatrix4x4 & viewMatrix,bool reflectingDraw,bool drawingSelectionBuffer)1694 void Bars3DRenderer::drawBackground(GLfloat backgroundRotation,
1695 const QMatrix4x4 &depthProjectionViewMatrix,
1696 const QMatrix4x4 &projectionViewMatrix,
1697 const QMatrix4x4 &viewMatrix, bool reflectingDraw,
1698 bool drawingSelectionBuffer)
1699 {
1700 // Draw background
1701 if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
1702 QVector3D lightPos = m_cachedScene->activeLight()->position();
1703 QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
1704 GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
1705 ShaderHelper *shader = 0;
1706
1707 // Bind background shader
1708 if (drawingSelectionBuffer)
1709 shader = m_selectionShader; // Use single color shader when drawing to selection buffer
1710 else
1711 shader = m_backgroundShader;
1712 shader->bind();
1713
1714 QMatrix4x4 modelMatrix;
1715 QMatrix4x4 MVPMatrix;
1716 QMatrix4x4 itModelMatrix;
1717
1718 QVector3D backgroundScaler(m_scaleXWithBackground, m_scaleYWithBackground,
1719 m_scaleZWithBackground);
1720 QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
1721 if (m_reflectionEnabled)
1722 backgroundColor.setW(backgroundColor.w() * m_reflectivity);
1723
1724 // Set shader bindings
1725 shader->setUniformValue(shader->lightP(), lightPos);
1726 shader->setUniformValue(shader->view(), viewMatrix);
1727 if (drawingSelectionBuffer) {
1728 // Use selectionSkipColor for background when drawing to selection buffer
1729 shader->setUniformValue(shader->color(), selectionSkipColor);
1730 } else {
1731 shader->setUniformValue(shader->color(), backgroundColor);
1732 }
1733 shader->setUniformValue(shader->ambientS(),
1734 m_cachedTheme->ambientLightStrength() * 2.0f);
1735 shader->setUniformValue(shader->lightColor(), lightColor);
1736
1737 // Draw floor
1738 modelMatrix.scale(backgroundScaler);
1739
1740 if (m_yFlipped)
1741 modelMatrix.rotate(m_xRightAngleRotation);
1742 else
1743 modelMatrix.rotate(m_xRightAngleRotationNeg);
1744
1745 itModelMatrix = modelMatrix;
1746
1747 #ifdef SHOW_DEPTH_TEXTURE_SCENE
1748 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1749 #else
1750 MVPMatrix = projectionViewMatrix * modelMatrix;
1751 #endif
1752 // Set changed shader bindings
1753 shader->setUniformValue(shader->model(), modelMatrix);
1754 shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
1755 shader->setUniformValue(shader->MVP(), MVPMatrix);
1756
1757 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1758 // Set shadow shader bindings
1759 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1760 shader->setUniformValue(shader->depth(), depthMVPMatrix);
1761 // Draw the object
1762 m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
1763 } else {
1764 // Draw the object
1765 m_drawer->drawObject(shader, m_gridLineObj);
1766 }
1767
1768 // Draw walls
1769 modelMatrix = QMatrix4x4();
1770 itModelMatrix = QMatrix4x4();
1771 modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f);
1772
1773 modelMatrix.scale(backgroundScaler);
1774 itModelMatrix.scale(backgroundScaler);
1775 modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
1776 itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
1777
1778 #ifdef SHOW_DEPTH_TEXTURE_SCENE
1779 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1780 #else
1781 MVPMatrix = projectionViewMatrix * modelMatrix;
1782 #endif
1783
1784 // Set changed shader bindings
1785 shader->setUniformValue(shader->model(), modelMatrix);
1786 shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
1787 shader->setUniformValue(shader->MVP(), MVPMatrix);
1788 if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) {
1789 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1790 // Set shadow shader bindings
1791 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1792 shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader);
1793 shader->setUniformValue(shader->depth(), depthMVPMatrix);
1794 shader->setUniformValue(shader->lightS(), adjustedLightStrength);
1795
1796 // Draw the object
1797 m_drawer->drawObject(shader, m_backgroundObj, 0, m_depthTexture);
1798 } else {
1799 // Set shadowless shader bindings
1800 shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
1801
1802 // Draw the object
1803 m_drawer->drawObject(shader, m_backgroundObj);
1804 }
1805 }
1806 }
1807 }
1808
drawGridLines(const QMatrix4x4 & depthProjectionViewMatrix,const QMatrix4x4 & projectionViewMatrix,const QMatrix4x4 & viewMatrix)1809 void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix,
1810 const QMatrix4x4 &projectionViewMatrix,
1811 const QMatrix4x4 &viewMatrix)
1812 {
1813 if (m_cachedTheme->isGridEnabled()) {
1814 ShaderHelper *lineShader;
1815 if (m_isOpenGLES)
1816 lineShader = m_selectionShader; // Plain color shader for GL_LINES
1817 else
1818 lineShader = m_backgroundShader;
1819
1820 QQuaternion lineRotation;
1821
1822 QVector3D lightPos = m_cachedScene->activeLight()->position();
1823 QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
1824
1825 // Bind bar shader
1826 lineShader->bind();
1827
1828 // Set unchanging shader bindings
1829 QVector4D barColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
1830 lineShader->setUniformValue(lineShader->lightP(), lightPos);
1831 lineShader->setUniformValue(lineShader->view(), viewMatrix);
1832 lineShader->setUniformValue(lineShader->color(), barColor);
1833 lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
1834 lineShader->setUniformValue(lineShader->lightColor(), lightColor);
1835 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1836 // Set shadowed shader bindings
1837 lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
1838 lineShader->setUniformValue(lineShader->lightS(),
1839 m_cachedTheme->lightStrength() / 20.0f);
1840 } else {
1841 // Set shadowless shader bindings
1842 lineShader->setUniformValue(lineShader->lightS(),
1843 m_cachedTheme->lightStrength() / 2.5f);
1844 }
1845
1846 GLfloat yFloorLinePosition = gridLineOffset;
1847 if (m_yFlipped)
1848 yFloorLinePosition = -yFloorLinePosition;
1849
1850 QVector3D gridLineScaler(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
1851
1852 if (m_yFlipped)
1853 lineRotation = m_xRightAngleRotation;
1854 else
1855 lineRotation = m_xRightAngleRotationNeg;
1856
1857 // Floor lines: rows
1858 for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) {
1859 QMatrix4x4 modelMatrix;
1860 QMatrix4x4 MVPMatrix;
1861 QMatrix4x4 itModelMatrix;
1862
1863 GLfloat rowPos = row * m_cachedBarSpacing.height();
1864 modelMatrix.translate(0.0f, yFloorLinePosition,
1865 (m_columnDepth - rowPos) / m_scaleFactor);
1866 modelMatrix.scale(gridLineScaler);
1867 itModelMatrix.scale(gridLineScaler);
1868 modelMatrix.rotate(lineRotation);
1869 itModelMatrix.rotate(lineRotation);
1870
1871 MVPMatrix = projectionViewMatrix * modelMatrix;
1872
1873 // Set the rest of the shader bindings
1874 lineShader->setUniformValue(lineShader->model(), modelMatrix);
1875 lineShader->setUniformValue(lineShader->nModel(),
1876 itModelMatrix.inverted().transposed());
1877 lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
1878
1879 if (m_isOpenGLES) {
1880 m_drawer->drawLine(lineShader);
1881 } else {
1882 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1883 // Set shadow shader bindings
1884 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1885 lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
1886 // Draw the object
1887 m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1888 } else {
1889 // Draw the object
1890 m_drawer->drawObject(lineShader, m_gridLineObj);
1891 }
1892 }
1893 }
1894
1895 // Floor lines: columns
1896 if (m_isOpenGLES)
1897 lineRotation = m_yRightAngleRotation;
1898 gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
1899 for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) {
1900 QMatrix4x4 modelMatrix;
1901 QMatrix4x4 MVPMatrix;
1902 QMatrix4x4 itModelMatrix;
1903
1904 GLfloat colPos = bar * m_cachedBarSpacing.width();
1905 modelMatrix.translate((m_rowWidth - colPos) / m_scaleFactor,
1906 yFloorLinePosition, 0.0f);
1907 modelMatrix.scale(gridLineScaler);
1908 itModelMatrix.scale(gridLineScaler);
1909 modelMatrix.rotate(lineRotation);
1910 itModelMatrix.rotate(lineRotation);
1911
1912 MVPMatrix = projectionViewMatrix * modelMatrix;
1913
1914 // Set the rest of the shader bindings
1915 lineShader->setUniformValue(lineShader->model(), modelMatrix);
1916 lineShader->setUniformValue(lineShader->nModel(),
1917 itModelMatrix.inverted().transposed());
1918 lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
1919
1920 if (m_isOpenGLES) {
1921 m_drawer->drawLine(lineShader);
1922 } else {
1923 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1924 // Set shadow shader bindings
1925 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1926 lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
1927 // Draw the object
1928 m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1929 } else {
1930 // Draw the object
1931 m_drawer->drawObject(lineShader, m_gridLineObj);
1932 }
1933 }
1934 }
1935
1936 if (m_axisCacheY.segmentCount() > 0) {
1937 // Wall lines: back wall
1938 int gridLineCount = m_axisCacheY.gridLineCount();
1939
1940 GLfloat zWallLinePosition = -m_scaleZWithBackground + gridLineOffset;
1941 if (m_zFlipped)
1942 zWallLinePosition = -zWallLinePosition;
1943
1944 gridLineScaler = QVector3D(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
1945 for (int line = 0; line < gridLineCount; line++) {
1946 QMatrix4x4 modelMatrix;
1947 QMatrix4x4 MVPMatrix;
1948 QMatrix4x4 itModelMatrix;
1949
1950 modelMatrix.translate(0.0f,
1951 m_axisCacheY.gridLinePosition(line),
1952 zWallLinePosition);
1953 modelMatrix.scale(gridLineScaler);
1954 itModelMatrix.scale(gridLineScaler);
1955 if (m_zFlipped) {
1956 modelMatrix.rotate(m_xFlipRotation);
1957 itModelMatrix.rotate(m_xFlipRotation);
1958 }
1959
1960 MVPMatrix = projectionViewMatrix * modelMatrix;
1961
1962 // Set the rest of the shader bindings
1963 lineShader->setUniformValue(lineShader->model(), modelMatrix);
1964 lineShader->setUniformValue(lineShader->nModel(),
1965 itModelMatrix.inverted().transposed());
1966 lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
1967
1968 if (m_isOpenGLES) {
1969 m_drawer->drawLine(lineShader);
1970 } else {
1971 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1972 // Set shadow shader bindings
1973 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1974 lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
1975 // Draw the object
1976 m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
1977 } else {
1978 // Draw the object
1979 m_drawer->drawObject(lineShader, m_gridLineObj);
1980 }
1981 }
1982 }
1983
1984 // Wall lines: side wall
1985 GLfloat xWallLinePosition = -m_scaleXWithBackground + gridLineOffset;
1986 if (m_xFlipped)
1987 xWallLinePosition = -xWallLinePosition;
1988
1989 if (m_xFlipped)
1990 lineRotation = m_yRightAngleRotationNeg;
1991 else
1992 lineRotation = m_yRightAngleRotation;
1993
1994 gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
1995 for (int line = 0; line < gridLineCount; line++) {
1996 QMatrix4x4 modelMatrix;
1997 QMatrix4x4 MVPMatrix;
1998 QMatrix4x4 itModelMatrix;
1999
2000 modelMatrix.translate(xWallLinePosition,
2001 m_axisCacheY.gridLinePosition(line),
2002 0.0f);
2003 modelMatrix.scale(gridLineScaler);
2004 itModelMatrix.scale(gridLineScaler);
2005 modelMatrix.rotate(lineRotation);
2006 itModelMatrix.rotate(lineRotation);
2007
2008 MVPMatrix = projectionViewMatrix * modelMatrix;
2009
2010 // Set the rest of the shader bindings
2011 lineShader->setUniformValue(lineShader->model(), modelMatrix);
2012 lineShader->setUniformValue(lineShader->nModel(),
2013 itModelMatrix.inverted().transposed());
2014 lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
2015
2016 if (m_isOpenGLES) {
2017 m_drawer->drawLine(lineShader);
2018 } else {
2019 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
2020 // Set shadow shader bindings
2021 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
2022 lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
2023 // Draw the object
2024 m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
2025 } else {
2026 // Draw the object
2027 m_drawer->drawObject(lineShader, m_gridLineObj);
2028 }
2029 }
2030 }
2031 }
2032 }
2033 }
2034
drawLabels(bool drawSelection,const Q3DCamera * activeCamera,const QMatrix4x4 & viewMatrix,const QMatrix4x4 & projectionMatrix)2035 void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
2036 const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix) {
2037 ShaderHelper *shader = 0;
2038 GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
2039 GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
2040 GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
2041 if (drawSelection) {
2042 shader = m_selectionShader;
2043 // m_selectionShader is already bound
2044 } else {
2045 shader = m_labelShader;
2046 shader->bind();
2047
2048 glEnable(GL_BLEND);
2049 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2050 }
2051
2052 glEnable(GL_POLYGON_OFFSET_FILL);
2053
2054 float labelAutoAngle = m_axisCacheY.labelAutoRotation();
2055 float labelAngleFraction = labelAutoAngle / 90.0f;
2056 float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2057 float fractionCamX = activeCamera->xRotation() * labelAngleFraction;
2058 float labelsMaxWidth = 0.0f;
2059
2060 int startIndex;
2061 int endIndex;
2062 int indexStep;
2063
2064 // Y Labels
2065 int labelCount = m_axisCacheY.labelCount();
2066 GLfloat labelMarginXTrans = labelMargin;
2067 GLfloat labelMarginZTrans = labelMargin;
2068 GLfloat labelXTrans = m_scaleXWithBackground;
2069 GLfloat labelZTrans = m_scaleZWithBackground;
2070 QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
2071 QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
2072 Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2073 Qt::AlignmentFlag sideAlignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2074
2075 if (!m_xFlipped) {
2076 labelXTrans = -labelXTrans;
2077 labelMarginXTrans = -labelMargin;
2078 }
2079 if (m_zFlipped) {
2080 labelZTrans = -labelZTrans;
2081 labelMarginZTrans = -labelMargin;
2082 }
2083
2084 if (labelAutoAngle == 0.0f) {
2085 if (!m_xFlipped)
2086 backLabelRotation.setY(90.0f);
2087 if (m_zFlipped)
2088 sideLabelRotation.setY(180.f);
2089 } else {
2090 // Orient side labels somewhat towards the camera
2091 if (m_xFlipped) {
2092 if (m_zFlipped)
2093 sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
2094 else
2095 sideLabelRotation.setY(-fractionCamX);
2096 backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
2097 } else {
2098 if (m_zFlipped)
2099 sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
2100 else
2101 sideLabelRotation.setY(-fractionCamX);
2102 backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
2103 }
2104 }
2105 sideLabelRotation.setX(-fractionCamY);
2106 backLabelRotation.setX(-fractionCamY);
2107
2108 QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation);
2109 QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation);
2110
2111 QVector3D backLabelTrans = QVector3D(labelXTrans, 0.0f,
2112 labelZTrans + labelMarginZTrans);
2113 QVector3D sideLabelTrans = QVector3D(-labelXTrans - labelMarginXTrans,
2114 0.0f, -labelZTrans);
2115
2116 if (m_yFlipped) {
2117 startIndex = labelCount - 1;
2118 endIndex = -1;
2119 indexStep = -1;
2120 } else {
2121 startIndex = 0;
2122 endIndex = labelCount;
2123 indexStep = 1;
2124 }
2125 float offsetValue = 0.0f;
2126 for (int i = startIndex; i != endIndex; i = i + indexStep) {
2127 backLabelTrans.setY(m_axisCacheY.labelPosition(i));
2128 sideLabelTrans.setY(backLabelTrans.y());
2129
2130 glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
2131
2132 const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i);
2133
2134 if (drawSelection) {
2135 QVector4D labelColor = QVector4D(0.0f, 0.0f, i / 255.0f,
2136 alphaForValueSelection);
2137 shader->setUniformValue(shader->color(), labelColor);
2138 }
2139
2140 // Back wall
2141 m_dummyBarRenderItem.setTranslation(backLabelTrans);
2142 m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2143 zeroVector, totalBackRotation, 0, m_cachedSelectionMode,
2144 shader, m_labelObj, activeCamera,
2145 true, true, Drawer::LabelMid, backAlignment, false, drawSelection);
2146
2147 // Side wall
2148 m_dummyBarRenderItem.setTranslation(sideLabelTrans);
2149 m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2150 zeroVector, totalSideRotation, 0, m_cachedSelectionMode,
2151 shader, m_labelObj, activeCamera,
2152 true, true, Drawer::LabelMid, sideAlignment, false, drawSelection);
2153
2154 labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
2155 }
2156
2157 if (!drawSelection && m_axisCacheY.isTitleVisible()) {
2158 sideLabelTrans.setY(m_backgroundAdjustment);
2159 backLabelTrans.setY(m_backgroundAdjustment);
2160 drawAxisTitleY(sideLabelRotation, backLabelRotation, sideLabelTrans, backLabelTrans,
2161 totalSideRotation, totalBackRotation, m_dummyBarRenderItem, activeCamera,
2162 labelsMaxWidth, viewMatrix, projectionMatrix, shader);
2163 }
2164
2165 // Z labels
2166 // Calculate the positions for row and column labels and store them
2167 labelsMaxWidth = 0.0f;
2168 labelAutoAngle = m_axisCacheZ.labelAutoRotation();
2169 labelAngleFraction = labelAutoAngle / 90.0f;
2170 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2171 fractionCamX = activeCamera->xRotation() * labelAngleFraction;
2172 GLfloat labelYAdjustment = 0.005f;
2173 GLfloat colPosValue = m_scaleXWithBackground + labelMargin;
2174 GLfloat rowPosValue = m_scaleZWithBackground + labelMargin;
2175 GLfloat rowPos = 0.0f;
2176 GLfloat colPos = 0.0f;
2177 Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2178 QVector3D labelRotation;
2179
2180 if (labelAutoAngle == 0.0f) {
2181 if (m_zFlipped)
2182 labelRotation.setY(180.0f);
2183 if (m_yFlipped) {
2184 if (m_zFlipped)
2185 labelRotation.setY(180.0f);
2186 else
2187 labelRotation.setY(0.0f);
2188 labelRotation.setX(90.0f);
2189 } else {
2190 labelRotation.setX(-90.0f);
2191 }
2192 } else {
2193 if (m_zFlipped)
2194 labelRotation.setY(180.0f);
2195 if (m_yFlipped) {
2196 if (m_zFlipped) {
2197 if (m_xFlipped) {
2198 labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
2199 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
2200 labelRotation.setZ(labelAutoAngle + fractionCamY);
2201 } else {
2202 labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
2203 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2204 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2205 }
2206 } else {
2207 if (m_xFlipped) {
2208 labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
2209 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
2210 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2211 } else {
2212 labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
2213 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2214 labelRotation.setZ(labelAutoAngle + fractionCamY);
2215 }
2216 }
2217 } else {
2218 if (m_zFlipped) {
2219 if (m_xFlipped) {
2220 labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
2221 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2222 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2223 } else {
2224 labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
2225 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2226 labelRotation.setZ(labelAutoAngle - fractionCamY);
2227 }
2228 } else {
2229 if (m_xFlipped) {
2230 labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
2231 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2232 labelRotation.setZ(labelAutoAngle - fractionCamY);
2233 } else {
2234 labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
2235 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2236 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2237 }
2238 }
2239 }
2240 }
2241
2242 QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
2243 labelCount = qMin(m_axisCacheZ.labelCount(), m_cachedRowCount);
2244 if (m_zFlipped) {
2245 startIndex = 0;
2246 endIndex = labelCount;
2247 indexStep = 1;
2248 } else {
2249 startIndex = labelCount - 1;
2250 endIndex = -1;
2251 indexStep = -1;
2252 }
2253 offsetValue = 0.0f;
2254 for (int row = startIndex; row != endIndex; row = row + indexStep) {
2255 // Go through all rows and get position of max+1 or min-1 column, depending on x flip
2256 // We need only positions for them, labels have already been generated
2257 rowPos = (row + 0.5f) * m_cachedBarSpacing.height();
2258 if (m_xFlipped)
2259 colPos = -colPosValue;
2260 else
2261 colPos = colPosValue;
2262
2263 glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
2264
2265 QVector3D labelPos = QVector3D(colPos,
2266 labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
2267 (m_columnDepth - rowPos) / m_scaleFactor);
2268
2269 m_dummyBarRenderItem.setTranslation(labelPos);
2270 const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(row);
2271
2272 if (drawSelection) {
2273 QVector4D labelColor = QVector4D(row / 255.0f, 0.0f, 0.0f, alphaForRowSelection);
2274 shader->setUniformValue(shader->color(), labelColor);
2275 }
2276
2277 m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2278 zeroVector, totalRotation, 0, m_cachedSelectionMode,
2279 shader, m_labelObj, activeCamera,
2280 true, true, Drawer::LabelMid, alignment,
2281 false, drawSelection);
2282 labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
2283 }
2284
2285 if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
2286 QVector3D titleTrans(colPos, 0.0f, 0.0f);
2287 drawAxisTitleZ(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem,
2288 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
2289 }
2290
2291 // X labels
2292 labelsMaxWidth = 0.0f;
2293 labelAutoAngle = m_axisCacheX.labelAutoRotation();
2294 labelAngleFraction = labelAutoAngle / 90.0f;
2295 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2296 fractionCamX = activeCamera->xRotation() * labelAngleFraction;
2297 alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2298 if (labelAutoAngle == 0.0f) {
2299 labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
2300 if (m_xFlipped)
2301 labelRotation.setY(-90.0f);
2302 if (m_yFlipped) {
2303 if (m_xFlipped)
2304 labelRotation.setY(-90.0f);
2305 else
2306 labelRotation.setY(90.0f);
2307 labelRotation.setX(90.0f);
2308 }
2309 } else {
2310 if (m_xFlipped)
2311 labelRotation.setY(-90.0f);
2312 else
2313 labelRotation.setY(90.0f);
2314 if (m_yFlipped) {
2315 if (m_zFlipped) {
2316 if (m_xFlipped) {
2317 labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
2318 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2319 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2320 } else {
2321 labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
2322 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2323 labelRotation.setZ(labelAutoAngle + fractionCamY);
2324 }
2325 } else {
2326 if (m_xFlipped) {
2327 labelRotation.setX(90.0f + fractionCamX
2328 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
2329 labelRotation.setZ(labelAutoAngle + fractionCamY);
2330 } else {
2331 labelRotation.setX(90.0f - fractionCamX
2332 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
2333 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2334 }
2335 }
2336 } else {
2337 if (m_zFlipped) {
2338 if (m_xFlipped) {
2339 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
2340 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2341 labelRotation.setZ(labelAutoAngle - fractionCamY);
2342 } else {
2343 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
2344 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2345 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2346 }
2347 } else {
2348 if (m_xFlipped) {
2349 labelRotation.setX(-90.0f - fractionCamX
2350 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2351 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2352 } else {
2353 labelRotation.setX(-90.0f + fractionCamX
2354 * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
2355 labelRotation.setZ(labelAutoAngle - fractionCamY);
2356 }
2357 }
2358 }
2359 }
2360
2361 totalRotation = Utils::calculateRotation(labelRotation);
2362 labelCount = qMin(m_axisCacheX.labelCount(), m_cachedColumnCount);
2363 if (m_xFlipped) {
2364 startIndex = labelCount - 1;
2365 endIndex = -1;
2366 indexStep = -1;
2367 } else {
2368 startIndex = 0;
2369 endIndex = labelCount;
2370 indexStep = 1;
2371 }
2372 offsetValue = 0.0f;
2373 for (int column = startIndex; column != endIndex; column = column + indexStep) {
2374 // Go through all columns and get position of max+1 or min-1 row, depending on z flip
2375 // We need only positions for them, labels have already been generated
2376 colPos = (column + 0.5f) * m_cachedBarSpacing.width();
2377 if (m_zFlipped)
2378 rowPos = -rowPosValue;
2379 else
2380 rowPos = rowPosValue;
2381
2382 glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
2383
2384 QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor,
2385 labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
2386 rowPos);
2387
2388 m_dummyBarRenderItem.setTranslation(labelPos);
2389 const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(column);
2390
2391 if (drawSelection) {
2392 QVector4D labelColor = QVector4D(0.0f, column / 255.0f, 0.0f,
2393 alphaForColumnSelection);
2394 shader->setUniformValue(shader->color(), labelColor);
2395 }
2396
2397 m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
2398 zeroVector, totalRotation, 0, m_cachedSelectionMode,
2399 shader, m_labelObj, activeCamera,
2400 true, true, Drawer::LabelMid, alignment, false, drawSelection);
2401 labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
2402 }
2403
2404 if (!drawSelection && m_axisCacheX.isTitleVisible()) {
2405 QVector3D titleTrans(0.0f, 0.0f, rowPos);
2406 drawAxisTitleX(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem,
2407 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
2408 }
2409
2410 #if 0 // Debug label
2411 static LabelItem debugLabelItem;
2412 QString debugLabelString(QStringLiteral("Flips: x:%1 y:%2 z:%3 xr:%4 yr:%5"));
2413 QString finalDebugString = debugLabelString.arg(m_xFlipped).arg(m_yFlipped).arg(m_zFlipped)
2414 .arg(activeCamera->xRotation()).arg(activeCamera->yRotation());
2415 m_dummyBarRenderItem.setTranslation(QVector3D(m_xFlipped ? -1.5f : 1.5f,
2416 m_yFlipped ? 1.5f : -1.5f,
2417 m_zFlipped ? -1.5f : 1.5f));
2418
2419 m_drawer->generateLabelItem(debugLabelItem, finalDebugString);
2420 m_drawer->drawLabel(m_dummyBarRenderItem, debugLabelItem, viewMatrix, projectionMatrix,
2421 zeroVector, identityQuaternion, 0, m_cachedSelectionMode,
2422 shader, m_labelObj, activeCamera,
2423 true, false, Drawer::LabelMid, Qt::AlignHCenter, false, drawSelection);
2424 #endif
2425 glDisable(GL_POLYGON_OFFSET_FILL);
2426 }
2427
updateMultiSeriesScaling(bool uniform)2428 void Bars3DRenderer::updateMultiSeriesScaling(bool uniform)
2429 {
2430 m_keepSeriesUniform = uniform;
2431
2432 // Recalculate scale factors
2433 m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
2434 if (m_keepSeriesUniform)
2435 m_seriesScaleZ = m_seriesScaleX;
2436 else
2437 m_seriesScaleZ = 1.0f;
2438 }
2439
updateBarSpecs(GLfloat thicknessRatio,const QSizeF & spacing,bool relative)2440 void Bars3DRenderer::updateBarSpecs(GLfloat thicknessRatio, const QSizeF &spacing, bool relative)
2441 {
2442 // Convert ratio to QSizeF, as we need it in that format for autoscaling calculations
2443 m_cachedBarThickness.setWidth(1.0f);
2444 m_cachedBarThickness.setHeight(1.0f / thicknessRatio);
2445
2446 if (relative) {
2447 m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2)
2448 * (spacing.width() + 1.0f));
2449 m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2)
2450 * (spacing.height() + 1.0f));
2451 } else {
2452 m_cachedBarSpacing = m_cachedBarThickness * 2 + spacing * 2;
2453 }
2454
2455 // Slice mode doesn't update correctly without this
2456 if (m_cachedIsSlicingActivated)
2457 m_selectionDirty = true;
2458
2459 // Calculate here and at setting sample space
2460 calculateSceneScalingFactors();
2461 }
2462
updateAxisRange(QAbstract3DAxis::AxisOrientation orientation,float min,float max)2463 void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min,
2464 float max)
2465 {
2466 Abstract3DRenderer::updateAxisRange(orientation, min, max);
2467
2468 if (orientation == QAbstract3DAxis::AxisOrientationY)
2469 calculateHeightAdjustment();
2470 }
2471
updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation,bool enable)2472 void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable)
2473 {
2474 Abstract3DRenderer::updateAxisReversed(orientation, enable);
2475 if (orientation == QAbstract3DAxis::AxisOrientationY)
2476 calculateHeightAdjustment();
2477 }
2478
2479
updateSelectedBar(const QPoint & position,QBar3DSeries * series)2480 void Bars3DRenderer::updateSelectedBar(const QPoint &position, QBar3DSeries *series)
2481 {
2482 m_selectedBarPos = position;
2483 m_selectedSeriesCache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(series, 0));
2484 m_selectionDirty = true;
2485 m_selectionLabelDirty = true;
2486
2487 if (!m_selectedSeriesCache
2488 || !m_selectedSeriesCache->isVisible()
2489 || m_selectedSeriesCache->renderArray().isEmpty()) {
2490 m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition();
2491 return;
2492 }
2493
2494 int adjustedZ = m_selectedBarPos.x() - int(m_axisCacheZ.min());
2495 int adjustedX = m_selectedBarPos.y() - int(m_axisCacheX.min());
2496 int maxZ = m_selectedSeriesCache->renderArray().size() - 1;
2497 int maxX = maxZ >= 0 ? m_selectedSeriesCache->renderArray().at(0).size() - 1 : -1;
2498
2499 if (m_selectedBarPos == Bars3DController::invalidSelectionPosition()
2500 || adjustedZ < 0 || adjustedZ > maxZ
2501 || adjustedX < 0 || adjustedX > maxX) {
2502 m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition();
2503 } else {
2504 m_visualSelectedBarPos = QPoint(adjustedZ, adjustedX);
2505 }
2506 }
2507
resetClickedStatus()2508 void Bars3DRenderer::resetClickedStatus()
2509 {
2510 m_clickedPosition = Bars3DController::invalidSelectionPosition();
2511 m_clickedSeries = 0;
2512 }
2513
updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)2514 void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)
2515 {
2516 m_cachedShadowQuality = quality;
2517 switch (quality) {
2518 case QAbstract3DGraph::ShadowQualityLow:
2519 m_shadowQualityToShader = 33.3f;
2520 m_shadowQualityMultiplier = 1;
2521 break;
2522 case QAbstract3DGraph::ShadowQualityMedium:
2523 m_shadowQualityToShader = 100.0f;
2524 m_shadowQualityMultiplier = 3;
2525 break;
2526 case QAbstract3DGraph::ShadowQualityHigh:
2527 m_shadowQualityToShader = 200.0f;
2528 m_shadowQualityMultiplier = 5;
2529 break;
2530 case QAbstract3DGraph::ShadowQualitySoftLow:
2531 m_shadowQualityToShader = 7.5f;
2532 m_shadowQualityMultiplier = 1;
2533 break;
2534 case QAbstract3DGraph::ShadowQualitySoftMedium:
2535 m_shadowQualityToShader = 10.0f;
2536 m_shadowQualityMultiplier = 3;
2537 break;
2538 case QAbstract3DGraph::ShadowQualitySoftHigh:
2539 m_shadowQualityToShader = 15.0f;
2540 m_shadowQualityMultiplier = 4;
2541 break;
2542 default:
2543 m_shadowQualityToShader = 0.0f;
2544 m_shadowQualityMultiplier = 1;
2545 break;
2546 }
2547
2548 handleShadowQualityChange();
2549
2550 // Re-init depth buffer
2551 updateDepthBuffer();
2552
2553 // Redraw to handle both reflections and shadows on background
2554 if (m_reflectionEnabled)
2555 needRender();
2556 }
2557
loadBackgroundMesh()2558 void Bars3DRenderer::loadBackgroundMesh()
2559 {
2560 ObjectHelper::resetObjectHelper(this, m_backgroundObj,
2561 QStringLiteral(":/defaultMeshes/backgroundNoFloor"));
2562 }
2563
updateTextures()2564 void Bars3DRenderer::updateTextures()
2565 {
2566 Abstract3DRenderer::updateTextures();
2567
2568 // Drawer has changed; this flag needs to be checked when checking if we need to update labels
2569 m_updateLabels = true;
2570 }
2571
fixMeshFileName(QString & fileName,QAbstract3DSeries::Mesh mesh)2572 void Bars3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
2573 {
2574 if (!m_cachedTheme->isBackgroundEnabled()) {
2575 // Load full version of meshes that have it available
2576 // Note: Minimal, Point, and Arrow not supported in bar charts
2577 if (mesh != QAbstract3DSeries::MeshSphere)
2578 fileName.append(QStringLiteral("Full"));
2579 }
2580 }
2581
calculateSceneScalingFactors()2582 void Bars3DRenderer::calculateSceneScalingFactors()
2583 {
2584 // Calculate scene scaling and translation factors
2585 m_rowWidth = (m_cachedColumnCount * m_cachedBarSpacing.width()) / 2.0f;
2586 m_columnDepth = (m_cachedRowCount * m_cachedBarSpacing.height()) / 2.0f;
2587 m_maxDimension = qMax(m_rowWidth, m_columnDepth);
2588 m_scaleFactor = qMin((m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)),
2589 (m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
2590
2591 // Single bar scaling
2592 m_scaleX = m_cachedBarThickness.width() / m_scaleFactor;
2593 m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor;
2594
2595 // Whole graph scale factors
2596 m_xScaleFactor = m_rowWidth / m_scaleFactor;
2597 m_zScaleFactor = m_columnDepth / m_scaleFactor;
2598
2599 if (m_requestedMargin < 0.0f) {
2600 m_hBackgroundMargin = 0.0f;
2601 m_vBackgroundMargin = 0.0f;
2602 } else {
2603 m_hBackgroundMargin = m_requestedMargin;
2604 m_vBackgroundMargin = m_requestedMargin;
2605 }
2606
2607 m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin;
2608 m_scaleYWithBackground = 1.0f + m_vBackgroundMargin;
2609 m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin;
2610
2611 updateCameraViewport();
2612 updateCustomItemPositions();
2613 }
2614
calculateHeightAdjustment()2615 void Bars3DRenderer::calculateHeightAdjustment()
2616 {
2617 float min = m_axisCacheY.min();
2618 float max = m_axisCacheY.max();
2619 GLfloat newAdjustment = 1.0f;
2620 m_actualFloorLevel = qBound(min, m_floorLevel, max);
2621 GLfloat maxAbs = qFabs(max - m_actualFloorLevel);
2622
2623 // Check if we have negative values
2624 if (min < m_actualFloorLevel)
2625 m_hasNegativeValues = true;
2626 else if (min >= m_actualFloorLevel)
2627 m_hasNegativeValues = false;
2628
2629 if (max < m_actualFloorLevel) {
2630 m_heightNormalizer = GLfloat(qFabs(min) - qFabs(max));
2631 maxAbs = qFabs(max) - qFabs(min);
2632 } else {
2633 m_heightNormalizer = GLfloat(max - min);
2634 }
2635
2636 // Height fractions are used in gradient calculations and are therefore doubled
2637 // Note that if max or min is exactly zero, we still consider it outside the range
2638 if (max <= m_actualFloorLevel || min >= m_actualFloorLevel) {
2639 m_noZeroInRange = true;
2640 m_gradientFraction = 2.0f;
2641 } else {
2642 m_noZeroInRange = false;
2643 GLfloat minAbs = qFabs(min - m_actualFloorLevel);
2644 m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f;
2645 }
2646
2647 // Calculate translation adjustment for background floor
2648 newAdjustment = (qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) - 0.5f) * 2.0f;
2649 if (m_axisCacheY.reversed())
2650 newAdjustment = -newAdjustment;
2651
2652 if (newAdjustment != m_backgroundAdjustment) {
2653 m_backgroundAdjustment = newAdjustment;
2654 m_axisCacheY.setTranslate(m_backgroundAdjustment - 1.0f);
2655 }
2656 }
2657
isSelected(int row,int bar,const BarSeriesRenderCache * cache)2658 Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar,
2659 const BarSeriesRenderCache *cache)
2660 {
2661 Bars3DController::SelectionType isSelectedType = Bars3DController::SelectionNone;
2662
2663 if ((m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)
2664 && m_selectedSeriesCache) || cache == m_selectedSeriesCache) {
2665 if (row == m_visualSelectedBarPos.x() && bar == m_visualSelectedBarPos.y()
2666 && (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem))) {
2667 isSelectedType = Bars3DController::SelectionItem;
2668 } else if (row == m_visualSelectedBarPos.x()
2669 && (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow))) {
2670 isSelectedType = Bars3DController::SelectionRow;
2671 } else if (bar == m_visualSelectedBarPos.y()
2672 && (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn))) {
2673 isSelectedType = Bars3DController::SelectionColumn;
2674 }
2675 }
2676
2677 return isSelectedType;
2678 }
2679
selectionColorToArrayPosition(const QVector4D & selectionColor)2680 QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector4D &selectionColor)
2681 {
2682 QPoint position = Bars3DController::invalidSelectionPosition();
2683 m_clickedType = QAbstract3DGraph::ElementNone;
2684 m_selectedLabelIndex = -1;
2685 m_selectedCustomItemIndex = -1;
2686 if (selectionColor.w() == itemAlpha) {
2687 // Normal selection item
2688 position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())),
2689 int(selectionColor.y()) + int(m_axisCacheX.min()));
2690 // Pass item clicked info to input handler
2691 m_clickedType = QAbstract3DGraph::ElementSeries;
2692 } else if (selectionColor.w() == labelRowAlpha) {
2693 // Row selection
2694 if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) {
2695 // Use column from previous selection in case we have row + column mode
2696 GLint previousCol = qMax(0, m_selectedBarPos.y()); // Use 0 if previous is invalid
2697 position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), previousCol);
2698 }
2699 m_selectedLabelIndex = selectionColor.x();
2700 // Pass label clicked info to input handler
2701 m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
2702 } else if (selectionColor.w() == labelColumnAlpha) {
2703 // Column selection
2704 if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
2705 // Use row from previous selection in case we have row + column mode
2706 GLint previousRow = qMax(0, m_selectedBarPos.x()); // Use 0 if previous is invalid
2707 position = QPoint(previousRow, int(selectionColor.y()) + int(m_axisCacheX.min()));
2708 }
2709 m_selectedLabelIndex = selectionColor.y();
2710 // Pass label clicked info to input handler
2711 m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
2712 } else if (selectionColor.w() == labelValueAlpha) {
2713 // Value selection
2714 position = Bars3DController::invalidSelectionPosition();
2715 m_selectedLabelIndex = selectionColor.z();
2716 // Pass label clicked info to input handler
2717 m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
2718 } else if (selectionColor.w() == customItemAlpha) {
2719 // Custom item selection
2720 position = Bars3DController::invalidSelectionPosition();
2721 m_selectedCustomItemIndex = int(selectionColor.x())
2722 + (int(selectionColor.y()) << 8)
2723 + (int(selectionColor.z()) << 16);
2724 m_clickedType = QAbstract3DGraph::ElementCustomItem;
2725 }
2726 return position;
2727 }
2728
selectionColorToSeries(const QVector4D & selectionColor)2729 QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector4D &selectionColor)
2730 {
2731 if (selectionColor == selectionSkipColor) {
2732 return 0;
2733 } else {
2734 int seriesIndexFromColor(selectionColor.z());
2735 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2736 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
2737 if (cache->visualIndex() == seriesIndexFromColor)
2738 return cache->series();
2739 }
2740 }
2741 return 0;
2742 }
2743
updateSlicingActive(bool isSlicing)2744 void Bars3DRenderer::updateSlicingActive(bool isSlicing)
2745 {
2746 if (isSlicing == m_cachedIsSlicingActivated)
2747 return;
2748
2749 m_cachedIsSlicingActivated = isSlicing;
2750
2751 if (!m_cachedIsSlicingActivated) {
2752 // We need to re-init selection buffer in case there has been a resize
2753 initSelectionBuffer();
2754 initCursorPositionBuffer();
2755 }
2756
2757 updateDepthBuffer(); // Re-init depth buffer as well
2758 m_selectionDirty = true;
2759 }
2760
initShaders(const QString & vertexShader,const QString & fragmentShader)2761 void Bars3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
2762 {
2763 if (m_barShader)
2764 delete m_barShader;
2765 m_barShader = new ShaderHelper(this, vertexShader, fragmentShader);
2766 m_barShader->initialize();
2767 }
2768
initGradientShaders(const QString & vertexShader,const QString & fragmentShader)2769 void Bars3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader)
2770 {
2771 if (m_barGradientShader)
2772 delete m_barGradientShader;
2773 m_barGradientShader = new ShaderHelper(this, vertexShader, fragmentShader);
2774 m_barGradientShader->initialize();
2775 }
2776
initSelectionShader()2777 void Bars3DRenderer::initSelectionShader()
2778 {
2779 if (m_selectionShader)
2780 delete m_selectionShader;
2781 m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
2782 QStringLiteral(":/shaders/fragmentPlainColor"));
2783 m_selectionShader->initialize();
2784 }
2785
initSelectionBuffer()2786 void Bars3DRenderer::initSelectionBuffer()
2787 {
2788 m_textureHelper->deleteTexture(&m_selectionTexture);
2789
2790 if (m_cachedIsSlicingActivated || m_primarySubViewport.size().isEmpty())
2791 return;
2792
2793 m_selectionTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(),
2794 m_selectionFrameBuffer,
2795 m_selectionDepthBuffer);
2796 }
2797
initDepthShader()2798 void Bars3DRenderer::initDepthShader()
2799 {
2800 if (!m_isOpenGLES) {
2801 if (m_depthShader)
2802 delete m_depthShader;
2803 m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
2804 QStringLiteral(":/shaders/fragmentDepth"));
2805 m_depthShader->initialize();
2806 }
2807 }
2808
updateDepthBuffer()2809 void Bars3DRenderer::updateDepthBuffer()
2810 {
2811 if (!m_isOpenGLES) {
2812 m_textureHelper->deleteTexture(&m_depthTexture);
2813
2814 if (m_primarySubViewport.size().isEmpty())
2815 return;
2816
2817 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
2818 m_depthTexture =
2819 m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
2820 m_depthFrameBuffer,
2821 m_shadowQualityMultiplier);
2822 if (!m_depthTexture)
2823 lowerShadowQuality();
2824 }
2825 }
2826 }
2827
initBackgroundShaders(const QString & vertexShader,const QString & fragmentShader)2828 void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader,
2829 const QString &fragmentShader)
2830 {
2831 if (m_backgroundShader)
2832 delete m_backgroundShader;
2833 m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader);
2834 m_backgroundShader->initialize();
2835 }
2836
convertPositionToTranslation(const QVector3D & position,bool isAbsolute)2837 QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute)
2838 {
2839 float xTrans = 0.0f;
2840 float yTrans = 0.0f;
2841 float zTrans = 0.0f;
2842 if (!isAbsolute) {
2843 // Convert row and column to translation on graph
2844 xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width())
2845 - m_rowWidth) / m_scaleFactor;
2846 zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f)
2847 * m_cachedBarSpacing.height())) / m_scaleFactor;
2848 yTrans = m_axisCacheY.positionAt(position.y());
2849 } else {
2850 xTrans = position.x() * m_xScaleFactor;
2851 yTrans = position.y() + m_backgroundAdjustment;
2852 zTrans = position.z() * -m_zScaleFactor;
2853 }
2854 return QVector3D(xTrans, yTrans, zTrans);
2855 }
2856
updateAspectRatio(float ratio)2857 void Bars3DRenderer::updateAspectRatio(float ratio)
2858 {
2859 Q_UNUSED(ratio)
2860 }
2861
updateFloorLevel(float level)2862 void Bars3DRenderer::updateFloorLevel(float level)
2863 {
2864 foreach (SeriesRenderCache *cache, m_renderCacheList)
2865 cache->setDataDirty(true);
2866 m_floorLevel = level;
2867 calculateHeightAdjustment();
2868 }
2869
updateMargin(float margin)2870 void Bars3DRenderer::updateMargin(float margin)
2871 {
2872 Abstract3DRenderer::updateMargin(margin);
2873 calculateSceneScalingFactors();
2874 }
2875
2876 QT_END_NAMESPACE_DATAVISUALIZATION
2877