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 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | const GLfloat ScatterObjectBufferHelper::itemScaler = 3.0f; |
13 | |
14 | ScatterObjectBufferHelper::ScatterObjectBufferHelper() |
15 | : m_scaleY(0.0f) |
16 | { |
17 | } |
18 | |
19 | ScatterObjectBufferHelper::~ScatterObjectBufferHelper() |
20 | { |
21 | } |
22 | |
23 | void 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 | |
172 | void 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 | |
212 | uint 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 | |
254 | uint 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 | |
283 | void 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 | |
367 | QT_END_NAMESPACE |
368 | |