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 "scatterobjectbufferhelper_p.h"
31#include "objecthelper_p.h"
32#include <QtGui/QVector2D>
33#include <QtGui/QMatrix4x4>
34#include <QtCore/qmath.h>
35
36QT_BEGIN_NAMESPACE_DATAVISUALIZATION
37
38const GLfloat itemScaler = 3.0f;
39
40ScatterObjectBufferHelper::ScatterObjectBufferHelper()
41 : m_scaleY(0.0f)
42{
43}
44
45ScatterObjectBufferHelper::~ScatterObjectBufferHelper()
46{
47}
48
49void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale)
50{
51 m_indexCount = 0;
52
53 ObjectHelper *dotObj = cache->object();
54 const ScatterRenderItemArray &renderArray = cache->renderArray();
55 const uint renderArraySize = renderArray.size();
56
57 if (renderArraySize == 0)
58 return; // No use to go forward
59
60 uint itemCount = 0;
61 QQuaternion seriesRotation(cache->meshRotation());
62
63 if (m_meshDataLoaded) {
64 // Delete old data
65 glDeleteBuffers(n: 1, buffers: &m_vertexbuffer);
66 glDeleteBuffers(n: 1, buffers: &m_uvbuffer);
67 glDeleteBuffers(n: 1, buffers: &m_normalbuffer);
68 glDeleteBuffers(n: 1, buffers: &m_elementbuffer);
69 m_vertexbuffer = 0;
70 m_uvbuffer = 0;
71 m_normalbuffer = 0;
72 m_elementbuffer = 0;
73 m_meshDataLoaded = false;
74 }
75
76 // Index vertices
77 const QVector<GLuint> indices = dotObj->indices();
78 const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
79 const QVector<QVector2D> indexed_uvs = dotObj->indexedUVs();
80 const QVector<QVector3D> indexed_normals = dotObj->indexedNormals();
81 const int indicesCount = indices.count();
82 const int verticeCount = indexed_vertices.count();
83 const int uvsCount = indexed_uvs.count();
84 const int normalsCount = indexed_normals.count();
85
86 float itemSize = cache->itemSize() / itemScaler;
87 if (itemSize == 0.0f)
88 itemSize = dotScale;
89 QVector3D modelScaler(itemSize, itemSize, itemSize);
90 QMatrix4x4 modelMatrix;
91 if (!seriesRotation.isIdentity()) {
92 QMatrix4x4 matrix;
93 matrix.rotate(quaternion: seriesRotation);
94 modelMatrix = matrix.transposed();
95 }
96 modelMatrix.scale(vector: modelScaler);
97
98 QVector<QVector3D> scaled_vertices;
99 scaled_vertices.resize(asize: verticeCount);
100 for (int i = 0; i < verticeCount; i++)
101 scaled_vertices[i] = indexed_vertices[i] * modelMatrix;
102
103 QVector<GLuint> buffered_indices;
104 QVector<QVector3D> buffered_vertices;
105 QVector<QVector2D> buffered_uvs;
106 QVector<QVector3D> buffered_normals;
107
108 buffered_indices.resize(asize: indicesCount * renderArraySize);
109 buffered_vertices.resize(asize: verticeCount * renderArraySize);
110 buffered_normals.resize(asize: normalsCount * renderArraySize);
111 buffered_uvs.resize(asize: uvsCount * renderArraySize);
112
113 if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
114 createRangeGradientUVs(cache, buffered_uvs);
115 else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient)
116 createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
117
118 QVector2D dummyUV(0.0f, 0.0f);
119
120 cache->bufferIndices().resize(asize: renderArraySize);
121
122 for (uint i = 0; i < renderArraySize; i++) {
123 const ScatterRenderItem &item = renderArray.at(i);
124 if (!item.isVisible())
125 continue;
126 else
127 cache->bufferIndices()[i] = itemCount;
128
129 int offset = itemCount * verticeCount;
130 if (item.rotation().isIdentity()) {
131 for (int j = 0; j < verticeCount; j++) {
132 buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
133 buffered_normals[j + offset] = indexed_normals[j];
134 }
135 } else {
136 QMatrix4x4 matrix;
137 QQuaternion totalRotation = seriesRotation * item.rotation();
138 matrix.rotate(quaternion: totalRotation);
139 matrix.scale(vector: modelScaler);
140 QMatrix4x4 itModelMatrix = matrix.inverted();
141 modelMatrix = matrix.transposed(); // Because of row-column major difference
142
143 for (int j = 0; j < verticeCount; j++) {
144 buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
145 + item.translation();
146 buffered_normals[j + offset] = indexed_normals[j] * itModelMatrix;
147 }
148 }
149
150 if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
151 offset = itemCount * uvsCount;
152 for (int j = 0; j < uvsCount; j++)
153 buffered_uvs[j + offset] = dummyUV;
154 }
155
156 int offsetVertice = itemCount * verticeCount;
157 offset = itemCount * indicesCount;
158 for (int j = 0; j < indicesCount; j++)
159 buffered_indices[j + offset] = GLuint(indices[j] + offsetVertice);
160
161 itemCount++;
162 }
163
164 m_indexCount = indicesCount * itemCount;
165
166 if (itemCount > 0) {
167 glGenBuffers(n: 1, buffers: &m_vertexbuffer);
168 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_vertexbuffer);
169 glBufferData(GL_ARRAY_BUFFER, size: verticeCount * itemCount * sizeof(QVector3D),
170 data: &buffered_vertices.at(i: 0),
171 GL_STATIC_DRAW);
172
173 glGenBuffers(n: 1, buffers: &m_normalbuffer);
174 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_normalbuffer);
175 glBufferData(GL_ARRAY_BUFFER, size: normalsCount * itemCount * sizeof(QVector3D),
176 data: &buffered_normals.at(i: 0),
177 GL_STATIC_DRAW);
178
179 glGenBuffers(n: 1, buffers: &m_uvbuffer);
180 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_uvbuffer);
181 glBufferData(GL_ARRAY_BUFFER, size: uvsCount * itemCount * sizeof(QVector2D),
182 data: &buffered_uvs.at(i: 0), GL_STATIC_DRAW);
183
184 glGenBuffers(n: 1, buffers: &m_elementbuffer);
185 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: m_elementbuffer);
186 glBufferData(GL_ELEMENT_ARRAY_BUFFER, size: indicesCount * itemCount * sizeof(GLint),
187 data: &buffered_indices.at(i: 0), GL_STATIC_DRAW);
188
189 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
190 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
191
192 m_meshDataLoaded = true;
193 }
194}
195
196void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache)
197{
198 ObjectHelper *dotObj = cache->object();
199 const int uvsCount = dotObj->indexedUVs().count();
200 const ScatterRenderItemArray &renderArray = cache->renderArray();
201 const bool updateAll = (cache->updateIndices().size() == 0);
202 const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
203
204 if (!updateSize)
205 return;
206
207 QVector<QVector2D> buffered_uvs;
208 buffered_uvs.resize(asize: uvsCount * updateSize);
209
210 uint itemCount = 0;
211 if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) {
212 itemCount = createRangeGradientUVs(cache, buffered_uvs);
213 } else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
214 const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
215 itemCount = createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
216 }
217
218 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_uvbuffer);
219 int itemSize = uvsCount * sizeof(QVector2D);
220 if (cache->updateIndices().size()) {
221 int pos = 0;
222 for (int i = 0; i < updateSize; i++) {
223 int index = cache->updateIndices().at(i);
224 if (renderArray.at(i: index).isVisible()) {
225 int dataPos = cache->bufferIndices().at(i: index);
226 glBufferSubData(GL_ARRAY_BUFFER, offset: itemSize * dataPos, size: itemSize,
227 data: &buffered_uvs.at(i: uvsCount * pos++));
228 }
229 }
230 } else {
231 glBufferData(GL_ARRAY_BUFFER, size: itemSize * itemCount, data: &buffered_uvs.at(i: 0), GL_STATIC_DRAW);
232 }
233 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
234}
235
236uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache,
237 QVector<QVector2D> &buffered_uvs)
238{
239 ObjectHelper *dotObj = cache->object();
240 const int uvsCount = dotObj->indexedUVs().count();
241 const ScatterRenderItemArray &renderArray = cache->renderArray();
242 const bool updateAll = (cache->updateIndices().size() == 0);
243 const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
244 const float yAdjustment = 0.1f;
245 const float flippedYAdjustment = 0.9f;
246
247 QVector2D uv;
248 uv.setX(0.0f);
249 uint pos = 0;
250 for (int i = 0; i < updateSize; i++) {
251 int index = updateAll ? i : cache->updateIndices().at(i);
252 const ScatterRenderItem &item = renderArray.at(i: index);
253 if (!item.isVisible())
254 continue;
255
256 float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY;
257
258 // Avoid values near gradient texel boundary, as this causes artifacts
259 // with some graphics cards.
260 const float floorY = float(qFloor(v: y * gradientTextureHeight));
261 const float diff = (y * gradientTextureHeight) - floorY;
262 if (diff < yAdjustment)
263 y += yAdjustment / gradientTextureHeight;
264 else if (diff > flippedYAdjustment)
265 y -= yAdjustment / gradientTextureHeight;
266 uv.setY(y);
267
268 int offset = pos * uvsCount;
269 for (int j = 0; j < uvsCount; j++)
270 buffered_uvs[j + offset] = uv;
271
272 pos++;
273 }
274
275 return pos;
276}
277
278uint ScatterObjectBufferHelper::createObjectGradientUVs(ScatterSeriesRenderCache *cache,
279 QVector<QVector2D> &buffered_uvs,
280 const QVector<QVector3D> &indexed_vertices)
281{
282 ObjectHelper *dotObj = cache->object();
283 const int uvsCount = dotObj->indexedUVs().count();
284 const ScatterRenderItemArray &renderArray = cache->renderArray();
285 const uint renderArraySize = renderArray.size();
286
287 QVector2D uv;
288 uv.setX(0.0f);
289 uint pos = 0;
290 for (uint i = 0; i < renderArraySize; i++) {
291 const ScatterRenderItem &item = renderArray.at(i);
292 if (!item.isVisible())
293 continue;
294
295 int offset = pos * uvsCount;
296 for (int j = 0; j < uvsCount; j++) {
297 uv.setY((indexed_vertices.at(i: j).y() + 1.0f) / 2.0f);
298 buffered_uvs[j + offset] = uv;
299 }
300
301 pos++;
302 }
303
304 return pos;
305}
306
307void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale)
308{
309 ObjectHelper *dotObj = cache->object();
310 const ScatterRenderItemArray &renderArray = cache->renderArray();
311 const bool updateAll = (cache->updateIndices().size() == 0);
312 const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
313 QQuaternion seriesRotation(cache->meshRotation());
314
315 if (!updateSize)
316 return;
317
318 // Index vertices
319 const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
320 int verticeCount = indexed_vertices.count();
321
322 float itemSize = cache->itemSize() / itemScaler;
323 if (itemSize == 0.0f)
324 itemSize = dotScale;
325 QVector3D modelScaler(itemSize, itemSize, itemSize);
326 QMatrix4x4 modelMatrix;
327 if (!seriesRotation.isIdentity()) {
328 QMatrix4x4 matrix;
329 matrix.rotate(quaternion: seriesRotation);
330 modelMatrix = matrix.transposed();
331 }
332 modelMatrix.scale(vector: modelScaler);
333
334 QVector<QVector3D> scaled_vertices;
335 scaled_vertices.resize(asize: verticeCount);
336 for (int i = 0; i < verticeCount; i++)
337 scaled_vertices[i] = indexed_vertices[i] * modelMatrix;
338
339 QVector<QVector3D> buffered_vertices;
340 buffered_vertices.resize(asize: verticeCount * updateSize);
341
342 int itemCount = 0;
343 for (int i = 0; i < updateSize; i++) {
344 int index = updateAll ? i : cache->updateIndices().at(i);
345 const ScatterRenderItem &item = renderArray.at(i: index);
346 if (!item.isVisible())
347 continue;
348
349 const int offset = itemCount * verticeCount;
350 if (item.rotation().isIdentity()) {
351 for (int j = 0; j < verticeCount; j++)
352 buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
353 } else {
354 QMatrix4x4 matrix;
355 matrix.rotate(quaternion: seriesRotation * item.rotation());
356 modelMatrix = matrix.transposed();
357 modelMatrix.scale(vector: modelScaler);
358
359 for (int j = 0; j < verticeCount; j++)
360 buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
361 + item.translation();
362 }
363 itemCount++;
364 }
365
366 glBindBuffer(GL_ARRAY_BUFFER, buffer: m_vertexbuffer);
367 int sizeOfItem = verticeCount * sizeof(QVector3D);
368 if (updateAll) {
369 if (itemCount) {
370 glBufferData(GL_ARRAY_BUFFER, size: itemCount * sizeOfItem,
371 data: &buffered_vertices.at(i: 0), GL_STATIC_DRAW);
372 }
373 } else {
374 itemCount = 0;
375 for (int i = 0; i < updateSize; i++) {
376 int index = updateAll ? i : cache->updateIndices().at(i);
377 if (renderArray.at(i: index).isVisible()) {
378 glBufferSubData(GL_ARRAY_BUFFER, offset: cache->bufferIndices().at(i: index) * sizeOfItem,
379 size: sizeOfItem, data: &buffered_vertices.at(i: itemCount * verticeCount));
380 itemCount++;
381 }
382 }
383 }
384 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
385
386 m_meshDataLoaded = true;
387}
388
389QT_END_NAMESPACE_DATAVISUALIZATION
390

source code of qtdatavis3d/src/datavisualization/utils/scatterobjectbufferhelper.cpp