diff options
Diffstat (limited to 'src/core/render/frontend/qitemmodelbuffer.cpp')
-rw-r--r-- | src/core/render/frontend/qitemmodelbuffer.cpp | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/core/render/frontend/qitemmodelbuffer.cpp b/src/core/render/frontend/qitemmodelbuffer.cpp new file mode 100644 index 000000000..d13e389b4 --- /dev/null +++ b/src/core/render/frontend/qitemmodelbuffer.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qitemmodelbuffer.h" + +#include <QDebug> +#include <QColor> + +namespace Qt3D { + +void variantToBytes(void* dest, const QVariant& v, GLint type) +{ + int varType = v.type(); + switch (type) { + case GL_FLOAT: { + float f = v.toFloat(); + memcpy(dest, &f, sizeof(float)); + return; + } + + case GL_INT: { + GLint i = v.toInt(); + memcpy(dest, &i, sizeof(GLint)); + return; + } + + case GL_UNSIGNED_INT: { + GLuint i = v.toUInt(); + memcpy(dest, &i, sizeof(GLuint)); + return; + } + + case GL_FLOAT_VEC3: + if (varType == QVariant::Vector3D) { + QVector3D v3(v.value<QVector3D>()); + memcpy(dest, &v3[0], sizeof(float) * 3); + return; + } + + break; + + case GL_FLOAT_VEC4: + if (varType == QVariant::Vector4D) { + QVector4D v4(v.value<QVector4D>()); + memcpy(dest, &v4[0], sizeof(float) * 4); + return; + } + + if (varType == QVariant::Color) { + QColor c = v.value<QColor>(); + float* fptr = reinterpret_cast<float*>(dest); + *fptr++ = c.redF(); + *fptr++ = c.greenF(); + *fptr++ = c.blueF(); + *fptr++ = c.alphaF(); + return; + } + + if (varType == QVariant::Quaternion) { + QVector4D qv = v.value<QQuaternion>().toVector4D(); + memcpy(dest, &qv[0], sizeof(float) * 4); + return; + } + + break; + + default: + break; + } + + qWarning() << Q_FUNC_INFO << "failed to convert" << v << "to GL type" << + QString::number(type, 16); +} + +QItemModelBuffer::QItemModelBuffer() +{ +} + +void QItemModelBuffer::setModel(QAbstractItemModel *model) +{ + if (model == m_model) + return; + + m_model = model; + m_buffer.clear(); + + connect(m_model, SIGNAL(modelReset()), this, SLOT(onModelReset())); + connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(onModelDataChanged(QModelIndex,QModelIndex))); +} + +void QItemModelBuffer::setRoot(const QModelIndex &rootIndex) +{ + m_rootIndex = rootIndex; + m_buffer.clear(); +} + +void QItemModelBuffer::mapRoleName(QByteArray roleName, int elementType) +{ + mapRoleName(roleName, roleName, elementType); +} + +void QItemModelBuffer::mapRoleName(QByteArray roleName, QString attributeName, int elementType) +{ + if (m_model) { + if (!m_model->roleNames().values().contains(roleName)) { + qWarning() << Q_FUNC_INFO << "unknown role name" << roleName; + } + } + + m_mappings.append(RoleMapping(roleName, attributeName, elementType)); + m_buffer.clear(); +} + +BufferPtr QItemModelBuffer::buffer() +{ + if (!m_buffer) { + if (!validateRoles()) + return m_buffer; + + m_attributes.clear(); + m_itemStride = 0; + + m_buffer.reset(new Buffer(QOpenGLBuffer::VertexBuffer)); + // assume model will change + m_buffer->setUsage(QOpenGLBuffer::DynamicDraw); + + int rowCount = m_model->rowCount(m_rootIndex); + int offset = 0; + int mappingCount = m_mappings.count(); + for (int m=0; m<mappingCount; ++m) + m_itemStride += m_mappings.at(m).byteSize; + + for (int m=0; m<mappingCount; ++m) { + const RoleMapping mapping(m_mappings.at(m)); + AttributePtr attr(new Attribute(m_buffer, mapping.type, + rowCount, + offset, m_itemStride)); + m_attributes[mapping.attribute] = attr; + offset += byteSizeFromType(mapping.type); + } // of mappings iteration + + m_buffer->setData(computeBufferData()); + } + + return m_buffer; +} + +QStringList QItemModelBuffer::attributeNames() const +{ + return m_attributes.keys(); +} + +AttributePtr QItemModelBuffer::attributeByName(QString nm) const +{ + return m_attributes.value(nm); +} + +void QItemModelBuffer::onModelDataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + if (!m_buffer) + return; + + if (topLeft.parent() != m_rootIndex) + return; + + QByteArray newBytes = m_buffer->data(); + for (int row=topLeft.row(); row<=bottomRight.row(); ++row) { + QModelIndex index = topLeft.sibling(row, topLeft.column()); + + char* itemPtr = newBytes.data(); + itemPtr += m_itemStride * row; + + writeDataForIndex(index, m_mappings.count(), itemPtr); + } // of rows changed iteration + + m_buffer->setData(newBytes); +} + +void QItemModelBuffer::onModelReset() +{ + if (!m_buffer) + return; + + QByteArray b = computeBufferData(); + m_buffer->setData(b); +} + +QByteArray QItemModelBuffer::computeBufferData() +{ + int rowCount = m_model->rowCount(m_rootIndex); + + int mappingCount = m_mappings.count(); + QByteArray newData; + newData.resize(m_itemStride * rowCount); + char* bufferPtr = newData.data(); + + for (int row=0; row<rowCount; ++row) { + writeDataForIndex(m_model->index(row, 0, m_rootIndex), mappingCount, bufferPtr); + bufferPtr += m_itemStride; + } // of rows iteration + + return newData; +} + +void QItemModelBuffer::writeDataForIndex(const QModelIndex& index, int mappingCount, char* bufferPtr) +{ + char* fieldPtr = bufferPtr; + for (int m=0; m<mappingCount; ++m) { + const RoleMapping& mapping(m_mappings.at(m)); + QVariant v = m_model->data(index, mapping.cachedRole); + variantToBytes(fieldPtr, v, mapping.type); + + fieldPtr += mapping.byteSize; + } // of mappings iteration +} + +bool QItemModelBuffer::validateRoles() +{ + Q_ASSERT(m_model); + + QHash<int, QByteArray> roles(m_model->roleNames()); + // create a lookup that's the the way round we need + QHash<QByteArray, int> inverseRoles; + foreach (int r, roles.keys()) + inverseRoles[roles.value(r)] = r; + + for (int m=0; m<m_mappings.count(); ++m) { + QByteArray rnm(m_mappings.at(m).roleName); + if (!inverseRoles.contains(rnm)) { + qWarning() << "unknown role:" << rnm; + return false; + } + + m_mappings[m].cachedRole = inverseRoles[rnm]; + } // of mappings iteration + + return true; +} + +QItemModelBuffer::RoleMapping::RoleMapping(QByteArray rnm, QString nm, int ty) : + roleName(rnm), + cachedRole(-1), + attribute(nm), + type(ty) +{ + byteSize = byteSizeFromType(ty); +} + +} // namespace Qt3D |