summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2016-12-09 17:53:04 +0200
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2016-12-13 13:49:46 +0000
commit3395491984ae9ad5f73247979b7ec921ad20ba7f (patch)
treefe139b8f8b81781673a774dbd2d4a2a26f2243b8
parent68472bdf4447013f6bf2c31cda8f2b717de0246c (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.cpp196
-rw-r--r--src/plugins/sceneparsers/gltfexport/gltfexporter.cpp576
-rw-r--r--src/plugins/sceneparsers/gltfexport/gltfexporter.h18
-rw-r--r--tests/auto/render/gltfplugins/tst_gltfplugins.cpp2
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);