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

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