diff options
author | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2016-12-09 17:53:04 +0200 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2016-12-13 13:49:46 +0000 |
commit | 3395491984ae9ad5f73247979b7ec921ad20ba7f (patch) | |
tree | fe139b8f8b81781673a774dbd2d4a2a26f2243b8 | |
parent | 68472bdf4447013f6bf2c31cda8f2b717de0246c (diff) |
QGLTF exporting basic mesh types
QGLTF exported basic mesh types will now properly import as the same
basic mesh types instead of generic QGeometryRenderers.
Change-Id: I6fb2f076f62f66116b26370a5c2ef99550c56233
Reviewed-by: Antti Määttä <antti.maatta@qt.io>
-rw-r--r-- | src/plugins/sceneparsers/gltf/gltfimporter.cpp | 196 | ||||
-rw-r--r-- | src/plugins/sceneparsers/gltfexport/gltfexporter.cpp | 576 | ||||
-rw-r--r-- | src/plugins/sceneparsers/gltfexport/gltfexporter.h | 18 | ||||
-rw-r--r-- | tests/auto/render/gltfplugins/tst_gltfplugins.cpp | 2 |
4 files changed, 484 insertions, 308 deletions
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.cpp b/src/plugins/sceneparsers/gltf/gltfimporter.cpp index 61c940eb1..e5434da3f 100644 --- a/src/plugins/sceneparsers/gltf/gltfimporter.cpp +++ b/src/plugins/sceneparsers/gltf/gltfimporter.cpp @@ -97,6 +97,12 @@ #include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h> #include <Qt3DExtras/qgoochmaterial.h> #include <Qt3DExtras/qpervertexcolormaterial.h> +#include <Qt3DExtras/qconemesh.h> +#include <Qt3DExtras/qcuboidmesh.h> +#include <Qt3DExtras/qcylindermesh.h> +#include <Qt3DExtras/qplanemesh.h> +#include <Qt3DExtras/qspheremesh.h> +#include <Qt3DExtras/qtorusmesh.h> #include <private/qurlhelper_p.h> @@ -206,6 +212,7 @@ #define KEY_RENDERPASSES QLatin1String("renderpasses") #define KEY_EFFECT QLatin1String("effect") #define KEY_EFFECTS QLatin1String("effects") +#define KEY_PROPERTIES QLatin1String("properties") QT_BEGIN_NAMESPACE @@ -1312,94 +1319,147 @@ void GLTFImporter::processJSONAccessor( const QString &id, const QJsonObject& js void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json) { - const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray(); const QString meshName = json.value(KEY_NAME).toString(); - for (const QJsonValue &primitiveValue : primitivesArray) { - QJsonObject primitiveObject = primitiveValue.toObject(); - int type = primitiveObject.value(KEY_MODE).toInt(); - QString material = primitiveObject.value(KEY_MATERIAL).toString(); - - if (Q_UNLIKELY(material.isEmpty())) { - qCWarning(GLTFImporterLog, "malformed primitive on %ls, missing material value %ls", - qUtf16PrintableImpl(id), qUtf16PrintableImpl(material)); - continue; - } - - QGeometryRenderer *geometryRenderer = new QGeometryRenderer; - QGeometry *meshGeometry = new QGeometry(geometryRenderer); - - //Set Primitive Type - geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type)); - - //Save Material for mesh - m_meshMaterialDict[geometryRenderer] = material; - - const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject(); - for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) { - QString k = it.value().toString(); - const auto accessorIt = qAsConst(m_accessorDict).find(k); - if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) { - qCWarning(GLTFImporterLog, "unknown attribute accessor: %ls on mesh %ls", - qUtf16PrintableImpl(k), qUtf16PrintableImpl(id)); - continue; - } - - const QString attrName = it.key(); - QString attributeName = standardAttributeNameFromSemantic(attrName); - if (attributeName.isEmpty()) - attributeName = attrName; - - //Get buffer handle for accessor - Qt3DRender::QBuffer *buffer = m_buffers.value(accessorIt->bufferViewName, nullptr); - if (Q_UNLIKELY(!buffer)) { - qCWarning(GLTFImporterLog, "unknown buffer-view: %ls processing accessor: %ls", - qUtf16PrintableImpl(accessorIt->bufferViewName), qUtf16PrintableImpl(id)); - continue; - } + const QString meshType = json.value(KEY_TYPE).toString(); + if (meshType.isEmpty()) { + // Custom mesh + const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray(); + for (const QJsonValue &primitiveValue : primitivesArray) { + QJsonObject primitiveObject = primitiveValue.toObject(); + int type = primitiveObject.value(KEY_MODE).toInt(); + QString material = primitiveObject.value(KEY_MATERIAL).toString(); + + QGeometryRenderer *geometryRenderer = new QGeometryRenderer; + QGeometry *meshGeometry = new QGeometry(geometryRenderer); + + //Set Primitive Type + geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type)); + + //Save Material for mesh + m_meshMaterialDict[geometryRenderer] = material; + + const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject(); + for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) { + QString k = it.value().toString(); + const auto accessorIt = qAsConst(m_accessorDict).find(k); + if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) { + qCWarning(GLTFImporterLog, "unknown attribute accessor: %ls on mesh %ls", + qUtf16PrintableImpl(k), qUtf16PrintableImpl(id)); + continue; + } - QAttribute *attribute = new QAttribute(buffer, - attributeName, - accessorIt->type, - accessorIt->dataSize, - accessorIt->count, - accessorIt->offset, - accessorIt->stride); - attribute->setAttributeType(QAttribute::VertexAttribute); - meshGeometry->addAttribute(attribute); - } + const QString attrName = it.key(); + QString attributeName = standardAttributeNameFromSemantic(attrName); + if (attributeName.isEmpty()) + attributeName = attrName; - const auto indices = primitiveObject.value(KEY_INDICES); - if (!indices.isUndefined()) { - QString k = indices.toString(); - const auto accessorIt = qAsConst(m_accessorDict).find(k); - if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) { - qCWarning(GLTFImporterLog, "unknown index accessor: %ls on mesh %ls", - qUtf16PrintableImpl(k), qUtf16PrintableImpl(id)); - } else { //Get buffer handle for accessor Qt3DRender::QBuffer *buffer = m_buffers.value(accessorIt->bufferViewName, nullptr); if (Q_UNLIKELY(!buffer)) { qCWarning(GLTFImporterLog, "unknown buffer-view: %ls processing accessor: %ls", - qUtf16PrintableImpl(accessorIt->bufferViewName), qUtf16PrintableImpl(id)); + qUtf16PrintableImpl(accessorIt->bufferViewName), + qUtf16PrintableImpl(id)); continue; } QAttribute *attribute = new QAttribute(buffer, + attributeName, accessorIt->type, accessorIt->dataSize, accessorIt->count, accessorIt->offset, accessorIt->stride); - attribute->setAttributeType(QAttribute::IndexAttribute); + attribute->setAttributeType(QAttribute::VertexAttribute); meshGeometry->addAttribute(attribute); } - } // of has indices - geometryRenderer->setGeometry(meshGeometry); - geometryRenderer->setObjectName(meshName); + const auto indices = primitiveObject.value(KEY_INDICES); + if (!indices.isUndefined()) { + QString k = indices.toString(); + const auto accessorIt = qAsConst(m_accessorDict).find(k); + if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) { + qCWarning(GLTFImporterLog, "unknown index accessor: %ls on mesh %ls", + qUtf16PrintableImpl(k), qUtf16PrintableImpl(id)); + } else { + //Get buffer handle for accessor + Qt3DRender::QBuffer *buffer = m_buffers.value(accessorIt->bufferViewName, + nullptr); + if (Q_UNLIKELY(!buffer)) { + qCWarning(GLTFImporterLog, + "unknown buffer-view: %ls processing accessor: %ls", + qUtf16PrintableImpl(accessorIt->bufferViewName), + qUtf16PrintableImpl(id)); + continue; + } + + QAttribute *attribute = new QAttribute(buffer, + accessorIt->type, + accessorIt->dataSize, + accessorIt->count, + accessorIt->offset, + accessorIt->stride); + attribute->setAttributeType(QAttribute::IndexAttribute); + meshGeometry->addAttribute(attribute); + } + } // of has indices + + geometryRenderer->setGeometry(meshGeometry); + geometryRenderer->setObjectName(meshName); - m_meshDict.insert( id, geometryRenderer); - } // of primitives iteration + m_meshDict.insert(id, geometryRenderer); + } // of primitives iteration + } else { + QGeometryRenderer *mesh = nullptr; + if (meshType == QStringLiteral("cone")) { + mesh = new QConeMesh; + } else if (meshType == QStringLiteral("cuboid")) { + mesh = new QCuboidMesh; + } else if (meshType == QStringLiteral("cylinder")) { + mesh = new QCylinderMesh; + } else if (meshType == QStringLiteral("plane")) { + mesh = new QPlaneMesh; + } else if (meshType == QStringLiteral("sphere")) { + mesh = new QSphereMesh; + } else if (meshType == QStringLiteral("torus")) { + mesh = new QTorusMesh; + } else { + qCWarning(GLTFImporterLog, + "Invalid mesh type: %ls for mesh: %ls", + qUtf16PrintableImpl(meshType), + qUtf16PrintableImpl(id)); + } + + if (mesh) { + // Read and set properties + const QJsonObject propObj = json.value(KEY_PROPERTIES).toObject(); + for (auto it = propObj.begin(), end = propObj.end(); it != end; ++it) { + const QByteArray propName = it.key().toLatin1(); + // Basic mesh types only have bool, int, float, and QSize type properties + if (it.value().isBool()) { + mesh->setProperty(propName.constData(), QVariant(it.value().toBool())); + } else if (it.value().isArray()) { + const QJsonArray valueArray = it.value().toArray(); + if (valueArray.size() == 2) { + QSize size; + size.setWidth(valueArray.at(0).toInt()); + size.setHeight(valueArray.at(1).toInt()); + mesh->setProperty(propName.constData(), QVariant(size)); + } + } else { + const QVariant::Type propType = mesh->property(propName.constData()).type(); + if (propType == QVariant::Int) { + mesh->setProperty(propName.constData(), QVariant(it.value().toInt())); + } else { + mesh->setProperty(propName.constData(), + QVariant(float(it.value().toDouble()))); + } + } + } + mesh->setObjectName(meshName); + m_meshMaterialDict[mesh] = json.value(KEY_MATERIAL).toString(); + m_meshDict.insert(id, mesh); + } + } } void GLTFImporter::processJSONImage(const QString &id, const QJsonObject &jsonObject) diff --git a/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp b/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp index 61a448666..befe7770d 100644 --- a/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp +++ b/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp @@ -52,6 +52,7 @@ #include <QtCore/qmath.h> #include <QtCore/qtemporarydir.h> #include <QtCore/qregularexpression.h> +#include <QtCore/qmetaobject.h> #include <QtGui/qvector2d.h> #include <QtGui/qvector4d.h> #include <QtGui/qmatrix4x4.h> @@ -140,6 +141,12 @@ inline QJsonArray vec2jsvec(const QVector<T> &v) return arr; } +inline QJsonArray size2jsvec(const QSize &size) { + QJsonArray arr; + arr << size.width() << size.height(); + return arr; +} + inline QJsonArray vec2jsvec(const QVector2D &v) { QJsonArray arr; @@ -272,27 +279,6 @@ GLTFExporter::~GLTFExporter() bool GLTFExporter::exportScene(QEntity *sceneRoot, const QString &outDir, const QString &exportName, const QVariantHash &options) { - m_buffer.clear(); - m_meshMap.clear(); - m_materialMap.clear(); - m_cameraMap.clear(); - m_lightMap.clear(); - m_transformMap.clear(); - m_imageMap.clear(); - m_textureIdMap.clear(); - m_meshInfo.clear(); - m_materialInfo.clear(); - m_cameraInfo.clear(); - m_lightInfo.clear(); - m_exportedFiles.clear(); - m_renderPassIdMap.clear(); - m_shaderInfo.clear(); - m_programInfo.clear(); - m_techniqueIdMap.clear(); - m_effectIdMap.clear(); - - delNode(m_rootNode); - m_bufferViewCount = 0; m_accessorCount = 0; m_meshCount = 0; @@ -405,9 +391,80 @@ bool GLTFExporter::exportScene(QEntity *sceneRoot, const QString &outDir, } } + // Clean up after export + + m_buffer.clear(); + m_meshMap.clear(); + m_materialMap.clear(); + m_cameraMap.clear(); + m_lightMap.clear(); + m_transformMap.clear(); + m_imageMap.clear(); + m_textureIdMap.clear(); + m_meshInfo.clear(); + m_materialInfo.clear(); + m_cameraInfo.clear(); + m_lightInfo.clear(); + m_exportedFiles.clear(); + m_renderPassIdMap.clear(); + m_shaderInfo.clear(); + m_programInfo.clear(); + m_techniqueIdMap.clear(); + m_effectIdMap.clear(); + qDeleteAll(m_defaultObjectCache); + m_defaultObjectCache.clear(); + m_propertyCache.clear(); + + delNode(m_rootNode); + return true; } +void GLTFExporter::cacheDefaultProperties(GLTFExporter::PropertyCacheType type) +{ + if (m_defaultObjectCache.contains(type)) + return; + + QObject *defaultObject = nullptr; + + switch (type) { + case TypeConeMesh: + defaultObject = new QConeMesh; + break; + case TypeCuboidMesh: + defaultObject = new QCuboidMesh; + break; + case TypeCylinderMesh: + defaultObject = new QCylinderMesh; + break; + case TypePlaneMesh: + defaultObject = new QPlaneMesh; + break; + case TypeSphereMesh: + defaultObject = new QSphereMesh; + break; + case TypeTorusMesh: + defaultObject = new QTorusMesh; + break; + default: + return; // Unsupported type + } + + // Store the default object for property comparisons + m_defaultObjectCache.insert(type, defaultObject); + + // Cache metaproperties of supported types (but not their parent class types) + const QMetaObject *meta = defaultObject->metaObject(); + QVector<QMetaProperty> properties; + properties.reserve(meta->propertyCount() - meta->propertyOffset()); + for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) { + if (meta->property(i).isWritable()) + properties.append(meta->property(i)); + } + + m_propertyCache.insert(type, properties); +} + // Copies textures from original locations to the temporary export directory. // If texture names conflict, they are renamed. void GLTFExporter::copyTextures() @@ -656,230 +713,245 @@ void GLTFExporter::parseMeshes() meshInfo.name = newMeshName(); meshInfo.materialName = m_materialInfo.value(m_materialMap.value(node)).name; - bool defaultType = qobject_cast<QConeMesh *>(mesh) || qobject_cast<QCuboidMesh *>(mesh) - || qobject_cast<QCylinderMesh *>(mesh) || qobject_cast<QPlaneMesh *>(mesh) - || qobject_cast<QSphereMesh *>(mesh) || qobject_cast<QTorusMesh *>(mesh); - - QGeometry *meshGeometry = nullptr; - QGeometryFactoryPtr geometryFunctorPtr = mesh->geometryFactory(); - if (defaultType || !geometryFunctorPtr.data()) { - meshGeometry = mesh->geometry(); + if (qobject_cast<QConeMesh *>(mesh)) { + meshInfo.meshType = TypeConeMesh; + meshInfo.meshTypeStr = QStringLiteral("cone"); + } else if (qobject_cast<QCuboidMesh *>(mesh)) { + meshInfo.meshType = TypeCuboidMesh; + meshInfo.meshTypeStr = QStringLiteral("cuboid"); + } else if (qobject_cast<QCylinderMesh *>(mesh)) { + meshInfo.meshType = TypeCylinderMesh; + meshInfo.meshTypeStr = QStringLiteral("cylinder"); + } else if (qobject_cast<QPlaneMesh *>(mesh)) { + meshInfo.meshType = TypePlaneMesh; + meshInfo.meshTypeStr = QStringLiteral("plane"); + } else if (qobject_cast<QSphereMesh *>(mesh)) { + meshInfo.meshType = TypeSphereMesh; + meshInfo.meshTypeStr = QStringLiteral("sphere"); + } else if (qobject_cast<QTorusMesh *>(mesh)) { + meshInfo.meshType = TypeTorusMesh; + meshInfo.meshTypeStr = QStringLiteral("torus"); } else { - // Execute the geometry functor to get the geometry, if it is available. - // Functor gives us the latest data if geometry has changed. - meshGeometry = geometryFunctorPtr.data()->operator()(); + meshInfo.meshType = TypeNone; } - if (!meshGeometry) { - qCWarning(GLTFExporterLog, "Ignoring mesh without geometry!"); - continue; - } - - QAttribute *indexAttrib = nullptr; - const quint16 *indexPtr = nullptr; - - struct VertexAttrib { - QAttribute *att; - const float *ptr; - QString usage; - uint offset; - uint stride; - int index; - }; - - QVector<VertexAttrib> vAttribs; - vAttribs.reserve(meshGeometry->attributes().size()); - - uint stride(0); - - for (QAttribute *att : meshGeometry->attributes()) { - if (att->attributeType() == QAttribute::IndexAttribute) { - indexAttrib = att; - indexPtr = reinterpret_cast<const quint16 *>(att->buffer()->data().constData()); + if (meshInfo.meshType != TypeNone) { + meshInfo.meshComponent = mesh; + cacheDefaultProperties(meshInfo.meshType); + + if (GLTFExporterLog().isDebugEnabled()) { + qCDebug(GLTFExporterLog, " Mesh #%i: (%ls/%ls)", meshCount, + qUtf16PrintableImpl(meshInfo.name), qUtf16PrintableImpl(meshInfo.originalName)); + qCDebug(GLTFExporterLog, " material: '%ls'", + qUtf16PrintableImpl(meshInfo.materialName)); + qCDebug(GLTFExporterLog, " basic mesh type: '%s'", + mesh->metaObject()->className()); + } + } else { + meshInfo.meshComponent = nullptr; + QGeometry *meshGeometry = nullptr; + QGeometryFactoryPtr geometryFunctorPtr = mesh->geometryFactory(); + if (!geometryFunctorPtr.data()) { + meshGeometry = mesh->geometry(); } else { - VertexAttrib vAtt; - vAtt.att = att; - vAtt.ptr = reinterpret_cast<const float *>(att->buffer()->data().constData()); - if (att->name() == VERTICES_ATTRIBUTE_NAME) - vAtt.usage = QStringLiteral("POSITION"); - else if (att->name() == NORMAL_ATTRIBUTE_NAME) - vAtt.usage = QStringLiteral("NORMAL"); - else if (att->name() == TEXTCOORD_ATTRIBUTE_NAME) - vAtt.usage = QStringLiteral("TEXCOORD_0"); - else if (att->name() == COLOR_ATTRIBUTE_NAME) - vAtt.usage = QStringLiteral("COLOR"); - else if (att->name() == TANGENT_ATTRIBUTE_NAME) - vAtt.usage = QStringLiteral("TANGENT"); - else - vAtt.usage = att->name(); - - vAtt.offset = att->byteOffset() / sizeof(float); - vAtt.index = vAtt.offset; - vAtt.stride = att->byteStride() > 0 - ? att->byteStride() / sizeof(float) - att->vertexSize() : 0; - stride += att->vertexSize(); - - vAttribs << vAtt; + // Execute the geometry functor to get the geometry, if it is available. + // Functor gives us the latest data if geometry has changed. + meshGeometry = geometryFunctorPtr.data()->operator()(); } - } - int attribCount(vAttribs.size()); - if (!attribCount) { - qCWarning(GLTFExporterLog, "Ignoring mesh without any attributes!"); - continue; - } + if (!meshGeometry) { + qCWarning(GLTFExporterLog, "Ignoring mesh without geometry!"); + continue; + } - // Default types use single interleaved buffer for all vertex data, but we must regenerate - // it as it is not available on the frontend by default. - QByteArray defaultVertexArray; - QByteArray defaultIndexArray; - if (defaultType) { - defaultVertexArray = - vAttribs.at(0).att->buffer()->dataGenerator().data()->operator()(); - const float *defaultVertexBufferPtr = - reinterpret_cast<const float *>(defaultVertexArray.constData()); - for (int i = 0; i < attribCount; i++) - vAttribs[i].ptr = defaultVertexBufferPtr; - - defaultIndexArray = indexAttrib->buffer()->dataGenerator().data()->operator()(); - indexPtr = reinterpret_cast<const quint16 *>(defaultIndexArray.constData()); - } + QAttribute *indexAttrib = nullptr; + const quint16 *indexPtr = nullptr; + + struct VertexAttrib { + QAttribute *att; + const float *ptr; + QString usage; + uint offset; + uint stride; + int index; + }; + + QVector<VertexAttrib> vAttribs; + vAttribs.reserve(meshGeometry->attributes().size()); + + uint stride(0); + + for (QAttribute *att : meshGeometry->attributes()) { + if (att->attributeType() == QAttribute::IndexAttribute) { + indexAttrib = att; + indexPtr = reinterpret_cast<const quint16 *>(att->buffer()->data().constData()); + } else { + VertexAttrib vAtt; + vAtt.att = att; + vAtt.ptr = reinterpret_cast<const float *>(att->buffer()->data().constData()); + if (att->name() == VERTICES_ATTRIBUTE_NAME) + vAtt.usage = QStringLiteral("POSITION"); + else if (att->name() == NORMAL_ATTRIBUTE_NAME) + vAtt.usage = QStringLiteral("NORMAL"); + else if (att->name() == TEXTCOORD_ATTRIBUTE_NAME) + vAtt.usage = QStringLiteral("TEXCOORD_0"); + else if (att->name() == COLOR_ATTRIBUTE_NAME) + vAtt.usage = QStringLiteral("COLOR"); + else if (att->name() == TANGENT_ATTRIBUTE_NAME) + vAtt.usage = QStringLiteral("TANGENT"); + else + vAtt.usage = att->name(); + + vAtt.offset = att->byteOffset() / sizeof(float); + vAtt.index = vAtt.offset; + vAtt.stride = att->byteStride() > 0 + ? att->byteStride() / sizeof(float) - att->vertexSize() : 0; + stride += att->vertexSize(); + + vAttribs << vAtt; + } + } - QByteArray vertexBuf; - const int vertexCount = vAttribs.at(0).att->count(); - vertexBuf.resize(stride * vertexCount * sizeof(float)); - float *p = reinterpret_cast<float *>(vertexBuf.data()); - - // Create interleaved buffer - for (int i = 0; i < vertexCount; ++i) { - for (int j = 0; j < attribCount; ++j) { - VertexAttrib &vAtt = vAttribs[j]; - for (uint k = 0; k < vAtt.att->vertexSize(); ++k) - *p++ = vAtt.ptr[vAtt.index++]; - vAtt.index += vAtt.stride; + int attribCount(vAttribs.size()); + if (!attribCount) { + qCWarning(GLTFExporterLog, "Ignoring mesh without any attributes!"); + continue; } - } - MeshInfo::BufferView vertexBufView; - vertexBufView.name = newBufferViewName(); - vertexBufView.length = vertexBuf.size(); - vertexBufView.offset = m_buffer.size(); - vertexBufView.componentType = GL_FLOAT; - vertexBufView.target = GL_ARRAY_BUFFER; - meshInfo.views.append(vertexBufView); - - QByteArray indexBuf; - MeshInfo::BufferView indexBufView; - uint indexCount = 0; - if (indexAttrib) { - const uint indexSize = indexAttrib->vertexBaseType() == QAttribute::UnsignedShort - ? sizeof(quint16) : sizeof(quint32); - indexCount = indexAttrib->count(); - uint srcIndex = indexAttrib->byteOffset() / indexSize; - const uint indexStride = indexAttrib->byteStride() - ? indexAttrib->byteStride() / indexSize - 1: 0; - indexBuf.resize(indexCount * indexSize); - if (indexSize == sizeof(quint32)) { - quint32 *dst = reinterpret_cast<quint32 *>(indexBuf.data()); - const quint32 *src = reinterpret_cast<const quint32 *>(indexPtr); - for (uint j = 0; j < indexCount; ++j) { - *dst++ = src[srcIndex++]; - srcIndex += indexStride; - } - } else { - quint16 *dst = reinterpret_cast<quint16 *>(indexBuf.data()); - for (uint j = 0; j < indexCount; ++j) { - *dst++ = indexPtr[srcIndex++]; - srcIndex += indexStride; + QByteArray vertexBuf; + const int vertexCount = vAttribs.at(0).att->count(); + vertexBuf.resize(stride * vertexCount * sizeof(float)); + float *p = reinterpret_cast<float *>(vertexBuf.data()); + + // Create interleaved buffer + for (int i = 0; i < vertexCount; ++i) { + for (int j = 0; j < attribCount; ++j) { + VertexAttrib &vAtt = vAttribs[j]; + for (uint k = 0; k < vAtt.att->vertexSize(); ++k) + *p++ = vAtt.ptr[vAtt.index++]; + vAtt.index += vAtt.stride; } } - indexBufView.name = newBufferViewName(); - indexBufView.length = indexBuf.size(); - indexBufView.offset = vertexBufView.offset + vertexBufView.length; - indexBufView.componentType = indexSize == sizeof(quint32) - ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; - indexBufView.target = GL_ELEMENT_ARRAY_BUFFER; - meshInfo.views.append(indexBufView); - } + MeshInfo::BufferView vertexBufView; + vertexBufView.name = newBufferViewName(); + vertexBufView.length = vertexBuf.size(); + vertexBufView.offset = m_buffer.size(); + vertexBufView.componentType = GL_FLOAT; + vertexBufView.target = GL_ARRAY_BUFFER; + meshInfo.views.append(vertexBufView); + + QByteArray indexBuf; + MeshInfo::BufferView indexBufView; + uint indexCount = 0; + if (indexAttrib) { + const uint indexSize = indexAttrib->vertexBaseType() == QAttribute::UnsignedShort + ? sizeof(quint16) : sizeof(quint32); + indexCount = indexAttrib->count(); + uint srcIndex = indexAttrib->byteOffset() / indexSize; + const uint indexStride = indexAttrib->byteStride() + ? indexAttrib->byteStride() / indexSize - 1: 0; + indexBuf.resize(indexCount * indexSize); + if (indexSize == sizeof(quint32)) { + quint32 *dst = reinterpret_cast<quint32 *>(indexBuf.data()); + const quint32 *src = reinterpret_cast<const quint32 *>(indexPtr); + for (uint j = 0; j < indexCount; ++j) { + *dst++ = src[srcIndex++]; + srcIndex += indexStride; + } + } else { + quint16 *dst = reinterpret_cast<quint16 *>(indexBuf.data()); + for (uint j = 0; j < indexCount; ++j) { + *dst++ = indexPtr[srcIndex++]; + srcIndex += indexStride; + } + } - MeshInfo::Accessor acc; - uint startOffset = 0; - - acc.bufferView = vertexBufView.name; - acc.stride = stride * sizeof(float); - acc.count = vertexCount; - acc.componentType = vertexBufView.componentType; - for (int i = 0; i < attribCount; ++i) { - const VertexAttrib &vAtt = vAttribs.at(i); - acc.name = newAccessorName(); - acc.usage = vAtt.usage; - acc.offset = startOffset * sizeof(float); - switch (vAtt.att->vertexSize()) { - case 1: - acc.type = QStringLiteral("SCALAR"); - break; - case 2: - acc.type = QStringLiteral("VEC2"); - break; - case 3: - acc.type = QStringLiteral("VEC3"); - break; - case 4: - acc.type = QStringLiteral("VEC4"); - break; - case 9: - acc.type = QStringLiteral("MAT3"); - break; - case 16: - acc.type = QStringLiteral("MAT4"); - break; - default: - qCWarning(GLTFExporterLog, "Invalid vertex size: %d", vAtt.att->vertexSize()); - break; + indexBufView.name = newBufferViewName(); + indexBufView.length = indexBuf.size(); + indexBufView.offset = vertexBufView.offset + vertexBufView.length; + indexBufView.componentType = indexSize == sizeof(quint32) + ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + indexBufView.target = GL_ELEMENT_ARRAY_BUFFER; + meshInfo.views.append(indexBufView); } - meshInfo.accessors.append(acc); - startOffset += vAtt.att->vertexSize(); - } - // Index - if (indexAttrib) { - acc.name = newAccessorName(); - acc.usage = QStringLiteral("INDEX"); - acc.bufferView = indexBufView.name; - acc.offset = 0; - acc.stride = 0; - acc.count = indexCount; - acc.componentType = indexBufView.componentType; - acc.type = QStringLiteral("SCALAR"); - meshInfo.accessors.append(acc); - } + MeshInfo::Accessor acc; + uint startOffset = 0; + + acc.bufferView = vertexBufView.name; + acc.stride = stride * sizeof(float); + acc.count = vertexCount; + acc.componentType = vertexBufView.componentType; + for (int i = 0; i < attribCount; ++i) { + const VertexAttrib &vAtt = vAttribs.at(i); + acc.name = newAccessorName(); + acc.usage = vAtt.usage; + acc.offset = startOffset * sizeof(float); + switch (vAtt.att->vertexSize()) { + case 1: + acc.type = QStringLiteral("SCALAR"); + break; + case 2: + acc.type = QStringLiteral("VEC2"); + break; + case 3: + acc.type = QStringLiteral("VEC3"); + break; + case 4: + acc.type = QStringLiteral("VEC4"); + break; + case 9: + acc.type = QStringLiteral("MAT3"); + break; + case 16: + acc.type = QStringLiteral("MAT4"); + break; + default: + qCWarning(GLTFExporterLog, "Invalid vertex size: %d", vAtt.att->vertexSize()); + break; + } + meshInfo.accessors.append(acc); + startOffset += vAtt.att->vertexSize(); + } - if (GLTFExporterLog().isDebugEnabled()) { - qCDebug(GLTFExporterLog, " Mesh #%i: (%ls/%ls)", meshCount, - qUtf16PrintableImpl(meshInfo.name), qUtf16PrintableImpl(meshInfo.originalName)); - qCDebug(GLTFExporterLog, " Vertex count: %i", vertexCount); - qCDebug(GLTFExporterLog, " Bytes per vertex: %i", stride); - qCDebug(GLTFExporterLog, " Vertex buffer size (bytes): %i", vertexBuf.size()); - qCDebug(GLTFExporterLog, " Index buffer size (bytes): %i", indexBuf.size()); - - QStringList sl; - for (auto bv : meshInfo.views) - sl << bv.name; - qCDebug(GLTFExporterLog) << " buffer views:" << sl; - sl.clear(); - for (auto acc : meshInfo.accessors) - sl << acc.name; - qCDebug(GLTFExporterLog) << " accessors:" << sl; - qCDebug(GLTFExporterLog, " material: '%ls'", - qUtf16PrintableImpl(meshInfo.materialName)); + // Index + if (indexAttrib) { + acc.name = newAccessorName(); + acc.usage = QStringLiteral("INDEX"); + acc.bufferView = indexBufView.name; + acc.offset = 0; + acc.stride = 0; + acc.count = indexCount; + acc.componentType = indexBufView.componentType; + acc.type = QStringLiteral("SCALAR"); + meshInfo.accessors.append(acc); + } + m_buffer.append(vertexBuf); + m_buffer.append(indexBuf); + + if (GLTFExporterLog().isDebugEnabled()) { + qCDebug(GLTFExporterLog, " Mesh #%i: (%ls/%ls)", meshCount, + qUtf16PrintableImpl(meshInfo.name), qUtf16PrintableImpl(meshInfo.originalName)); + qCDebug(GLTFExporterLog, " Vertex count: %i", vertexCount); + qCDebug(GLTFExporterLog, " Bytes per vertex: %i", stride); + qCDebug(GLTFExporterLog, " Vertex buffer size (bytes): %i", vertexBuf.size()); + qCDebug(GLTFExporterLog, " Index buffer size (bytes): %i", indexBuf.size()); + QStringList sl; + for (auto bv : meshInfo.views) + sl << bv.name; + qCDebug(GLTFExporterLog) << " buffer views:" << sl; + sl.clear(); + for (auto acc : meshInfo.accessors) + sl << acc.name; + qCDebug(GLTFExporterLog) << " accessors:" << sl; + qCDebug(GLTFExporterLog, " material: '%ls'", + qUtf16PrintableImpl(meshInfo.materialName)); + } } - m_buffer.append(vertexBuf); - m_buffer.append(indexBuf); - - m_meshInfo.insert(mesh, meshInfo); meshCount++; + m_meshInfo.insert(mesh, meshInfo); } qCDebug(GLTFExporterLog, "Total buffer size: %i", m_buffer.size()); @@ -1143,20 +1215,28 @@ bool GLTFExporter::saveScene() for (auto meshInfo : m_meshInfo.values()) { QJsonObject mesh; mesh["name"] = meshInfo.originalName; - QJsonArray prims; - QJsonObject prim; - prim["mode"] = 4; // triangles - QJsonObject attrs; - for (auto acc : meshInfo.accessors) { - if (acc.usage != QStringLiteral("INDEX")) - attrs[acc.usage] = acc.name; - else - prim["indices"] = acc.name; + if (meshInfo.meshType != TypeNone) { + QJsonObject properties; + exportGenericProperties(properties, meshInfo.meshType, meshInfo.meshComponent); + mesh["type"] = meshInfo.meshTypeStr; + mesh["properties"] = properties; + mesh["material"] = meshInfo.materialName; + } else { + QJsonArray prims; + QJsonObject prim; + prim["mode"] = 4; // triangles + QJsonObject attrs; + for (auto acc : meshInfo.accessors) { + if (acc.usage != QStringLiteral("INDEX")) + attrs[acc.usage] = acc.name; + else + prim["indices"] = acc.name; + } + prim["attributes"] = attrs; + prim["material"] = meshInfo.materialName; + prims.append(prim); + mesh["primitives"] = prims; } - prim["attributes"] = attrs; - prim["material"] = meshInfo.materialName; - prims.append(prim); - mesh["primitives"] = prims; meshes[meshInfo.name] = mesh; } if (meshes.size()) @@ -1640,6 +1720,20 @@ void GLTFExporter::exportMaterials(QJsonObject &materials) } } +void GLTFExporter::exportGenericProperties(QJsonObject &jsonObj, PropertyCacheType type, + QObject *obj) +{ + QVector<QMetaProperty> properties = m_propertyCache.value(type); + QObject *defaultObject = m_defaultObjectCache.value(type); + for (const QMetaProperty &property : properties) { + // Only output property if it is different from default + QVariant defaultValue = defaultObject->property(property.name()); + QVariant objectValue = obj->property(property.name()); + if (defaultValue != objectValue) + setVarToJSonObject(jsonObj, QString::fromLatin1(property.name()), objectValue); + } +} + void GLTFExporter::clearOldExport(const QString &dir) { // Look for .qrc file with same name @@ -1946,9 +2040,15 @@ void GLTFExporter::setVarToJSonObject(QJsonObject &jsObj, const QString &key, co case QMetaType::Bool: jsObj[key] = var.toBool(); break; + case QMetaType::Int: + jsObj[key] = var.toInt(); + break; case QMetaType::Float: jsObj[key] = var.value<float>(); break; + case QMetaType::QSize: + jsObj[key] = size2jsvec(var.value<QSize>()); + break; case QMetaType::QVector2D: jsObj[key] = vec2jsvec(var.value<QVector2D>()); break; diff --git a/src/plugins/sceneparsers/gltfexport/gltfexporter.h b/src/plugins/sceneparsers/gltfexport/gltfexporter.h index 5dd2e337b..e43bfe29c 100644 --- a/src/plugins/sceneparsers/gltfexport/gltfexporter.h +++ b/src/plugins/sceneparsers/gltfexport/gltfexporter.h @@ -98,6 +98,16 @@ public: }; private: + enum PropertyCacheType { + TypeNone = 0, + TypeConeMesh, + TypeCuboidMesh, + TypeCylinderMesh, + TypePlaneMesh, + TypeSphereMesh, + TypeTorusMesh + }; + struct MeshInfo { struct BufferView { BufferView() : bufIndex(0), offset(0), length(0), componentType(0), target(0) { } @@ -124,6 +134,9 @@ private: QString name; // generated QString originalName; // may be empty QString materialName; + Qt3DRender::QGeometryRenderer *meshComponent; + PropertyCacheType meshType; + QString meshTypeStr; }; struct MaterialInfo { @@ -202,6 +215,7 @@ private: QVector<Node *> children; }; + void cacheDefaultProperties(PropertyCacheType type); void copyTextures(); void createShaders(); void parseEntities(const Qt3DCore::QEntity *entity, Node *parentNode); @@ -218,6 +232,7 @@ private: void delNode(Node *n); QString exportNodes(Node *n, QJsonObject &nodes); void exportMaterials(QJsonObject &materials); + void exportGenericProperties(QJsonObject &jsonObj, PropertyCacheType type, QObject *obj); void clearOldExport(const QString &dir); void exportParameter(QJsonObject &jsonObj, const QString &name, const QVariant &variant); void exportRenderStates(QJsonObject &jsonObj, const QRenderPass *pass); @@ -275,6 +290,8 @@ private: QHash<Qt3DRender::QRenderPass *, QString> m_renderPassIdMap; QHash<Qt3DRender::QEffect *, QString> m_effectIdMap; QHash<Qt3DRender::QTechnique *, QString> m_techniqueIdMap; + QHash<PropertyCacheType, QObject *> m_defaultObjectCache; + QHash<PropertyCacheType, QVector<QMetaProperty> > m_propertyCache; QHash<Qt3DRender::QGeometryRenderer *, MeshInfo> m_meshInfo; QHash<Qt3DRender::QMaterial *, MaterialInfo> m_materialInfo; @@ -283,7 +300,6 @@ private: QHash<Qt3DRender::QShaderProgram *, ProgramInfo> m_programInfo; QVector<ShaderInfo> m_shaderInfo; - Node *m_rootNode; bool m_rootNodeEmpty; QSet<QString> m_exportedFiles; diff --git a/tests/auto/render/gltfplugins/tst_gltfplugins.cpp b/tests/auto/render/gltfplugins/tst_gltfplugins.cpp index 1a49771d8..3ab10f17a 100644 --- a/tests/auto/render/gltfplugins/tst_gltfplugins.cpp +++ b/tests/auto/render/gltfplugins/tst_gltfplugins.cpp @@ -723,6 +723,7 @@ void tst_gltfPlugins::compareComponents(Qt3DCore::QComponent *c1, Qt3DCore::QCom // Transform names are lost in export, as the transform is just part of the node item if (!qobject_cast<Qt3DCore::QTransform *>(c1)) QCOMPARE(c1->objectName(), c2->objectName()); + QCOMPARE(c1->metaObject()->className(), c2->metaObject()->className()); // Meshes are all imported as generic meshes if (auto mesh1 = qobject_cast<Qt3DRender::QGeometryRenderer *>(c1)) { auto mesh2 = qobject_cast<Qt3DRender::QGeometryRenderer *>(c2); @@ -773,7 +774,6 @@ void tst_gltfPlugins::compareComponents(Qt3DCore::QComponent *c1, Qt3DCore::QCom Qt3DRender::QAttribute::IndexAttribute, geometry2)); } else { - QCOMPARE(c1->metaObject()->className(), c2->metaObject()->className()); int count = c1->metaObject()->propertyCount(); for (int i = 0; i < count; i++) { auto property = c1->metaObject()->property(i); |