Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "scatterobjectbufferhelper_p.h"
#include "objecthelper_p.h"
#include <QtGui/QVector2D>
#include <QtGui/QMatrix4x4>
#include <QtGui/qquaternion.h>
#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
const GLfloat ScatterObjectBufferHelper::itemScaler = 3.0f;
ScatterObjectBufferHelper::ScatterObjectBufferHelper()
: m_scaleY(0.0f)
{
}
ScatterObjectBufferHelper::~ScatterObjectBufferHelper()
{
}
void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale)
{
m_indexCount = 0;
ObjectHelper *dotObj = cache->object();
const ScatterRenderItemArray &renderArray = cache->renderArray();
const uint renderArraySize = renderArray.size();
if (renderArraySize == 0)
return; // No use to go forward
uint itemCount = 0;
QQuaternion seriesRotation(cache->meshRotation());
if (m_meshDataLoaded) {
// Delete old data
glDeleteBuffers(1, &m_vertexbuffer);
glDeleteBuffers(1, &m_uvbuffer);
glDeleteBuffers(1, &m_normalbuffer);
glDeleteBuffers(1, &m_elementbuffer);
m_vertexbuffer = 0;
m_uvbuffer = 0;
m_normalbuffer = 0;
m_elementbuffer = 0;
m_meshDataLoaded = false;
}
// Index vertices
const QList<GLuint> indices = dotObj->indices();
const QList<QVector3D> indexed_vertices = dotObj->indexedvertices();
const QList<QVector2D> indexed_uvs = dotObj->indexedUVs();
const QList<QVector3D> indexed_normals = dotObj->indexedNormals();
const int indicesCount = indices.size();
const int verticeCount = indexed_vertices.size();
const int uvsCount = indexed_uvs.size();
const int normalsCount = indexed_normals.size();
float itemSize = cache->itemSize() / itemScaler;
if (itemSize == 0.0f)
itemSize = dotScale;
QVector3D modelScaler(itemSize, itemSize, itemSize);
QMatrix4x4 modelMatrix;
if (!seriesRotation.isIdentity()) {
QMatrix4x4 matrix;
matrix.rotate(seriesRotation);
modelMatrix = matrix.transposed();
}
modelMatrix.scale(modelScaler);
QList<QVector3D> scaled_vertices;
scaled_vertices.resize(verticeCount);
for (int i = 0; i < verticeCount; i++)
scaled_vertices[i] = (QVector4D(indexed_vertices[i]) * modelMatrix).toVector3D();
QList<GLuint> buffered_indices;
QList<QVector3D> buffered_vertices;
QList<QVector2D> buffered_uvs;
QList<QVector3D> buffered_normals;
buffered_indices.resize(indicesCount * renderArraySize);
buffered_vertices.resize(verticeCount * renderArraySize);
buffered_normals.resize(normalsCount * renderArraySize);
buffered_uvs.resize(uvsCount * renderArraySize);
if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
createRangeGradientUVs(cache, buffered_uvs);
else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient)
createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
QVector2D dummyUV(0.0f, 0.0f);
cache->bufferIndices().resize(renderArraySize);
for (uint i = 0; i < renderArraySize; i++) {
const ScatterRenderItem &item = renderArray.at(i);
if (!item.isVisible())
continue;
else
cache->bufferIndices()[i] = itemCount;
int offset = itemCount * verticeCount;
if (item.rotation().isIdentity()) {
for (int j = 0; j < verticeCount; j++) {
buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
buffered_normals[j + offset] = indexed_normals[j];
}
} else {
QMatrix4x4 matrix;
QQuaternion totalRotation = seriesRotation * item.rotation();
matrix.rotate(totalRotation);
matrix.scale(modelScaler);
QMatrix4x4 itModelMatrix = matrix.inverted();
modelMatrix = matrix.transposed(); // Because of row-column major difference
for (int j = 0; j < verticeCount; j++) {
buffered_vertices[j + offset]
= (QVector4D(indexed_vertices[j]) * modelMatrix).toVector3D()
+ item.translation();
buffered_normals[j + offset]
= (QVector4D(indexed_normals[j]) * itModelMatrix).toVector3D();
}
}
if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
offset = itemCount * uvsCount;
for (int j = 0; j < uvsCount; j++)
buffered_uvs[j + offset] = dummyUV;
}
int offsetVertice = itemCount * verticeCount;
offset = itemCount * indicesCount;
for (int j = 0; j < indicesCount; j++)
buffered_indices[j + offset] = GLuint(indices[j] + offsetVertice);
itemCount++;
}
m_indexCount = indicesCount * itemCount;
if (itemCount > 0) {
glGenBuffers(1, &m_vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, verticeCount * itemCount * sizeof(QVector3D),
&buffered_vertices.at(0),
GL_STATIC_DRAW);
glGenBuffers(1, &m_normalbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_normalbuffer);
glBufferData(GL_ARRAY_BUFFER, normalsCount * itemCount * sizeof(QVector3D),
&buffered_normals.at(0),
GL_STATIC_DRAW);
glGenBuffers(1, &m_uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
glBufferData(GL_ARRAY_BUFFER, uvsCount * itemCount * sizeof(QVector2D),
&buffered_uvs.at(0), GL_STATIC_DRAW);
glGenBuffers(1, &m_elementbuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount * itemCount * sizeof(GLint),
&buffered_indices.at(0), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
}
void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache)
{
ObjectHelper *dotObj = cache->object();
const int uvsCount = dotObj->indexedUVs().size();
const ScatterRenderItemArray &renderArray = cache->renderArray();
const bool updateAll = (cache->updateIndices().size() == 0);
const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
if (!updateSize)
return;
QList<QVector2D> buffered_uvs;
buffered_uvs.resize(uvsCount * updateSize);
uint itemCount = 0;
if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) {
itemCount = createRangeGradientUVs(cache, buffered_uvs);
} else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
const QList<QVector3D> indexed_vertices = dotObj->indexedvertices();
itemCount = createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
}
glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
int itemSize = uvsCount * sizeof(QVector2D);
if (cache->updateIndices().size()) {
int pos = 0;
for (int i = 0; i < updateSize; i++) {
int index = cache->updateIndices().at(i);
if (renderArray.at(index).isVisible()) {
int dataPos = cache->bufferIndices().at(index);
glBufferSubData(GL_ARRAY_BUFFER, itemSize * dataPos, itemSize,
&buffered_uvs.at(uvsCount * pos++));
}
}
} else {
glBufferData(GL_ARRAY_BUFFER, itemSize * itemCount, &buffered_uvs.at(0), GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache,
QList<QVector2D> &buffered_uvs)
{
ObjectHelper *dotObj = cache->object();
const int uvsCount = dotObj->indexedUVs().size();
const ScatterRenderItemArray &renderArray = cache->renderArray();
const bool updateAll = (cache->updateIndices().size() == 0);
const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
const float yAdjustment = 0.1f;
const float flippedYAdjustment = 0.9f;
QVector2D uv;
uv.setX(0.0f);
uint pos = 0;
for (int i = 0; i < updateSize; i++) {
int index = updateAll ? i : cache->updateIndices().at(i);
const ScatterRenderItem &item = renderArray.at(index);
if (!item.isVisible())
continue;
float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY;
// Avoid values near gradient texel boundary, as this causes artifacts
// with some graphics cards.
const float floorY = float(qFloor(y * gradientTextureHeight));
const float diff = (y * gradientTextureHeight) - floorY;
if (diff < yAdjustment)
y += yAdjustment / gradientTextureHeight;
else if (diff > flippedYAdjustment)
y -= yAdjustment / gradientTextureHeight;
uv.setY(y);
int offset = pos * uvsCount;
for (int j = 0; j < uvsCount; j++)
buffered_uvs[j + offset] = uv;
pos++;
}
return pos;
}
uint ScatterObjectBufferHelper::createObjectGradientUVs(ScatterSeriesRenderCache *cache,
QList<QVector2D> &buffered_uvs,
const QList<QVector3D> &indexed_vertices)
{
ObjectHelper *dotObj = cache->object();
const int uvsCount = dotObj->indexedUVs().size();
const ScatterRenderItemArray &renderArray = cache->renderArray();
const uint renderArraySize = renderArray.size();
QVector2D uv;
uv.setX(0.0f);
uint pos = 0;
for (uint i = 0; i < renderArraySize; i++) {
const ScatterRenderItem &item = renderArray.at(i);
if (!item.isVisible())
continue;
int offset = pos * uvsCount;
for (int j = 0; j < uvsCount; j++) {
uv.setY((indexed_vertices.at(j).y() + 1.0f) / 2.0f);
buffered_uvs[j + offset] = uv;
}
pos++;
}
return pos;
}
void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale)
{
ObjectHelper *dotObj = cache->object();
const ScatterRenderItemArray &renderArray = cache->renderArray();
const bool updateAll = (cache->updateIndices().size() == 0);
const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
QQuaternion seriesRotation(cache->meshRotation());
if (!updateSize)
return;
// Index vertices
const QList<QVector3D> indexed_vertices = dotObj->indexedvertices();
int verticeCount = indexed_vertices.size();
float itemSize = cache->itemSize() / itemScaler;
if (itemSize == 0.0f)
itemSize = dotScale;
QVector3D modelScaler(itemSize, itemSize, itemSize);
QMatrix4x4 modelMatrix;
if (!seriesRotation.isIdentity()) {
QMatrix4x4 matrix;
matrix.rotate(seriesRotation);
modelMatrix = matrix.transposed();
}
modelMatrix.scale(modelScaler);
QList<QVector3D> scaled_vertices;
scaled_vertices.resize(verticeCount);
for (int i = 0; i < verticeCount; i++)
scaled_vertices[i] = (QVector4D(indexed_vertices[i]) * modelMatrix).toVector3D();
QList<QVector3D> buffered_vertices;
buffered_vertices.resize(verticeCount * updateSize);
int itemCount = 0;
for (int i = 0; i < updateSize; i++) {
int index = updateAll ? i : cache->updateIndices().at(i);
const ScatterRenderItem &item = renderArray.at(index);
if (!item.isVisible())
continue;
const int offset = itemCount * verticeCount;
if (item.rotation().isIdentity()) {
for (int j = 0; j < verticeCount; j++)
buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
} else {
QMatrix4x4 matrix;
matrix.rotate(seriesRotation * item.rotation());
modelMatrix = matrix.transposed();
modelMatrix.scale(modelScaler);
for (int j = 0; j < verticeCount; j++) {
buffered_vertices[j + offset]
= (QVector4D(indexed_vertices[j]) * modelMatrix).toVector3D()
+ item.translation();
}
}
itemCount++;
}
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
int sizeOfItem = verticeCount * sizeof(QVector3D);
if (updateAll) {
if (itemCount) {
glBufferData(GL_ARRAY_BUFFER, itemCount * sizeOfItem,
&buffered_vertices.at(0), GL_STATIC_DRAW);
}
} else {
itemCount = 0;
for (int i = 0; i < updateSize; i++) {
int index = updateAll ? i : cache->updateIndices().at(i);
if (renderArray.at(index).isVisible()) {
glBufferSubData(GL_ARRAY_BUFFER, cache->bufferIndices().at(index) * sizeOfItem,
sizeOfItem, &buffered_vertices.at(itemCount * verticeCount));
itemCount++;
}
}
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
QT_END_NAMESPACE