From dc093d844948715ab2d77eb5eb38dd81b2655521 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 15 May 2019 13:55:24 +0300 Subject: Add methods to create/delete multiple elements/materials at once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also modified the notification signals to notify multiple creations with a single signal. Task-number: QT3DS-3376 Change-Id: I75bc867601d3f7943733b451ddfad18fab9383ea Reviewed-by: Mahmoud Badri Reviewed-by: Tomi Korpipää --- src/Runtime/Source/engine/Qt3DSRuntimeView.cpp | 38 +- src/Runtime/Source/engine/Qt3DSRuntimeView.h | 15 +- src/Runtime/Source/runtime/Qt3DSIScriptBridge.h | 18 +- src/Runtime/Source/runtime/Qt3DSPresentation.h | 4 +- src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp | 1209 ++++++++++++----------- src/Runtime/Source/viewer/Qt3DSViewerApp.cpp | 24 +- src/Runtime/Source/viewer/Qt3DSViewerApp.h | 18 +- src/Runtime/api/studio3d/q3dscommandqueue.cpp | 52 +- src/Runtime/api/studio3d/q3dscommandqueue_p.h | 9 +- src/Runtime/api/studio3d/q3dspresentation.cpp | 65 +- src/Runtime/api/studio3d/q3dspresentation.h | 12 +- src/Runtime/api/studio3dqml/q3dsrenderer.cpp | 33 +- src/Runtime/api/studio3dqml/q3dsrenderer.h | 4 +- src/Runtime/api/studio3dqml/q3dsstudio3d.cpp | 8 +- src/Runtime/api/studio3dqml/q3dsstudio3d.h | 2 +- tests/auto/viewer/tst_qt3dsviewer.cpp | 191 ++-- tests/auto/viewer/tst_qt3dsviewer.h | 2 +- 17 files changed, 929 insertions(+), 775 deletions(-) diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp index 3e95f46d..eb17a9b2 100644 --- a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp +++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp @@ -211,10 +211,11 @@ public: float dataInputMax(const QString &name) const override; float dataInputMin(const QString &name) const override; - void createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties) override; - void deleteElement(const QString &elementPath) override; - void createMaterial(const QString &elementPath, const QString &materialDefinition) override; + void createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties) override; + void deleteElements(const QStringList &elementPaths) override; + void createMaterials(const QString &elementPath, + const QStringList &materialDefinitions) override; void SetAttribute(const char *elementPath, const char *attributeName, const char *value) override; bool GetAttribute(const char *elementPath, const char *attributeName, void *value) override; @@ -309,10 +310,10 @@ bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayed QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigPresentationReady, signalProxy(), &QRuntimeViewSignalProxy::SigPresentationReady); - QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigElementCreated, - signalProxy(), &QRuntimeViewSignalProxy::SigElementCreated); - QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigMaterialCreated, - signalProxy(), &QRuntimeViewSignalProxy::SigMaterialCreated); + QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigElementsCreated, + signalProxy(), &QRuntimeViewSignalProxy::SigElementsCreated); + QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigMaterialsCreated, + signalProxy(), &QRuntimeViewSignalProxy::SigMaterialsCreated); m_TimeProvider.Reset(); return true; @@ -619,34 +620,35 @@ float CRuntimeView::dataInputMin(const QString &name) const return 0; } -void CRuntimeView::createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties) +void CRuntimeView::createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties) { if (m_Application) { Q3DStudio::CQmlEngine &theBridgeEngine = static_cast(m_RuntimeFactoryCore->GetScriptEngineQml()); - theBridgeEngine.createElement(parentElementPath, slideName, properties, - &m_RuntimeFactory->GetQt3DSRenderContext().GetRenderer()); + theBridgeEngine.createElements(parentElementPath, slideName, properties, + &m_RuntimeFactory->GetQt3DSRenderContext().GetRenderer()); } } -void CRuntimeView::deleteElement(const QString &elementPath) +void CRuntimeView::deleteElements(const QStringList &elementPaths) { if (m_Application) { Q3DStudio::CQmlEngine &theBridgeEngine = static_cast(m_RuntimeFactoryCore->GetScriptEngineQml()); - theBridgeEngine.deleteElement(elementPath, - &m_RuntimeFactory->GetQt3DSRenderContext().GetRenderer()); + theBridgeEngine.deleteElements(elementPaths, + &m_RuntimeFactory->GetQt3DSRenderContext().GetRenderer()); } } -void CRuntimeView::createMaterial(const QString &elementPath, const QString &materialDefinition) +void CRuntimeView::createMaterials(const QString &elementPath, + const QStringList &materialDefinitions) { if (m_Application) { Q3DStudio::CQmlEngine &theBridgeEngine = static_cast(m_RuntimeFactoryCore->GetScriptEngineQml()); - theBridgeEngine.createMaterial( - elementPath, materialDefinition, + theBridgeEngine.createMaterials( + elementPath, materialDefinitions, &m_RuntimeFactory->GetQt3DSRenderContext().GetCustomMaterialSystem(), &m_RuntimeFactory->GetQt3DSRenderContext().GetDynamicObjectSystem(), &m_RuntimeFactory->GetQt3DSRenderContext().GetRenderer()); diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.h b/src/Runtime/Source/engine/Qt3DSRuntimeView.h index f728f220..38d30fa7 100644 --- a/src/Runtime/Source/engine/Qt3DSRuntimeView.h +++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.h @@ -44,6 +44,8 @@ #include "Qt3DSPresentation.h" #include "Qt3DSRenderRuntimeBinding.h" #include +#include +#include #include typedef void (*qml_Function)(void *inUserData); @@ -56,8 +58,8 @@ Q_SIGNALS: void SigSlideExited(const QString &elementPath, unsigned int index, const QString &name); void SigCustomSignal(const QString &elementPath, const QString &name); void SigPresentationReady(); - void SigElementCreated(const QString &elementName, const QString &error); - void SigMaterialCreated(const QString &name, const QString &error); + void SigElementsCreated(const QStringList &elementPaths, const QString &error); + void SigMaterialsCreated(const QStringList &materialNames, const QString &error); }; namespace qt3ds { @@ -192,10 +194,11 @@ public: virtual QList dataInputs() const = 0; virtual float dataInputMax(const QString &name) const = 0; virtual float dataInputMin(const QString &name) const = 0; - virtual void createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties) = 0; - virtual void deleteElement(const QString &elementPath) = 0; - virtual void createMaterial(const QString &elementPath, const QString &materialDefinition) = 0; + virtual void createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties) = 0; + virtual void deleteElements(const QStringList &elementPaths) = 0; + virtual void createMaterials(const QString &elementPath, + const QStringList &materialDefinitions) = 0; virtual void SetAttribute(const char *elementPath, const char *attributeName, const char *value) = 0; virtual bool GetAttribute(const char *elementPath, const char *attributeName, void *value) = 0; diff --git a/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h b/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h index 8edced36..69a4be66 100644 --- a/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h +++ b/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h @@ -36,6 +36,8 @@ #include "Qt3DSApplication.h" #include +#include +#include namespace qt3ds { namespace runtime { @@ -161,15 +163,15 @@ public: // Elements const QString &name, const QVariant &value, qt3ds::runtime::DataInputValueRole property = qt3ds::runtime::DataInputValueRole::Value) = 0; - virtual void createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties, - qt3ds::render::IQt3DSRenderer *renderer) = 0; - virtual void deleteElement(const QString &elementPath, - qt3ds::render::IQt3DSRenderer *renderer) = 0; - virtual void createMaterial(const QString &elementPath, const QString &materialDefinition, - qt3ds::render::ICustomMaterialSystem *customMaterialSystem, - qt3ds::render::IDynamicObjectSystem *dynamicObjectSystem, + virtual void createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties, qt3ds::render::IQt3DSRenderer *renderer) = 0; + virtual void deleteElements(const QStringList &elementPath, + qt3ds::render::IQt3DSRenderer *renderer) = 0; + virtual void createMaterials(const QString &elementPath, const QStringList &materialDefinitions, + qt3ds::render::ICustomMaterialSystem *customMaterialSystem, + qt3ds::render::IDynamicObjectSystem *dynamicObjectSystem, + qt3ds::render::IQt3DSRenderer *renderer) = 0; public: // Components virtual void GotoSlide(const char *component, const char *slideName, diff --git a/src/Runtime/Source/runtime/Qt3DSPresentation.h b/src/Runtime/Source/runtime/Qt3DSPresentation.h index ff5656e3..b2edd416 100644 --- a/src/Runtime/Source/runtime/Qt3DSPresentation.h +++ b/src/Runtime/Source/runtime/Qt3DSPresentation.h @@ -51,8 +51,8 @@ Q_SIGNALS: void SigSlideExited(const QString &elementPath, unsigned int index, const QString &name); void SigCustomSignal(const QString &elementPath, const QString &name); void SigPresentationReady(); - void SigElementCreated(const QString &elementName, const QString &error); - void SigMaterialCreated(const QString &name, const QString &error); + void SigElementsCreated(const QStringList &elementPaths, const QString &error); + void SigMaterialsCreated(const QStringList &materialNames, const QString &error); }; namespace qt3ds { diff --git a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp index c837aad1..448fc2be 100644 --- a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp +++ b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp @@ -427,16 +427,15 @@ public: void FireEvent(const char *element, const char *evtName) override; void SetDataInputValue(const QString &name, const QVariant &value, qt3ds::runtime::DataInputValueRole valueRole) override; - void createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties, - qt3ds::render::IQt3DSRenderer *renderer) override; - void deleteElement(const QString &elementPath, - qt3ds::render::IQt3DSRenderer *renderer) override; - void createMaterial(const QString &elementPath, const QString &materialDefinition, - qt3ds::render::ICustomMaterialSystem *customMaterialSystem, - IDynamicObjectSystem *dynamicObjectSystem, + void createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties, qt3ds::render::IQt3DSRenderer *renderer) override; - //void createMesh() override; // TODO (QT3DS-3378) + void deleteElements(const QStringList &elementPaths, + qt3ds::render::IQt3DSRenderer *renderer) override; + void createMaterials(const QString &elementPath, const QStringList &materialDefinitions, + qt3ds::render::ICustomMaterialSystem *customMaterialSystem, + IDynamicObjectSystem *dynamicObjectSystem, + qt3ds::render::IQt3DSRenderer *renderer) override; void GotoSlide(const char *component, const char *slideName, const SScriptEngineGotoSlideArgs &inArgs) override; @@ -519,9 +518,12 @@ private: QVector3D parseFloat3Property(const QString &propValue); QVector4D parseFloat4Property(const QString &propValue); - void notifyElementCreation(CPresentation *pres, const QString &elementName, + void notifyElementCreation(CPresentation *pres, const QStringList &elementNames, const QString &error); - void notifyMaterialCreation(CPresentation *pres, const QString &name, const QString &error); + void notifyMaterialCreation(CPresentation *pres, const QStringList &materialNames, + const QString &error); + void deleteElements(const QVector &elements, + qt3ds::render::IQt3DSRenderer *renderer); }; CQmlEngineImpl::CQmlEngineImpl(NVFoundationBase &fnd, ITimeProvider &) @@ -919,24 +921,37 @@ void CQmlEngineImpl::SetDataInputValue( static int _idCounter = 0; -void CQmlEngineImpl::createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties, - qt3ds::render::IQt3DSRenderer *renderer) +void CQmlEngineImpl::createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties, + qt3ds::render::IQt3DSRenderer *renderer) { + int elementIndex = -1; QString error; CPresentation *presentation = nullptr; - QString elementPath = parentElementPath + QLatin1Char('.') - + properties.value(QStringLiteral("name")).toString(); + QStringList elementPaths; + elementPaths.reserve(properties.size()); + for (auto &props : properties) { + elementPaths << parentElementPath + QLatin1Char('.') + + props.value(QStringLiteral("name")).toString(); + } + QVector createdElements; + TElement *parentElement = nullptr; + + auto handleError = [&]() { + if (!error.isEmpty()) + deleteElements(createdElements, renderer); + notifyElementCreation(presentation, elementPaths, error); + }; // Resolve parent element QByteArray theParentPath = parentElementPath.toUtf8(); - TElement *parentElement = getTarget(theParentPath.constData()); + parentElement = getTarget(theParentPath.constData()); if (!parentElement) { - error = QStringLiteral("Invalid parent element"); + error = QObject::tr("Invalid parent element: '%1'").arg(parentElementPath); TElement *sceneElement = getTarget("Scene"); presentation = static_cast(sceneElement->GetBelongedPresentation()); - notifyElementCreation(presentation, elementPath, error); + handleError(); return; } @@ -945,8 +960,8 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri if (!parentTranslator || !qt3ds::render::GraphObjectTypes::IsNodeType( parentTranslator->GetUIPType())) { - error = QStringLiteral("Parent element is not a valid node"); - notifyElementCreation(presentation, elementPath, error); + error = QObject::tr("Parent element is not a valid node: '%1'").arg(parentElementPath); + handleError(); return; } @@ -954,304 +969,271 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri auto &parentObject = static_cast(parentTranslator->RenderObject()); presentation = static_cast(parentElement->GetBelongedPresentation()); - ++_idCounter; - // Resolve slide QByteArray theSlideName = slideName.toUtf8(); ISlideSystem &slideSystem = presentation->GetSlideSystem(); int slideIndex = slideSystem.FindSlide(component, theSlideName.constData()); int currentSlide = static_cast(component).GetCurrentSlide(); if (slideIndex == 0xff) { - error = QStringLiteral("Invalid slide name for time context: '%1'").arg(slideName); - notifyElementCreation(presentation, elementPath, error); - return; - } - - // Remove properties requiring custom handling - QHash theProperties = properties; - QString newElementName = theProperties.take(QStringLiteral("name")).toString(); - QString refMatName = theProperties.take(QStringLiteral("material")).toString(); - if (refMatName.startsWith(QLatin1Char('#'))) // Absolute reference - refMatName = refMatName.mid(1); - else if (!refMatName.isEmpty() && !refMatName.contains(QLatin1Char('/'))) - refMatName = QStringLiteral("/") + refMatName; - - if (newElementName.isEmpty()) - newElementName = QStringLiteral("NewElement_%1").arg(_idCounter); - QByteArray newElementNameBa = newElementName.toUtf8(); - - // Make sure the name is not duplicate - TElement *existingChild - = parentElement->FindChild(CHash::HashString(newElementNameBa.constData())); - if (existingChild) { - qWarning() << __FUNCTION__ - << "The specified parent" << parentElementPath - << "already has a child with the same name:" << newElementName; - error = QStringLiteral("Element already exists"); - notifyElementCreation(presentation, elementPath, error); + error = QObject::tr("Invalid slide name for time context: '%1'").arg(slideName); + handleError(); return; } auto &strTable = presentation->GetStringTable(); - const CRegisteredString regName = strTable.RegisterStr(newElementNameBa); - // TODO: Support also some non-model element types, like group and text elements (QT3DS-3381) - const CRegisteredString elementType = strTable.RegisterStr("Model"); - const CRegisteredString elementSubType; - TPropertyDescAndValueList elementProperties; - // Set default values for missing mandatory properties const QString sourcePathPropName = QStringLiteral("sourcepath"); const QString startTimePropName = QStringLiteral("starttime"); const QString endTimePropName = QStringLiteral("endtime"); const QString eyeBallPropName = QStringLiteral("eyeball"); + Q3DStudio::UVariant attValue; - bool eyeBall = true; - theProperties.value(QStringLiteral("eyeball"), true).toBool(); - if (!theProperties.contains(sourcePathPropName)) { - addStringAttribute(strTable, elementProperties, sourcePathPropName, - QStringLiteral("#Cube")); - } - if (!theProperties.contains(startTimePropName)) { - parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue); - addIntAttribute(strTable, elementProperties, startTimePropName, int(attValue.m_INT32)); - } - if (!theProperties.contains(endTimePropName)) { - parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue); - addIntAttribute(strTable, elementProperties, endTimePropName, int(attValue.m_INT32)); - } - if (!theProperties.contains(eyeBallPropName)) - addBoolAttribute(strTable, elementProperties, eyeBallPropName, true); - else - eyeBall = theProperties.value(QStringLiteral("eyeball")).toBool(); - - QHashIterator it(theProperties); - while (it.hasNext()) { - it.next(); - switch (it.value().type()) { - case QVariant::Double: - addFloatAttribute(strTable, elementProperties, it.key(), it.value().toFloat()); - break; - case QVariant::Bool: - addBoolAttribute(strTable, elementProperties, it.key(), it.value().toBool()); - break; - case QVariant::Int: - addIntAttribute(strTable, elementProperties, it.key(), it.value().toInt()); - break; - case QVariant::String: - addStringAttribute(strTable, elementProperties, it.key(), it.value().toString()); - break; - case QVariant::Vector3D: { - QVector3D vec = it.value().value(); - if (it.key() == QLatin1String("rotation")) { - vec.setX(qDegreesToRadians(vec.x())); - vec.setY(qDegreesToRadians(vec.y())); - vec.setZ(qDegreesToRadians(vec.z())); + parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue); + const int parentStartTime = int(attValue.m_INT32); + parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue); + const int parentEndTime = int(attValue.m_INT32); + + for (const auto ¤tProperties : properties) { + ++_idCounter; + ++elementIndex; + + // Remove properties requiring custom handling + QHash theProperties = currentProperties; + QString newElementName = theProperties.take(QStringLiteral("name")).toString(); + QString refMatName = theProperties.take(QStringLiteral("material")).toString(); + if (refMatName.startsWith(QLatin1Char('#'))) // Absolute reference + refMatName = refMatName.mid(1); + else if (!refMatName.isEmpty() && !refMatName.contains(QLatin1Char('/'))) + refMatName = QStringLiteral("/") + refMatName; + + if (newElementName.isEmpty()) + newElementName = QStringLiteral("NewElement_%1").arg(_idCounter); + QByteArray newElementNameBa = newElementName.toUtf8(); + + // Make sure the name is not duplicate + TElement *existingChild + = parentElement->FindChild(CHash::HashString(newElementNameBa.constData())); + if (existingChild) { + error = QObject::tr("Element already exists: '%1'").arg(elementPaths[elementIndex]); + handleError(); + return; + } + + const CRegisteredString regName = strTable.RegisterStr(newElementNameBa); + // TODO: Support also some non-model elements, like group and text elements (QT3DS-3381) + const CRegisteredString elementType = strTable.RegisterStr("Model"); + const CRegisteredString elementSubType; + TPropertyDescAndValueList elementProperties; + + // Set default values for missing mandatory properties + bool eyeBall = true; + theProperties.value(eyeBallPropName, true).toBool(); + if (!theProperties.contains(sourcePathPropName)) { + addStringAttribute(strTable, elementProperties, sourcePathPropName, + QStringLiteral("#Cube")); + } + if (!theProperties.contains(startTimePropName)) + addIntAttribute(strTable, elementProperties, startTimePropName, parentStartTime); + if (!theProperties.contains(endTimePropName)) + addIntAttribute(strTable, elementProperties, endTimePropName, parentEndTime); + if (!theProperties.contains(eyeBallPropName)) + addBoolAttribute(strTable, elementProperties, eyeBallPropName, true); + else + eyeBall = theProperties.value(eyeBallPropName).toBool(); + + QHashIterator it(theProperties); + while (it.hasNext()) { + it.next(); + switch (it.value().type()) { + case QVariant::Double: + addFloatAttribute(strTable, elementProperties, it.key(), it.value().toFloat()); + break; + case QVariant::Bool: + addBoolAttribute(strTable, elementProperties, it.key(), it.value().toBool()); + break; + case QVariant::Int: + addIntAttribute(strTable, elementProperties, it.key(), it.value().toInt()); + break; + case QVariant::String: + addStringAttribute(strTable, elementProperties, it.key(), it.value().toString()); + break; + case QVariant::Vector3D: { + QVector3D vec = it.value().value(); + if (it.key() == QLatin1String("rotation")) { + vec.setX(qDegreesToRadians(vec.x())); + vec.setY(qDegreesToRadians(vec.y())); + vec.setZ(qDegreesToRadians(vec.z())); + } + // TODO: Need to support also colors if non-model elements what need colors are + // TODO: supported (QT3DS-3381) + QStringList atts; + atts << (it.key() + QLatin1String(".x")) + << (it.key() + QLatin1String(".y")) + << (it.key() + QLatin1String(".z")); + addFloat3Attribute(strTable, elementProperties, atts, vec); + break; + } + default: + error = QObject::tr("Unsupported property type for: '%1'").arg(it.key()); + handleError(); + return; } - // TODO: Need to support also colors if non-model elements what need colors are - // TODO: supported (QT3DS-3381) - QStringList atts; - atts << (it.key() + QLatin1String(".x")) - << (it.key() + QLatin1String(".y")) - << (it.key() + QLatin1String(".z")); - addFloat3Attribute(strTable, elementProperties, atts, vec); - break; } - default: - error = QStringLiteral("Unsupported property type for: '%1'").arg(it.key()); - notifyElementCreation(presentation, elementPath, error); + + // Create new element + TElement &newElem = m_Application->GetElementAllocator().CreateElement( + regName, elementType, elementSubType, + toConstDataRef(elementProperties.data(), QT3DSU32(elementProperties.size())), + presentation, parentElement, false); + newElem.m_Path = strTable.RegisterStr(elementPaths[elementIndex]); + + // Insert the new element into the correct slide + if (!slideSystem.addSlideElement(component, slideIndex, newElem, eyeBall)) { + // Delete created element if adding to slide failed + m_Application->GetElementAllocator().ReleaseElement(newElem, true); + error = QObject::tr("Failed to add the new element to a slide"); + handleError(); return; } - } - // Create new element - TElement &newElem = m_Application->GetElementAllocator().CreateElement( - regName, elementType, elementSubType, - toConstDataRef(elementProperties.data(), QT3DSU32(elementProperties.size())), - presentation, parentElement, false); - newElem.m_Path = strTable.RegisterStr(elementPath); - - // Insert the new element into the correct slide - if (!slideSystem.addSlideElement(component, slideIndex, newElem, eyeBall)) { - // Delete created element if adding to slide failed - m_Application->GetElementAllocator().ReleaseElement(newElem, true); - error = QStringLiteral("Failed to add the new element to a slide"); - notifyElementCreation(presentation, elementPath, error); - return; - } + // Create material element + const CRegisteredString matName = strTable.RegisterStr("refmat"); + const CRegisteredString matType = strTable.RegisterStr("ReferencedMaterial"); + TPropertyDescAndValueList matProperties; + TElement &newMatElem = m_Application->GetElementAllocator().CreateElement( + matName, matType, elementSubType, + toConstDataRef(matProperties.data(), QT3DSU32(matProperties.size())), + presentation, &newElem, false); - // Create material element - const CRegisteredString matName = strTable.RegisterStr("refmat"); - const CRegisteredString matType = strTable.RegisterStr("ReferencedMaterial"); - TPropertyDescAndValueList matProperties; - TElement &newMatElem = m_Application->GetElementAllocator().CreateElement( - matName, matType, elementSubType, - toConstDataRef(matProperties.data(), QT3DSU32(matProperties.size())), - presentation, &newElem, false); - - QString matElemPath = elementPath + QLatin1String(".refmat"); - newMatElem.m_Path = strTable.RegisterStr(matElemPath); - - if (!slideSystem.addSlideElement(component, slideIndex, newMatElem, eyeBall)) { - // Delete created element and material element if adding to slide failed - m_Application->GetElementAllocator().ReleaseElement(newElem, true); - error = QStringLiteral("Failed to add the new material element to a slide"); - notifyElementCreation(presentation, elementPath, error); - return; - } + QString matElemPath = elementPaths[elementIndex] + QLatin1String(".refmat"); + newMatElem.m_Path = strTable.RegisterStr(matElemPath); - // First check if we can resolve the referenced material before creating any graph objects - // Find a match in material container - // If the specified material is not available in original presentation, or was not specified, - // use the first material found as placeholder - TElement *rootElement = presentation->GetRoot(); - TElement *container = rootElement->FindChild(CHash::HashString("__Container")); - TElement *firstChild = nullptr; - SGraphObject *referencedMaterial = nullptr; - if (container) { - TElement *nextChild = container->GetChild(); - firstChild = nextChild; - while (nextChild) { - QString childName = QString::fromUtf8(nextChild->m_Name); - if (childName.endsWith(refMatName)) { + if (!slideSystem.addSlideElement(component, slideIndex, newMatElem, eyeBall)) { + // Delete created element and material element if adding to slide failed + m_Application->GetElementAllocator().ReleaseElement(newElem, true); + error = QObject::tr("Failed to add the new material element to a slide"); + handleError(); + return; + } + + // First check if we can resolve the referenced material before creating any graph objects + // Find a match in material container + // If the specified material is not available in original presentation, or was not + // specified, use the first material found as placeholder + TElement *rootElement = presentation->GetRoot(); + TElement *container = rootElement->FindChild(CHash::HashString("__Container")); + TElement *firstChild = nullptr; + SGraphObject *referencedMaterial = nullptr; + if (container) { + TElement *nextChild = container->GetChild(); + firstChild = nextChild; + while (nextChild) { + QString childName = QString::fromUtf8(nextChild->m_Name); + if (childName.endsWith(refMatName)) { + auto tr = static_cast( + nextChild->GetAssociation()); + referencedMaterial = static_cast( + &tr->RenderObject()); + break; + } + nextChild = nextChild->GetSibling(); + } + } + + if (!referencedMaterial) { + // Empty material is assumed to be deliberate, so don't warn in that case + if (!refMatName.isEmpty()) { + qWarning() << __FUNCTION__ << "Requested material" << refMatName + << "was not found. Trying to find a fallback material."; + } + if (firstChild) { auto tr = static_cast( - nextChild->GetAssociation()); + firstChild->GetAssociation()); referencedMaterial = static_cast( &tr->RenderObject()); - break; } - nextChild = nextChild->GetSibling(); + if (!referencedMaterial) { + // We could create default material into the container in case there is no materials + // in there, but it is unlikely that such a presentation would be used in practice. + m_Application->GetElementAllocator().ReleaseElement(newElem, true); + error = QObject::tr("Unable to resolve a fallback material"); + handleError(); + return; + } } - } - if (!referencedMaterial) { - // Empty material is assumed to be deliberate, so don't warn in that case - if (!refMatName.isEmpty()) { - qWarning() << __FUNCTION__ << "Requested material" << refMatName - << "was not found. Trying to find a fallback material."; - } - if (firstChild) { - auto tr = static_cast(firstChild->GetAssociation()); - referencedMaterial = static_cast(&tr->RenderObject()); - } - if (!referencedMaterial) { - // We could create default material into the container in case there is no materials - // in there, but it is unlikely that such a presentation would be used in practice. - m_Application->GetElementAllocator().ReleaseElement(newElem, true); - error = QStringLiteral("Unable to resolve a fallback material"); - notifyElementCreation(presentation, elementPath, error); - return; - } - } + // Create model SGraphObject + NVAllocatorCallback &allocator = presentation->GetScene()->allocator(); + qt3ds::render::SModel *newObject = QT3DS_NEW(allocator, qt3ds::render::SModel)(); + newObject->m_Id = strTable.RegisterStr((QByteArrayLiteral("_newObject_") + + QByteArray::number(_idCounter)).constData()); + parentObject.AddChild(*newObject); - // Create model SGraphObject - NVAllocatorCallback &allocator = presentation->GetScene()->allocator(); - qt3ds::render::SModel *newObject = QT3DS_NEW(allocator, qt3ds::render::SModel)(); - newObject->m_Id = strTable.RegisterStr((QByteArrayLiteral("_newObject_") - + QByteArray::number(_idCounter)).constData()); - parentObject.AddChild(*newObject); - - qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(newElem, *newObject, allocator); - - // Create material SGraphObject - qt3ds::render::SReferencedMaterial *newMaterial - = QT3DS_NEW(allocator, qt3ds::render::SReferencedMaterial)(); - newMaterial->m_Id = strTable.RegisterStr((QByteArrayLiteral("_newMaterial_") - + QByteArray::number(_idCounter)).constData()); - newMaterial->m_ReferencedMaterial = referencedMaterial; - - newObject->AddMaterial(*newMaterial); - - // Determine if element should be active based on start/end times - TTimeUnit startTime = 0; - TTimeUnit stopTime = 0; - if (newElem.GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue)) - startTime = TTimeUnit(attValue.m_INT32); - if (newElem.GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue)) - stopTime = TTimeUnit(attValue.m_INT32); - TTimeUnit localTime = newElem.GetActivityZone().GetItemLocalTime(newElem); - - bool isActiveRightNow = eyeBall && localTime >= startTime && localTime <= stopTime - && currentSlide == slideIndex; - - newElem.SetActive(isActiveRightNow); - newObject->m_Flags.SetActive(isActiveRightNow); - if (eyeBall) - newElem.GetActivityZone().UpdateItemInfo(newElem); + qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(newElem, *newObject, allocator); - renderer->ChildrenUpdated(parentObject); + // Create material SGraphObject + qt3ds::render::SReferencedMaterial *newMaterial + = QT3DS_NEW(allocator, qt3ds::render::SReferencedMaterial)(); + newMaterial->m_Id = strTable.RegisterStr((QByteArrayLiteral("_newMaterial_") + + QByteArray::number(_idCounter)).constData()); + newMaterial->m_ReferencedMaterial = referencedMaterial; - notifyElementCreation(presentation, elementPath, {}); -} + newObject->AddMaterial(*newMaterial); -// Only supports deleting element types that can be added via createElement. -void CQmlEngineImpl::deleteElement(const QString &elementPath, - qt3ds::render::IQt3DSRenderer *renderer) -{ - // Resolve element - QByteArray thePath = elementPath.toUtf8(); - TElement *element = getTarget(thePath.constData()); + // Determine if element should be active based on start/end times + TTimeUnit startTime = 0; + TTimeUnit stopTime = 0; + if (newElem.GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue)) + startTime = TTimeUnit(attValue.m_INT32); + if (newElem.GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue)) + stopTime = TTimeUnit(attValue.m_INT32); + TTimeUnit localTime = newElem.GetActivityZone().GetItemLocalTime(newElem); - if (!element) { - qWarning() << __FUNCTION__ << "Invalid element:" << elementPath; - return; - } + bool isActiveRightNow = eyeBall && localTime >= startTime && localTime <= stopTime + && currentSlide == slideIndex; - // Remove element recursively from slide system - IPresentation *presentation = element->GetBelongedPresentation(); - TElement &component = element->GetComponentParent(); - ISlideSystem &slideSystem = presentation->GetSlideSystem(); - slideSystem.removeElement(component, *element); + newElem.SetActive(isActiveRightNow); + newObject->m_Flags.SetActive(isActiveRightNow); + if (eyeBall) + newElem.GetActivityZone().UpdateItemInfo(newElem); - TElement *parentElement = element->GetParent(); - Q_ASSERT(parentElement); + createdElements << &newElem; + } - NVAllocatorCallback &allocator = presentation->GetScene()->allocator(); + renderer->ChildrenUpdated(parentObject); - // Recursive deleter for translators and graph objects - std::function deleteRenderObjects; - deleteRenderObjects = [&](TElement *elem) { - TElement *child = elem->m_Child; - while (child) { - TElement *sibling = child->m_Sibling; - deleteRenderObjects(child); - child = sibling; - } + handleError(); +} - auto translator = static_cast(elem->GetAssociation()); - if (translator) { - if (translator->GetUIPType() == qt3ds::render::GraphObjectTypes::Model) { - auto model = static_cast(&translator->RenderObject()); - // Delete material - if (model->m_FirstMaterial) { - auto material = static_cast( - model->m_FirstMaterial); - QT3DS_FREE(allocator, material); - } - QT3DS_FREE(allocator, model); +// Only supports deleting element types that can be added via createElement. +void CQmlEngineImpl::deleteElements(const QStringList &elementPaths, + qt3ds::render::IQt3DSRenderer *renderer) +{ + QVector elements; + // Convert the path list to set for quicker lookups + QSet pathSet = elementPaths.toSet(); + + for (auto &elementPath : elementPaths) { + // Check that parent is not already included in the deleted elements + int idx = elementPath.lastIndexOf(QLatin1Char('.')); + bool parentFound = false; + while (idx != -1) { + QString parentPath = elementPath.left(idx); + if (pathSet.contains(parentPath)) { + parentFound = true; + break; } - QT3DS_FREE(allocator, translator); + idx = parentPath.lastIndexOf(QLatin1Char('.')); } - }; - - qt3ds::render::SNode *node = nullptr; - qt3ds::render::SNode *parentNode = nullptr; - auto translator = static_cast(element->GetAssociation()); - - if (translator) { - node = &static_cast(translator->RenderObject()); - auto parentTranslator = static_cast( - parentElement->GetAssociation()); - if (parentTranslator) { - parentNode = &static_cast(parentTranslator->RenderObject()); - parentNode->RemoveChild(*node); - renderer->ChildrenUpdated(*parentNode); + if (!parentFound) { + // Resolve element + QByteArray thePath = elementPath.toUtf8(); + TElement *element = getTarget(thePath.constData()); + if (element) + elements << element; } - // Release child element graph objects/translators - deleteRenderObjects(element); } - - // Remove element recursively - m_Application->GetElementAllocator().ReleaseElement(*element, true); + deleteElements(elements, renderer); } /** @@ -1259,52 +1241,64 @@ void CQmlEngineImpl::deleteElement(const QString &elementPath, The materialDefinition parameter can contain a .materialdef file path or the entire material definition in the .materialdef format. */ -void CQmlEngineImpl::createMaterial(const QString &elementPath, - const QString &materialDefinition, - ICustomMaterialSystem *customMaterialSystem, - IDynamicObjectSystem *dynamicObjectSystem, - qt3ds::render::IQt3DSRenderer *renderer) +void CQmlEngineImpl::createMaterials(const QString &elementPath, + const QStringList &materialDefinitions, + ICustomMaterialSystem *customMaterialSystem, + IDynamicObjectSystem *dynamicObjectSystem, + qt3ds::render::IQt3DSRenderer *renderer) { - QString materialName; - QMap materialProps; - QMap> textureProps; CPresentation *presentation = nullptr; QString error; + QString presPath; + QString projPath; + struct MaterialInfo { + QString materialDefinition; + QString materialName; + QMap materialProps; + QMap> textureProps; + }; + QVector materialInfos; + QVector createdElements; + + auto handleError = [&]() { + if (!error.isEmpty()) + deleteElements(createdElements, renderer); + QStringList materialNames; + for (auto &materialInfo : materialInfos) + materialNames << materialInfo->materialName; + qDeleteAll(materialInfos); + notifyMaterialCreation(presentation, materialNames, error); + }; - auto getMaterialInfo = [&]() { - QString presPath = QFileInfo(presentation->GetFilePath()).absolutePath(); - QString projPath = presentation->getProjectPath(); - - Q3DSMaterialDefinitionParser::getMaterialInfo(materialDefinition, - projPath, presPath, - materialName, materialProps, textureProps); + auto getMaterialInfos = [&]() { + for (auto &materialDefinition : materialDefinitions) { + MaterialInfo *info = new MaterialInfo; + info->materialDefinition = materialDefinition; + Q3DSMaterialDefinitionParser::getMaterialInfo(materialDefinition, projPath, presPath, + info->materialName, info->materialProps, + info->textureProps); + materialInfos << info; + } }; QByteArray thePath = elementPath.toUtf8(); TElement *element = getTarget(thePath.constData()); if (!element) { - error = QStringLiteral("Invalid element: '%1'").arg(elementPath); + error = QObject::tr("Invalid element: '%1'").arg(elementPath); TElement *sceneElement = getTarget("Scene"); presentation = static_cast(sceneElement->GetBelongedPresentation()); - getMaterialInfo(); - notifyMaterialCreation(presentation, materialName, error); + presPath = QFileInfo(presentation->GetFilePath()).absolutePath(); + projPath = presentation->getProjectPath(); + getMaterialInfos(); + handleError(); return; } presentation = static_cast(element->GetBelongedPresentation()); - getMaterialInfo(); - - if (materialName.isEmpty() || materialProps.isEmpty()) { - error = QStringLiteral("Invalid material definition: '%1'").arg(materialDefinition); - notifyMaterialCreation(presentation, materialName, error); - return; - } - - // We don't care about the path parameter - const QString pathStr = QStringLiteral("path"); - if (materialProps.contains(pathStr)) - materialProps.remove(pathStr); + presPath = QFileInfo(presentation->GetFilePath()).absolutePath(); + projPath = presentation->getProjectPath(); + getMaterialInfos(); // Find material container auto &strTable = presentation->GetStringTable(); @@ -1312,289 +1306,310 @@ void CQmlEngineImpl::createMaterial(const QString &elementPath, TElement *rootElement = presentation->GetRoot(); const auto containerName = strTable.RegisterStr("__Container"); TElement *container = rootElement->FindChild(CHash::HashString(containerName.c_str())); - if (container) { + if (!container) { + // TODO: Create a material container if it doesn't exist (QT3DS-3412) + error = QObject::tr("Presentation has no material container"); + handleError(); + return; + } + + for (auto &materialInfo : materialInfos) { + if (materialInfo->materialName.isEmpty() || materialInfo->materialProps.isEmpty()) { + error = QObject::tr("Invalid material definition: '%1'") + .arg(materialInfo->materialDefinition); + handleError(); + return; + } + + // We don't care about the path parameter + const QString pathStr = QStringLiteral("path"); + if (materialInfo->materialProps.contains(pathStr)) + materialInfo->materialProps.remove(pathStr); + // Check that the material doesn't already exist in container TElement *firstChild = nullptr; TElement *nextChild = container->GetChild(); firstChild = nextChild; while (nextChild) { QString childName = QString::fromUtf8(nextChild->m_Name); - if (childName == materialName) { - error = QStringLiteral("Material already exists in material container"); - notifyMaterialCreation(presentation, materialName, error); + if (childName == materialInfo->materialName) { + error = QObject::tr("Material already exists in material container"); + handleError(); return; } nextChild = nextChild->GetSibling(); } - } else { - // TODO: Create a material container if it doesn't exist (QT3DS-3412) - error = QStringLiteral("Presentation has no material container"); - notifyMaterialCreation(presentation, materialName, error); - return; - } - // Create material element in the container based on the material definition - auto &metaData = m_Application->GetMetaData(); - const auto matName = strTable.RegisterStr(materialName); - const bool isCustomMaterial - = materialProps.value(QStringLiteral("type")) == QLatin1String("CustomMaterial"); - CRegisteredString matType; - CRegisteredString matClass; - QHash dynPropDefs; - - if (isCustomMaterial) { - CRegisteredString sourcePath - = strTable.RegisterStr(materialProps.value(QStringLiteral("sourcepath")).toUtf8()); - matType = strTable.RegisterStr("CustomMaterial"); - matClass = sourcePath; // Create just one class per shader - if (sourcePath.IsValid()) { - Option matMetaData = - metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str()); - if (!matMetaData.hasValue()) { - metaData.LoadMaterialXMLFile(matType.c_str(), matClass.c_str(), matName.c_str(), - sourcePath.c_str()); - matMetaData = metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str()); - } - if (matMetaData.hasValue()) { - qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial( - matClass, m_Foundation, *customMaterialSystem, matMetaData, - strTable); - NVConstDataRef customProperties; - customProperties = dynamicObjectSystem->GetProperties(matClass); - for (QT3DSU32 i = 0, end = customProperties.size(); i < end; ++i) { - QString propName = QString::fromUtf8(customProperties[i].m_Name.c_str()); - if (materialProps.contains(propName)) - dynPropDefs.insert(propName, customProperties[i]); + // Create material element in the container based on the material definition + auto &metaData = m_Application->GetMetaData(); + const auto matName = strTable.RegisterStr(materialInfo->materialName); + const bool isCustomMaterial = (materialInfo->materialProps.value(QStringLiteral("type")) + == QLatin1String("CustomMaterial")); + CRegisteredString matType; + CRegisteredString matClass; + QHash dynPropDefs; + + if (isCustomMaterial) { + CRegisteredString sourcePath + = strTable.RegisterStr(materialInfo->materialProps.value( + QStringLiteral("sourcepath")).toUtf8()); + matType = strTable.RegisterStr("CustomMaterial"); + matClass = sourcePath; // Create just one class per shader + if (sourcePath.IsValid()) { + Option matMetaData = + metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str()); + if (!matMetaData.hasValue()) { + metaData.LoadMaterialXMLFile(matType.c_str(), matClass.c_str(), matName.c_str(), + sourcePath.c_str()); + matMetaData = metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str()); + } + if (matMetaData.hasValue()) { + qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial( + matClass, m_Foundation, *customMaterialSystem, matMetaData, + strTable); + NVConstDataRef customProperties; + customProperties = dynamicObjectSystem->GetProperties(matClass); + for (QT3DSU32 i = 0, end = customProperties.size(); i < end; ++i) { + QString propName = QString::fromUtf8(customProperties[i].m_Name.c_str()); + if (materialInfo->materialProps.contains(propName)) + dynPropDefs.insert(propName, customProperties[i]); + } + } else { + error = QObject::tr("Could not resolve properties for CustomMaterial"); + handleError(); + return; } } else { - error = QStringLiteral("Could not resolve material properties for CustomMaterial"); - notifyMaterialCreation(presentation, materialName, error); + error = QObject::tr("Missing sourcepath in definition of CustomMaterial"); + handleError(); return; } } else { - error = QStringLiteral("Missing sourcepath in material definition of a CustomMaterial"); - notifyMaterialCreation(presentation, materialName, error); - return; + matType = strTable.RegisterStr("Material"); } - } else { - matType = strTable.RegisterStr("Material"); - } - auto createElementPropsFromDefProps = [&](const QMap &defProps, - TPropertyDescAndValueList &elementProps, - const CRegisteredString &elementType) { - QMapIterator propIter(defProps); - while (propIter.hasNext()) { - propIter.next(); - if (dynPropDefs.contains(propIter.key())) - continue; // Dynamic properties are added directly to graph objects later - - auto propName = strTable.RegisterStr(propIter.key()); - ERuntimeDataModelDataType dataType; - ERuntimeAdditionalMetaDataType additionalType; - dataType = metaData.GetPropertyType(elementType, propName); - additionalType = metaData.GetAdditionalType(elementType, propName); - - switch (dataType) { - case ERuntimeDataModelDataTypeLong: { - addIntAttribute(strTable, elementProps, propIter.key(), - propIter.value().toInt()); - break; - } - case ERuntimeDataModelDataTypeFloat: { - addFloatAttribute(strTable, elementProps, propIter.key(), - propIter.value().toFloat()); - break; - } - case ERuntimeDataModelDataTypeFloat2: { - QVector2D vec = parseFloat2Property(propIter.value()); - QStringList atts; - atts << (propIter.key() + QLatin1String(".x")) - << (propIter.key() + QLatin1String(".y")); - addFloat2Attribute(strTable, elementProps, atts, vec); - break; - } - case ERuntimeDataModelDataTypeFloat3: { - QVector3D vec = parseFloat3Property(propIter.value()); - if (additionalType == ERuntimeAdditionalMetaDataTypeRotation) { - vec.setX(qDegreesToRadians(vec.x())); - vec.setY(qDegreesToRadians(vec.y())); - vec.setZ(qDegreesToRadians(vec.z())); + auto createElementPropsFromDefProps = [&](const QMap &defProps, + TPropertyDescAndValueList &elementProps, + const CRegisteredString &elementType) { + QMapIterator propIter(defProps); + while (propIter.hasNext()) { + propIter.next(); + if (dynPropDefs.contains(propIter.key())) + continue; // Dynamic properties are added directly to graph objects later + + auto propName = strTable.RegisterStr(propIter.key()); + ERuntimeDataModelDataType dataType; + ERuntimeAdditionalMetaDataType additionalType; + dataType = metaData.GetPropertyType(elementType, propName); + additionalType = metaData.GetAdditionalType(elementType, propName); + + switch (dataType) { + case ERuntimeDataModelDataTypeLong: { + addIntAttribute(strTable, elementProps, propIter.key(), + propIter.value().toInt()); + break; } - QStringList atts; - atts << (propIter.key() + QLatin1String(".x")) - << (propIter.key() + QLatin1String(".y")) - << (propIter.key() + QLatin1String(".z")); - addFloat3Attribute(strTable, elementProps, atts, vec); - break; - } - case ERuntimeDataModelDataTypeFloat4: { - QVector4D vec = parseFloat4Property(propIter.value()); - QStringList atts; - if (additionalType == ERuntimeAdditionalMetaDataTypeColor) { - atts << (propIter.key() + QLatin1String(".r")) - << (propIter.key() + QLatin1String(".g")) - << (propIter.key() + QLatin1String(".b")) - << (propIter.key() + QLatin1String(".a")); - } else { + case ERuntimeDataModelDataTypeFloat: { + addFloatAttribute(strTable, elementProps, propIter.key(), + propIter.value().toFloat()); + break; + } + case ERuntimeDataModelDataTypeFloat2: { + QVector2D vec = parseFloat2Property(propIter.value()); + QStringList atts; + atts << (propIter.key() + QLatin1String(".x")) + << (propIter.key() + QLatin1String(".y")); + addFloat2Attribute(strTable, elementProps, atts, vec); + break; + } + case ERuntimeDataModelDataTypeFloat3: { + QVector3D vec = parseFloat3Property(propIter.value()); + if (additionalType == ERuntimeAdditionalMetaDataTypeRotation) { + vec.setX(qDegreesToRadians(vec.x())); + vec.setY(qDegreesToRadians(vec.y())); + vec.setZ(qDegreesToRadians(vec.z())); + } + QStringList atts; atts << (propIter.key() + QLatin1String(".x")) << (propIter.key() + QLatin1String(".y")) - << (propIter.key() + QLatin1String(".z")) - << (propIter.key() + QLatin1String(".w")); + << (propIter.key() + QLatin1String(".z")); + addFloat3Attribute(strTable, elementProps, atts, vec); + break; } - addFloat4Attribute(strTable, elementProps, atts, vec); - break; - } - case ERuntimeDataModelDataTypeBool: { - bool boolValue = propIter.value().compare(QLatin1String("true"), - Qt::CaseInsensitive) == 0; - addBoolAttribute(strTable, elementProps, propIter.key(), boolValue); - break; - } - case ERuntimeDataModelDataTypeStringRef: - case ERuntimeDataModelDataTypeString: { - addStringAttribute(strTable, elementProps, propIter.key(), propIter.value()); - break; - } - case ERuntimeDataModelDataTypeLong4: { - if (additionalType == ERuntimeAdditionalMetaDataTypeImage) { - // Insert placeholder for now, will be patched later - addElementRefAttribute(strTable, elementProps, propIter.key(), nullptr); + case ERuntimeDataModelDataTypeFloat4: { + QVector4D vec = parseFloat4Property(propIter.value()); + QStringList atts; + if (additionalType == ERuntimeAdditionalMetaDataTypeColor) { + atts << (propIter.key() + QLatin1String(".r")) + << (propIter.key() + QLatin1String(".g")) + << (propIter.key() + QLatin1String(".b")) + << (propIter.key() + QLatin1String(".a")); + } else { + atts << (propIter.key() + QLatin1String(".x")) + << (propIter.key() + QLatin1String(".y")) + << (propIter.key() + QLatin1String(".z")) + << (propIter.key() + QLatin1String(".w")); + } + addFloat4Attribute(strTable, elementProps, atts, vec); + break; + } + case ERuntimeDataModelDataTypeBool: { + bool boolValue = propIter.value().compare(QLatin1String("true"), + Qt::CaseInsensitive) == 0; + addBoolAttribute(strTable, elementProps, propIter.key(), boolValue); + break; + } + case ERuntimeDataModelDataTypeStringRef: + case ERuntimeDataModelDataTypeString: { + addStringAttribute(strTable, elementProps, propIter.key(), propIter.value()); + break; + } + case ERuntimeDataModelDataTypeLong4: { + if (additionalType == ERuntimeAdditionalMetaDataTypeImage) { + // Insert placeholder for now, will be patched later + addElementRefAttribute(strTable, elementProps, propIter.key(), nullptr); + } + break; + } + default: + error = QObject::tr("Unsupported material property type for property: '%1'") + .arg(propIter.key()); + return; } - break; - } - default: - error = QStringLiteral("Unsupported material property type for property: '%1'") - .arg(propIter.key()); - break; } - } - }; + }; - TPropertyDescAndValueList elementProps; - createElementPropsFromDefProps(materialProps, elementProps, matType); - if (!error.isEmpty()) { - notifyMaterialCreation(presentation, materialName, error); - return; - } - - TElement &newMatElem = m_Application->GetElementAllocator().CreateElement( - matName, matType, matClass, - toConstDataRef(elementProps.data(), QT3DSU32(elementProps.size())), - presentation, container, false); - newMatElem.SetActive(true); - - // Create image elements - CRegisteredString imageType = strTable.RegisterStr("Image"); - QMapIterator> texIter(textureProps); - QHash imageElementMap; - while (texIter.hasNext()) { - texIter.next(); - elementProps.clear(); - createElementPropsFromDefProps(texIter.value(), elementProps, imageType); + TPropertyDescAndValueList elementProps; + createElementPropsFromDefProps(materialInfo->materialProps, elementProps, matType); if (!error.isEmpty()) { - m_Application->GetElementAllocator().ReleaseElement(newMatElem, true); - notifyMaterialCreation(presentation, materialName, error); + handleError(); return; } - CRegisteredString imageName = strTable.RegisterStr(texIter.key()); - TElement &newImageElem = m_Application->GetElementAllocator().CreateElement( - imageName, imageType, CRegisteredString(), + TElement &newMatElem = m_Application->GetElementAllocator().CreateElement( + matName, matType, matClass, toConstDataRef(elementProps.data(), QT3DSU32(elementProps.size())), - presentation, &newMatElem, false); - imageElementMap.insert(texIter.key(), &newImageElem); - newImageElem.SetActive(true); - } - - // Create render object for the material - qt3ds::render::SGraphObject *newMaterial = nullptr; - CRegisteredString newMatId = strTable.RegisterStr( - (QByteArrayLiteral("_newMaterial_") + QByteArray::number(++_idCounter)) - .constData()); - if (isCustomMaterial) { - newMaterial = customMaterialSystem->CreateCustomMaterial(matClass, allocator); - newMaterial->m_Id = newMatId; - auto dynObj = static_cast(newMaterial); - - QHashIterator - dynPropIter(dynPropDefs); - while (dynPropIter.hasNext()) { - dynPropIter.next(); - QByteArray propValStr = materialProps.value(dynPropIter.key()).toUtf8(); - const auto propDesc = dynPropIter.value(); - switch (propDesc.m_DataType) { - case qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool: { - bool boolValue = propValStr.compare(QByteArrayLiteral("true"), - Qt::CaseInsensitive) == 0; - setDynamicObjectProperty(*dynObj, propDesc, boolValue); - break; + presentation, container, false); + newMatElem.SetActive(true); + + // Create image elements + CRegisteredString imageType = strTable.RegisterStr("Image"); + QMapIterator> texIter(materialInfo->textureProps); + QHash imageElementMap; + while (texIter.hasNext()) { + texIter.next(); + elementProps.clear(); + createElementPropsFromDefProps(texIter.value(), elementProps, imageType); + if (!error.isEmpty()) { + m_Application->GetElementAllocator().ReleaseElement(newMatElem, true); + handleError(); + return; } - case qt3ds::render::NVRenderShaderDataTypes::QT3DSF32: - setDynamicObjectProperty(*dynObj, propDesc, propValStr.toFloat()); - break; - case qt3ds::render::NVRenderShaderDataTypes::QT3DSI32: - if (!propDesc.m_IsEnumProperty) { - setDynamicObjectProperty(*dynObj, propDesc, propValStr.toInt()); - } else { - const NVConstDataRef &enumNames = propDesc.m_EnumValueNames; - for (QT3DSU32 i = 0, end = enumNames.size(); i < end; ++i) { - if (propValStr.compare(enumNames[i].c_str()) == 0) { - setDynamicObjectProperty(*dynObj, propDesc, i); - break; + CRegisteredString imageName = strTable.RegisterStr(texIter.key()); + + TElement &newImageElem = m_Application->GetElementAllocator().CreateElement( + imageName, imageType, CRegisteredString(), + toConstDataRef(elementProps.data(), QT3DSU32(elementProps.size())), + presentation, &newMatElem, false); + imageElementMap.insert(texIter.key(), &newImageElem); + newImageElem.SetActive(true); + } + + // Create render object for the material + qt3ds::render::SGraphObject *newMaterial = nullptr; + CRegisteredString newMatId = strTable.RegisterStr( + (QByteArrayLiteral("_newMaterial_") + QByteArray::number(++_idCounter)) + .constData()); + if (isCustomMaterial) { + newMaterial = customMaterialSystem->CreateCustomMaterial(matClass, allocator); + newMaterial->m_Id = newMatId; + auto dynObj = static_cast(newMaterial); + + QHashIterator + dynPropIter(dynPropDefs); + while (dynPropIter.hasNext()) { + dynPropIter.next(); + QByteArray propValStr = materialInfo->materialProps.value( + dynPropIter.key()).toUtf8(); + const auto propDesc = dynPropIter.value(); + switch (propDesc.m_DataType) { + case qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool: { + bool boolValue = propValStr.compare(QByteArrayLiteral("true"), + Qt::CaseInsensitive) == 0; + setDynamicObjectProperty(*dynObj, propDesc, boolValue); + break; + } + case qt3ds::render::NVRenderShaderDataTypes::QT3DSF32: + setDynamicObjectProperty(*dynObj, propDesc, propValStr.toFloat()); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSI32: + if (!propDesc.m_IsEnumProperty) { + setDynamicObjectProperty(*dynObj, propDesc, propValStr.toInt()); + } else { + const NVConstDataRef &enumNames + = propDesc.m_EnumValueNames; + for (QT3DSU32 i = 0, end = enumNames.size(); i < end; ++i) { + if (propValStr.compare(enumNames[i].c_str()) == 0) { + setDynamicObjectProperty(*dynObj, propDesc, i); + break; + } } } + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2: + setDynamicObjectProperty(*dynObj, propDesc, parseFloat2Property(propValStr)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3: + setDynamicObjectProperty(*dynObj, propDesc, parseFloat3Property(propValStr)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4: + setDynamicObjectProperty(*dynObj, propDesc, parseFloat4Property(propValStr)); + break; + case qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr: + case qt3ds::render::NVRenderShaderDataTypes::NVRenderImage2DPtr: { + CRegisteredString regStr; + regStr = strTable.RegisterStr(propValStr); + setDynamicObjectProperty(*dynObj, propDesc, regStr); + break; + } + default: + error = QObject::tr("Unsupported custom material property type for '%1'") + .arg(dynPropIter.key()); + m_Application->GetElementAllocator().ReleaseElement(newMatElem, true); + handleError(); + return; } - break; - case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2: - setDynamicObjectProperty(*dynObj, propDesc, parseFloat2Property(propValStr)); - break; - case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3: - setDynamicObjectProperty(*dynObj, propDesc, parseFloat3Property(propValStr)); - break; - case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4: - setDynamicObjectProperty(*dynObj, propDesc, parseFloat4Property(propValStr)); - break; - case qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr: - case qt3ds::render::NVRenderShaderDataTypes::NVRenderImage2DPtr: { - CRegisteredString regStr; - regStr = strTable.RegisterStr(propValStr); - setDynamicObjectProperty(*dynObj, propDesc, regStr); - break; - } - default: - error = QStringLiteral("Unsupported custom material property type for '%1'") - .arg(dynPropIter.key()); - notifyMaterialCreation(presentation, materialName, error); - return; } - } - } else { - newMaterial = QT3DS_NEW(allocator, qt3ds::render::SDefaultMaterial)(); - newMaterial->m_Id = newMatId; - - // Update element refs for image elements and create graph objects & translators - QHashIterator imageIter(imageElementMap); - while (imageIter.hasNext()) { - imageIter.next(); - TElement *imageElem = imageIter.value(); - UVariant *propValue = newMatElem.FindPropertyValue(imageElem->m_Name); - if (propValue) { - propValue->m_ElementHandle = imageIter.value()->GetHandle(); - - qt3ds::render::SImage *newImageObj = QT3DS_NEW(allocator, qt3ds::render::SImage)(); - newImageObj->m_Id - = strTable.RegisterStr((QByteArrayLiteral("_newImage_") - + QByteArray::number(++_idCounter)).constData()); - qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(*imageElem, - *newImageObj, allocator); + } else { + newMaterial = QT3DS_NEW(allocator, qt3ds::render::SDefaultMaterial)(); + newMaterial->m_Id = newMatId; + + // Update element refs for image elements and create graph objects & translators + QHashIterator imageIter(imageElementMap); + while (imageIter.hasNext()) { + imageIter.next(); + TElement *imageElem = imageIter.value(); + UVariant *propValue = newMatElem.FindPropertyValue(imageElem->m_Name); + if (propValue) { + propValue->m_ElementHandle = imageIter.value()->GetHandle(); + + qt3ds::render::SImage *newImageObj + = QT3DS_NEW(allocator, qt3ds::render::SImage)(); + newImageObj->m_Id = strTable.RegisterStr( + (QByteArrayLiteral("_newImage_") + + QByteArray::number(++_idCounter)).constData()); + qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement( + *imageElem, *newImageObj, allocator); + } } + createdElements << &newMatElem; } - } - qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(newMatElem, *newMaterial, allocator); + qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(newMatElem, *newMaterial, + allocator); + } - notifyMaterialCreation(presentation, materialName, {}); + handleError(); } void CQmlEngineImpl::GotoSlide(const char *component, const char *slideName, @@ -2258,32 +2273,110 @@ QVector4D CQmlEngineImpl::parseFloat4Property(const QString &propValue) return retVal; } -void CQmlEngineImpl::notifyElementCreation(CPresentation *pres, const QString &elementName, +void CQmlEngineImpl::notifyElementCreation(CPresentation *pres, const QStringList &elementNames, const QString &error) { // Notify presentation asynchronously to give renderer time to initialize the elements properly if (!error.isEmpty()) { - qWarning() << "Warning: Element creation failed:" << elementName << error; + qWarning() << "Warning: Element creation failed:" << error; QT3DS_ASSERT(false); } - QTimer::singleShot(0, [pres, elementName, error]() { - pres->signalProxy()->SigElementCreated(elementName, error); + QTimer::singleShot(0, [pres, elementNames, error]() { + pres->signalProxy()->SigElementsCreated(elementNames, error); }); } -void CQmlEngineImpl::notifyMaterialCreation(CPresentation *pres, const QString &name, +void CQmlEngineImpl::notifyMaterialCreation(CPresentation *pres, + const QStringList &materialNames, const QString &error) { // Notify presentation asynchronously to give renderer time to initialize the materials properly if (!error.isEmpty()) { - qWarning() << "Warning: Material creation failed:" << name << error; + qWarning() << "Warning: Material creation failed:" << materialNames << error; QT3DS_ASSERT(false); } - QTimer::singleShot(0, [pres, name, error]() { - pres->signalProxy()->SigMaterialCreated(name, error); + QTimer::singleShot(0, [pres, materialNames, error]() { + pres->signalProxy()->SigMaterialsCreated(materialNames, error); }); } +void CQmlEngineImpl::deleteElements(const QVector &elements, + IQt3DSRenderer *renderer) +{ + TElement *lastParent = nullptr; + IPresentation *presentation = nullptr; + TElement *component = nullptr; + ISlideSystem *slideSystem = nullptr; + QSet parentNodes; + for (auto element : elements) { + TElement *parentElement = element->GetParent(); + if (parentElement != lastParent) { + lastParent = parentElement; + presentation = element->GetBelongedPresentation(); + component = &element->GetComponentParent(); + slideSystem = &presentation->GetSlideSystem(); + } + // Remove element recursively from slide system + slideSystem->removeElement(*component, *element); + + Q_ASSERT(parentElement); + + NVAllocatorCallback &allocator = presentation->GetScene()->allocator(); + + // Recursive deleter for translators and graph objects + std::function deleteRenderObjects; + deleteRenderObjects = [&](TElement *elem) { + TElement *child = elem->m_Child; + while (child) { + TElement *sibling = child->m_Sibling; + deleteRenderObjects(child); + child = sibling; + } + + auto translator = static_cast(elem->GetAssociation()); + if (translator) { + qt3ds::render::GraphObjectTypes::Enum type = translator->GetUIPType(); + if (type == qt3ds::render::GraphObjectTypes::Model) { + auto model = static_cast(&translator->RenderObject()); + // Delete material + if (model->m_FirstMaterial) { + auto material = static_cast( + model->m_FirstMaterial); + QT3DS_FREE(allocator, material); + } + } + QT3DS_FREE(allocator, &translator->RenderObject()); + QT3DS_FREE(allocator, translator); + } + }; + + qt3ds::render::SNode *node = nullptr; + qt3ds::render::SNode *parentNode = nullptr; + auto translator = static_cast(element->GetAssociation()); + + if (translator) { + if (qt3ds::render::GraphObjectTypes::IsNodeType(translator->GetUIPType())) { + node = &static_cast(translator->RenderObject()); + auto parentTranslator = static_cast( + parentElement->GetAssociation()); + if (parentTranslator) { + parentNode = &static_cast( + parentTranslator->RenderObject()); + parentNode->RemoveChild(*node); + parentNodes.insert(parentNode); + } + } + // Release child element graph objects/translators + deleteRenderObjects(element); + } + + // Remove element recursively + m_Application->GetElementAllocator().ReleaseElement(*element, true); + } + for (auto parentNode : qAsConst(parentNodes)) + renderer->ChildrenUpdated(*parentNode); +} + template void CQmlEngineImpl::setDynamicObjectProperty(qt3ds::render::SDynamicObject &material, const dynamic::SPropertyDefinition &propDesc, diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp index a74fb57e..c5ac0d6b 100644 --- a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp +++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp @@ -357,10 +357,6 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor &QRuntimeViewSignalProxy::SigSlideExited, this, &Q3DSViewerApp::SigSlideExited); connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigCustomSignal, this, &Q3DSViewerApp::SigCustomSignal); - connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigElementCreated, this, - &Q3DSViewerApp::SigElementCreated); - connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigMaterialCreated, this, - &Q3DSViewerApp::SigMaterialCreated); QMetaObject::Connection *presReadyconn = new QMetaObject::Connection(); *presReadyconn = connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigPresentationReady, [&, presReadyconn]{ @@ -370,6 +366,11 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor disconnect(*presReadyconn); delete presReadyconn; }); + connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigElementsCreated, this, + &Q3DSViewerApp::SigElementsCreated); + connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigMaterialsCreated, this, + &Q3DSViewerApp::SigMaterialsCreated); + Resize(winWidth, winHeight); Q_EMIT SigPresentationLoaded(); @@ -806,29 +807,30 @@ float Q3DSViewerApp::dataInputMin(const QString &name) const return m_Impl.m_view->dataInputMin(name); } -void Q3DSViewerApp::createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties) +void Q3DSViewerApp::createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties) { if (!m_Impl.m_view) return; - m_Impl.m_view->createElement(parentElementPath, slideName, properties); + m_Impl.m_view->createElements(parentElementPath, slideName, properties); } -void Q3DSViewerApp::deleteElement(const QString &elementPath) +void Q3DSViewerApp::deleteElements(const QStringList &elementPaths) { if (!m_Impl.m_view) return; - m_Impl.m_view->deleteElement(elementPath); + m_Impl.m_view->deleteElements(elementPaths); } -void Q3DSViewerApp::createMaterial(const QString &elementPath, const QString &materialDefinition) +void Q3DSViewerApp::createMaterials(const QString &elementPath, + const QStringList &materialDefinitions) { if (!m_Impl.m_view) return; - m_Impl.m_view->createMaterial(elementPath, materialDefinition); + m_Impl.m_view->createMaterials(elementPath, materialDefinitions); } Q3DSViewerApp &Q3DSViewerApp::Create(void *glContext, Q3DStudio::IAudioPlayer *inAudioPlayer, diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.h b/src/Runtime/Source/viewer/Qt3DSViewerApp.h index 971bf473..8c873098 100644 --- a/src/Runtime/Source/viewer/Qt3DSViewerApp.h +++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.h @@ -32,13 +32,15 @@ #define QT3DS_VIEWER_H #include "qt3dsruntimeglobal.h" +#include "Qt3DSApplication.h" +#include "Qt3DSInputDefs.h" #include #include #include #include -#include "Qt3DSApplication.h" -#include "Qt3DSInputDefs.h" +#include +#include #include namespace Q3DStudio { @@ -359,10 +361,10 @@ public: float dataInputMax(const QString &name) const; float dataInputMin(const QString &name) const; - void createElement(const QString &parentElementPath, const QString &slideName, - const QHash &properties); - void deleteElement(const QString &elementPath); - void createMaterial(const QString &elementPath, const QString &materialDefinition); + void createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties); + void deleteElements(const QStringList &elementPaths); + void createMaterials(const QString &elementPath, const QStringList &materialDefinitions); QString error(); @@ -405,8 +407,8 @@ Q_SIGNALS: void SigSlideEntered(const QString &elementPath, unsigned int index, const QString &name); void SigSlideExited(const QString &elementPath, unsigned int index, const QString &name); void SigCustomSignal(const QString &elementPath, const QString &name); - void SigElementCreated(const QString &elementName, const QString &error); - void SigMaterialCreated(const QString &name, const QString &error); + void SigElementsCreated(const QStringList &elementPaths, const QString &error); + void SigMaterialsCreated(const QStringList &materialNames, const QString &error); void SigPresentationReady(); void SigPresentationLoaded(); }; diff --git a/src/Runtime/api/studio3d/q3dscommandqueue.cpp b/src/Runtime/api/studio3d/q3dscommandqueue.cpp index ed0ad8b5..02ae9395 100644 --- a/src/Runtime/api/studio3d/q3dscommandqueue.cpp +++ b/src/Runtime/api/studio3d/q3dscommandqueue.cpp @@ -30,6 +30,8 @@ #include "q3dscommandqueue_p.h" #include "q3dspresentation.h" +#include + ElementCommand::ElementCommand() : m_commandType(CommandType_Invalid) { @@ -160,6 +162,16 @@ ElementCommand &CommandQueue::queueCommand(const QString &elementPath, CommandTy return cmd; } +ElementCommand &CommandQueue::queueCommand(CommandType commandType, void *commandData) +{ + ElementCommand &cmd = nextFreeCommand(); + + cmd.m_commandType = commandType; + cmd.m_data = commandData; + + return cmd; +} + ElementCommand &CommandQueue::queueCommand(const QString &elementPath, CommandType commandType) { ElementCommand &cmd = nextFreeCommand(); @@ -170,6 +182,18 @@ ElementCommand &CommandQueue::queueCommand(const QString &elementPath, CommandTy return cmd; } +ElementCommand &CommandQueue::queueCommand(const QString &elementPath, CommandType commandType, + void *commandData) +{ + ElementCommand &cmd = nextFreeCommand(); + + cmd.m_commandType = commandType; + cmd.m_elementPath = elementPath; + cmd.m_data = commandData; + + return cmd; +} + void CommandQueue::copyCommands(CommandQueue &fromQueue) { m_visibleChanged = m_visibleChanged || fromQueue.m_visibleChanged; @@ -217,16 +241,13 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue) break; case CommandType_GoToSlideByName: case CommandType_FireEvent: - case CommandType_CreateMaterial: queueCommand(source.m_elementPath, source.m_commandType, source.m_stringValue); break; case CommandType_SetPresentationActive: - queueCommand(source.m_elementPath, source.m_commandType, - source.m_boolValue); + queueCommand(source.m_elementPath, source.m_commandType, source.m_boolValue); break; case CommandType_GoToTime: - queueCommand(source.m_elementPath, source.m_commandType, - source.m_floatValue); + queueCommand(source.m_elementPath, source.m_commandType, source.m_floatValue); break; case CommandType_GoToSlide: case CommandType_GoToSlideRelative: @@ -240,12 +261,19 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue) source.m_intValues[0], source.m_intValues[1], source.m_intValues[2], source.m_intValues[3]); break; - case CommandType_CreateElement: + case CommandType_CreateElements: queueCommand(source.m_elementPath, source.m_commandType, source.m_stringValue, source.m_data); fromQueue.commandAt(i).m_data = nullptr; // This queue takes ownership of data break; - case CommandType_DeleteElement: + case CommandType_DeleteElements: + queueCommand(source.m_commandType, source.m_data); + fromQueue.commandAt(i).m_data = nullptr; // This queue takes ownership of data + break; + case CommandType_CreateMaterials: + queueCommand(source.m_elementPath, source.m_commandType, source.m_data); + fromQueue.commandAt(i).m_data = nullptr; // This queue takes ownership of data + break; case CommandType_RequestSlideInfo: case CommandType_UnloadSlide: case CommandType_PreloadSlide: @@ -277,8 +305,14 @@ void CommandQueue::clear(bool deleteCommandData) ElementCommand &cmd = m_elementCommands[i]; if (cmd.m_data) { switch (cmd.m_commandType) { - case CommandType_CreateElement: - delete static_cast *>(cmd.m_data); + case CommandType_CreateElements: + delete static_cast> *>(cmd.m_data); + break; + case CommandType_DeleteElements: + delete static_cast(cmd.m_data); + break; + case CommandType_CreateMaterials: + delete static_cast(cmd.m_data); break; default: Q_ASSERT(false); // Should never come here diff --git a/src/Runtime/api/studio3d/q3dscommandqueue_p.h b/src/Runtime/api/studio3d/q3dscommandqueue_p.h index ff30829f..c396ec8e 100644 --- a/src/Runtime/api/studio3d/q3dscommandqueue_p.h +++ b/src/Runtime/api/studio3d/q3dscommandqueue_p.h @@ -68,9 +68,9 @@ enum CommandType { CommandType_KeyRelease, CommandType_SetGlobalAnimationTime, CommandType_SetDataInputValue, - CommandType_CreateElement, - CommandType_DeleteElement, - CommandType_CreateMaterial, + CommandType_CreateElements, + CommandType_DeleteElements, + CommandType_CreateMaterials, // Requests CommandType_RequestSlideInfo, @@ -122,7 +122,10 @@ public: int value2 = 0, int value3 = 0); ElementCommand &queueCommand(const QString &elementPath, CommandType commandType, const QString &stringValue, void *commandData); + ElementCommand &queueCommand(CommandType commandType, void *commandData); ElementCommand &queueCommand(const QString &elementPath, CommandType commandType); + ElementCommand &queueCommand(const QString &elementPath, CommandType commandType, + void *commandData); void copyCommands(CommandQueue &fromQueue); diff --git a/src/Runtime/api/studio3d/q3dspresentation.cpp b/src/Runtime/api/studio3d/q3dspresentation.cpp index 0887d702..880780ff 100644 --- a/src/Runtime/api/studio3d/q3dspresentation.cpp +++ b/src/Runtime/api/studio3d/q3dspresentation.cpp @@ -282,13 +282,22 @@ void Q3DSPresentation::setDataInputValue(const QString &name, const QVariant &va */ void Q3DSPresentation::createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties) +{ + QVector> theProperties; + theProperties << properties; + createElements(parentElementPath, slideName, theProperties); +} + +void Q3DSPresentation::createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties) { if (d_ptr->m_viewerApp) { - d_ptr->m_viewerApp->createElement(parentElementPath, slideName, properties); + d_ptr->m_viewerApp->createElements(parentElementPath, slideName, properties); } else if (d_ptr->m_commandQueue) { // We need to copy the properties map as queue takes ownership of it - QHash *theProperties = new QHash(properties); - d_ptr->m_commandQueue->queueCommand(parentElementPath, CommandType_CreateElement, + QVector> *theProperties + = new QVector>(properties); + d_ptr->m_commandQueue->queueCommand(parentElementPath, CommandType_CreateElements, slideName, theProperties); } } @@ -298,10 +307,20 @@ void Q3DSPresentation::createElement(const QString &parentElementPath, const QSt */ void Q3DSPresentation::deleteElement(const QString &elementPath) { - if (d_ptr->m_viewerApp) - d_ptr->m_viewerApp->deleteElement(elementPath); - else if (d_ptr->m_commandQueue) - d_ptr->m_commandQueue->queueCommand(elementPath, CommandType_DeleteElement); + QStringList elementPaths; + elementPaths << elementPath; + deleteElements(elementPaths); +} + +void Q3DSPresentation::deleteElements(const QStringList &elementPaths) +{ + if (d_ptr->m_viewerApp) { + d_ptr->m_viewerApp->deleteElements(elementPaths); + } else if (d_ptr->m_commandQueue) { + // We need to copy the list as queue takes ownership of it + QStringList *theElementPaths = new QStringList(elementPaths); + d_ptr->m_commandQueue->queueCommand(CommandType_DeleteElements, theElementPaths); + } } /** @@ -313,12 +332,22 @@ void Q3DSPresentation::deleteElement(const QString &elementPath) */ void Q3DSPresentation::createMaterial(const QString &elementPath, const QString &materialDefinition) +{ + QStringList materialDefinitions; + materialDefinitions << materialDefinition; + createMaterials(elementPath, materialDefinitions); +} + +void Q3DSPresentation::createMaterials(const QString &elementPath, + const QStringList &materialDefinitions) { if (d_ptr->m_viewerApp) { - d_ptr->m_viewerApp->createMaterial(elementPath, materialDefinition); + d_ptr->m_viewerApp->createMaterials(elementPath, materialDefinitions); } else if (d_ptr->m_commandQueue) { - d_ptr->m_commandQueue->queueCommand(elementPath, CommandType_CreateMaterial, - materialDefinition); + // We need to copy the list as queue takes ownership of it + QStringList *theMaterialDefinitions = new QStringList(materialDefinitions); + d_ptr->m_commandQueue->queueCommand(elementPath, CommandType_CreateMaterials, + theMaterialDefinitions); } } @@ -464,10 +493,10 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool q_ptr, &Q3DSPresentation::slideExited); connect(app, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal, q_ptr, &Q3DSPresentation::customSignalEmitted); - connect(app, &Q3DSViewer::Q3DSViewerApp::SigElementCreated, - q_ptr, &Q3DSPresentation::elementCreated); - connect(app, &Q3DSViewer::Q3DSViewerApp::SigMaterialCreated, - q_ptr, &Q3DSPresentation::materialCreated); + connect(app, &Q3DSViewer::Q3DSViewerApp::SigElementsCreated, + q_ptr, &Q3DSPresentation::elementsCreated); + connect(app, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated, + q_ptr, &Q3DSPresentation::materialsCreated); } if (oldApp) { disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigSlideEntered, @@ -476,10 +505,10 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool q_ptr, &Q3DSPresentation::slideExited); disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal, q_ptr, &Q3DSPresentation::customSignalEmitted); - disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigElementCreated, - q_ptr, &Q3DSPresentation::elementCreated); - disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigMaterialCreated, - q_ptr, &Q3DSPresentation::materialCreated); + disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigElementsCreated, + q_ptr, &Q3DSPresentation::elementsCreated); + disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated, + q_ptr, &Q3DSPresentation::materialsCreated); } } } diff --git a/src/Runtime/api/studio3d/q3dspresentation.h b/src/Runtime/api/studio3d/q3dspresentation.h index d7f02e49..89daa262 100644 --- a/src/Runtime/api/studio3d/q3dspresentation.h +++ b/src/Runtime/api/studio3d/q3dspresentation.h @@ -31,9 +31,11 @@ #define Q3DSPRESENTATION_H #include +#include #include #include -#include +#include +#include QT_BEGIN_NAMESPACE @@ -85,8 +87,12 @@ public: void createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties); + void createElements(const QString &parentElementPath, const QString &slideName, + const QVector> &properties); void deleteElement(const QString &elementPath); + void deleteElements(const QStringList &elementPaths); void createMaterial(const QString &elementPath, const QString &materialDefinition); + void createMaterials(const QString &elementPath, const QStringList &materialDefinitions); public Q_SLOTS: void setSource(const QUrl &source); @@ -113,8 +119,8 @@ Q_SIGNALS: void dataInputsReady(); void customSignalEmitted(const QString &elementPath, const QString &name); void delayedLoadingChanged(bool enable); - void elementCreated(const QString &elementPath, const QString &error); - void materialCreated(const QString &name, const QString &error); + void elementsCreated(const QStringList &elementPaths, const QString &error); + void materialsCreated(const QStringList &materialNames, const QString &error); private: Q_DISABLE_COPY(Q3DSPresentation) diff --git a/src/Runtime/api/studio3dqml/q3dsrenderer.cpp b/src/Runtime/api/studio3dqml/q3dsrenderer.cpp index 7e063ab4..591cc37a 100644 --- a/src/Runtime/api/studio3dqml/q3dsrenderer.cpp +++ b/src/Runtime/api/studio3dqml/q3dsrenderer.cpp @@ -211,10 +211,10 @@ bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo) this, &Q3DSRenderer::exitSlide); connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal, this, &Q3DSRenderer::customSignalEmitted); - connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigElementCreated, - this, &Q3DSRenderer::elementCreated); - connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMaterialCreated, - this, &Q3DSRenderer::materialCreated); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigElementsCreated, + this, &Q3DSRenderer::elementsCreated); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated, + this, &Q3DSRenderer::materialsCreated); return true; } @@ -337,21 +337,30 @@ void Q3DSRenderer::processCommands() cmd.m_stringValue, cmd.m_variantValue, static_cast(cmd.m_intValues[0])); break; - case CommandType_CreateElement: { - m_runtime->createElement(cmd.m_elementPath, cmd.m_stringValue, - *static_cast *>(cmd.m_data)); + case CommandType_CreateElements: { + m_runtime->createElements( + cmd.m_elementPath, cmd.m_stringValue, + *static_cast> *>(cmd.m_data)); // Runtime makes copy of the data in its own format, so we can delete it now auto &command = m_commands.commandAt(i); - delete reinterpret_cast *>(command.m_data); + delete reinterpret_cast> *>(command.m_data); command.m_data = nullptr; break; } - case CommandType_DeleteElement: { - m_runtime->deleteElement(cmd.m_elementPath); + case CommandType_DeleteElements: { + m_runtime->deleteElements(*static_cast(cmd.m_data)); + // Runtime makes copy of the data in its own format, so we can delete it now + auto &command = m_commands.commandAt(i); + delete reinterpret_cast(command.m_data); + command.m_data = nullptr; break; } - case CommandType_CreateMaterial: { - m_runtime->createMaterial(cmd.m_elementPath, cmd.m_stringValue); + case CommandType_CreateMaterials: { + m_runtime->createMaterials(cmd.m_elementPath, *static_cast(cmd.m_data)); + // Runtime makes copy of the data in its own format, so we can delete it now + auto &command = m_commands.commandAt(i); + delete reinterpret_cast(command.m_data); + command.m_data = nullptr; break; } case CommandType_RequestSlideInfo: { diff --git a/src/Runtime/api/studio3dqml/q3dsrenderer.h b/src/Runtime/api/studio3dqml/q3dsrenderer.h index 12f98aef..757ec4af 100644 --- a/src/Runtime/api/studio3dqml/q3dsrenderer.h +++ b/src/Runtime/api/studio3dqml/q3dsrenderer.h @@ -63,8 +63,8 @@ Q_SIGNALS: void presentationReady(); void presentationLoaded(); void customSignalEmitted(const QString &elNmentPath, const QString &name); - void elementCreated(const QString &elementName, const QString &error); - void materialCreated(const QString &name, const QString &error); + void elementsCreated(const QStringList &elementPaths, const QString &error); + void materialsCreated(const QStringList &materialNames, const QString &error); protected: static void onInitHandler(void *userData); diff --git a/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp b/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp index 2a6c7be2..75633a09 100644 --- a/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp +++ b/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp @@ -207,10 +207,10 @@ QQuickFramebufferObject::Renderer *Q3DSStudio3D::createRenderer() const m_presentation, &Q3DSPresentation::slideExited); connect(renderer, &Q3DSRenderer::customSignalEmitted, m_presentation, &Q3DSPresentation::customSignalEmitted); - connect(renderer, &Q3DSRenderer::elementCreated, - m_presentation, &Q3DSPresentation::elementCreated); - connect(renderer, &Q3DSRenderer::materialCreated, - m_presentation, &Q3DSPresentation::materialCreated); + connect(renderer, &Q3DSRenderer::elementsCreated, + m_presentation, &Q3DSPresentation::elementsCreated); + connect(renderer, &Q3DSRenderer::materialsCreated, + m_presentation, &Q3DSPresentation::materialsCreated); connect(renderer, &Q3DSRenderer::requestResponse, this, &Q3DSStudio3D::requestResponseHandler); connect(renderer, &Q3DSRenderer::presentationLoaded, diff --git a/src/Runtime/api/studio3dqml/q3dsstudio3d.h b/src/Runtime/api/studio3dqml/q3dsstudio3d.h index 484e9f65..f27dbf17 100644 --- a/src/Runtime/api/studio3dqml/q3dsstudio3d.h +++ b/src/Runtime/api/studio3dqml/q3dsstudio3d.h @@ -51,7 +51,7 @@ class Q3DSStudio3D : public QQuickFramebufferObject public: Q3DSStudio3D(); - virtual ~Q3DSStudio3D(); + ~Q3DSStudio3D() override; QQuickFramebufferObject::Renderer *createRenderer() const override; diff --git a/tests/auto/viewer/tst_qt3dsviewer.cpp b/tests/auto/viewer/tst_qt3dsviewer.cpp index 333ac950..d261c2a9 100644 --- a/tests/auto/viewer/tst_qt3dsviewer.cpp +++ b/tests/auto/viewer/tst_qt3dsviewer.cpp @@ -102,7 +102,7 @@ void tst_qt3dsviewer::init() void tst_qt3dsviewer::cleanup() { - deleteCreatedElements(0); + deleteCreatedElements(); if (!m_ignoreError) QCOMPARE(m_studio3DItem->property("error").toString(), {}); m_studio3DItem = nullptr; @@ -185,14 +185,16 @@ void tst_qt3dsviewer::testCreateElement() QSignalSpy spyExited(m_presentation, SIGNAL(slideExited(const QString &, unsigned int, const QString &))); - QSignalSpy spyElemCreated(m_presentation, SIGNAL(elementCreated(const QString &, - const QString &))); - - QObject::connect(m_presentation, &Q3DSPresentation::elementCreated, - [this](const QString &elementName, const QString &error) { - QVERIFY(error.isEmpty()); - if (!m_createdElements.contains(elementName)) - QVERIFY(false); + QSignalSpy spyElemCreated(m_presentation, SIGNAL(elementsCreated(const QStringList &, + const QString &))); + + QObject::connect(m_presentation, &Q3DSPresentation::elementsCreated, + [this](const QStringList &elementNames, const QString &error) { + QCOMPARE(error, QString()); + for (auto &elementName : elementNames) { + if (!m_createdElements.contains(elementName)) + QVERIFY(false); + } }); int animValue = 0; @@ -304,45 +306,32 @@ void tst_qt3dsviewer::testCreateElement() // Switch to slide 1 QVERIFY(spyExited.wait(20000)); - QTimer createTimer; - createTimer.setInterval(0); - static int elemCounter = 0; QRandomGenerator rnd; - - auto createElementsConnection = QObject::connect(&createTimer, &QTimer::timeout, [&]() { - // Create a bunch of elements to slide 2 in small batches to avoid slowdown - for (int i = 0; i < 5; ++i) { - ++elemCounter; - data.clear(); - QString elementName = QStringLiteral("MassElement_%1").arg(elemCounter); - data.insert(QStringLiteral("name"), elementName); - data.insert(QStringLiteral("sourcepath"), - elemCounter % 2 ? QStringLiteral("#Cube") : QStringLiteral("#Cone")); - data.insert(QStringLiteral("material"), - elemCounter % 2 ? QStringLiteral("Basic Green") - : QStringLiteral("Basic Red")); - data.insert(QStringLiteral("position"), - QVariant::fromValue(QVector3D(rnd.bounded(-600, 600), - rnd.bounded(-600, 600), - rnd.bounded(800, 1200)))); - - createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide2"), data); - } - if (elemCounter >= 1000) { - qDebug() << "Extra elements created:" << elemCounter; - createTimer.stop(); - } - }); - qDebug() << "Start creating extra elements"; - createTimer.start(); + QVector> massProps; + for (int i = 0; i < 1000; ++i) { + data.clear(); + QString elementName = QStringLiteral("MassElement_%1").arg(i); + data.insert(QStringLiteral("name"), elementName); + data.insert(QStringLiteral("sourcepath"), + i % 2 ? QStringLiteral("#Cube") : QStringLiteral("#Cone")); + data.insert(QStringLiteral("material"), + i % 2 ? QStringLiteral("Basic Green") : QStringLiteral("Basic Red")); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(rnd.bounded(-600, 600), + rnd.bounded(-600, 600), + rnd.bounded(800, 1200)))); + massProps << data; + m_createdElements << QStringLiteral("Scene.Layer.") + elementName; + } + m_presentation->createElements(QStringLiteral("Scene.Layer"), QStringLiteral("Slide2"), + massProps); // Switch to slide 2 QVERIFY(spyExited.wait(20000)); - QObject::disconnect(createElementsConnection); QTest::qWait(500); - QCOMPARE(spyElemCreated.count(), m_createdElements.count()); - deleteCreatedElements(1); + QCOMPARE(spyElemCreated.count(), 7); + deleteCreatedElements(); // Switch to slide 1 QVERIFY(spyExited.wait(20000)); @@ -358,19 +347,16 @@ void tst_qt3dsviewer::testCreateMaterial() QSignalSpy spyExited(m_presentation, SIGNAL(slideExited(const QString &, unsigned int, const QString &))); - QSignalSpy spyMatCreated(m_presentation, SIGNAL(materialCreated(const QString &, - const QString &))); - QSignalSpy spyElemCreated(m_presentation, SIGNAL(elementCreated(const QString &, - const QString &))); + QSignalSpy spyMatCreated(m_presentation, SIGNAL(materialsCreated(const QStringList &, + const QString &))); + QSignalSpy spyElemCreated(m_presentation, SIGNAL(elementsCreated(const QStringList &, + const QString &))); + + QStringList materialDefinitions; // Create material via .materialdef file in resources - m_presentation->createMaterial( - QStringLiteral("Scene"), - QStringLiteral( - ":/scenes/simple_cube_animation/materials/Basic Blue.materialdef")); - m_presentation->createMaterial( - QStringLiteral("Scene"), - QStringLiteral( - ":/scenes/simple_cube_animation/materials/Basic Texture.materialdef")); + materialDefinitions + << QStringLiteral(":/scenes/simple_cube_animation/materials/Basic Blue.materialdef") + << QStringLiteral(":/scenes/simple_cube_animation/materials/Basic Texture.materialdef"); // Create material directly from materialdef content auto loadMatDefFile = [&](const QString &fileName) -> QString { @@ -384,46 +370,47 @@ void tst_qt3dsviewer::testCreateMaterial() QString matDef = loadMatDefFile( QStringLiteral(":/scenes/simple_cube_animation/materials/Copper.materialdef")); QVERIFY(!matDef.isEmpty()); + materialDefinitions << matDef; - m_presentation->createMaterial(QStringLiteral("Scene"), matDef); - - QObject::connect(m_presentation, &Q3DSPresentation::materialCreated, - [this](const QString &name, const QString &error) { - QHash data; + m_presentation->createMaterials(QStringLiteral("Scene"), materialDefinitions); + QObject::connect(m_presentation, &Q3DSPresentation::materialsCreated, + [this](const QStringList &materialNames, const QString &error) { QVERIFY(error.isEmpty()); - - if (name == QLatin1String("materials/Basic Blue")) { - data.insert(QStringLiteral("name"), QStringLiteral("Blue Cylinder")); - data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cylinder")); - data.insert(QStringLiteral("material"), name); - data.insert(QStringLiteral("position"), - QVariant::fromValue(QVector3D(200, 300, 200))); - createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); - } else if (name == QLatin1String("materials/Basic Texture")) { - data.insert(QStringLiteral("name"), QStringLiteral("Textured Cone")); - data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cone")); - data.insert(QStringLiteral("material"), name); - data.insert(QStringLiteral("position"), - QVariant::fromValue(QVector3D(-200, -300, 200))); - createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); - } else if (name == QLatin1String("materials/Copper")) { - data.insert(QStringLiteral("name"), QStringLiteral("Copper Sphere")); - data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Sphere")); - data.insert(QStringLiteral("material"), name); - data.insert(QStringLiteral("position"), - QVariant::fromValue(QVector3D(-200, 300, 200))); - createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); - } else if (name == QLatin1String("materials/Just Yellow")) { + for (auto &name : materialNames) { QHash data; - data.insert(QStringLiteral("name"), QStringLiteral("Yellow Cube")); - data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cube")); - data.insert(QStringLiteral("material"), name); - data.insert(QStringLiteral("position"), - QVariant::fromValue(QVector3D(200, -300, 200))); - createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); - } else { - QVERIFY(false); + if (name == QLatin1String("materials/Basic Blue")) { + data.insert(QStringLiteral("name"), QStringLiteral("Blue Cylinder")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cylinder")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(200, 300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } else if (name == QLatin1String("materials/Basic Texture")) { + data.insert(QStringLiteral("name"), QStringLiteral("Textured Cone")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cone")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(-200, -300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } else if (name == QLatin1String("materials/Copper")) { + data.insert(QStringLiteral("name"), QStringLiteral("Copper Sphere")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Sphere")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(-200, 300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } else if (name == QLatin1String("materials/Just Yellow")) { + QHash data; + data.insert(QStringLiteral("name"), QStringLiteral("Yellow Cube")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cube")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(200, -300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } else { + QVERIFY(false); + } } }); @@ -439,32 +426,14 @@ void tst_qt3dsviewer::testCreateMaterial() }); QVERIFY(spyExited.wait(20000)); - QCOMPARE(spyMatCreated.count(), 4); + QCOMPARE(spyMatCreated.count(), 2); QCOMPARE(spyElemCreated.count(), 4); QTest::qWait(200); // Extra wait to verify slide change visually } -void tst_qt3dsviewer::deleteCreatedElements(int interval) +void tst_qt3dsviewer::deleteCreatedElements() { - QTimer deleteTimer; - deleteTimer.setInterval(interval); - int elemCounter = m_createdElements.size() - 1; - QObject::connect(&deleteTimer, &QTimer::timeout, [&]() { - // Delete all elements we created previously - if (elemCounter >= 0) { - m_presentation->deleteElement(m_createdElements[elemCounter]); - --elemCounter; - } else { - qDebug() << "Extra elements deleted"; - deleteTimer.stop(); - } - }); - qDebug() << "Start deleting extra elements"; - deleteTimer.start(); - - while (deleteTimer.isActive()) - QTest::qWait(20); - + m_presentation->deleteElements(m_createdElements); m_createdElements.clear(); } diff --git a/tests/auto/viewer/tst_qt3dsviewer.h b/tests/auto/viewer/tst_qt3dsviewer.h index 41c7ed31..f6281881 100644 --- a/tests/auto/viewer/tst_qt3dsviewer.h +++ b/tests/auto/viewer/tst_qt3dsviewer.h @@ -59,7 +59,7 @@ private Q_SLOTS: void testCreateMaterial(); private: - void deleteCreatedElements(int interval); + void deleteCreatedElements(); void createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties); -- cgit v1.2.3