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

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