summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2019-04-01 15:22:28 +0300
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2019-04-01 15:24:22 +0300
commit9aa02769f69ecf5f1204494efd1686af7160baef (patch)
tree9ed57808b60a8fcd35aede63a957009bb4a9eed4
parent22203c3521bc0213226c0c01d8782f0d0656da1e (diff)
parent5e0bfe934b9930b7e41d457b4f88fce91342a0ea (diff)
Merge branch '2.3'
-rw-r--r--.qmake.conf2
-rw-r--r--features/custominstall.prf10
-rw-r--r--src/imports/studio3d/plugin.cpp1
-rw-r--r--src/imports/studio3d/plugins.qmltypes30
-rw-r--r--src/runtime/animator/q3dsanimator.cpp7
-rw-r--r--src/runtime/api/q3dsdatainput.cpp7
-rw-r--r--src/runtime/api/q3dspresentation.cpp2
-rw-r--r--src/runtime/api/q3dspresentation.h8
-rw-r--r--src/runtime/dragon/dragon.pri3
-rw-r--r--src/runtime/dragon/dragonactivatedsurface.cpp2
-rw-r--r--src/runtime/dragon/dragonbackendnode.cpp7
-rw-r--r--src/runtime/dragon/dragonbackendnode_p.h11
-rw-r--r--src/runtime/dragon/dragonentity.cpp30
-rw-r--r--src/runtime/dragon/dragonentity_p.h4
-rw-r--r--src/runtime/dragon/dragonmapper_p.h40
-rw-r--r--src/runtime/dragon/dragonnodetree_p.h124
-rw-r--r--src/runtime/dragon/dragonrenderer.cpp4
-rw-r--r--src/runtime/dragon/dragonrendersettings.cpp16
-rw-r--r--src/runtime/dragon/dragonrendersettings_p.h8
-rw-r--r--src/runtime/dragon/dragonscene2d.cpp2
-rw-r--r--src/runtime/dragon/dragontask_p.h1
-rw-r--r--src/runtime/dragon/dragontrackingchangescontainer_p.h115
-rw-r--r--src/runtime/dragon/framegraph/dragonframegraphnode.cpp44
-rw-r--r--src/runtime/dragon/framegraph/dragonframegraphnode_p.h6
-rw-r--r--src/runtime/dragon/geometry/dragonbuffervisitor_p.h12
-rw-r--r--src/runtime/dragon/jobs/dragonboundingvolumejobs.cpp23
-rw-r--r--src/runtime/dragon/jobs/dragonboundingvolumejobs_p.h8
-rw-r--r--src/runtime/dragon/jobs/dragonjobs_common_p.h124
-rw-r--r--src/runtime/dragon/jobs/dragonrenderviewjobs.cpp4
-rw-r--r--src/runtime/dragon/jobs/dragontexturejobs.cpp10
-rw-r--r--src/runtime/dragon/jobs/dragontransformjobs.cpp44
-rw-r--r--src/runtime/dragon/jobs/dragontransformjobs_p.h2
-rw-r--r--src/runtime/dragon/jobs/dragontreejobs.cpp87
-rw-r--r--src/runtime/dragon/jobs/dragontreejobs_p.h94
-rw-r--r--src/runtime/dragon/materialsystem/dragonparameter.cpp2
-rw-r--r--src/runtime/dragon/materialsystem/dragonrenderpass.cpp3
-rw-r--r--src/runtime/dragon/materialsystem/dragontechnique.cpp4
-rw-r--r--src/runtime/dragon/qdragonrenderaspect.cpp52
-rw-r--r--src/runtime/dragon/qdragonrenderaspect_p.h54
-rw-r--r--src/runtime/dragon/renderer/dragondraw.cpp2
-rw-r--r--src/runtime/q3dsabstractslideplayer_p.h1
-rw-r--r--src/runtime/q3dscustommaterialgenerator.cpp18
-rw-r--r--src/runtime/q3dscustommaterialgenerator_p.h7
-rw-r--r--src/runtime/q3dsdefaultmaterialgenerator.cpp9
-rw-r--r--src/runtime/q3dsdefaultmaterialgenerator_p.h4
-rw-r--r--src/runtime/q3dsdistancefieldglyphcache.cpp5
-rw-r--r--src/runtime/q3dsdistancefieldglyphcache_p.h2
-rw-r--r--src/runtime/q3dsengine.cpp236
-rw-r--r--src/runtime/q3dsengine_p.h140
-rw-r--r--src/runtime/q3dsfontdatabase.cpp12
-rw-r--r--src/runtime/q3dsimageloaders_p.h17
-rw-r--r--src/runtime/q3dsimagemanager.cpp506
-rw-r--r--src/runtime/q3dsimagemanager_p.h117
-rw-r--r--src/runtime/q3dsmeshloader.cpp2
-rw-r--r--src/runtime/q3dsprofiler.cpp16
-rw-r--r--src/runtime/q3dsprofiler_p.h1
-rw-r--r--src/runtime/q3dsscenemanager.cpp820
-rw-r--r--src/runtime/q3dsscenemanager_p.h26
-rw-r--r--src/runtime/q3dsslideplayer.cpp41
-rw-r--r--src/runtime/q3dsslideplayer_p.h48
-rw-r--r--src/runtime/q3dssourceinfo_p.h55
-rw-r--r--src/runtime/q3dstextrenderer.cpp11
-rw-r--r--src/runtime/q3dsuipparser.cpp6
-rw-r--r--src/runtime/q3dsuippresentation.cpp51
-rw-r--r--src/runtime/q3dsuippresentation_p.h39
-rw-r--r--src/runtime/shadergenerator/q3dsshadermanager.cpp2
-rw-r--r--src/runtime/shadergenerator/q3dsshadermanager_p.h2
-rw-r--r--src/runtime/shaders/distancefieldtext_dropshadow.frag2
-rw-r--r--src/runtime/shaders/distancefieldtext_dropshadow_core.frag2
-rw-r--r--src/runtime/slideplayerng/q3dsslideplayerng.cpp40
-rw-r--r--src/runtime/slideplayerng/q3dsslideplayerng_p.h49
-rw-r--r--tests/auto/slideplayer/tst_q3dsslideplayer.cpp43
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/dynamicloading.uia15
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/maps/Abstract_001_COLOR.jpgbin0 -> 56180 bytes
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/maps/Blue_Marble_002_COLOR.jpgbin0 -> 77199 bytes
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/maps/Ice_002_COLOR.jpgbin0 -> 21439 bytes
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/maps/Moon_001_COLOR.jpgbin0 -> 13648 bytes
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/materials/Material.materialdef44
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/materials/Material2.materialdef44
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/materials/Material3.materialdef44
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/materials/Material4.materialdef44
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloading/presentations/dynamicloading.uip86
-rw-r--r--tests/manual/dynamicloadingapp/dynamicloadingapp.pro29
-rw-r--r--tests/manual/dynamicloadingapp/main.cpp69
-rw-r--r--tests/manual/dynamicloadingapp/main.qml98
-rw-r--r--tests/manual/dynamicloadingapp/qml.qrc16
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tools/q3dsviewer/main.cpp5
-rw-r--r--tools/q3dsviewer/q3dsviewer.pro2
89 files changed, 2846 insertions, 992 deletions
diff --git a/.qmake.conf b/.qmake.conf
index ce779ab..f70168b 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,6 +1,6 @@
load(qt_build_config)
-CONFIG += warning_clean
+CONFIG += warning_clean custominstall
DEFINES += QT_NO_FOREACH
MODULE_VERSION = 2.4.0
diff --git a/features/custominstall.prf b/features/custominstall.prf
new file mode 100644
index 0000000..835b3f9
--- /dev/null
+++ b/features/custominstall.prf
@@ -0,0 +1,10 @@
+# Fixes all INSTALLS paths so that QT_INSTALL_PREFIX is replaced by the value of
+# the environment variable QT3DS_CUSTOM_INSTALL_PATH
+
+QT3DS_CUSTOM_INSTALL_PATH = $$clean_path($$(QT3DS_CUSTOM_INSTALL_PATH))
+!isEmpty(QT3DS_CUSTOM_INSTALL_PATH) {
+ for (install, INSTALLS) {
+ CUR_PATH = $$eval($${install}.path)
+ $${install}.path = $$replace(CUR_PATH, $$[QT_INSTALL_PREFIX], $$QT3DS_CUSTOM_INSTALL_PATH)
+ }
+}
diff --git a/src/imports/studio3d/plugin.cpp b/src/imports/studio3d/plugin.cpp
index 9470abb..3129168 100644
--- a/src/imports/studio3d/plugin.cpp
+++ b/src/imports/studio3d/plugin.cpp
@@ -73,6 +73,7 @@ public:
{
qmlRegisterType<Q3DSStudio3DItem>(uri, 2, 0, "Studio3D");
qmlRegisterType<Q3DSPresentationItem>(uri, 2, 0, "Presentation");
+ qmlRegisterRevision<Q3DSPresentation, 3>(uri, 2, 3);
qmlRegisterType<Q3DSSubPresentationSettings>(uri, 2, 0, "SubPresentationSettings");
qmlRegisterType<Q3DSViewerSettings>(uri, 2, 0, "ViewerSettings");
diff --git a/src/imports/studio3d/plugins.qmltypes b/src/imports/studio3d/plugins.qmltypes
index d29725a..b1e15e0 100644
--- a/src/imports/studio3d/plugins.qmltypes
+++ b/src/imports/studio3d/plugins.qmltypes
@@ -22,6 +22,8 @@ Module {
exportMetaObjectRevisions: [0]
Property { name: "name"; type: "string" }
Property { name: "value"; type: "QVariant" }
+ Property { name: "max"; type: "float" }
+ Property { name: "min"; type: "float" }
Method {
name: "setName"
Parameter { name: "name"; type: "string" }
@@ -30,6 +32,14 @@ Module {
name: "setValue"
Parameter { name: "value"; type: "QVariant" }
}
+ Method {
+ name: "setMin"
+ Parameter { name: "min"; type: "float" }
+ }
+ Method {
+ name: "setMax"
+ Parameter { name: "max"; type: "float" }
+ }
}
Component {
name: "Q3DSElement"
@@ -180,6 +190,7 @@ Module {
Property { name: "profilingEnabled"; type: "bool" }
Property { name: "profileUiVisible"; type: "bool" }
Property { name: "profileUiScale"; type: "float" }
+ Property { name: "delayedLoading"; revision: 3; type: "bool" }
Signal {
name: "customSignalEmitted"
Parameter { name: "elementPath"; type: "string" }
@@ -197,6 +208,7 @@ Module {
Parameter { name: "index"; type: "int" }
Parameter { name: "name"; type: "string" }
}
+ Signal { name: "delayedLoadingChanged"; revision: 3 }
Method { name: "reload" }
Method {
name: "setDataInputValue"
@@ -230,6 +242,16 @@ Module {
Parameter { name: "wrap"; type: "bool" }
}
Method {
+ name: "preloadSlide"
+ revision: 3
+ Parameter { name: "elementPath"; type: "string" }
+ }
+ Method {
+ name: "unloadSlide"
+ revision: 3
+ Parameter { name: "elementPath"; type: "string" }
+ }
+ Method {
name: "getAttribute"
type: "QVariant"
Parameter { name: "elementPath"; type: "string" }
@@ -241,13 +263,17 @@ Module {
Parameter { name: "attributeName"; type: "string" }
Parameter { name: "value"; type: "QVariant" }
}
+ Method { name: "getDataInputs"; type: "QVariantList" }
}
Component {
name: "Q3DSPresentationItem"
defaultProperty: "qmlChildren"
prototype: "Q3DSPresentation"
- exports: ["QtStudio3D/Presentation 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtStudio3D/Presentation 2.0",
+ "QtStudio3D/Presentation 2.3"
+ ]
+ exportMetaObjectRevisions: [0, 3]
Property { name: "qmlChildren"; type: "QObject"; isList: true; isReadonly: true }
Method {
name: "appendQmlChildren"
diff --git a/src/runtime/animator/q3dsanimator.cpp b/src/runtime/animator/q3dsanimator.cpp
index 3f0265e..eb9db08 100644
--- a/src/runtime/animator/q3dsanimator.cpp
+++ b/src/runtime/animator/q3dsanimator.cpp
@@ -187,10 +187,11 @@ void Q3DSAnimator::advance(float dt)
bool eos = false;
bool resetLocalTime = false;
- // 1. If localTime is > 0.0f and newTime == 0.0f => EOS
- // 2. If localTime is < duration and newTime == duration => EOS
+ // 1. +rate && newTime == duration => EOS
+ // 2. -rate && newTime == 0.0f => EOS
+ // 3. !rate => no-op
// Note that we only notify aboud EOS when we're not looping/pingponging.
- if ((localTime > 0.0f && qFuzzyCompare(newTime, 0.0f)) || (localTime < duration && qFuzzyCompare(newTime, duration))) {
+ if (((rate > 0.0f) && qFuzzyCompare(newTime, duration)) || ((rate < 0.0f) && qFuzzyCompare(newTime, 0.0f))) {
switch (playMode) {
case PlayMode::PlayThroughTo:
// We don't handle slide changes here, as that's a foreign concept,
diff --git a/src/runtime/api/q3dsdatainput.cpp b/src/runtime/api/q3dsdatainput.cpp
index 0abeba4..d70f4ca 100644
--- a/src/runtime/api/q3dsdatainput.cpp
+++ b/src/runtime/api/q3dsdatainput.cpp
@@ -220,6 +220,9 @@ void Q3DSDataInput::setValue(const QVariant &value)
float Q3DSDataInput::min() const
{
Q_D(const Q3DSDataInput);
+ if (!d->presentation)
+ return 0.0f;
+
return Q3DSPresentationPrivate::get(d->presentation)->dataInputMin(d->name);
}
@@ -244,6 +247,10 @@ void Q3DSDataInput::setMin(float min)
float Q3DSDataInput::max() const
{
Q_D(const Q3DSDataInput);
+
+ if (!d->presentation)
+ return 0.0f;
+
return Q3DSPresentationPrivate::get(d->presentation)->dataInputMax(d->name);
}
diff --git a/src/runtime/api/q3dspresentation.cpp b/src/runtime/api/q3dspresentation.cpp
index 2233790..0e5ad47 100644
--- a/src/runtime/api/q3dspresentation.cpp
+++ b/src/runtime/api/q3dspresentation.cpp
@@ -137,7 +137,7 @@ void Q3DSPresentation::setSource(const QUrl &source)
/*!
\property Q3DSPresentation::variantList
- Holds a list of <variant group>:<variant> tags that are loaded when the
+ Holds a list of (variant group):(variant) tags that are loaded when the
\c{source} property is set. If this list is left empty (default), no variant
filtering is applied and all items are loaded regardless of variant tags in
the presentation. Variant mechanism allows one presentation project to
diff --git a/src/runtime/api/q3dspresentation.h b/src/runtime/api/q3dspresentation.h
index 9aaffa5..a2e48d1 100644
--- a/src/runtime/api/q3dspresentation.h
+++ b/src/runtime/api/q3dspresentation.h
@@ -59,7 +59,7 @@ class Q3DSV_EXPORT Q3DSPresentation : public QObject
Q_PROPERTY(bool profilingEnabled READ isProfilingEnabled WRITE setProfilingEnabled NOTIFY profilingEnabledChanged)
Q_PROPERTY(bool profileUiVisible READ isProfileUiVisible WRITE setProfileUiVisible NOTIFY profileUiVisibleChanged)
Q_PROPERTY(float profileUiScale READ profileUiScale WRITE setProfileUiScale NOTIFY profileUiScaleChanged)
- Q_PROPERTY(bool delayedLoading READ isDelayedLoadingEnabled WRITE setDelayedLoading NOTIFY delayedLoadingChanged)
+ Q_PROPERTY(bool delayedLoading READ isDelayedLoadingEnabled WRITE setDelayedLoading NOTIFY delayedLoadingChanged REVISION 3)
public:
explicit Q3DSPresentation(QObject *parent = nullptr);
@@ -94,8 +94,8 @@ public:
Q_INVOKABLE void goToSlide(const QString &elementPath, int index);
Q_INVOKABLE void goToSlide(const QString &elementPath, bool next, bool wrap);
- Q_INVOKABLE void preloadSlide(const QString &elementPath);
- Q_INVOKABLE void unloadSlide(const QString &elementPath);
+ Q_REVISION(3) Q_INVOKABLE void preloadSlide(const QString &elementPath);
+ Q_REVISION(3) Q_INVOKABLE void unloadSlide(const QString &elementPath);
Q_INVOKABLE QVariant getAttribute(const QString &elementPath, const QString &attributeName);
Q_INVOKABLE void setAttribute(const QString &elementPath, const QString &attributeName, const QVariant &value);
@@ -129,7 +129,7 @@ Q_SIGNALS:
void customSignalEmitted(const QString &elementPath, const QString &name);
void slideEntered(const QString &elementPath, int index, const QString &name);
void slideExited(const QString &elementPath, int index, const QString &name);
- void delayedLoadingChanged();
+ Q_REVISION(3) void delayedLoadingChanged();
protected:
Q3DSPresentation(Q3DSPresentationPrivate &dd, QObject *parent);
diff --git a/src/runtime/dragon/dragon.pri b/src/runtime/dragon/dragon.pri
index a32fbec..2b09cbe 100644
--- a/src/runtime/dragon/dragon.pri
+++ b/src/runtime/dragon/dragon.pri
@@ -46,7 +46,8 @@ HEADERS += \
$$PWD/dragonmutable_p.h \
$$PWD/dragoncomparegenerators_p.h \
$$PWD/dragontextureproperties_p.h \
- $$PWD/dragonscene2d_p.h
+ $$PWD/dragonscene2d_p.h \
+ $$PWD/dragonnodetree_p.h
SOURCES += \
$$PWD/dragonattachment.cpp \
diff --git a/src/runtime/dragon/dragonactivatedsurface.cpp b/src/runtime/dragon/dragonactivatedsurface.cpp
index 9699c5c..42c86ea 100644
--- a/src/runtime/dragon/dragonactivatedsurface.cpp
+++ b/src/runtime/dragon/dragonactivatedsurface.cpp
@@ -526,7 +526,7 @@ Immutable<GLVertexArrayObject> ActivatedSurface::createVertexArrayObject(
const auto attributeType = attributeTypeToGLBufferType(attribute->attributeType());
// Index Attribute
- bool attributeWasDirty = attributes.dirtyOrNew().contains(attributeId);
+ bool attributeWasDirty = attributes.hasDirtyOrCreated(attributeId);
// TODO fix this by checking in which cases we need a real update
bool geometryDirtyOrSomething = true;
diff --git a/src/runtime/dragon/dragonbackendnode.cpp b/src/runtime/dragon/dragonbackendnode.cpp
index fe00d75..f3b9721 100644
--- a/src/runtime/dragon/dragonbackendnode.cpp
+++ b/src/runtime/dragon/dragonbackendnode.cpp
@@ -48,14 +48,13 @@ void BackendNode::setManager(AbstractContainer *manager)
void BackendNode::markDirty()
{
- Q_ASSERT(m_manager != nullptr);
- m_manager->markDirty(m_peerId);
+ markDirty(Change::DirtyInfo::Other);
}
-void BackendNode::markDirty(const Immutable<AbstractContainer::DirtyInfo> &dirtyInfo)
+void BackendNode::markDirty(Change::DirtyInfo info)
{
Q_ASSERT(m_manager != nullptr);
- m_manager->markDirty(m_peerId, dirtyInfo);
+ m_manager->markDirty(m_peerId, info);
}
Qt3DCore::QNodeId BackendNode::peerId() const
diff --git a/src/runtime/dragon/dragonbackendnode_p.h b/src/runtime/dragon/dragonbackendnode_p.h
index 40ec8fb..31de54a 100644
--- a/src/runtime/dragon/dragonbackendnode_p.h
+++ b/src/runtime/dragon/dragonbackendnode_p.h
@@ -68,7 +68,7 @@ public:
// TODO consider removing, this is a bit hacky
// TODO perhaps we could just use the fact that we changed?
void markDirty();
- void markDirty(const Immutable<AbstractContainer::DirtyInfo> &dirtyInfo);
+ void markDirty(Change::DirtyInfo info);
Qt3DCore::QNodeId peerId() const;
// TODO consider making private and protected by befriending DragonNodeFunctor and
@@ -85,13 +85,18 @@ public:
bool isEnabled() const
{
- // TODO copy from real backend node
- return true;
+ return m_enabled;
+ }
+
+ void setEnabled(bool enabled)
+ {
+ m_enabled = enabled;
}
private:
AbstractContainer *m_manager = nullptr;
Qt3DCore::QNodeId m_peerId;
+ bool m_enabled = true;
};
} // namespace Dragon
diff --git a/src/runtime/dragon/dragonentity.cpp b/src/runtime/dragon/dragonentity.cpp
index 2ffee1c..a4e838c 100644
--- a/src/runtime/dragon/dragonentity.cpp
+++ b/src/runtime/dragon/dragonentity.cpp
@@ -67,7 +67,6 @@ void Entity::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &chang
// Note this is *not* the parentId as that is the ID of the parent QNode, which is not
// necessarily the same as the parent QEntity (which may be further up the tree).
m_parentEntityId = data.parentEntityId;
- m_childEntityIds = data.childEntityIds;
for (const auto &idAndType : qAsConst(data.componentIdsAndTypes))
addComponent(idAndType);
}
@@ -93,35 +92,6 @@ void Entity::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
markDirty();
break;
}
-
- case PropertyValueAdded: {
- QPropertyNodeAddedChangePtr change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
- if (change->metaObject()->inherits(&QEntity::staticMetaObject)) {
- if (!m_childEntityIds.contains(change->addedNodeId())) {
- m_childEntityIds.push_back(change->addedNodeId());
- markDirty(DirtyTreeInfo{});
- }
- }
- break;
- }
-
- case PropertyValueRemoved: {
- QPropertyNodeRemovedChangePtr change = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
- if (change->metaObject()->inherits(&QEntity::staticMetaObject)) {
- m_childEntityIds.removeAll(change->removedNodeId());
- markDirty(DirtyTreeInfo{});
- }
- break;
- }
-
- case PropertyUpdated: {
- auto propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e);
- if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) {
- m_enabled = propertyChange->value().toBool();
- markDirty();
- }
- break;
- }
default:
break;
}
diff --git a/src/runtime/dragon/dragonentity_p.h b/src/runtime/dragon/dragonentity_p.h
index 2b15fdb..73143ea 100644
--- a/src/runtime/dragon/dragonentity_p.h
+++ b/src/runtime/dragon/dragonentity_p.h
@@ -68,10 +68,6 @@ public:
Qt3DCore::QNodeId parentId() const;
Qt3DCore::QNodeId cameraLensComponent() const;
- // TODO not so happy about this, only needed for tree building
- Qt3DCore::QNodeIdVector treeChildren() const { return m_childEntityIds; }
-
- QVector<Qt3DCore::QNodeId> m_childEntityIds;
Qt3DCore::QNodeId m_materialComponent;
QVector<Qt3DCore::QNodeId> m_layerComponents;
Qt3DCore::QNodeId m_transformComponent;
diff --git a/src/runtime/dragon/dragonmapper_p.h b/src/runtime/dragon/dragonmapper_p.h
index 130c60c..a688249 100644
--- a/src/runtime/dragon/dragonmapper_p.h
+++ b/src/runtime/dragon/dragonmapper_p.h
@@ -44,6 +44,13 @@
#include <private/dragonbackendnode_p.h>
#include <private/dragonvaluecontainer_p.h>
+#include <private/dragonnodetree_p.h>
+
+#include <Qt3DCore/qpropertynodeaddedchange.h>
+#include <Qt3DCore/qpropertynoderemovedchange.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+
+#include <Qt3DCore/qpropertyupdatedchange.h>
QT_BEGIN_NAMESPACE
@@ -63,13 +70,15 @@ template <class Backend, class BackendTarget=Backend>
class NodeFunctor : public Qt3DCore::QBackendNodeMapper
{
public:
- NodeFunctor()
+ NodeFunctor(NodeTree *nodeTree)
: m_localNodes(new ValueContainer<Backend>())
, m_nodes(m_localNodes.get())
+ , m_nodeTree(nodeTree)
{}
- NodeFunctor(ValueContainer<Backend> *nodes)
+ NodeFunctor(NodeTree *nodeTree, ValueContainer<Backend> *nodes)
: m_nodes(nodes)
+ , m_nodeTree(nodeTree)
{}
// TODO constructors, one takes nothing, creates nodes - other takes existing list
@@ -82,6 +91,8 @@ public:
void destroy(Qt3DCore::QNodeId id) const final
{
+ m_nodeTree->removeNode(id);
+
auto &nodes = *m_nodes;
// TODO consider use modified to show there's a copy
nodes.remove(id);
@@ -90,20 +101,42 @@ public:
void initializeFromPeer(Qt3DCore::QNodeId id, const Qt3DCore::QNodeCreatedChangeBasePtr &change) const
{
+ m_nodeTree->setParent(id, change->parentId());
+
auto &nodes = *m_nodes;
// Using a std::move here because we are assigning to the same node.
nodes[id] = std::move(nodes[id]).modified([&change](Backend *node){
node->initializeFromPeer(change);
+ node->setEnabled(change->isNodeEnabled());
});
}
void sceneChangeEvent(Qt3DCore::QNodeId id, const Qt3DCore::QSceneChangePtr &e) const
{
+ switch (e->type()) {
+ case Qt3DCore::PropertyUpdated: {
+ Qt3DCore::QPropertyUpdatedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
+ if (change->propertyName() == QByteArrayLiteral("parent")) {
+ m_nodeTree->setParent(id, change->value().value<Qt3DCore::QNodeId>());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
auto &nodes = *m_nodes;
// Using a std::move here because we are assigning to the same node.
nodes[id] = std::move(nodes[id]).modified([&e](Backend *element){
+ if (e->type() == Qt3DCore::PropertyUpdated) {
+ Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
+ if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) {
+ element->setEnabled(propertyChange->value().toBool());
+ element->markDirty();
+ }
+ }
element->sceneChangeEvent(e);
});
@@ -139,6 +172,7 @@ private:
// TODO consider storing ValueContainer<BackendNode> (need to verify polymorphism)
mutable QScopedPointer<ValueContainer<Backend>> m_localNodes;
mutable ValueContainer<Backend> *m_nodes;
+ mutable NodeTree *m_nodeTree = nullptr;
};
template <typename T, typename U>
@@ -170,6 +204,8 @@ private:
template<class Backend, class BackendTarget>
Qt3DCore::QBackendNode *NodeFunctor<Backend, BackendTarget>::create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const
{
+ m_nodeTree->addNode(change->subjectId());
+
auto peerId = change->subjectId();
auto *wrapper = new MapperWrapper<Backend, BackendTarget>(this);
m_wrappers[peerId] = wrapper;
diff --git a/src/runtime/dragon/dragonnodetree_p.h b/src/runtime/dragon/dragonnodetree_p.h
new file mode 100644
index 0000000..94cd72a
--- /dev/null
+++ b/src/runtime/dragon/dragonnodetree_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DRAGONNODETREE_P_H
+#define DRAGONNODETREE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Dragon {
+
+/*!
+ * Qt3D passes very limited information to the backend about the child/parent hierarchy.
+ * Only parent information is passed on creation and only child information is passed in updates.
+ * The nodeTree tries its best to use information from the different
+ * NodeFunctors (a.k.a. QBackendNodeMappers) to construct a usable tree.
+ *
+ * TODO: Make Qt3D pass the parent id to the backend when parent changes and/or
+ * children on node creation.
+ */
+class NodeTree
+{
+public:
+ struct NodeInfo
+ {
+ Qt3DCore::QNodeId parentId;
+ QVector<Qt3DCore::QNodeId> childIds;
+ };
+ using Nodes = QHash<Qt3DCore::QNodeId, NodeInfo>;
+
+ void addNode(Qt3DCore::QNodeId id)
+ {
+ // node might already exist if setParent or addChild was called first
+ if (!m_nodes.contains(id))
+ m_nodes.insert(id, NodeInfo());
+ }
+
+ void removeNode(Qt3DCore::QNodeId id)
+ {
+ // this is mostly to clear the data in case the node appears again
+ NodeInfo node = m_nodes.take(id);
+ if (!node.parentId.isNull())
+ m_nodes[node.parentId].childIds.removeOne(id);
+ }
+
+ void setParent(Qt3DCore::QNodeId childId, Qt3DCore::QNodeId parentId)
+ {
+ // remove child from old parent
+ if (m_nodes.contains(childId)) {
+ Qt3DCore::QNodeId oldParentId = m_nodes[childId].parentId;
+ if (m_nodes.contains(oldParentId)) {
+ m_nodes[oldParentId].childIds.removeOne(childId);
+ }
+ }
+ // using operator[] here on purpose - we want to set it even if addNode has not been called
+ m_nodes[childId].parentId = parentId;
+ if (!parentId.isNull() && !m_nodes[parentId].childIds.contains(childId))
+ m_nodes[parentId].childIds.push_back(childId);
+ }
+
+ const QHash<Qt3DCore::QNodeId, NodeInfo> &nodes()
+ {
+ return m_nodes;
+ }
+
+ // Task interface
+ QHash<Qt3DCore::QNodeId, NodeInfo> result()
+ {
+ return m_nodes;
+ }
+
+ bool isFinished()
+ {
+ return true;
+ }
+
+private:
+ QHash<Qt3DCore::QNodeId, NodeInfo> m_nodes;
+};
+
+} // namespace Dragon
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // DRAGONNODETREE_P_H
diff --git a/src/runtime/dragon/dragonrenderer.cpp b/src/runtime/dragon/dragonrenderer.cpp
index 72b7301..2fef519 100644
--- a/src/runtime/dragon/dragonrenderer.cpp
+++ b/src/runtime/dragon/dragonrenderer.cpp
@@ -466,7 +466,7 @@ Renderer::Frame Renderer::doRender(Renderer::Frame frame)
// TODO actually check if attributes changed (see requiresVAOAttributeUpdate and requiresFullVAOUpdate)
if (!frame.uploadedVaos.contains(vaoKey)) {
// TODO make createVertexArrayObject take the shader and geometry (or just command) and return a complete VAO instead
- const auto &glShader = frame.uploadedShaders[command.m_shader->peerId()];
+ const auto &glShader = frame.uploadedShaders.get(command.m_shader->peerId());
const auto &geometry = command.m_geometry;
const auto &vao = activeSurface.createVertexArrayObject(
vaoKey, glShader, geometry, attributes, frame.uploadedBuffers);
@@ -591,7 +591,7 @@ Renderer::Frame Renderer::doRender(Renderer::Frame frame)
// TODO upload geometry data (in a job)
// TODO prepare shader uniforms
// TODO check if anything is missing from commands (see old prepareCommandSubmission)
- const auto &glShader = frame.uploadedShaders[command.m_shader->peerId()];
+ const auto &glShader = frame.uploadedShaders.get(command.m_shader->peerId());
stateSet = activeSurface.applyStateSet(stateSet, command.m_renderStateSet.get());
diff --git a/src/runtime/dragon/dragonrendersettings.cpp b/src/runtime/dragon/dragonrendersettings.cpp
index 6bf33f0..dd24064 100644
--- a/src/runtime/dragon/dragonrendersettings.cpp
+++ b/src/runtime/dragon/dragonrendersettings.cpp
@@ -44,7 +44,7 @@ using namespace Qt3DCore;
namespace Qt3DRender {
namespace Dragon {
-RenderSettings::RenderSettings()
+RenderSettings::RenderSettings(NodeTree *nodeTree)
: Qt3DCore::QBackendNode()
, m_renderPolicy(QRenderSettings::OnDemand)
, m_pickMethod(QPickingSettings::BoundingVolumePicking)
@@ -52,11 +52,17 @@ RenderSettings::RenderSettings()
, m_faceOrientationPickingMode(QPickingSettings::FrontFace)
, m_pickWorldSpaceTolerance(.1f)
, m_activeFrameGraph()
+ , m_nodeTree(nodeTree)
{
}
+// TODO see if maybe all that was needed was for this to have a parent?
+
void RenderSettings::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
{
+ m_nodeTree->addNode(peerId());
+ m_nodeTree->setParent(peerId(), change->parentId());
+
const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QRenderSettingsData>>(change);
const auto &data = typedChange->data;
m_activeFrameGraph = data.activeFrameGraphId;
@@ -84,14 +90,18 @@ void RenderSettings::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
m_activeFrameGraph = propertyChange->value().value<QNodeId>();
else if (propertyChange->propertyName() == QByteArrayLiteral("renderPolicy"))
m_renderPolicy = propertyChange->value().value<QRenderSettings::RenderPolicy>();
+ else if (propertyChange->propertyName() == QByteArrayLiteral("parent")) {
+ m_nodeTree->setParent(peerId(), propertyChange->value().value<QNodeId>());
+ }
// markDirty(AbstractRenderer::AllDirty);
}
QBackendNode::sceneChangeEvent(e);
}
-RenderSettingsFunctor::RenderSettingsFunctor(QDragonRenderAspect *renderer)
+RenderSettingsFunctor::RenderSettingsFunctor(QDragonRenderAspect *renderer, NodeTree *nodeTree)
: m_renderer(renderer)
+ , m_nodeTree(nodeTree)
{
}
@@ -103,7 +113,7 @@ Qt3DCore::QBackendNode *RenderSettingsFunctor::create(const Qt3DCore::QNodeCreat
return nullptr;
}
- RenderSettings *settings = new RenderSettings;
+ RenderSettings *settings = new RenderSettings(m_nodeTree);
m_renderer->m_renderSettings = settings;
return settings;
}
diff --git a/src/runtime/dragon/dragonrendersettings_p.h b/src/runtime/dragon/dragonrendersettings_p.h
index d3dcf34..bda7fda 100644
--- a/src/runtime/dragon/dragonrendersettings_p.h
+++ b/src/runtime/dragon/dragonrendersettings_p.h
@@ -43,6 +43,8 @@
//
#include <private/dragonbackendnode_p.h>
+#include <private/dragonmapper_p.h>
+
#include <Qt3DRender/qrendersettings.h>
#include <Qt3DRender/qpickingsettings.h>
@@ -56,7 +58,7 @@ class QDragonRenderAspect;
class Q_AUTOTEST_EXPORT RenderSettings : public Qt3DCore::QBackendNode
{
public:
- RenderSettings();
+ RenderSettings(NodeTree *nodeTree);
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override;
@@ -79,18 +81,20 @@ private:
QPickingSettings::FaceOrientationPickingMode m_faceOrientationPickingMode;
float m_pickWorldSpaceTolerance;
Qt3DCore::QNodeId m_activeFrameGraph;
+ NodeTree *m_nodeTree = nullptr;
};
class RenderSettingsFunctor : public Qt3DCore::QBackendNodeMapper
{
public:
- explicit RenderSettingsFunctor(QDragonRenderAspect *renderer);
+ explicit RenderSettingsFunctor(QDragonRenderAspect *renderer, NodeTree *nodeTree);
Qt3DCore::QBackendNode *create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const override;
Qt3DCore::QBackendNode *get(Qt3DCore::QNodeId id) const override;
void destroy(Qt3DCore::QNodeId id) const override;
private:
QDragonRenderAspect *m_renderer = nullptr;
+ NodeTree *m_nodeTree = nullptr;
};
} // namespace Render
diff --git a/src/runtime/dragon/dragonscene2d.cpp b/src/runtime/dragon/dragonscene2d.cpp
index b65cf16..57e201f 100644
--- a/src/runtime/dragon/dragonscene2d.cpp
+++ b/src/runtime/dragon/dragonscene2d.cpp
@@ -378,7 +378,7 @@ ValueContainer<Scene2DState> updateScene2Ds(ValueContainer<Scene2DState> scene2d
continue;
}
- if (outputs.dirty().contains(scene2d.m_data->m_outputId)) {
+ if (outputs.changes().contains(Change{Change::Action::Modified, scene2d.m_data->m_outputId})) {
RenderTargetOutput output = *outputs[scene2d.m_data->m_outputId];
Attachment attachment;
attachment.m_name = output.name;
diff --git a/src/runtime/dragon/dragontask_p.h b/src/runtime/dragon/dragontask_p.h
index 4d3150c..3fe2ed9 100644
--- a/src/runtime/dragon/dragontask_p.h
+++ b/src/runtime/dragon/dragontask_p.h
@@ -175,6 +175,7 @@ struct Task : public Qt3DCore::QAspectJob
template <typename OtherTask, typename... Tasks>
void assertTaskFinished(OtherTask task, Tasks... tasks)
{
+ Q_UNUSED(task);
Q_ASSERT(task != nullptr);
Q_ASSERT(task->isFinished());
assertTaskFinished(tasks...);
diff --git a/src/runtime/dragon/dragontrackingchangescontainer_p.h b/src/runtime/dragon/dragontrackingchangescontainer_p.h
index bba173c..c4df989 100644
--- a/src/runtime/dragon/dragontrackingchangescontainer_p.h
+++ b/src/runtime/dragon/dragontrackingchangescontainer_p.h
@@ -49,19 +49,56 @@ QT_BEGIN_NAMESPACE
namespace Qt3DRender {
namespace Dragon {
+struct Change
+{
+ enum class Action
+ {
+ Created,
+ Modified,
+ Removed
+ };
+
+ enum class DirtyInfo
+ {
+ Other,
+ Tree
+ };
+
+ Change() = default;
+
+ Change(Action action_, Qt3DCore::QNodeId id_)
+ : action(action_)
+ , id(id_)
+ , info(DirtyInfo::Other)
+ {
+ }
+
+ Change(Action action_, Qt3DCore::QNodeId id_, DirtyInfo info_)
+ : action(action_)
+ , id(id_)
+ , info(info_)
+ {
+ Q_ASSERT_X(action == Action::Modified, "Dragon::Change",
+ "ERROR: DirtyInfo provided for non-dirty change");
+ }
+
+ Action action = Action::Created;
+ Qt3DCore::QNodeId id;
+ DirtyInfo info = DirtyInfo::Other;
+};
+
+inline bool operator ==(const Change &a, const Change &b)
+{
+ return a.action == b.action && a.id == b.id;
+};
+
// TODO consider if this is really needed
class AbstractContainer
{
public:
- class DirtyInfo {
- public:
- DirtyInfo() = default;
- virtual ~DirtyInfo() = default;
- };
-
virtual ~AbstractContainer() {}
virtual void markDirty(const Qt3DCore::QNodeId &id) = 0;
- virtual void markDirty(const Qt3DCore::QNodeId &id, const Immutable<DirtyInfo> &dirtyInfo) = 0;
+ virtual void markDirty(const Qt3DCore::QNodeId &id, Change::DirtyInfo info) = 0;
};
// TODO make sure creating and removing is counted properly - consider adding a changelist instead
@@ -79,17 +116,12 @@ public:
void markDirty(const Key &id) override
{
- // If m_dirty already contains id, don't replace it. Otherwise there
- // is a risk to override DirtyTreeInfo{} with DirtyInfo{}
- if (m_dirty.contains(id))
- return;
-
- m_dirty.insert(id, DirtyInfo{});
+ m_changes.push_back(Change{Change::Action::Modified, id, Change::DirtyInfo::Other});
}
- void markDirty(const Key &id, const Immutable<DirtyInfo> &dirtyInfo) override
+ void markDirty(const Key &id, Change::DirtyInfo info) override
{
- m_dirty.insert(id, dirtyInfo);
+ m_changes.push_back(Change{Change::Action::Modified, id, info});
}
QList<Key> keys() const
@@ -116,7 +148,7 @@ public:
T& operator[](const Key &key)
{
if (!m_container.contains(key))
- m_created.push_back(key);
+ m_changes.push_back(Change{Change::Action::Created, key});
return m_container[key];
}
@@ -140,48 +172,46 @@ public:
return m_container.end();
}
- QVector<Key> created() const
+ bool anythingDirty() const
{
- return m_created;
+ return !m_changes.isEmpty();
}
- QVector<Key> removed() const
+ bool hasCreated(const Key &key) const
{
- return m_removed;
+ return m_changes.contains(Change{Change::Action::Created, key});
}
- QSet<Key> dirty() const
+ bool hasDirty(const Key &key) const
{
- return m_dirty.keys().toSet();
+ return m_changes.contains(Change{Change::Action::Modified, key});
}
- // TODO reconsider the use of QList...
- QList<Key> dirtyOrNew() const
+ bool hasRemoved(const Key &key) const
{
- return m_dirty.keys() + m_created.toList();
+ return m_changes.contains(Change{Change::Action::Removed, key});
}
- bool anythingDirty() const
+ bool hasDirtyOrCreated(const Key &key) const
{
- return !m_dirty.isEmpty()
- || !m_created.isEmpty()
- || !m_removed.isEmpty();
+ return hasDirty(key) || hasCreated(key);
}
void insert(const Key & key, T value)
{
- m_created.push_back(key);
+ m_changes.push_back(Change{Change::Action::Created, key});
m_container.insert(key, value);
}
- T value(const Key & key)
+ T get(const Key & key)
{
+ Q_ASSERT(m_container.contains(key));
return m_container.value(key);
}
T take(const Key & key)
{
- m_removed.push_back(key);
+ m_changes.push_back(Change{Change::Action::Removed, key});
return m_container.take(key);
}
@@ -197,28 +227,25 @@ public:
void reset()
{
- m_created.clear();
- m_removed.clear();
- m_dirty.clear();
+ m_changes.clear();
}
- void clear()
+ QVector<Change> changes() const
{
- m_removed = m_removed + m_container.keys();
- m_container.clear();
+ return m_changes;
}
- // TODO rename to dirty
- const QHash<Key, Immutable<DirtyInfo>> &dirtyInfo()
+ void clear()
{
- return m_dirty;
+ for (const auto &key : m_container.keys()) {
+ m_changes.push_back(Change{Change::Action::Removed, key});
+ }
+ m_container.clear();
}
private:
DataContainer m_container;
- QHash<Key, Immutable<DirtyInfo>> m_dirty;
- QVector<Key> m_created;
- QVector<Key> m_removed;
+ QVector<Change> m_changes;
};
template<typename T, typename Key>
diff --git a/src/runtime/dragon/framegraph/dragonframegraphnode.cpp b/src/runtime/dragon/framegraph/dragonframegraphnode.cpp
index 09dd558..5b058bf 100644
--- a/src/runtime/dragon/framegraph/dragonframegraphnode.cpp
+++ b/src/runtime/dragon/framegraph/dragonframegraphnode.cpp
@@ -70,52 +70,8 @@ FrameGraphNode::~FrameGraphNode()
{
}
-void FrameGraphNode::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
-{
- // Set up the parent child relationship and enabled state
- const auto creationChange = qSharedPointerCast<QFrameGraphNodeCreatedChangeBase>(change);
- m_childFrameGraphNodeIds = QFrameGraphNodeCreatedChangeBasePrivate::get(creationChange.get())->m_childFrameGraphNodeIds;
-}
-
-QVector<Qt3DCore::QNodeId> FrameGraphNode::childrenIds() const
-{
- return m_childFrameGraphNodeIds;
-}
-
void FrameGraphNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
{
- switch (e->type()) {
-
- case Qt3DCore::PropertyUpdated: {
- Qt3DCore::QPropertyUpdatedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
- break;
- }
-
- case Qt3DCore::PropertyValueAdded: {
- Qt3DCore::QPropertyNodeAddedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeAddedChange>(e);
- if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) {
- if (!m_childFrameGraphNodeIds.contains(change->addedNodeId())) {
- markDirty(DirtyTreeInfo{});
- m_childFrameGraphNodeIds.append(change->addedNodeId());
- }
- }
- break;
- }
-
- case Qt3DCore::PropertyValueRemoved: {
- Qt3DCore::QPropertyNodeRemovedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeRemovedChange>(e);
- if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) {
- m_childFrameGraphNodeIds.removeAll(change->removedNodeId());
- markDirty(DirtyTreeInfo{});
- }
- break;
- }
- default:
- break;
- }
- // TODO what if the parent changes?
-
- markDirty();
BackendNode::sceneChangeEvent(e);
}
diff --git a/src/runtime/dragon/framegraph/dragonframegraphnode_p.h b/src/runtime/dragon/framegraph/dragonframegraphnode_p.h
index 680f834..44ba9e5 100644
--- a/src/runtime/dragon/framegraph/dragonframegraphnode_p.h
+++ b/src/runtime/dragon/framegraph/dragonframegraphnode_p.h
@@ -91,18 +91,12 @@ public:
};
FrameGraphNodeType nodeType() const { return m_nodeType; }
- QVector<Qt3DCore::QNodeId> childrenIds() const;
-
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override;
FrameGraphNode(FrameGraphNodeType nodeType, Qt3DCore::QBackendNode::Mode mode = Qt3DCore::QBackendNode::ReadOnly);
- void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) override;
-
- Qt3DCore::QNodeIdVector treeChildren() const { return m_childFrameGraphNodeIds; }
private:
FrameGraphNodeType m_nodeType;
- QVector<Qt3DCore::QNodeId> m_childFrameGraphNodeIds;
friend class FrameGraphVisitor;
};
diff --git a/src/runtime/dragon/geometry/dragonbuffervisitor_p.h b/src/runtime/dragon/geometry/dragonbuffervisitor_p.h
index 8526e38..70be1f1 100644
--- a/src/runtime/dragon/geometry/dragonbuffervisitor_p.h
+++ b/src/runtime/dragon/geometry/dragonbuffervisitor_p.h
@@ -95,7 +95,7 @@ public:
}
bool apply(const Attribute &attribute,
- const Buffer &attributeBuffer,
+ const LoadedBuffer &attributeBuffer,
int drawVertexCount)
{
if (attribute.vertexBaseType() != VertexBaseType)
@@ -103,7 +103,7 @@ public:
if (attribute.vertexSize() < dataSize)
return false;
- auto data = attributeBuffer.m_data;
+ auto data = attributeBuffer.data;
auto buffer = BufferTypeInfo::castToType<VertexBaseType>(data, attribute.byteOffset());
switch (dataSize) {
@@ -118,9 +118,9 @@ public:
}
bool apply(const Attribute &attribute,
- const Buffer &attributeBuffer,
+ const LoadedBuffer &attributeBuffer,
const Attribute &indexAttribute,
- const Buffer &indexBuffer,
+ const LoadedBuffer &indexBuffer,
int drawVertexCount,
bool primitiveRestartEnabled,
int primitiveRestartIndex)
@@ -130,10 +130,10 @@ public:
if (attribute.vertexSize() < dataSize)
return false;
- auto data = attributeBuffer.m_data;
+ auto data = attributeBuffer.data;
auto buffer = BufferTypeInfo::castToType<VertexBaseType>(data, attribute.byteOffset());
- auto indexData = indexBuffer.m_data;
+ auto indexData = indexBuffer.data;
if (indexAttribute.vertexBaseType() == QAttribute::UnsignedShort) {
auto indexBuffer = BufferTypeInfo::castToType<QAttribute::UnsignedShort>(indexData, indexAttribute.byteOffset());
switch (dataSize) {
diff --git a/src/runtime/dragon/jobs/dragonboundingvolumejobs.cpp b/src/runtime/dragon/jobs/dragonboundingvolumejobs.cpp
index 2e1b321..0106151 100644
--- a/src/runtime/dragon/jobs/dragonboundingvolumejobs.cpp
+++ b/src/runtime/dragon/jobs/dragonboundingvolumejobs.cpp
@@ -57,9 +57,9 @@ public:
const Sphere &result() { return m_volume; }
bool apply(const Attribute &positionAttribute,
- const Buffer &attributeBuffer,
+ const LoadedBuffer &attributeBuffer,
const Attribute &indexAttribute,
- const Buffer &indexBuffer,
+ const LoadedBuffer &indexBuffer,
int drawVertexCount,
bool primitiveRestartEnabled,
int primitiveRestartIndex)
@@ -181,7 +181,7 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
const ValueContainer<Attribute> &attributes,
const ValueContainer<GeometryRenderer> &geometryRenderers,
const ValueContainer<Geometry> &geometries,
- const ValueContainer<Buffer> &buffers)
+ const ValueContainer<LoadedBuffer> &buffers)
{
localBoundingVolumes.reset();
@@ -195,7 +195,8 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
// TODO consider using an Optional here
LocalBoundingVolumeResult result = *localBoundingVolumes[id];
- bool entityDirty = entities.dirtyOrNew().contains(id);
+ bool entityDirty = entities.changes().contains(Change{Change::Action::Created, id}) ||
+ entities.changes().contains(Change{Change::Action::Modified, id});
if (entityDirty) {
// TODO make all these asserts part of the [] operator
Q_ASSERT(entities.contains(id));
@@ -205,7 +206,7 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
result.geometryRendererId = entity->m_geometryRendererComponent;
}
- if (!entity->m_enabled)
+ if (!entity->isEnabled())
return result;
if (result.geometryRendererId.isNull())
@@ -213,7 +214,7 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
// Geometry renderer is dirty if the entity is new, or if the geometry renderer itself changed
// TODO repeated pattern - can we generalize this somehow?
- bool geometryRendererDirty = entityDirty || geometryRenderers.dirtyOrNew().contains(result.geometryRendererId);
+ bool geometryRendererDirty = entityDirty || geometryRenderers.hasDirtyOrCreated(result.geometryRendererId);
if (geometryRendererDirty) {
Q_ASSERT(geometryRenderers.contains(result.geometryRendererId));
const auto &geometryRenderer = geometryRenderers[result.geometryRendererId];
@@ -229,7 +230,7 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
return result;
// TODO what if we have a geometry factory?
- bool geometryDirty = geometryRendererDirty || geometries.dirtyOrNew().contains(result.geometryId);
+ bool geometryDirty = geometryRendererDirty || geometries.hasDirtyOrCreated(result.geometryId);
if (geometryDirty) {
result.positionAttributeId = QNodeId(); // Reset the ID in case it was set previously
result.indexAttributeId = QNodeId();
@@ -269,7 +270,7 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
return result;
}
- bool positionAttributeDirty = geometryDirty || attributes.dirtyOrNew().contains(result.positionAttributeId);
+ bool positionAttributeDirty = geometryDirty || attributes.hasDirtyOrCreated(result.positionAttributeId);
if (positionAttributeDirty) {
Q_ASSERT(attributes.contains(result.positionAttributeId));
result.positionAttribute = attributes[result.positionAttributeId];
@@ -288,7 +289,7 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
return result;
}
- bool indexAttributeDirty = geometryDirty || attributes.dirtyOrNew().contains(result.indexAttributeId);
+ bool indexAttributeDirty = geometryDirty || attributes.hasDirtyOrCreated(result.indexAttributeId);
if (indexAttributeDirty) {
Q_ASSERT(attributes.contains(result.indexAttributeId));
result.indexAttribute = attributes[result.indexAttributeId];
@@ -310,12 +311,12 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
return 0;
}();
- bool indexBufferDirty = indexAttributeDirty || buffers.dirtyOrNew().contains(result.indexBufferId);
+ bool indexBufferDirty = indexAttributeDirty || buffers.hasDirtyOrCreated(result.indexBufferId);
if (indexBufferDirty) {
result.indexBuffer = buffers[result.indexBufferId];
}
- bool positionBufferDirty = positionAttributeDirty || buffers.dirtyOrNew().contains(result.positionBufferId);
+ bool positionBufferDirty = positionAttributeDirty || buffers.hasDirtyOrCreated(result.positionBufferId);
if (indexBufferDirty) {
result.positionBuffer = buffers[result.positionBufferId];
}
diff --git a/src/runtime/dragon/jobs/dragonboundingvolumejobs_p.h b/src/runtime/dragon/jobs/dragonboundingvolumejobs_p.h
index 7105dd5..55a0907 100644
--- a/src/runtime/dragon/jobs/dragonboundingvolumejobs_p.h
+++ b/src/runtime/dragon/jobs/dragonboundingvolumejobs_p.h
@@ -55,7 +55,7 @@ class Attribute;
class Entity;
class GeometryRenderer;
class Geometry;
-class Buffer;
+struct LoadedBuffer;
struct LocalBoundingVolumeResult {
Sphere boundingVolume;
@@ -69,9 +69,9 @@ struct LocalBoundingVolumeResult {
Qt3DCore::QNodeId indexAttributeId;
Immutable<Attribute> indexAttribute;
Qt3DCore::QNodeId positionBufferId;
- Immutable<Buffer> positionBuffer;
+ Immutable<LoadedBuffer> positionBuffer;
Qt3DCore::QNodeId indexBufferId;
- Immutable<Buffer> indexBuffer;
+ Immutable<LoadedBuffer> indexBuffer;
};
using LocalBoundingVolumes = ValueContainer<LocalBoundingVolumeResult>;
@@ -82,7 +82,7 @@ LocalBoundingVolumes calculateLocalBoundingVolumes(LocalBoundingVolumes localBou
const ValueContainer<Attribute> &attributes,
const ValueContainer<GeometryRenderer> &geometryRenderers,
const ValueContainer<Geometry> &geometries,
- const ValueContainer<Buffer> &buffers);
+ const ValueContainer<Qt3DRender::Dragon::LoadedBuffer> &buffers);
WorldBoundingVolumes calculateWorldBoundingVolumes(WorldBoundingVolumes worldBoundingVolumes,
const LocalBoundingVolumes &localBoundingVolumes,
diff --git a/src/runtime/dragon/jobs/dragonjobs_common_p.h b/src/runtime/dragon/jobs/dragonjobs_common_p.h
index bc6e9f0..c32f21e 100644
--- a/src/runtime/dragon/jobs/dragonjobs_common_p.h
+++ b/src/runtime/dragon/jobs/dragonjobs_common_p.h
@@ -55,11 +55,14 @@ namespace Dragon {
template<typename Target, typename Reference>
Target synchronizeKeys(Target target, const Reference &reference)
{
- for (const auto &key : reference.created()) {
- target[key];
- }
- for (const auto &key : reference.removed()) {
- target.remove(key);
+ const auto &changes = reference.changes();
+ for (const auto &change : changes) {
+ const auto &key = change.id;
+ if (change.action == Change::Action::Created) {
+ target[key];
+ } else if (change.action == Change::Action::Removed) {
+ target.remove(key);
+ }
}
return target;
}
@@ -67,14 +70,17 @@ Target synchronizeKeys(Target target, const Reference &reference)
template<typename Target, typename Reference, typename Callback>
Target synchronizeKeys(Target target, const Reference &reference, Callback callback)
{
- for (const auto &key : reference.dirtyOrNew()) {
- if (target.contains(key))
- target.markDirty(key);
- const auto &item = reference[key];
- target.insert(key, callback(key, item));
- }
- for (const auto &key : reference.removed()) {
- target.remove(key);
+ const auto &changes = reference.changes();
+ for (const auto &change : changes) {
+ const auto &key = change.id;
+ if (change.action == Change::Action::Created || change.action == Change::Action::Modified) {
+ if (target.contains(key))
+ target.markDirty(key);
+ const auto &item = reference[key];
+ target.insert(key, callback(key, item));
+ } else if (change.action == Change::Action::Removed) {
+ target.remove(key);
+ }
}
return target;
}
@@ -82,13 +88,15 @@ Target synchronizeKeys(Target target, const Reference &reference, Callback callb
template<typename Target, typename Reference, typename Callback>
Target synchronizeNew(Target target, const Reference &reference, Callback callback)
{
- for (const auto &key : reference.created()) {
-// Q_ASSERT(!target.contains(key));
- const auto &item = reference[key];
- target.insert(key, callback(key, item));
- }
- for (const auto &key : reference.removed()) {
- target.remove(key);
+ const auto &changes = reference.changes();
+ for (const auto &change : changes) {
+ const auto &key = change.id;
+ if (change.action == Change::Action::Created) {
+ const auto &item = reference[key];
+ target.insert(key, callback(key, item));
+ } else if (change.action == Change::Action::Removed) {
+ target.remove(key);
+ }
}
return target;
}
@@ -96,13 +104,16 @@ Target synchronizeNew(Target target, const Reference &reference, Callback callba
template<typename Target, typename Reference, typename Callback>
Target synchronizeDirty(Target target, const Reference &reference, Callback callback)
{
- for (const auto &key : reference.dirty()) {
- Q_ASSERT(target.contains(key));
- const auto &sourceItem = reference[key];
- target[key] = callback(key, std::move(target[key]), sourceItem);
- }
- for (const auto &key : reference.removed()) {
- target.remove(key);
+ const auto &changes = reference.changes();
+ for (const auto &change : changes) {
+ const auto &key = change.id;
+ if (change.action == Change::Action::Modified) {
+ Q_ASSERT(target.contains(key));
+ const auto &sourceItem = reference[key];
+ target[key] = callback(key, std::move(target[key]), sourceItem);
+ } else if (change.action == Change::Action::Removed) {
+ target.remove(key);
+ }
}
return target;
}
@@ -119,25 +130,27 @@ Target synchronizeKeys(Target target,
Compare compare,
CreateFunction create)
{
- for (const auto &id : reference.dirtyOrNew()) {
- if (target.contains(id))
- target.markDirty(id);
-
- const auto &item = reference[id];
- bool foundExisting = false;
- for (const auto &cached : target) {
- if (compare(*cached, item)) {
- target[id] = cached;
- foundExisting = true;
- break;
+ const auto &changes = reference.changes();
+ for (const auto &change : changes) {
+ const auto &id = change.id;
+ if (change.action == Change::Action::Modified || change.action == Change::Action::Created) {
+ if (target.contains(id))
+ target.markDirty(id);
+
+ const auto &item = reference[id];
+ bool foundExisting = false;
+ for (const auto &cached : target) {
+ if (compare(*cached, item)) {
+ target[id] = cached;
+ foundExisting = true;
+ break;
+ }
}
+ if (!foundExisting)
+ target[id] = create(id, *item);
+ } else if (change.action == Change::Action::Removed) {
+ target.remove(id);
}
- if (!foundExisting)
- target[id] = create(id, *item);
- }
- // Remove unused
- for (const auto &key : reference.removed()) {
- target.remove(key);
}
return target;
}
@@ -148,8 +161,12 @@ Target synchronizeNew(Target target,
Compare compare,
CreateFunction create)
{
- for (const auto &id : reference.created()) {
-// Q_ASSERT(!target.contains(id)); // this is a bit strict
+ const auto &changes = reference.changes();
+ for (const auto &change : changes) {
+ const auto &id = change.id;
+ if (change.action != Change::Action::Created)
+ continue;
+
const auto &item = reference[id];
bool foundExisting = false;
for (const auto &cached : target) {
@@ -171,7 +188,12 @@ Target synchronizeDirty(Target target,
Compare compare,
CreateFunction create)
{
- for (const auto &id : reference.dirty()) {
+ const auto &changes = reference.changes();
+ for (const auto &change : changes) {
+ const auto &id = change.id;
+ if (change.action != Change::Action::Modified)
+ continue;
+
Q_ASSERT(target.contains(id)); // this is a bit strict
target.markDirty(id);
const auto &item = reference[id];
@@ -192,15 +214,17 @@ Target synchronizeDirty(Target target,
template<typename Target, typename Reference, typename Callback>
Target rebuildAll(Target target, const Reference &reference, Callback callback)
{
+ for (const auto &change : reference.changes()) {
+ const auto &key = change.id;
+ if (change.action == Change::Action::Removed)
+ target.remove(key);
+ }
for (const auto &key : reference.keys()) {
// if (target.contains(key))
const auto &item = reference[key];
target[key] = callback(key, item);
target.markDirty(key);
}
- for (const auto &key : reference.removed()) {
- target.remove(key);
- }
return target;
}
diff --git a/src/runtime/dragon/jobs/dragonrenderviewjobs.cpp b/src/runtime/dragon/jobs/dragonrenderviewjobs.cpp
index 6371cf9..3f67116 100644
--- a/src/runtime/dragon/jobs/dragonrenderviewjobs.cpp
+++ b/src/runtime/dragon/jobs/dragonrenderviewjobs.cpp
@@ -729,7 +729,7 @@ RenderCommands buildDrawRenderCommands(RenderCommands renderCommands,
continue;
// TODO consider if this is the correct behavior (to hide when disabled)
- if (!entity->m_enabled)
+ if (!entity->isEnabled())
continue;
bool accepted = true;
@@ -1119,7 +1119,7 @@ GatheredParameters gatherMaterialParameters(GatheredParameters gatherResult,
result = update(std::move(result), technique->parameters());
for (const auto &renderPass : passes) {
- if (!renderPass->m_enabled)
+ if (!renderPass->isEnabled())
continue;
RenderPassParameterIds renderPassParameterIds;
renderPassParameterIds.renderPass = renderPass;
diff --git a/src/runtime/dragon/jobs/dragontexturejobs.cpp b/src/runtime/dragon/jobs/dragontexturejobs.cpp
index 09d4387..6ba6c5f 100644
--- a/src/runtime/dragon/jobs/dragontexturejobs.cpp
+++ b/src/runtime/dragon/jobs/dragontexturejobs.cpp
@@ -108,11 +108,13 @@ LoadedTextures loadTextures(LoadedTextures loadedTextures, const ValueContainer<
// TODO this is not the cleanest approach to finding dirty texture images that affect our
// texture. Consider storing a hash of textures using different images, or implementing
// more sophisticated dependency tracking between different types of nodes.
- const auto &dirtyOrNewImages = loadedImages.dirtyOrNew();
+ const auto &changes = loadedImages.changes();
for (const auto &textureId : loadedTextures.keys()) {
- const auto &loadedTexture = loadedTextures[textureId];
- for (const auto &loadedTextureImage : loadedTexture->images) {
- if (dirtyOrNewImages.contains(loadedTextureImage->image->peerId())) {
+ const auto loadedTextureImages = loadedTextures[textureId]->images;
+ for (const auto &loadedTextureImage : loadedTextureImages) {
+ const QNodeId textureImageId = loadedTextureImage->image->peerId();
+ if (changes.contains(Change{Change::Action::Modified, textureImageId}) ||
+ changes.contains(Change{Change::Action::Created, textureImageId})) {
loadedTextures[textureId] = generateTexture(textureId, textures[textureId]);
loadedTextures.markDirty(textureId);
}
diff --git a/src/runtime/dragon/jobs/dragontransformjobs.cpp b/src/runtime/dragon/jobs/dragontransformjobs.cpp
index 6d15796..2b84adc 100644
--- a/src/runtime/dragon/jobs/dragontransformjobs.cpp
+++ b/src/runtime/dragon/jobs/dragontransformjobs.cpp
@@ -46,25 +46,27 @@ namespace Qt3DRender {
namespace Dragon {
ValueContainer<Matrix4x4> calculateWorldTransforms(ValueContainer<Matrix4x4> worldTransforms,
+ const QHash<QNodeId, NodeTree::NodeInfo> hierarchy,
const ValueContainer<Entity> &entities,
const ValueContainer<Transform> &transforms,
QNodeId rootEntityId)
{
worldTransforms.reset();
- if (!entities.anythingDirty() && !transforms.anythingDirty())
+ // TODO remove false
+ if (!entities.anythingDirty() && !transforms.anythingDirty() && false)
return worldTransforms;
+ worldTransforms = ValueContainer<Matrix4x4>();
+
struct Data {
Data() { }
- Data(QNodeId entityId_, bool dirty_) : entityId(entityId_), dirty(dirty_) { }
+ Data(QNodeId entityId_, bool dirty_, int level_) : entityId(entityId_), dirty(dirty_), level(level_) { }
QNodeId entityId;
bool dirty = false;
+ int level = 0;
};
- const auto &dirtyTransforms = transforms.dirtyOrNew();
- const auto &dirtyEntities = entities.dirtyOrNew();
-
// If entities are dirty, a transform component may have changed and we set the entire tree to
// be recalculated. Note that this could be optimized.
bool rootDirty = entities.anythingDirty();
@@ -74,45 +76,63 @@ ValueContainer<Matrix4x4> calculateWorldTransforms(ValueContainer<Matrix4x4> wor
worldTransforms[rootEntityId] = Matrix4x4{};
} else {
const auto &transform = transforms[rootEntity->m_transformComponent];
- if (transforms.dirty().contains(rootEntity->m_transformComponent))
+ if (transforms.hasDirty(rootEntity->m_transformComponent))
rootDirty = true;
worldTransforms[rootEntityId] = transform->m_transformMatrix;
}
QStack<Data> stack;
stack.reserve(10);
- stack.push({ rootEntityId, rootDirty });
+ stack.push({ rootEntityId, rootDirty, 0 });
while (!stack.isEmpty()) {
auto currentItem = stack.pop();
QNodeId currentId = currentItem.entityId;
- bool currentDirty = currentItem.dirty;
+ int level = currentItem.level;
+// bool currentDirty = currentItem.dirty;
+ bool currentDirty = true;
if (currentDirty)
worldTransforms.markDirty(currentId);
const auto &currentWorldTransform = *worldTransforms[currentId];
- const auto &children = entities[currentId]->treeChildren();
+ Q_ASSERT(hierarchy.contains(currentId));
+
+ const auto &children = hierarchy[currentId].childIds;
// Iterate the children in reverse order (because the stack is last-in-first-out)
// TODO use std::reverse_iterator once we have C++17
for (auto it = children.rbegin(); it != children.rend(); ++it) {
const auto &childId = *it;
+ if (!entities.contains(childId)) {
+ // Skip past non-QEntity nodes
+ worldTransforms[childId] = currentWorldTransform;
+ stack.push({childId, currentDirty, level + 1});
+ continue;
+ }
const auto &childEntity = entities[childId];
- bool childDirty = currentDirty || dirtyEntities.contains(childId);
+ bool childDirty = currentDirty || entities.hasDirtyOrCreated(childId);
if (!childEntity->m_transformComponent.isNull()) {
const auto &transform = transforms[childEntity->m_transformComponent];
- childDirty |= dirtyTransforms.contains(childEntity->m_transformComponent);
+ childDirty |= transforms.hasDirtyOrCreated(childEntity->m_transformComponent);
if (childDirty)
worldTransforms[childId] = currentWorldTransform * transform->m_transformMatrix;
} else {
if (childDirty)
worldTransforms[childId] = currentWorldTransform;
}
- stack.push({ childId, childDirty });
+ stack.push({ childId, childDirty, level + 1 });
}
}
+#ifdef QT_DEBUG
+ // Verify that all exist in debug mode only
+ for (const auto &key : entities.keys()) {
+ Q_ASSERT_X(worldTransforms.contains(key), "calculateWorldTransforms",
+ QStringLiteral("ERROR: Key %1 missing").arg(key.id()).toStdString().c_str());
+ }
+#endif
+
return worldTransforms;
};
diff --git a/src/runtime/dragon/jobs/dragontransformjobs_p.h b/src/runtime/dragon/jobs/dragontransformjobs_p.h
index 91269f6..991f2d0 100644
--- a/src/runtime/dragon/jobs/dragontransformjobs_p.h
+++ b/src/runtime/dragon/jobs/dragontransformjobs_p.h
@@ -49,6 +49,7 @@
#include <private/dragontreejobs_p.h>
#include <private/dragonsphere_p.h>
+#include <private/dragonmapper_p.h>
QT_BEGIN_NAMESPACE
@@ -61,6 +62,7 @@ class Geometry;
class Buffer;
ValueContainer<Matrix4x4> calculateWorldTransforms(ValueContainer<Matrix4x4> worldTransforms,
+ const QHash<Qt3DCore::QNodeId, NodeTree::NodeInfo> hierarchy,
const ValueContainer<Entity> &entities,
const ValueContainer<Transform> &transforms,
Qt3DCore::QNodeId rootEntityId);
diff --git a/src/runtime/dragon/jobs/dragontreejobs.cpp b/src/runtime/dragon/jobs/dragontreejobs.cpp
index e60dc60..c3b01fc 100644
--- a/src/runtime/dragon/jobs/dragontreejobs.cpp
+++ b/src/runtime/dragon/jobs/dragontreejobs.cpp
@@ -33,6 +33,93 @@
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
namespace Dragon {
+
+TreeInfo generateInheritanceTable(TreeInfo inheritanceTable,
+ const NodeTree::Nodes hierarchy,
+ const ValueContainer<FrameGraphNode> nodes,
+ Qt3DCore::QNodeId rootEntityId)
+{
+ inheritanceTable.nodes.reset();
+
+ if (!nodes.anythingDirty())
+ return inheritanceTable;
+
+ const auto &changes = nodes.changes();
+ bool dirtyTree = false;
+ for (const auto &change : changes) {
+ if (change.action != Change::Action::Modified || change.info == Change::DirtyInfo::Tree) {
+ dirtyTree = true;
+ break;
+ }
+ }
+ if (!dirtyTree)
+ return inheritanceTable;
+
+ // Clear old data
+ inheritanceTable.leafNodes.clear();
+ inheritanceTable.nodes = synchronizeKeys(std::move(inheritanceTable.nodes), nodes);
+ inheritanceTable.rootNode = rootEntityId;
+
+ // Clear old data, set parent and find all children of all nodes
+ for (const auto &id : nodes.keys()) {
+ auto tableNode = *(inheritanceTable.nodes[id]);
+ inheritanceTable.nodes.markDirty(id);
+ inheritanceTable.nodes[id] = tableNode;
+ }
+
+
+ {
+ // Traverse the tree from top to bottom to add ancestor and descendant information
+ // using a depth-first search.
+ QStack<Qt3DCore::QNodeId> stack;
+ stack.push(rootEntityId);
+ while (!stack.isEmpty()) {
+ auto currentId = stack.pop();
+ QVector<Qt3DCore::QNodeId> currentAncestors = inheritanceTable.nodes[currentId]->ancestors
+ + QVector<Qt3DCore::QNodeId>{{currentId}};
+
+ // NOTE: A copy is necessary, because we might insert into the map below
+ const QVector<Qt3DCore::QNodeId> children = hierarchy[currentId].childIds;
+ QVector<Qt3DCore::QNodeId> nodeChildren;
+ for (const auto &childId : children) {
+ if (nodes.contains(childId)) {
+ nodeChildren.push_back(childId);
+ }
+ }
+
+ if (nodeChildren.isEmpty()) {
+ inheritanceTable.leafNodes.push_back(currentId);
+ continue;
+ }
+
+ // Iterate the children in reverse order (because the stack is last-in-first-out)
+ // TODO use std::reverse_iterator once we have C++17
+ for (auto it = nodeChildren.rbegin(); it != nodeChildren.rend(); ++it) {
+ const auto &childId = *it;
+ auto child = *inheritanceTable.nodes[childId];
+ child.ancestors = currentAncestors;
+ inheritanceTable.nodes[childId] = child;
+ // Go through all ancestors and add us as a child
+ for (const auto &ancestorId : qAsConst(currentAncestors)) {
+ auto ancestor = *inheritanceTable.nodes[ancestorId];
+ ancestor.descendants.push_back(childId);
+ inheritanceTable.nodes[ancestorId] = ancestor;
+ }
+ // Only add if not added as one of the dirty nodes
+ stack.push(childId);
+ }
+ }
+ // Reverse all the ancestor lists
+ for (const auto &id : inheritanceTable.nodes.keys()) {
+ auto node = *inheritanceTable.nodes[id];
+ std::reverse(node.ancestors.begin(), node.ancestors.end());
+ inheritanceTable.nodes[id] = node;
+ }
+ }
+
+ return inheritanceTable;
+}
+
} // namespace Dragon
} // namespace Qt3DRender
QT_END_NAMESPACE
diff --git a/src/runtime/dragon/jobs/dragontreejobs_p.h b/src/runtime/dragon/jobs/dragontreejobs_p.h
index 9dd4955..33cafbb 100644
--- a/src/runtime/dragon/jobs/dragontreejobs_p.h
+++ b/src/runtime/dragon/jobs/dragontreejobs_p.h
@@ -45,6 +45,8 @@
#include <private/dragonjobs_common_p.h>
#include <private/dragontextureimage_p.h>
#include <private/dragonvaluecontainer_p.h>
+#include <private/dragonnodetree_p.h>
+#include <private/dragonframegraphnode_p.h>
#include <Qt3DCore/QNodeId>
@@ -64,13 +66,11 @@ struct InheritanceInfo
// TODO consider adding an ancestor list that includes ourselves (useful to build render views)
QVector<Key> ancestors;
QVector<Key> descendants;
- QVector<Key> children;
void clear()
{
ancestors.clear();
descendants.clear();
- children.clear();
}
};
@@ -82,92 +82,10 @@ struct TreeInfo
Key rootNode;
};
-class DirtyTreeInfo : public AbstractContainer::DirtyInfo
-{
-public:
-};
-
-template<typename T>
-TreeInfo generateInheritanceTable(TreeInfo inheritanceTable, T nodes, Qt3DCore::QNodeId rootEntityId)
-{
- inheritanceTable.nodes.reset();
-
- if (!nodes.anythingDirty())
- return inheritanceTable;
-
- if (nodes.created().isEmpty()) {
- const auto &dirtyInfos = nodes.dirtyInfo();
- bool dirtyTreeStuff = false;
- for (const auto &id : dirtyInfos.keys()) {
- const Immutable<AbstractContainer::DirtyInfo> &dirtyInfo = dirtyInfos[id];
- if (dirtyInfo.can_convert<DirtyTreeInfo>()) {
- dirtyTreeStuff = true;
- }
- }
-
- if (!dirtyTreeStuff)
- return inheritanceTable;
- }
-
- // Clear old data
- inheritanceTable.leafNodes.clear();
- inheritanceTable.nodes = synchronizeKeys(std::move(inheritanceTable.nodes), nodes);
- inheritanceTable.rootNode = rootEntityId;
-
- // Clear old data, set parent and find all children of all nodes
- for (const auto &id : nodes.keys()) {
- const auto &node = nodes[id];
- auto tableNode = *(inheritanceTable.nodes[id]);
- inheritanceTable.nodes.markDirty(id);
- tableNode.children = node->treeChildren();
- inheritanceTable.nodes[id] = tableNode;
- }
-
- {
- // Traverse the tree from top to bottom to add ancestor and descendant information
- // using a depth-first search.
- QStack<Qt3DCore::QNodeId> stack;
- stack.push(rootEntityId);
- while (!stack.isEmpty()) {
- auto currentId = stack.pop();
- QVector<Qt3DCore::QNodeId> currentAncestors = inheritanceTable.nodes[currentId]->ancestors
- + QVector<Qt3DCore::QNodeId>{{currentId}};
-
- // NOTE: A copy is necessary, because we might insert into the map below
- const auto children = inheritanceTable.nodes[currentId]->children;
-
- if (children.isEmpty()) {
- inheritanceTable.leafNodes.push_back(currentId);
- continue;
- }
-
- // Iterate the children in reverse order (because the stack is last-in-first-out)
- // TODO use std::reverse_iterator once we have C++17
- for (auto it = children.rbegin(); it != children.rend(); ++it) {
- const auto &childId = *it;
- auto child = *inheritanceTable.nodes[childId];
- child.ancestors = currentAncestors;
- inheritanceTable.nodes[childId] = child;
- // Go through all ancestors and add us as a child
- for (const auto &ancestorId : qAsConst(currentAncestors)) {
- auto ancestor = *inheritanceTable.nodes[ancestorId];
- ancestor.descendants.push_back(childId);
- inheritanceTable.nodes[ancestorId] = ancestor;
- }
- // Only add if not added as one of the dirty nodes
- stack.push(childId);
- }
- }
- // Reverse all the ancestor lists
- for (const auto &id : inheritanceTable.nodes.keys()) {
- auto node = *inheritanceTable.nodes[id];
- std::reverse(node.ancestors.begin(), node.ancestors.end());
- inheritanceTable.nodes[id] = node;
- }
- }
-
- return inheritanceTable;
-}
+TreeInfo generateInheritanceTable(TreeInfo inheritanceTable,
+ const NodeTree::Nodes hierarchy,
+ const ValueContainer<FrameGraphNode> nodes,
+ Qt3DCore::QNodeId rootEntityId);
} // namespace Dragon
} // namespace Qt3DRender
diff --git a/src/runtime/dragon/materialsystem/dragonparameter.cpp b/src/runtime/dragon/materialsystem/dragonparameter.cpp
index f1ebc22..558babe 100644
--- a/src/runtime/dragon/materialsystem/dragonparameter.cpp
+++ b/src/runtime/dragon/materialsystem/dragonparameter.cpp
@@ -70,8 +70,6 @@ void Parameter::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
} else if (propertyChange->propertyName() == QByteArrayLiteral("value")) {
m_uniformValue = UniformValue::fromVariant(propertyChange->value());
markDirty();
- } else if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) {
- markDirty();
}
}
diff --git a/src/runtime/dragon/materialsystem/dragonrenderpass.cpp b/src/runtime/dragon/materialsystem/dragonrenderpass.cpp
index b4ea1d8..e4eae52 100644
--- a/src/runtime/dragon/materialsystem/dragonrenderpass.cpp
+++ b/src/runtime/dragon/materialsystem/dragonrenderpass.cpp
@@ -80,9 +80,6 @@ void RenderPass::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
if (change->propertyName() == QByteArrayLiteral("shaderProgram")) {
m_shaderUuid = change->value().value<Qt3DCore::QNodeId>();
markDirty();
- } else if (change->propertyName() == QByteArrayLiteral("enabled")) {
- m_enabled = change->value().toBool();
- markDirty();
}
break;
}
diff --git a/src/runtime/dragon/materialsystem/dragontechnique.cpp b/src/runtime/dragon/materialsystem/dragontechnique.cpp
index bc0f04b..6bd7914 100644
--- a/src/runtime/dragon/materialsystem/dragontechnique.cpp
+++ b/src/runtime/dragon/materialsystem/dragontechnique.cpp
@@ -64,9 +64,7 @@ void Technique::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
switch (e->type()) {
case PropertyUpdated: {
const auto change = qSharedPointerCast<QPropertyUpdatedChange>(e);
- if (change->propertyName() == QByteArrayLiteral("enabled")) {
- markDirty();
- } else if (change->propertyName() == QByteArrayLiteral("graphicsApiFilterData")) {
+ if (change->propertyName() == QByteArrayLiteral("graphicsApiFilterData")) {
m_graphicsApiFilterData = change->value().value<GraphicsApiFilterData>();
markDirty();
}
diff --git a/src/runtime/dragon/qdragonrenderaspect.cpp b/src/runtime/dragon/qdragonrenderaspect.cpp
index e6d57c0..2f53d16 100644
--- a/src/runtime/dragon/qdragonrenderaspect.cpp
+++ b/src/runtime/dragon/qdragonrenderaspect.cpp
@@ -216,60 +216,65 @@ QDragonRenderAspect::QDragonRenderAspect(Renderer::RenderType renderType)
registerBackendType<Quick::QScene2D>(m_scene2ds);
// Custom functors
- registerBackendType<QRenderSettings>(QSharedPointer<RenderSettingsFunctor>::create(this));
+ registerBackendType<QRenderSettings>(QSharedPointer<RenderSettingsFunctor>::create(this, &m_nodeTree));
// Framegraph
registerBackendType<QFrameGraphNode>(
- NodeFunctorPtr<FrameGraphNode, FrameGraphNode>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, FrameGraphNode>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QCameraSelector>(
- NodeFunctorPtr<FrameGraphNode, CameraSelector>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, CameraSelector>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QClearBuffers>(
- NodeFunctorPtr<FrameGraphNode, ClearBuffers>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, ClearBuffers>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QDispatchCompute>(
- NodeFunctorPtr<FrameGraphNode, DispatchCompute>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, DispatchCompute>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QFrustumCulling>(
- NodeFunctorPtr<FrameGraphNode, FrustumCulling>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, FrustumCulling>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QLayerFilter>(
- NodeFunctorPtr<FrameGraphNode, LayerFilterNode>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, LayerFilterNode>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QNoDraw>(
- NodeFunctorPtr<FrameGraphNode, NoDraw>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, NoDraw>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QRenderPassFilter>(
- NodeFunctorPtr<FrameGraphNode, RenderPassFilter>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, RenderPassFilter>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QRenderStateSet>(
- NodeFunctorPtr<FrameGraphNode, StateSetNode>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, StateSetNode>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QRenderSurfaceSelector>(
- NodeFunctorPtr<FrameGraphNode, RenderSurfaceSelector>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, RenderSurfaceSelector>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QRenderTargetSelector>(
- NodeFunctorPtr<FrameGraphNode, RenderTargetSelector>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, RenderTargetSelector>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QSortPolicy>(
- NodeFunctorPtr<FrameGraphNode, SortPolicy>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, SortPolicy>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QTechniqueFilter>(
- NodeFunctorPtr<FrameGraphNode, TechniqueFilter>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, TechniqueFilter>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QViewport>(
- NodeFunctorPtr<FrameGraphNode, ViewportNode>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, ViewportNode>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QRenderCapture>(
- NodeFunctorPtr<FrameGraphNode, RenderCapture>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, RenderCapture>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QBufferCapture>(
- NodeFunctorPtr<FrameGraphNode, BufferCapture>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, BufferCapture>::create(&m_nodeTree, &m_frameGraphNodesContainer));
// TODO add this back when MemoryBarrier is expanded correctly on MinGW...
// registerBackendType<QMemoryBarrier>(
// NodeFunctorPtr<FrameGraphNode, MemoryBarrier>::create(&m_frameGraphNodesContainer));
registerBackendType<QProximityFilter>(
- NodeFunctorPtr<FrameGraphNode, ProximityFilter>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, ProximityFilter>::create(&m_nodeTree, &m_frameGraphNodesContainer));
registerBackendType<QBlitFramebuffer>(
- NodeFunctorPtr<FrameGraphNode, BlitFramebuffer>::create(&m_frameGraphNodesContainer));
+ NodeFunctorPtr<FrameGraphNode, BlitFramebuffer>::create(&m_nodeTree, &m_frameGraphNodesContainer));
+
+ // Catch-all for other nodes. Necessary to gather information about the hierarchy.
+ registerBackendType<Qt3DCore::QNode>(m_otherNodes);
// TODO could be done in header or initializer
// TODO syntax could be improved, consider adding a createEdge/setInput function
m_calculateWorldTransforms = TaskPtr<ValueContainer<Matrix4x4>>::create(
- calculateWorldTransforms, Self, m_entities, m_transforms, m_rootEntitySource);
+ calculateWorldTransforms, Self, &m_nodeTree, m_entities, m_transforms, m_rootEntitySource);
+
+ m_loadBuffers = TaskPtr<LoadedBuffers>::create(loadBuffers, Self, m_buffers);
m_calculateLocalBoundingVolumes = TaskPtr<LocalBoundingVolumes>::create(
calculateLocalBoundingVolumes, Self, m_entities, m_attributes,
- m_geometryRenderers, m_geometries, m_buffers);
+ m_geometryRenderers, m_geometries, m_loadBuffers);
m_calculateWorldBoundingVolumes = TaskPtr<WorldBoundingVolumes>::create(
calculateWorldBoundingVolumes, Self, m_calculateLocalBoundingVolumes,
@@ -293,13 +298,12 @@ QDragonRenderAspect::QDragonRenderAspect(Renderer::RenderType renderType)
m_renderTargetOutputs,
m_shareContextSource);
- // TODO might as well upload them as well...
- m_loadBuffers = TaskPtr<LoadedBuffers>::create(loadBuffers, Self, m_buffers);
// m_loadShaders = TaskPtr<ValueContainer<GLShader>>::create(uploadShaders, Self, m_shaders);
- m_generateFrameGraph = TaskPtr<TreeInfo>::create(generateInheritanceTable<ValueContainer<FrameGraphNode>>,
+ m_generateFrameGraph = TaskPtr<TreeInfo>::create(generateInheritanceTable,
Self,
+ &m_nodeTree,
m_frameGraphNodes,
m_rootFrameGraphNodeSource);
diff --git a/src/runtime/dragon/qdragonrenderaspect_p.h b/src/runtime/dragon/qdragonrenderaspect_p.h
index e788243..dedecac 100644
--- a/src/runtime/dragon/qdragonrenderaspect_p.h
+++ b/src/runtime/dragon/qdragonrenderaspect_p.h
@@ -109,6 +109,10 @@ using CacheValueContainer = CacheContainer<Immutable<T>, U>;
template<typename T>
using BasicContainer = TrackingChangesContainer<T>;
+class DummyNode : public BackendNode
+{
+};
+
// aspect.h
class Q3DSV_EXPORT QDragonRenderAspect : public Qt3DCore::QAbstractAspect
{
@@ -125,34 +129,40 @@ private:
QVector<Qt3DCore::QAspectJobPtr> m_jobs;
+ // Hierarchy manager, necessary because Qt 3D passes limited hierarchy information to the backend
+ NodeTree m_nodeTree;
+
// Backend nodes
- NodeFunctorPtr<Entity> m_entities = NodeFunctorPtr<Entity>::create();
- NodeFunctorPtr<Transform> m_transforms = NodeFunctorPtr<Transform>::create();
- NodeFunctorPtr<Texture> m_textures = NodeFunctorPtr<Texture>::create();
- NodeFunctorPtr<TextureImage> m_textureImages = NodeFunctorPtr<TextureImage>::create();
- NodeFunctorPtr<Buffer> m_buffers = NodeFunctorPtr<Buffer>::create();
- NodeFunctorPtr<Parameter> m_parameters = NodeFunctorPtr<Parameter>::create();
- NodeFunctorPtr<Material> m_materials = NodeFunctorPtr<Material>::create();
- NodeFunctorPtr<Effect> m_effects = NodeFunctorPtr<Effect>::create();
- NodeFunctorPtr<Technique> m_techniques = NodeFunctorPtr<Technique>::create();
- NodeFunctorPtr<FilterKey> m_filterKeys = NodeFunctorPtr<FilterKey>::create();
- NodeFunctorPtr<Geometry> m_geometries = NodeFunctorPtr<Geometry>::create();
- NodeFunctorPtr<GeometryRenderer> m_geometryRenderers = NodeFunctorPtr<GeometryRenderer>::create();
- NodeFunctorPtr<Attribute> m_attributes = NodeFunctorPtr<Attribute>::create();
- NodeFunctorPtr<CameraLens> m_cameraLenses = NodeFunctorPtr<CameraLens>::create();
- NodeFunctorPtr<RenderTarget> m_renderTargets = NodeFunctorPtr<RenderTarget>::create();
- NodeFunctorPtr<RenderPass> m_renderPasses = NodeFunctorPtr<RenderPass>::create();
+ NodeFunctorPtr<Entity> m_entities = NodeFunctorPtr<Entity>::create(&m_nodeTree);
+ NodeFunctorPtr<Transform> m_transforms = NodeFunctorPtr<Transform>::create(&m_nodeTree);
+ NodeFunctorPtr<Texture> m_textures = NodeFunctorPtr<Texture>::create(&m_nodeTree);
+ NodeFunctorPtr<TextureImage> m_textureImages = NodeFunctorPtr<TextureImage>::create(&m_nodeTree);
+ NodeFunctorPtr<Buffer> m_buffers = NodeFunctorPtr<Buffer>::create(&m_nodeTree);
+ NodeFunctorPtr<Parameter> m_parameters = NodeFunctorPtr<Parameter>::create(&m_nodeTree);
+ NodeFunctorPtr<Material> m_materials = NodeFunctorPtr<Material>::create(&m_nodeTree);
+ NodeFunctorPtr<Effect> m_effects = NodeFunctorPtr<Effect>::create(&m_nodeTree);
+ NodeFunctorPtr<Technique> m_techniques = NodeFunctorPtr<Technique>::create(&m_nodeTree);
+ NodeFunctorPtr<FilterKey> m_filterKeys = NodeFunctorPtr<FilterKey>::create(&m_nodeTree);
+ NodeFunctorPtr<Geometry> m_geometries = NodeFunctorPtr<Geometry>::create(&m_nodeTree);
+ NodeFunctorPtr<GeometryRenderer> m_geometryRenderers = NodeFunctorPtr<GeometryRenderer>::create(&m_nodeTree);
+ NodeFunctorPtr<Attribute> m_attributes = NodeFunctorPtr<Attribute>::create(&m_nodeTree);
+ NodeFunctorPtr<CameraLens> m_cameraLenses = NodeFunctorPtr<CameraLens>::create(&m_nodeTree);
+ NodeFunctorPtr<RenderTarget> m_renderTargets = NodeFunctorPtr<RenderTarget>::create(&m_nodeTree);
+ NodeFunctorPtr<RenderPass> m_renderPasses = NodeFunctorPtr<RenderPass>::create(&m_nodeTree);
NodeFunctorPtr<RenderTargetOutput> m_renderTargetOutputs
- = NodeFunctorPtr<RenderTargetOutput>::create();
- NodeFunctorPtr<Shader> m_shaders = NodeFunctorPtr<Shader>::create();
- NodeFunctorPtr<ShaderData> m_shaderDatas = NodeFunctorPtr<ShaderData>::create();
- NodeFunctorPtr<RenderStateNode> m_renderStates = NodeFunctorPtr<RenderStateNode>::create();
- NodeFunctorPtr<Scene2D> m_scene2ds = NodeFunctorPtr<Scene2D>::create();
+ = NodeFunctorPtr<RenderTargetOutput>::create(&m_nodeTree);
+ NodeFunctorPtr<Shader> m_shaders = NodeFunctorPtr<Shader>::create(&m_nodeTree);
+ NodeFunctorPtr<ShaderData> m_shaderDatas = NodeFunctorPtr<ShaderData>::create(&m_nodeTree);
+ NodeFunctorPtr<RenderStateNode> m_renderStates = NodeFunctorPtr<RenderStateNode>::create(&m_nodeTree);
+ NodeFunctorPtr<Scene2D> m_scene2ds = NodeFunctorPtr<Scene2D>::create(&m_nodeTree);
// Frame graph
ValueContainer<FrameGraphNode> m_frameGraphNodesContainer;
NodeFunctorPtr<FrameGraphNode> m_frameGraphNodes = NodeFunctorPtr<FrameGraphNode>::create(
- &m_frameGraphNodesContainer);
+ &m_nodeTree, &m_frameGraphNodesContainer);
+
+ // Catch-all fallback for unknown nodes. Necessary for hierarchy information.
+ NodeFunctorPtr<DummyNode> m_otherNodes = NodeFunctorPtr<DummyNode>::create(&m_nodeTree);
RenderSettings *m_renderSettings = nullptr;
diff --git a/src/runtime/dragon/renderer/dragondraw.cpp b/src/runtime/dragon/renderer/dragondraw.cpp
index 91057fe..983f892 100644
--- a/src/runtime/dragon/renderer/dragondraw.cpp
+++ b/src/runtime/dragon/renderer/dragondraw.cpp
@@ -500,6 +500,8 @@ bool draw(const ActivatedSurface &activatedSurface,
// TODO consider emulated VAO
vao->m_vao->bind();
+
+ Q_ASSERT(glShader->shaderProgram != nullptr);
glShader->shaderProgram->bind();
const auto &worldTransform = worldTransforms[command.m_entity->peerId()];
diff --git a/src/runtime/q3dsabstractslideplayer_p.h b/src/runtime/q3dsabstractslideplayer_p.h
index 0ecf15f..409be28 100644
--- a/src/runtime/q3dsabstractslideplayer_p.h
+++ b/src/runtime/q3dsabstractslideplayer_p.h
@@ -103,6 +103,7 @@ public:
virtual void objectAddedToSlide(Q3DSGraphObject *obj, Q3DSSlide *slide) = 0;
virtual void objectRemovedFromSlide(Q3DSGraphObject *obj, Q3DSSlide *slide) = 0;
+ virtual float playbackRate() const = 0;
public Q_SLOTS:
virtual void play() = 0;
diff --git a/src/runtime/q3dscustommaterialgenerator.cpp b/src/runtime/q3dscustommaterialgenerator.cpp
index 23da459..2566f49 100644
--- a/src/runtime/q3dscustommaterialgenerator.cpp
+++ b/src/runtime/q3dscustommaterialgenerator.cpp
@@ -29,6 +29,7 @@
#include "q3dscustommaterialgenerator_p.h"
#include "q3dsdefaultmaterialgenerator_p.h"
+#include "q3dsengine_p.h"
#include <Qt3DCore/QEntity>
#include <Qt3DRender/QMaterial>
@@ -48,7 +49,11 @@
QT_BEGIN_NAMESPACE
-Qt3DRender::QMaterial *Q3DSCustomMaterialGenerator::generateMaterial(Q3DSCustomMaterialInstance *customMaterial, Q3DSReferencedMaterial *referencedMaterial, const QVector<Qt3DRender::QParameter *> &params, const QVector<Q3DSLightNode *> &lights, Q3DSLayerNode *layer3DS, const Q3DSMaterial::Pass &pass)
+Qt3DRender::QMaterial *Q3DSCustomMaterialGenerator::generateMaterial(
+ ParentChildPairs *pairs, Q3DSCustomMaterialInstance *customMaterial,
+ Q3DSReferencedMaterial *referencedMaterial, const QVector<Qt3DRender::QParameter *> &params,
+ const QVector<Q3DSLightNode *> &lights, Q3DSLayerNode *layer3DS,
+ const Q3DSMaterial::Pass &pass, bool gles)
{
Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
Qt3DRender::QEffect *effect = new Qt3DRender::QEffect;
@@ -59,7 +64,7 @@ Qt3DRender::QMaterial *Q3DSCustomMaterialGenerator::generateMaterial(Q3DSCustomM
Q_ASSERT(layerData);
Q3DSShaderFeatureSet features;
- fillFeatureSet(&features, layer3DS, customMaterial, referencedMaterial);
+ fillFeatureSet(&features, layer3DS, gles, customMaterial, referencedMaterial);
Qt3DRender::QShaderProgram *shaderProgram = Q3DSShaderManager::instance().generateShaderProgram(*customMaterial,
referencedMaterial,
@@ -70,7 +75,11 @@ Qt3DRender::QMaterial *Q3DSCustomMaterialGenerator::generateMaterial(Q3DSCustomM
// The returned program may be a cached instance, ensure it is not parented
// to the QMaterial by parenting it to something else now.
- shaderProgram->setParent(layerData->entity);
+ if (pairs)
+ pairs->addPair(layerData->entity, shaderProgram);
+ else
+ shaderProgram->setParent(layerData->entity);
+
// ### TODO This is does not take into consideration anything specified by the custom material pass
for (Qt3DRender::QRenderPass *pass : Q3DSSceneManager::standardRenderPasses(shaderProgram,
layer3DS,
@@ -101,7 +110,7 @@ Qt3DRender::QMaterial *Q3DSCustomMaterialGenerator::generateMaterial(Q3DSCustomM
return material;
}
-void Q3DSCustomMaterialGenerator::fillFeatureSet(Q3DSShaderFeatureSet *features, Q3DSLayerNode *layer3DS, Q3DSCustomMaterialInstance *material, Q3DSReferencedMaterial *referencedMaterial)
+void Q3DSCustomMaterialGenerator::fillFeatureSet(Q3DSShaderFeatureSet *features, Q3DSLayerNode *layer3DS, bool gles, Q3DSCustomMaterialInstance *material, Q3DSReferencedMaterial *referencedMaterial)
{
Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
Q_ASSERT(layerData);
@@ -131,6 +140,7 @@ void Q3DSCustomMaterialGenerator::fillFeatureSet(Q3DSShaderFeatureSet *features,
features->append(Q3DSShaderPreprocessorFeature(QLatin1String("QT3DS_ENABLE_SSDO"), false));
features->append(Q3DSShaderPreprocessorFeature(QLatin1String("QT3DS_ENABLE_SSM"), !layerData->shadowMapData.shadowCasters.isEmpty()));
features->append(Q3DSShaderPreprocessorFeature(QLatin1String("QT3DS_ENABLE_SSAO"), layerData->ssaoTextureData.enabled));
+ features->append(Q3DSShaderPreprocessorFeature(QLatin1String("Q3DS_NO_FRAGOUTPUT"), gles));
}
QT_END_NAMESPACE
diff --git a/src/runtime/q3dscustommaterialgenerator_p.h b/src/runtime/q3dscustommaterialgenerator_p.h
index 9acc732..fd2230e 100644
--- a/src/runtime/q3dscustommaterialgenerator_p.h
+++ b/src/runtime/q3dscustommaterialgenerator_p.h
@@ -50,18 +50,21 @@ namespace Qt3DRender {
class QParameter;
class QMaterial;
}
+struct ParentChildPairs;
class Q3DSCustomMaterialGenerator
{
public:
- Qt3DRender::QMaterial *generateMaterial(Q3DSCustomMaterialInstance *customMaterial,
+ Qt3DRender::QMaterial *generateMaterial(ParentChildPairs *pairs,
+ Q3DSCustomMaterialInstance *customMaterial,
Q3DSReferencedMaterial *referencedMaterial,
const QVector<Qt3DRender::QParameter *> &params,
const QVector<Q3DSLightNode *> &lights,
Q3DSLayerNode *layer3DS,
- const Q3DSMaterial::Pass &pass);
+ const Q3DSMaterial::Pass &pass, bool gles);
static void fillFeatureSet(Q3DSShaderFeatureSet *features,
Q3DSLayerNode *layer3DS,
+ bool gles,
Q3DSCustomMaterialInstance *material = nullptr,
Q3DSReferencedMaterial *referencedMaterial = nullptr);
diff --git a/src/runtime/q3dsdefaultmaterialgenerator.cpp b/src/runtime/q3dsdefaultmaterialgenerator.cpp
index 9a2fef9..6056492 100644
--- a/src/runtime/q3dsdefaultmaterialgenerator.cpp
+++ b/src/runtime/q3dsdefaultmaterialgenerator.cpp
@@ -42,6 +42,7 @@
#include "q3dsutils_p.h"
#include "q3dslogging_p.h"
#include "q3dsgraphicslimits_p.h"
+#include "q3dsengine_p.h"
#include "shadergenerator/q3dsshadermanager_p.h"
@@ -87,7 +88,8 @@ QT_BEGIN_NAMESPACE
// vec2 attr_uv0
// Caller takes ownership everything generated in the method
-Qt3DRender::QMaterial *Q3DSDefaultMaterialGenerator::generateMaterial(Q3DSDefaultMaterial *defaultMaterial,
+Qt3DRender::QMaterial *Q3DSDefaultMaterialGenerator::generateMaterial(ParentChildPairs *pairs,
+ Q3DSDefaultMaterial *defaultMaterial,
Q3DSReferencedMaterial *referencedMaterial,
const QVector<Qt3DRender::QParameter *> &params,
const QVector<Q3DSLightNode*> &lights,
@@ -112,7 +114,10 @@ Qt3DRender::QMaterial *Q3DSDefaultMaterialGenerator::generateMaterial(Q3DSDefaul
// The returned program may be a cached instance, ensure it is not parented
// to the QMaterial by parenting it to something else now.
- shaderProgram->setParent(layerData->entity);
+ if (pairs)
+ pairs->addPair(layerData->entity, shaderProgram);
+ else
+ shaderProgram->setParent(layerData->entity);
for (Qt3DRender::QRenderPass *pass : Q3DSSceneManager::standardRenderPasses(shaderProgram,
layer3DS,
diff --git a/src/runtime/q3dsdefaultmaterialgenerator_p.h b/src/runtime/q3dsdefaultmaterialgenerator_p.h
index e384d18..3355acc 100644
--- a/src/runtime/q3dsdefaultmaterialgenerator_p.h
+++ b/src/runtime/q3dsdefaultmaterialgenerator_p.h
@@ -51,11 +51,13 @@ class QParameter;
class QMaterial;
class QTechnique;
}
+struct ParentChildPairs;
class Q3DSDefaultMaterialGenerator
{
public:
- Qt3DRender::QMaterial *generateMaterial(Q3DSDefaultMaterial *defaultMaterial,
+ Qt3DRender::QMaterial *generateMaterial(ParentChildPairs *pairs,
+ Q3DSDefaultMaterial *defaultMaterial,
Q3DSReferencedMaterial *referencedMaterial,
const QVector<Qt3DRender::QParameter *> &params,
const QVector<Q3DSLightNode *> &lights,
diff --git a/src/runtime/q3dsdistancefieldglyphcache.cpp b/src/runtime/q3dsdistancefieldglyphcache.cpp
index 6086917..a6e5477 100644
--- a/src/runtime/q3dsdistancefieldglyphcache.cpp
+++ b/src/runtime/q3dsdistancefieldglyphcache.cpp
@@ -248,6 +248,11 @@ void Q3DSDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyph
}
}
+qreal Q3DSDistanceFieldGlyphCache::fontSize() const
+{
+ return QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution);
+}
+
void Q3DSDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
{
// Note: Most of this is copy-pasted from QSGDefaultDistanceFieldGlyphCache in Qt Quick.
diff --git a/src/runtime/q3dsdistancefieldglyphcache_p.h b/src/runtime/q3dsdistancefieldglyphcache_p.h
index e29f614..72dd164 100644
--- a/src/runtime/q3dsdistancefieldglyphcache_p.h
+++ b/src/runtime/q3dsdistancefieldglyphcache_p.h
@@ -74,6 +74,8 @@ public:
TextureInfo *textureInfoById(uint textureId) const;
+ qreal fontSize() const;
+
private:
bool loadPregeneratedCache(const QRawFont &font);
TextureInfo *textureInfo(int index) const;
diff --git a/src/runtime/q3dsengine.cpp b/src/runtime/q3dsengine.cpp
index 6c7be72..2e568b0 100644
--- a/src/runtime/q3dsengine.cpp
+++ b/src/runtime/q3dsengine.cpp
@@ -49,6 +49,7 @@
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QOffscreenSurface>
+#include <QtConcurrent>
#include <Qt3DCore/QEntity>
#include <Qt3DRender/QRenderAspect>
@@ -106,6 +107,12 @@ static QStringList q3ds_msg_buf;
static QtMessageHandler q3ds_prev_msg_handler = nullptr;
static Q3DSEngine *q3ds_msg_engine = nullptr;
+static bool isAsyncLoadingEnabled()
+{
+ static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING")) > 0;
+ return enabled;
+}
+
void Q3DSEngine::clearLog()
{
QMutexLocker locker(&q3ds_msg_mutex);
@@ -145,11 +152,37 @@ static void q3ds_msg_handler(QtMsgType type, const QMessageLogContext &ctx, cons
}
}
+void Q3DSEngine::SlideResourceCounter::print()
+{
+ static const bool debugging = qEnvironmentVariableIntValue("Q3DS_DEBUG") >= 1;
+ if (debugging) {
+ qDebug()<< "SlideResourceCounter resources:";
+ const auto keys = counters.keys();
+ for (auto &x : keys)
+ qDebug()<< x << ": " << counters[x];
+ if (createSet.size()) {
+ qDebug() << "New resources: ";
+ for (auto y : qAsConst(createSet))
+ qDebug() << y;
+ }
+ if (deleteSet.size()) {
+ qDebug() << "Deleted resources: ";
+ for (auto y : qAsConst(deleteSet))
+ qDebug() << y;
+ }
+ }
+}
+
Q3DSEngine::Q3DSEngine()
{
initResources();
registerQmlTypes();
+ if (isAsyncLoadingEnabled()) {
+ m_slideResourceMutex = new QMutex;
+ m_asyncLoadMutex = new QMutex;
+ }
+
QMutexLocker locker(&q3ds_msg_mutex);
q3ds_msg_engine = this;
// Install the message handler once, it is then set for ever. Engines come
@@ -717,10 +750,7 @@ void Q3DSEngine::finalizePresentations()
m_uipPresentations[0].q3dscene.renderSettings->setRenderPolicy(m_onDemandRendering ?
Qt3DRender::QRenderSettings::OnDemand : Qt3DRender::QRenderSettings::Always);
- for (const UipPresentation &pres : m_uipPresentations) {
- if (pres.sceneManager)
- pres.sceneManager->prepareAnimators();
- }
+ finishAsyncLoad(false);
Q3DSImageManager::instance().finishAsyncLoad();
@@ -834,6 +864,144 @@ Q3DSSubPresentation Q3DSEngine::loadSubUipPresentation(const QString &subPresId)
return sp;
}
+void Q3DSEngine::startAnimations()
+{
+ for (const UipPresentation &pres : qAsConst(m_uipPresentations)) {
+ if (pres.sceneManager)
+ pres.sceneManager->prepareAnimators();
+ }
+}
+
+void Q3DSEngine::SceneLoaderAsync::reparentChildren()
+{
+ for (auto pair : qAsConst(m_parentChildPairs)) {
+ Qt3DCore::QNode *parent = static_cast<Qt3DCore::QNode *>(pair.first);
+ Qt3DCore::QNode *child = static_cast<Qt3DCore::QNode *>(pair.second);
+ if (parent && child)
+ child->setParent(parent);
+ else
+ pair.second->setParent(pair.first);
+ }
+}
+
+void Q3DSEngine::beginAsyncLoad(Q3DSSubPresentation &sp, SceneLoaderAsync *loader)
+{
+ loader->sp = sp;
+ loader->targetThread = QThread::currentThread();
+ loader->future = QtConcurrent::run([this, loader] {
+ QElapsedTimer timer;
+ timer.start();
+ qCDebug(lcScene, "Begin loading subpresentation: %s", qPrintable(loader->pres->subPres.id));
+ if (!parseUipDocument(loader->pres)) {
+ Q3DSUtils::showMessage(QObject::tr("Failed to parse subpresentation"));
+ return;
+ }
+ buildSubUipPresentationScene(loader->pres, loader);
+ for (auto obj : qAsConst(loader->m_parentChildPairs)) {
+ if (obj.second->thread() != loader->targetThread) {
+ if (obj.second->parent()
+ && obj.second->parent()->thread() != loader->targetThread) {
+ QObject *parent = obj.second->parent();
+ while (parent->parent())
+ parent = parent->parent();
+ parent->moveToThread(loader->targetThread);
+ } else {
+ obj.second->moveToThread(loader->targetThread);
+ }
+ }
+ }
+ loader->sp = loader->pres->subPres;
+ qCDebug(lcScene, "Subpresentation %s loaded in %lld ms",
+ qPrintable(loader->pres->subPres.id), timer.elapsed());
+ });
+ QMutexLocker locker(m_asyncLoadMutex);
+ m_asyncSceneLoaders.push_back(loader);
+}
+
+void Q3DSEngine::finishAsyncLoadForSubpresentation(const QString &name)
+{
+ if (!m_asyncLoadMutex)
+ return;
+
+ QVector<SceneLoaderAsync *> loaders;
+ {
+ QMutexLocker locker(m_asyncLoadMutex);
+ loaders = m_asyncSceneLoaders;
+ }
+ for (auto &item : qAsConst(loaders)) {
+ if (item->done == false && item->pres->subPres.id == name) {
+ item->future.waitForFinished();
+ item->done = true;
+ item->function();
+ return;
+ }
+ }
+}
+
+void Q3DSEngine::finishAsyncLoad(bool wait)
+{
+ if (!m_asyncLoadMutex)
+ return;
+
+ QThread *currentThread = QThread::currentThread();
+ QVector<SceneLoaderAsync *> loaders;
+ {
+ QMutexLocker locker(m_asyncLoadMutex);
+ loaders = m_asyncSceneLoaders;
+ }
+ if (wait) {
+ {
+ QFutureSynchronizer<void> sync;
+ for (auto &item : qAsConst(loaders)) {
+ if (item->done == false)
+ sync.addFuture(item->future);
+ }
+ }
+ for (auto &item : qAsConst(loaders)) {
+ // Finalize only in target thread to make reparenting events succeed
+ if (item->targetThread == currentThread) {
+ item->done = true;
+ item->function();
+ }
+ }
+ } else {
+ for (auto &item : qAsConst(loaders)) {
+ if (!item->done && item->future.isFinished()) {
+ // Finalize only in target thread to make reparenting events succeed
+ if (item->targetThread == currentThread) {
+ item->done = true;
+ item->function();
+ }
+ }
+ }
+ }
+}
+
+void Q3DSEngine::loadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+{
+ if (m_slideResourceMutex) {
+ QSet<QUrl> createSet;
+ {
+ QMutexLocker locker(m_slideResourceMutex);
+ m_resourceReferenceCounter.handleLoadSlide(slide, presentation);
+ createSet = m_resourceReferenceCounter.createSet;
+ }
+ Q3DSImageManager::instance().beginImageLoad(createSet);
+ } else {
+ m_resourceReferenceCounter.handleLoadSlide(slide, presentation);
+ Q3DSImageManager::instance().beginImageLoad(m_resourceReferenceCounter.createSet);
+ }
+}
+
+void Q3DSEngine::unloadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+{
+ slide->setUnloadSlide(true);
+ if (!slide->active()) {
+ m_resourceReferenceCounter.handleUnloadSlide(slide, presentation);
+ Q3DSImageManager::instance().beginUnload(m_resourceReferenceCounter.deleteSet);
+ }
+}
+
bool Q3DSEngine::loadSubUipPresentation(UipPresentation *pres)
{
Q_ASSERT(pres);
@@ -845,7 +1013,7 @@ bool Q3DSEngine::loadSubUipPresentation(UipPresentation *pres)
return buildSubUipPresentationScene(pres);
}
-bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres)
+bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres, SceneLoaderAsync *loader)
{
Qt3DRender::QFrameGraphNode *fgParent = m_uipPresentations[0].q3dscene.subPresFrameGraphRoot;
Qt3DCore::QNode *entityParent = m_uipPresentations[0].q3dscene.rootEntity;
@@ -860,12 +1028,24 @@ bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres)
params.outputDpr = 1;
params.engine = this;
- Qt3DRender::QRenderTargetSelector *rtSel = new Qt3DRender::QRenderTargetSelector(fgParent);
+ Qt3DRender::QRenderTargetSelector *rtSel;
+ if (loader) {
+ rtSel = new Qt3DRender::QRenderTargetSelector;
+ loader->addPair(fgParent, rtSel);
+ } else {
+ rtSel = new Qt3DRender::QRenderTargetSelector(fgParent);
+ }
+
Qt3DRender::QRenderTarget *rt = new Qt3DRender::QRenderTarget;
Qt3DRender::QRenderTargetOutput *color = new Qt3DRender::QRenderTargetOutput;
color->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Color0);
- pres->subPres.colorTex = new Qt3DRender::QTexture2D(entityParent);
+ if (!loader) {
+ pres->subPres.colorTex = new Qt3DRender::QTexture2D(entityParent);
+ } else {
+ pres->subPres.colorTex = new Qt3DRender::QTexture2D;
+ loader->addPair(entityParent, pres->subPres.colorTex);
+ }
pres->subPres.colorTex->setFormat(Qt3DRender::QAbstractTexture::RGBA8_UNorm);
pres->subPres.colorTex->setWidth(pres3DS->presentationWidth());
pres->subPres.colorTex->setHeight(pres3DS->presentationHeight());
@@ -877,7 +1057,12 @@ bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres)
if (gfxLimits.packedDepthStencilBufferSupported) {
Qt3DRender::QRenderTargetOutput *ds = new Qt3DRender::QRenderTargetOutput;
ds->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::DepthStencil);
- pres->subPres.depthOrDepthStencilTex = new Qt3DRender::QTexture2D(entityParent);
+ if (loader) {
+ pres->subPres.depthOrDepthStencilTex = new Qt3DRender::QTexture2D();
+ loader->addPair(entityParent, pres->subPres.depthOrDepthStencilTex);
+ } else {
+ pres->subPres.depthOrDepthStencilTex = new Qt3DRender::QTexture2D(entityParent);
+ }
pres->subPres.depthOrDepthStencilTex->setFormat(Qt3DRender::QAbstractTexture::D24S8);
pres->subPres.depthOrDepthStencilTex->setWidth(pres3DS->presentationWidth());
pres->subPres.depthOrDepthStencilTex->setHeight(pres3DS->presentationHeight());
@@ -888,7 +1073,12 @@ bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres)
} else {
Qt3DRender::QRenderTargetOutput *depth = new Qt3DRender::QRenderTargetOutput;
depth->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Depth);
- pres->subPres.depthOrDepthStencilTex = new Qt3DRender::QTexture2D(entityParent);
+ if (loader) {
+ pres->subPres.depthOrDepthStencilTex = new Qt3DRender::QTexture2D();
+ loader->addPair(entityParent, pres->subPres.depthOrDepthStencilTex);
+ } else {
+ pres->subPres.depthOrDepthStencilTex = new Qt3DRender::QTexture2D(entityParent);
+ }
pres->subPres.depthOrDepthStencilTex->setFormat(Qt3DRender::QAbstractTexture::D16);
pres->subPres.depthOrDepthStencilTex->setWidth(pres3DS->presentationWidth());
pres->subPres.depthOrDepthStencilTex->setHeight(pres3DS->presentationHeight());
@@ -898,7 +1088,12 @@ bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres)
rt->addOutput(depth);
Qt3DRender::QRenderTargetOutput *stencil = new Qt3DRender::QRenderTargetOutput;
stencil->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Stencil);
- pres->subPres.stencilTex = new Qt3DRender::QTexture2D(entityParent);
+ if (loader) {
+ pres->subPres.stencilTex = new Qt3DRender::QTexture2D();
+ loader->addPair(entityParent, pres->subPres.stencilTex);
+ } else {
+ pres->subPres.stencilTex = new Qt3DRender::QTexture2D(entityParent);
+ }
pres->subPres.stencilTex->setFormat(Qt3DRender::QAbstractTexture::TextureFormat(0x1901)); // GL_STENCIL_INDEX
pres->subPres.stencilTex->setWidth(pres3DS->presentationWidth());
pres->subPres.stencilTex->setHeight(pres3DS->presentationHeight());
@@ -910,8 +1105,7 @@ bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres)
rtSel->setTarget(rt);
params.frameGraphRoot = rtSel;
-
- QScopedPointer<Q3DSSceneManager> sceneManager(new Q3DSSceneManager);
+ QScopedPointer<Q3DSSceneManager> sceneManager(new Q3DSSceneManager(loader));
pres->q3dscene = sceneManager->buildScene(pres->presentation, params);
if (!pres->q3dscene.rootEntity) {
Q3DSUtils::showMessage(QObject::tr("Failed to build Qt3D scene for subpresentation"));
@@ -920,7 +1114,10 @@ bool Q3DSEngine::buildSubUipPresentationScene(UipPresentation *pres)
pres->sceneManager = sceneManager.take();
pres->subPres.sceneManager = pres->sceneManager;
- pres->q3dscene.rootEntity->setParent(entityParent);
+ if (!loader)
+ pres->q3dscene.rootEntity->setParent(entityParent);
+ else
+ loader->addPair(entityParent, pres->q3dscene.rootEntity);
return true;
}
@@ -986,7 +1183,7 @@ bool Q3DSEngine::loadSubQmlPresentation(QmlPresentation *pres)
QQuickItem *item = nullptr;
if (component) {
QObject *object = component->create();
- item = static_cast<QQuickItem *>(object);
+ item = qobject_cast<QQuickItem *>(object);
if (!item) {
qCDebug(lcUip, "Failed to load qml. Root is not a quick item.");
delete component;
@@ -1733,16 +1930,12 @@ void Q3DSEngine::goToSlideByDirection(Q3DSGraphObject *context, Q3DSUipPresentat
void Q3DSEngine::preloadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
{
- Q_UNUSED(slide);
- Q_UNUSED(presentation);
- // TODO: Implementation
+ loadSlideResources(slide, presentation);
}
void Q3DSEngine::unloadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
{
- Q_UNUSED(slide);
- Q_UNUSED(presentation);
- // TODO: Implementation
+ unloadSlideResources(slide, presentation);
}
void Q3DSEngine::loadBehaviorInstance(Q3DSBehaviorInstance *behaviorInstance,
@@ -2008,7 +2201,7 @@ QRect Q3DSEngine::calculateViewport(const QSize &surfaceSize, const QSize &prese
Q3DSGraphObject *Q3DSEngine::findObjectByHashIdOrNameOrPath(Q3DSGraphObject *thisObject,
Q3DSUipPresentation *defaultPresentation,
const QString &idOrNameOrPath,
- Q3DSUipPresentation **actualPresentation) const
+ Q3DSUipPresentation **actualPresentation)
{
Q3DSUipPresentation *pres = defaultPresentation;
QString attr = idOrNameOrPath;
@@ -2016,6 +2209,7 @@ Q3DSGraphObject *Q3DSEngine::findObjectByHashIdOrNameOrPath(Q3DSGraphObject *thi
const QStringList presentationPathPair = attr.split(QLatin1Char(':'), QString::SkipEmptyParts);
if (presentationPathPair.count() < 2)
return nullptr;
+ finishAsyncLoadForSubpresentation(presentationPathPair[0]);
pres = presentationByName(presentationPathPair[0]);
attr = presentationPathPair[1];
}
diff --git a/src/runtime/q3dsengine_p.h b/src/runtime/q3dsengine_p.h
index 386f95c..1d3f23d 100644
--- a/src/runtime/q3dsengine_p.h
+++ b/src/runtime/q3dsengine_p.h
@@ -42,6 +42,7 @@
//
#include <QObject>
+#include <QThread>
#include <QElapsedTimer>
#include <QSurfaceFormat>
#include <Qt3DCore/QAspectEngine>
@@ -92,6 +93,16 @@ struct Q3DSV_PRIVATE_EXPORT Q3DSRenderLoopStats
float quickDeltaMs = 0;
};
+struct ParentChildPairs
+{
+ QVector<QPair<QObject *, QObject *>> m_parentChildPairs;
+
+ void addPair(QObject *parent, QObject *child)
+ {
+ m_parentChildPairs.push_back(QPair<QObject *, QObject *>(parent, child));
+ }
+};
+
class Q3DSV_PRIVATE_EXPORT Q3DSEngine : public QObject
{
Q_OBJECT
@@ -137,6 +148,9 @@ public:
// constructed scenes.
bool setPresentation(Q3DSUipPresentation *presentations);
+ // Start animating all uip presentations.
+ void startAnimations();
+
qint64 behaviorLoadTimeMsecs() const;
qint64 totalLoadTimeMsecs() const;
@@ -222,7 +236,7 @@ public:
Q3DSGraphObject *findObjectByHashIdOrNameOrPath(Q3DSGraphObject *thisObject,
Q3DSUipPresentation *defaultPresentation,
const QString &idOrNameOrPath,
- Q3DSUipPresentation **actualPresentation = nullptr) const;
+ Q3DSUipPresentation **actualPresentation = nullptr);
QString makePath(Q3DSGraphObject *obj) const;
Qt3DCore::QNodeId layerTextureNodeId(Q3DSLayerNode *layer3DS) const;
@@ -232,6 +246,36 @@ public:
Q3DSSubPresentation loadSubUipPresentation(const QString &subPresId);
+ template <typename FinalizeFunc, typename... Params>
+ Q3DSSubPresentation loadSubUipPresentation(const QString &subPresId, FinalizeFunc finalize, Params... params)
+ {
+ Q3DSSubPresentation sp;
+ SceneLoaderAsync *loader = nullptr;
+ UipPresentation *uip = nullptr;
+
+ for (int i = 0; i < m_uipPresentations.count(); ++i) {
+ if (m_uipPresentations[i].subPres.id == subPresId) {
+ uip = &m_uipPresentations[i];
+ break;
+ }
+ }
+ if (!uip)
+ return sp;
+
+ loader = new SceneLoaderAsync;
+ loader->function = [loader, finalize, params...]() {
+ loader->reparentChildren();
+ finalize(&loader->sp, params...);
+ };
+
+ loader->pres = uip;
+ beginAsyncLoad(sp, loader);
+ return sp;
+ }
+
+ void finishAsyncLoad(bool wait = true);
+ void loadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation);
+ void unloadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation);
void createAspectEngine();
public Q_SLOTS:
@@ -270,12 +314,99 @@ private:
Qt3DRender::Quick::QScene2D *scene2d = nullptr;
};
+ struct SceneLoaderAsync : public ParentChildPairs
+ {
+ QThread *targetThread;
+ QFuture<void> future;
+ UipPresentation *pres;
+ std::function<void()> function;
+ Q3DSSubPresentation sp;
+ bool done;
+
+ SceneLoaderAsync()
+ : targetThread(nullptr), pres(nullptr), done(false)
+ {}
+
+ SceneLoaderAsync(const SceneLoaderAsync &loader)
+ : ParentChildPairs(loader), targetThread(loader.targetThread),
+ future(loader.future), pres(loader.pres), function(loader.function),
+ sp(loader.sp), done(loader.done)
+ {}
+
+ void reparentChildren();
+ };
+
+ void beginAsyncLoad(Q3DSSubPresentation &sp, SceneLoaderAsync *loader);
+ void finishAsyncLoadForSubpresentation(const QString &name);
+ void reparentScene(SceneLoaderAsync *loader);
+
+ struct SlideResourceCounter
+ {
+ QHash<QUrl, int> counters;
+ QSet<QUrl> createSet;
+ QSet<QUrl> deleteSet;
+
+ QVector<Q3DSSlide *> loadedSlides;
+
+ void increment(const QSet<QUrl> &set)
+ {
+ for (auto &r : set) {
+ if (counters.value(r, 0) == 0)
+ createSet.insert(r);
+ counters[r]++;
+ }
+ }
+ void decrement(const QSet<QUrl> &set)
+ {
+ for (auto &r : set) {
+ if (counters.contains(r)) {
+ int count = qMax(counters[r] - 1, 0);
+ if (count == 0)
+ deleteSet.insert(r);
+ counters[r] = count;
+ }
+ }
+ }
+ void begin()
+ {
+ createSet.clear();
+ deleteSet.clear();
+ }
+ void reset()
+ {
+ loadedSlides.clear();
+ counters.clear();
+ begin();
+ }
+ void handleLoadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+ {
+ if (loadedSlides.contains(slide))
+ return;
+ slide->generateResourceSet(presentation);
+ loadedSlides.push_back(slide);
+ begin();
+ increment(slide->resourceSet());
+ print();
+ }
+ void handleUnloadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+ {
+ if (!loadedSlides.contains(slide))
+ return;
+ slide->generateResourceSet(presentation);
+ loadedSlides.removeOne(slide);
+ begin();
+ decrement(slide->resourceSet());
+ print();
+ }
+ void print();
+ };
+
bool loadPresentations();
void finalizePresentations();
bool loadUipPresentation(UipPresentation *pres);
bool buildUipPresentationScene(UipPresentation *pres);
bool loadSubUipPresentation(UipPresentation *pres);
- bool buildSubUipPresentationScene(UipPresentation *pres);
+ bool buildSubUipPresentationScene(UipPresentation *pres, SceneLoaderAsync *loader = nullptr);
bool loadSubQmlPresentation(QmlPresentation *pres);
bool parseUipDocument(UipPresentation *pres);
@@ -338,6 +469,11 @@ private:
QMutex m_renderLoopStatsMutex;
float m_quickDeltaSum = 0;
int m_quickDeltaCount = 0;
+
+ QVector<SceneLoaderAsync *> m_asyncSceneLoaders;
+ SlideResourceCounter m_resourceReferenceCounter;
+ QMutex *m_slideResourceMutex = nullptr;
+ QMutex *m_asyncLoadMutex= nullptr;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSEngine::Flags)
diff --git a/src/runtime/q3dsfontdatabase.cpp b/src/runtime/q3dsfontdatabase.cpp
index 384bf68..98f2c91 100644
--- a/src/runtime/q3dsfontdatabase.cpp
+++ b/src/runtime/q3dsfontdatabase.cpp
@@ -30,6 +30,7 @@
#include "q3dsfontdatabase_p.h"
#include <QtCore/qdir.h>
+#include <QtGui/private/qrawfont_p.h>
#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
@@ -90,8 +91,17 @@ QRawFont Q3DSFontDatabase::findFont(const QString &fontId)
{
for (Font &font : m_fonts) {
if (font.fontId == fontId) {
- if (!font.rawFont.isValid())
+ if (!font.rawFont.isValid()) {
font.rawFont = QRawFont(font.filePath, 16);
+
+#if QT_VERSION < QT_VERSION_CHECK(5,12,3)
+ // Work-around for Q3DS-3132
+ if (font.rawFont.isValid()) {
+ QRawFontPrivate *d = QRawFontPrivate::get(font.rawFont);
+ d->fontEngine->fontDef.stretch = 100;
+ }
+#endif
+ }
return font.rawFont;
}
}
diff --git a/src/runtime/q3dsimageloaders_p.h b/src/runtime/q3dsimageloaders_p.h
index f22d8bc..2904300 100644
--- a/src/runtime/q3dsimageloaders_p.h
+++ b/src/runtime/q3dsimageloaders_p.h
@@ -832,6 +832,7 @@ static inline quint32 q3ds_blockSizeForTextureFormat(QOpenGLTexture::TextureForm
case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2:
case QOpenGLTexture::R11_EAC_UNorm:
case QOpenGLTexture::R11_EAC_SNorm:
+ case QOpenGLTexture::RGB_DXT1:
return 8;
default:
@@ -914,18 +915,19 @@ inline QVector<Qt3DRender::QTextureImageDataPtr> q3ds_loadKtx(QIODevice *source)
return result;
}
+ const int rawDataSize = rawData.size();
const char *basep = rawData.constData();
const char *p = basep;
const int level0Width = decode(header.pixelWidth);
const int level0Height = decode(header.pixelHeight);
int faceCount = decode(header.numberOfFaces);
const int mipMapLevels = decode(header.numberOfMipmapLevels);
- auto createImageData = [=](const QByteArray &compressedData) {
+ auto createImageData = [=](const QByteArray &compressedData, int mipLevel) {
Qt3DRender::QTextureImageDataPtr imageData = Qt3DRender::QTextureImageDataPtr::create();
imageData->setTarget(faceCount == 6 ? QOpenGLTexture::TargetCubeMap : QOpenGLTexture::Target2D);
imageData->setFormat(QOpenGLTexture::TextureFormat(decode(header.glInternalFormat)));
- imageData->setWidth(level0Width);
- imageData->setHeight(level0Height);
+ imageData->setWidth(qMax(level0Width >> mipLevel, 1));
+ imageData->setHeight(qMax(level0Height >> mipLevel, 1));
imageData->setLayers(1);
imageData->setDepth(1);
// separate textureimage per face and per mipmap
@@ -944,11 +946,16 @@ inline QVector<Qt3DRender::QTextureImageDataPtr> q3ds_loadKtx(QIODevice *source)
}
for (int mip = 0; mip < mipMapLevels; ++mip) {
+ if (p + 4 - basep > rawDataSize)
+ break;
int imageSize = *reinterpret_cast<const quint32 *>(p);
p += 4;
for (int face = 0; face < faceCount; ++face) {
- result << createImageData(QByteArray(p, imageSize));
- p = basep + q3ds_alignedOffset(p + imageSize - basep, 4);
+ const int nextOffset = p + imageSize - basep;
+ if (nextOffset > rawDataSize)
+ break;
+ result << createImageData(QByteArray(p, imageSize), mip);
+ p = basep + q3ds_alignedOffset(nextOffset, 4);
}
}
diff --git a/src/runtime/q3dsimagemanager.cpp b/src/runtime/q3dsimagemanager.cpp
index 4788ac9..549f962 100644
--- a/src/runtime/q3dsimagemanager.cpp
+++ b/src/runtime/q3dsimagemanager.cpp
@@ -45,9 +45,21 @@ QT_BEGIN_NAMESPACE
QMutex Q3DSImageManager::s_loadMutex;
+static bool isAsyncImageLoadingEnabled()
+{
+ static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING") & 1) > 0;
+ return enabled;
+}
+
+static bool isDelayedImageLoadingEnabled()
+{
+ static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING") & 2) > 0;
+ return enabled;
+}
+
Q3DSImageManager::Q3DSImageManager()
{
- m_threadPool.setMaxThreadCount(qMax(1, QThread::idealThreadCount() - 2));
+ m_threadPool.setMaxThreadCount(qMax(2, QThread::idealThreadCount() - 2));
}
Q3DSImageManager &Q3DSImageManager::instance()
@@ -62,13 +74,18 @@ void Q3DSImageManager::invalidate()
m_cache.clear();
m_ioTime = 0;
m_iblTime = 0;
+ m_resourceSet.clear();
+ m_loadImageDataAsync.clear();
+ qDeleteAll(m_pendingSetSource);
+ m_pendingSetSource.clear();
+ m_reloadableTextures.clear();
}
Qt3DRender::QAbstractTexture *Q3DSImageManager::newTextureForImage(Qt3DCore::QEntity *parent,
ImageFlags flags,
const QByteArray &id,
Q3DSProfiler *profiler,
- const char *profDesc, ...)
+ const QString &profilerInfo)
{
auto tex = new Qt3DRender::QTexture2D(parent);
@@ -76,14 +93,87 @@ Qt3DRender::QAbstractTexture *Q3DSImageManager::newTextureForImage(Qt3DCore::QEn
info.flags = flags;
m_metadata.insert(tex, info);
+ if (profiler && !profilerInfo.isNull())
+ profiler->trackNewObject(tex, Q3DSProfiler::Texture2DObject, id, profilerInfo);
+
+ return tex;
+}
+
+ReloadableTexturePtr Q3DSImageManager::newReloadableTextureForImage(Qt3DCore::QEntity *parent,
+ ImageFlags flags,
+ const QByteArray &id,
+ Q3DSProfiler *profiler,
+ const char *profDesc, ...)
+{
+ ReloadableTexturePtr texture;
+ QString info;
if (profiler && profDesc) {
va_list ap;
va_start(ap, profDesc);
- profiler->vtrackNewObject(tex, Q3DSProfiler::Texture2DObject, id, profDesc, ap);
+ info = QString::vasprintf(profDesc, ap);
va_end(ap);
}
+ texture.reset(new ReloadableTexture(parent, flags & GenerateMipMapsForIBL, id, info, profiler));
+ m_reloadableTextures.push_back(texture);
+ return texture;
+}
- return tex;
+void ReloadableTexture::setSource(const QUrl &source)
+{
+ if (m_source.isEmpty() || m_source != source) {
+ m_source = source;
+ m_loaded = false;
+ triggerLoading();
+ }
+}
+
+void ReloadableTexture::triggerLoading()
+{
+ if (!m_source.isEmpty() && m_loadFunc && m_unloadFunc)
+ reload();
+}
+
+void ReloadableTexture::reload()
+{
+ if (m_loaded)
+ return;
+ if (isDelayedImageLoadingEnabled() && !Q3DSImageManager::instance().inResourceSet(m_source))
+ return;
+ Q3DSImageManager::ImageFlags flags;
+ if (m_generateIBL)
+ flags = Q3DSImageManager::GenerateMipMapsForIBL;
+ if (!m_texture) {
+ m_texture = Q3DSImageManager::instance().newTextureForImage(m_parent, flags, m_id,
+ m_profiler, m_profileInfo);
+ }
+ Q3DSImageManager::instance().loadImageData(m_source);
+ Q3DSImageManager::instance().setSource(m_texture, m_source);
+}
+
+void ReloadableTexture::unload()
+{
+ if (m_texture) {
+ m_unloadFunc();
+
+ const auto texImages = m_texture->textureImages();
+ for (Qt3DRender::QAbstractTextureImage *oldImage : texImages) {
+ m_texture->removeTextureImage(oldImage);
+ delete oldImage;
+ }
+ Qt3DCore::QNode *nullParent = nullptr;
+ m_texture->setParent(nullParent);
+ delete m_texture;
+ m_texture = nullptr;
+ m_loaded = false;
+ }
+}
+
+void ReloadableTexture::loaded(Qt3DRender::QAbstractTexture *texture)
+{
+ if (m_texture == texture) {
+ m_loaded = true;
+ m_loadFunc();
+ }
}
class Q3DSTextureImageDataGen : public Qt3DRender::QTextureImageDataGenerator
@@ -129,20 +219,17 @@ private:
Qt3DRender::QTextureImageDataGeneratorPtr m_gen;
};
-QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::load(const QUrl &source, ImageFlags flags, bool *wasCached)
+QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::load(const QUrl &source)
{
const QString sourceStr = source.toLocalFile();
s_loadMutex.lock();
auto it = m_cache.constFind(sourceStr);
if (it != m_cache.constEnd()) {
- *wasCached = true;
s_loadMutex.unlock();
return *it;
}
s_loadMutex.unlock();
- *wasCached = false;
-
QElapsedTimer t;
t.start();
qCDebug(lcScene, "Loading image %s", qPrintable(sourceStr));
@@ -197,58 +284,6 @@ QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::load(const QUrl &sou
if (!result.isEmpty()) {
qCDebug(lcPerf, "Image loaded (%d mip levels) in %lld ms", result.count(), t.elapsed());
-
- if (flags.testFlag(GenerateMipMapsForIBL) && result.count() == 1) {
- // IBL needs special mipmap generation. This could be done
- // asynchronously but the we rely on the previous level in each step so
- // it's not a good fit unfortunately. So do it all here. Also,
- // QTextureGenerator could provide all mipmaps in one go in one blob,
- // but there's no public API for that, have to stick with
- // QTextureImageDataGenerator.
- t.restart();
- Qt3DRender::QTextureImageDataPtr data = result.first();
- int w = data->width();
- int h = data->height();
- const int maxDim = w > h ? w : h;
- const int maxMipLevel = int(qLn(maxDim) / qLn(2.0f));
- // silly QTextureImageData does not expose blockSize
- const int blockSize = blockSizeForFormat(data->format());
- QByteArray prevLevelData = data->data();
- for (int i = 1; i <= maxMipLevel; ++i) {
- int prevW = w;
- int prevH = h;
- w >>= 1;
- h >>= 1;
- w = qMax(1, w);
- h = qMax(1, h);
-
- auto mipImageData = Qt3DRender::QTextureImageDataPtr::create();
- mipImageData->setTarget(QOpenGLTexture::Target2D);
- mipImageData->setFormat(data->format());
- mipImageData->setWidth(w);
- mipImageData->setHeight(h);
- mipImageData->setLayers(1);
- mipImageData->setDepth(1);
- mipImageData->setFaces(1);
- // again, make no mistake: not setting 1 does not actually
- // allow providing multiple mip level data in one texture image
- // due to the bizarre API design of Qt3D. (the behavior is
- // logical, technically, but the API over all is not) So
- // separate textureimages is the only way to go.
- mipImageData->setMipLevels(1);
- mipImageData->setPixelFormat(data->pixelFormat());
- mipImageData->setPixelType(data->pixelType());
-
- QByteArray mipData = generateIblMip(w, h, prevW, prevH, mipImageData->format(),
- blockSize, prevLevelData);
- mipImageData->setData(mipData, blockSize, false);
- result << mipImageData;
- prevLevelData = mipData;
- }
- m_iblTime += t.elapsed();
- qCDebug(lcPerf, "Generated %d IBL mip levels in %lld ms", maxMipLevel, t.elapsed());
- }
-
s_loadMutex.lock();
m_cache.insert(sourceStr, result);
s_loadMutex.unlock();
@@ -258,142 +293,275 @@ QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::load(const QUrl &sou
return result;
}
-void Q3DSImageManager::finishAsyncLoad()
+QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::generateIblForImageData(
+ QVector<Qt3DRender::QTextureImageDataPtr> result)
+{
+ if (result.count() == 1) {
+ // IBL needs special mipmap generation. This could be done
+ // asynchronously but the we rely on the previous level in each step so
+ // it's not a good fit unfortunately. So do it all here. Also,
+ // QTextureGenerator could provide all mipmaps in one go in one blob,
+ // but there's no public API for that, have to stick with
+ // QTextureImageDataGenerator.
+ QElapsedTimer t;
+ t.start();
+ Qt3DRender::QTextureImageDataPtr data = result.first();
+ int w = data->width();
+ int h = data->height();
+ const int maxDim = w > h ? w : h;
+ const int maxMipLevel = int(qLn(maxDim) / qLn(2.0f));
+ // silly QTextureImageData does not expose blockSize
+ const int blockSize = blockSizeForFormat(data->format());
+ QByteArray prevLevelData = data->data();
+ for (int i = 1; i <= maxMipLevel; ++i) {
+ int prevW = w;
+ int prevH = h;
+ w >>= 1;
+ h >>= 1;
+ w = qMax(1, w);
+ h = qMax(1, h);
+
+ auto mipImageData = Qt3DRender::QTextureImageDataPtr::create();
+ mipImageData->setTarget(QOpenGLTexture::Target2D);
+ mipImageData->setFormat(data->format());
+ mipImageData->setWidth(w);
+ mipImageData->setHeight(h);
+ mipImageData->setLayers(1);
+ mipImageData->setDepth(1);
+ mipImageData->setFaces(1);
+ // again, make no mistake: not setting 1 does not actually
+ // allow providing multiple mip level data in one texture image
+ // due to the bizarre API design of Qt3D. (the behavior is
+ // logical, technically, but the API over all is not) So
+ // separate textureimages is the only way to go.
+ mipImageData->setMipLevels(1);
+ mipImageData->setPixelFormat(data->pixelFormat());
+ mipImageData->setPixelType(data->pixelType());
+
+ QByteArray mipData = generateIblMip(w, h, prevW, prevH, mipImageData->format(),
+ blockSize, prevLevelData);
+ mipImageData->setData(mipData, blockSize, false);
+ result << mipImageData;
+ prevLevelData = mipData;
+ }
+ m_iblTime += t.elapsed();
+ qCDebug(lcPerf, "Generated %d IBL mip levels in %lld ms", maxMipLevel, t.elapsed());
+ }
+ return result;
+}
+
+void Q3DSImageManager::finishAsyncLoad(bool wait)
{
- if (m_setSourceAsync.isEmpty())
+ QMutexLocker lock(&m_finishAsyncLoadLock);
+ if (m_loadImageDataAsync.isEmpty() && m_pendingSetSource.isEmpty())
return;
- QElapsedTimer t;
- t.start();
- {
- QFutureSynchronizer<void> sync;
- for (auto &item : qAsConst(m_setSourceAsync))
- sync.addFuture(item.future);
- }
- qCDebug(lcPerf, "Finish async image loading took %lld ms", t.elapsed());
- for (auto &item : qAsConst(m_setSourceAsync))
- setSource(item.tex, item.source, item.preferKtx, false);
- m_setSourceAsync.clear();
- m_loadImageAsync.clear();
+
+ if (wait) {
+ lock.unlock();
+ QElapsedTimer t;
+ t.start();
+ {
+ QFutureSynchronizer<void> sync;
+ for (auto &item : qAsConst(m_loadImageDataAsync))
+ sync.addFuture(item.future);
+ }
+ qCDebug(lcPerf, "Finish async image loading took %lld ms", t.elapsed());
+ for (auto &item : qAsConst(m_loadImageDataAsync)) {
+ if (m_pendingSetSource.contains(item.source)) {
+ const auto *pending = m_pendingSetSource[item.source];
+ for (const auto &tex : qAsConst(*pending))
+ setSource(tex, QUrl::fromLocalFile(item.source));
+ delete pending;
+ m_pendingSetSource.remove(item.source);
+ }
+ }
+ m_pendingSetSource.clear();
+ } else {
+ lock.relock();
+ QThread *thread = QThread::currentThread();
+ for (auto &item : m_loadImageDataAsync) {
+ if (!item.done && item.future.isFinished()) {
+ item.done = true;
+ if (m_pendingSetSource.contains(item.source)) {
+ const auto pending = *m_pendingSetSource[item.source];
+ for (const auto &tex : pending) {
+ if (tex->thread() == thread) {
+ setSource(tex, QUrl::fromLocalFile(item.source));
+ m_pendingSetSource[item.source]->removeOne(tex);
+ }
+ }
+ if (m_pendingSetSource[item.source]->empty()) {
+ delete m_pendingSetSource[item.source];
+ m_pendingSetSource.remove(item.source);
+ }
+ }
+ }
+ }
+ }
}
-static bool isAsyncImageLoadingEnabled()
+void Q3DSImageManager::beginImageLoad(const QSet<QUrl> &imageSet)
{
- static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING") & 1) > 0;
- return enabled;
+ m_resourceSet.unite(imageSet);
+ for (auto url : imageSet) {
+ QFileInfo info(url.toLocalFile());
+ if (info.exists()) {
+ if (!m_loadImageDataAsync.contains(url.toLocalFile()))
+ loadImageData(url);
+ for (int i = 0; i < m_reloadableTextures.size(); ++i) {
+ if (m_reloadableTextures[i]->source() == url)
+ m_reloadableTextures[i]->reload();
+ }
+ }
+ }
+}
+
+bool Q3DSImageManager::inResourceSet(const QUrl &url) const
+{
+ return m_resourceSet.contains(url);
+}
+
+void Q3DSImageManager::beginUnload(const QSet<QUrl> &imageSet)
+{
+ m_resourceSet.subtract(imageSet);
+ if (!isDelayedImageLoadingEnabled())
+ return;
+ for (auto url : imageSet) {
+ for (int i = 0; i < m_reloadableTextures.size(); ++i) {
+ if (m_reloadableTextures[i]->source() == url) {
+ m_metadata.remove(m_reloadableTextures[i]->texture());
+ m_reloadableTextures[i]->unload();
+ }
+ }
+ const QString name = url.toLocalFile();
+ m_cache.remove(name);
+ if (m_loadImageDataAsync.contains(name) && m_loadImageDataAsync[name].done)
+ m_loadImageDataAsync.remove(name);
+ }
+}
+
+void Q3DSImageManager::loadImageData(const QUrl &source, bool async)
+{
+ auto loadImage = [this](const QUrl &source) {
+ QVector<Qt3DRender::QTextureImageDataPtr> imageData;
+ // The generator (invoked from some Qt3D job thread later on) will just return the already
+ // loaded data.
+ imageData = load(source);
+ };
+
+ QMutexLocker lock(&s_loadMutex);
+ if (m_cache.contains(source.toLocalFile()))
+ return;
+ lock.unlock();
+
+ const QString src = source.toLocalFile();
+ if (async && isAsyncImageLoadingEnabled()) {
+ if (!m_loadImageDataAsync.contains(src)) {
+ qCDebug(lcScene, "Load image data async %s", qPrintable(src));
+ LoadImageDataAsync item;
+ item.source = src;
+ item.future = QtConcurrent::run(&m_threadPool, loadImage, source);
+ m_loadImageDataAsync[src] = item;
+ }
+ } else {
+ loadImage(source);
+ }
}
-void Q3DSImageManager::setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source,
- bool preferKtx, bool async)
+void Q3DSImageManager::setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source)
{
TextureInfo info;
- async = async && isAsyncImageLoadingEnabled();
+ QString src = source.toLocalFile();
+
s_loadMutex.lock();
auto it = m_metadata.find(tex);
if (it != m_metadata.end()) {
- if (it->source == source) {
+ if (it->source == src) {
s_loadMutex.unlock();
return;
}
info = *it;
}
- s_loadMutex.unlock();
-
- auto loadImageData = [this](TextureInfo info, Qt3DRender::QAbstractTexture *tex,
- const QUrl &source, bool preferKtx, bool async) {
- QVector<Qt3DRender::QTextureImageDataPtr> imageData;
- if (!preferKtx) {
- info.source = source;
- // yes, it's all synchronous and this is intentional. The generator
- // (invoked from some Qt3D job thread later on) will just return the already
- // loaded data.
- imageData = load(source, info.flags, &info.wasCached);
- } else {
- QString ktxSource = source.toLocalFile();
- ktxSource = ktxSource.left(ktxSource.lastIndexOf(QLatin1Char('.')));
- ktxSource.append(QLatin1String(".ktx"));
- info.source = QUrl::fromLocalFile(ktxSource);
- imageData = load(info.source, info.flags, &info.wasCached);
- // If ktx is not found, load with the original extension
- if (imageData.isEmpty()) {
- qCWarning(lcPerf, "You have specified \"Use ktx texture if available\" option, "
- "but did not provide a ktx texture: %s", qPrintable(ktxSource));
- info.source = source;
- load(source, info.flags, &info.wasCached);
- }
- }
+ if (!m_cache.contains(src)) {
+ if (!m_pendingSetSource.contains(src))
+ m_pendingSetSource[src] = new QVector<Qt3DRender::QAbstractTexture *>();
+ m_pendingSetSource[src]->push_back(tex);
+ s_loadMutex.unlock();
+ return;
+ }
+ else {
+ info.wasCached = true;
+ }
+ s_loadMutex.unlock();
- // The rest will be done when the asynchronous loading has finished by calling this
- // function with async = false.
- if (async)
- return;
+ auto &imageData = m_cache[src];
+ if (info.flags.testFlag(GenerateMipMapsForIBL) && imageData.size() == 1) {
+ s_loadMutex.lock();
+ m_cache[src] = generateIblForImageData(imageData);
+ s_loadMutex.unlock();
+ }
- const auto texImages = tex->textureImages();
- for (Qt3DRender::QAbstractTextureImage *oldImage : texImages) {
- tex->removeTextureImage(oldImage);
- delete oldImage;
- }
- if (!imageData.isEmpty()) {
- info.size = QSize(imageData[0]->width(), imageData[0]->height());
- info.format = Qt3DRender::QAbstractTexture::TextureFormat(imageData[0]->format());
- s_loadMutex.lock();
- m_metadata.insert(tex, info);
- s_loadMutex.unlock();
+ const auto texImages = tex->textureImages();
+ for (Qt3DRender::QAbstractTextureImage *oldImage : texImages) {
+ tex->removeTextureImage(oldImage);
+ delete oldImage;
+ }
- // Mipmaps are used in three cases: in IBL images (with our own custom
- // mipmap images), when the source provides mipmaps (e.g. a .ktx file
- // with mipmaps in it), or when the custom property metadata (custom
- // materials/effects) says so (mipmaps autogenerated in this case). If
- // none of these holds, linear filtering is used.
-
- tex->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
- tex->setGenerateMipMaps(false); // autogen is only for custom props and that's handled elsewhere
-
- if (imageData.count() > 1) {
- tex->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
- if (!info.wasCached)
- qCDebug(lcScene, "%s provided mipmaps, mipmap filtering enabled", qPrintable(source.toLocalFile()));
- } else {
- tex->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ if (!imageData.isEmpty()) {
+ info.size = QSize(imageData[0]->width(), imageData[0]->height());
+ info.format = Qt3DRender::QAbstractTexture::TextureFormat(imageData[0]->format());
+ s_loadMutex.lock();
+ m_metadata.insert(tex, info);
+ s_loadMutex.unlock();
+ // Mipmaps are used in three cases: in IBL images (with our own custom
+ // mipmap images), when the source provides mipmaps (e.g. a .ktx file
+ // with mipmaps in it), or when the custom property metadata (custom
+ // materials/effects) says so (mipmaps autogenerated in this case). If
+ // none of these holds, linear filtering is used.
+
+ tex->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ // autogen is only for custom props and that's handled elsewhere
+ tex->setGenerateMipMaps(false);
+
+ if (imageData.count() > 1) {
+ tex->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
+ if (!info.wasCached) {
+ qCDebug(lcScene, "%s provided mipmaps, mipmap filtering enabled",
+ qPrintable(source.toLocalFile()));
}
-
- for (int i = 0; i < imageData.count(); ++i)
- tex->addTextureImage(new TextureImage(source, i, imageData[i]));
} else {
- // Provide a dummy image when failing to load since we want to see
- // something that makes it obvious a texture source file was missing.
- info.size = QSize(64, 64);
- info.format = Qt3DRender::QAbstractTexture::RGBA8_UNorm;
- s_loadMutex.lock();
- m_metadata.insert(tex, info);
- s_loadMutex.unlock();
-
- QImage dummy(info.size, QImage::Format_ARGB32);
- dummy.fill(Qt::magenta);
- auto dummyData = Qt3DRender::QTextureImageDataPtr::create();
- dummyData->setImage(dummy);
-
- tex->addTextureImage(new TextureImage(source, 0, dummyData));
- qWarning("Using placeholder texture in place of %s", qPrintable(source.toLocalFile()));
+ tex->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
}
- };
- const QString src = source.toLocalFile();
- if (async) {
- m_setSourceAsync.append(TextureImageAsyncLoad());
- auto &item = m_setSourceAsync.last();
- item.tex = tex;
- item.source = source;
- item.preferKtx = preferKtx;
- if (!m_loadImageAsync.contains(src)) {
- qCDebug(lcScene, "Load image data async %s", qPrintable(src));
- m_loadImageAsync[src] = true;
- item.future = QtConcurrent::run(&m_threadPool, loadImageData, info, tex,
- source, preferKtx, true);
- }
+ for (int i = 0; i < imageData.count(); ++i)
+ tex->addTextureImage(new TextureImage(source, i, imageData[i]));
} else {
- loadImageData(info, tex, source, preferKtx, false);
+ // Provide a dummy image when failing to load since we want to see
+ // something that makes it obvious a texture source file was missing.
+ info.size = QSize(64, 64);
+ info.format = Qt3DRender::QAbstractTexture::RGBA8_UNorm;
+ s_loadMutex.lock();
+ m_metadata.insert(tex, info);
+ s_loadMutex.unlock();
+
+ QImage dummy(info.size, QImage::Format_ARGB32);
+ dummy.fill(Qt::magenta);
+ auto dummyData = Qt3DRender::QTextureImageDataPtr::create();
+ dummyData->setImage(dummy);
+
+ tex->addTextureImage(new TextureImage(source, 0, dummyData));
+ qWarning("Using placeholder texture in place of %s", qPrintable(source.toLocalFile()));
+ }
+ textureLoaded(tex, source);
+}
+
+void Q3DSImageManager::textureLoaded(Qt3DRender::QAbstractTexture *tex, const QUrl &source)
+{
+ for (auto &reloadable : m_reloadableTextures) {
+ if (reloadable->source() == source)
+ reloadable->loaded(tex);
}
}
diff --git a/src/runtime/q3dsimagemanager_p.h b/src/runtime/q3dsimagemanager_p.h
index ac768bc..1c89467 100644
--- a/src/runtime/q3dsimagemanager_p.h
+++ b/src/runtime/q3dsimagemanager_p.h
@@ -50,6 +50,7 @@
#include <QThreadPool>
#include <Qt3DRender/QAbstractTexture>
#include <Qt3DRender/QTextureImageData>
+#include <Qt3DRender/QParameter>
QT_BEGIN_NAMESPACE
@@ -59,6 +60,85 @@ namespace Qt3DCore {
class QEntity;
}
+class ReloadableTexture
+{
+public:
+ ReloadableTexture(Qt3DCore::QEntity *parent, bool generateIBL, const QByteArray &id,
+ const QString &profileInfo, Q3DSProfiler *profiler)
+ : m_parent(parent), m_id(id), m_profileInfo(profileInfo), m_generateIBL(generateIBL),
+ m_profiler(profiler)
+ {
+
+ }
+ void setSource(const QUrl &source);
+
+ template <typename Caller, typename... Params>
+ void onLoad(Caller caller, Params... params)
+ {
+ m_loadFunc = [this, caller, params...]() {
+ caller(m_texture, params...);
+ };
+ // onLoad call might provide new parameters when called after loading so call it now
+ if (m_loaded)
+ m_loadFunc();
+ else
+ triggerLoading();
+ }
+
+ template <typename Caller, typename... Params>
+ void onUnload(Caller caller, Params... params)
+ {
+ m_unloadFunc = [this, caller, params...]() {
+ caller(m_texture, params...);
+ };
+ triggerLoading();
+ }
+
+ void reload();
+ void unload();
+
+ QUrl source() const
+ {
+ return m_source;
+ }
+ void loaded(Qt3DRender::QAbstractTexture *texture);
+ bool wasLoaded() const
+ {
+ return m_loaded;
+ }
+ Qt3DRender::QAbstractTexture *texture()
+ {
+ return m_texture;
+ }
+
+ QString info()
+ {
+ return m_profileInfo;
+ }
+
+ Q3DSProfiler *profiler()
+ {
+ return m_profiler;
+ }
+
+private:
+ void triggerLoading();
+
+ Qt3DCore::QEntity *m_parent = nullptr;
+ Qt3DRender::QAbstractTexture *m_texture = nullptr;
+ QByteArray m_id;
+ QString m_profileInfo;
+ QUrl m_source;
+ bool m_generateIBL = false;
+ bool m_loaded = false;
+ Q3DSProfiler *m_profiler = nullptr;
+
+ std::function<void()> m_loadFunc = nullptr;
+ std::function<void()> m_unloadFunc = nullptr;
+};
+
+typedef QSharedPointer<ReloadableTexture> ReloadableTexturePtr;
+
class Q3DSImageManager
{
public:
@@ -76,9 +156,13 @@ public:
ImageFlags flags,
const QByteArray &id,
Q3DSProfiler *profiler = nullptr,
- const char *profName = nullptr, ...);
- void setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source, bool preferKtx,
- bool async = true);
+ const QString &profilerInfo = {});
+ ReloadableTexturePtr newReloadableTextureForImage(Qt3DCore::QEntity *parent,
+ ImageFlags flags,
+ const QByteArray &id,
+ Q3DSProfiler *profiler = nullptr,
+ const char *profName = nullptr, ...);
+
void setSource(Qt3DRender::QAbstractTexture *tex, const QImage &image);
QSize size(Qt3DRender::QAbstractTexture *tex) const;
@@ -88,14 +172,23 @@ public:
qint64 ioTimeMsecs() const { return m_ioTime; }
qint64 iblTimeMsecs() const { return m_iblTime; }
- void finishAsyncLoad();
+ void finishAsyncLoad(bool wait = true);
+ void beginImageLoad(const QSet<QUrl> &imageSet);
+ void beginUnload(const QSet<QUrl> &imageSet);
+ bool inResourceSet(const QUrl &url) const;
private:
- QVector<Qt3DRender::QTextureImageDataPtr> load(const QUrl &source, ImageFlags flags, bool *wasCached);
+ void loadImageData(const QUrl &source, bool async = true);
+ void setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source);
+ void textureLoaded(Qt3DRender::QAbstractTexture *tex, const QUrl &source);
+ QVector<Qt3DRender::QTextureImageDataPtr> load(const QUrl &source);
int blockSizeForFormat(QOpenGLTexture::TextureFormat format);
QByteArray generateIblMip(int w, int h, int prevW, int prevH,
QOpenGLTexture::TextureFormat format,
int blockSize, const QByteArray &prevLevelData);
+ QVector<Qt3DRender::QTextureImageDataPtr> generateIblForImageData(
+ QVector<Qt3DRender::QTextureImageDataPtr> result);
+
struct TextureInfo {
ImageFlags flags;
@@ -105,22 +198,26 @@ private:
bool wasCached = false;
};
- struct TextureImageAsyncLoad
+ struct LoadImageDataAsync
{
QFuture<void> future;
- Qt3DRender::QAbstractTexture *tex = nullptr;
- QUrl source;
+ QString source;
bool preferKtx = false;
+ bool done = false;
};
+ QVector<ReloadableTexturePtr> m_reloadableTextures;
+ QSet<QUrl> m_resourceSet;
QHash<Qt3DRender::QAbstractTexture *, TextureInfo> m_metadata;
QHash<QString, QVector<Qt3DRender::QTextureImageDataPtr> > m_cache;
- QHash<QString, bool> m_loadImageAsync;
- QVector<TextureImageAsyncLoad> m_setSourceAsync;
+ QHash<QString, LoadImageDataAsync> m_loadImageDataAsync;
+ QHash<QString, QVector<Qt3DRender::QAbstractTexture *> *> m_pendingSetSource;
QThreadPool m_threadPool;
static QMutex s_loadMutex;
qint64 m_ioTime = 0;
qint64 m_iblTime = 0;
+ QMutex m_finishAsyncLoadLock;
+ friend ReloadableTexture;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSImageManager::ImageFlags)
diff --git a/src/runtime/q3dsmeshloader.cpp b/src/runtime/q3dsmeshloader.cpp
index f26f893..5ca3657 100644
--- a/src/runtime/q3dsmeshloader.cpp
+++ b/src/runtime/q3dsmeshloader.cpp
@@ -894,7 +894,7 @@ Q3DSMesh *loadMeshDataFromCustomGeometry(const Q3DSGeometry &geom, Q3DSMeshLoade
continue;
Qt3DRender::QBuffer *vertexBuffer = mapping->bufferMap[attrDesc->bufferIndex];
- const int vertexCount = vertexBuffer->data().size() / componentByteSize(attrDesc->componentType);
+ const int vertexCount = vertexBuffer->data().size() / attrDesc->stride;
Qt3DRender::QAttribute *attr = new Qt3DRender::QAttribute(vertexBuffer,
q3dsAttributeName(attrDesc->semantic),
Qt3DRender::QAttribute::VertexBaseType(attrDesc->componentType),
diff --git a/src/runtime/q3dsprofiler.cpp b/src/runtime/q3dsprofiler.cpp
index 727229a..1a02084 100644
--- a/src/runtime/q3dsprofiler.cpp
+++ b/src/runtime/q3dsprofiler.cpp
@@ -121,6 +121,22 @@ void Q3DSProfiler::trackNewObject(QObject *obj, ObjectType type, const QByteArra
va_end(ap);
}
+void Q3DSProfiler::trackNewObject(QObject *obj, ObjectType type, const QByteArray &id,
+ const QString &info)
+{
+ if (!m_enabled)
+ return;
+
+ ObjectData objd(obj, type, id);
+ objd.info = info;
+ m_objectData.insert(type, objd);
+
+ m_objectDestroyConnections.append(QObject::connect(obj, &QObject::destroyed,
+ [this, obj, type, id]() {
+ m_objectData.remove(type, ObjectData(obj, type, id));
+ }));
+}
+
void Q3DSProfiler::vtrackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const char *info, va_list args)
{
if (!m_enabled)
diff --git a/src/runtime/q3dsprofiler_p.h b/src/runtime/q3dsprofiler_p.h
index 7a65f45..2bc391c 100644
--- a/src/runtime/q3dsprofiler_p.h
+++ b/src/runtime/q3dsprofiler_p.h
@@ -89,6 +89,7 @@ public:
ShaderProgramObject
};
void trackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const char *info, ...);
+ void trackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const QString &info);
void vtrackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const char *info, va_list args);
void updateObjectInfo(QObject *obj, ObjectType type, const QByteArray &id, const char *info, ...);
diff --git a/src/runtime/q3dsscenemanager.cpp b/src/runtime/q3dsscenemanager.cpp
index 6c40962..77d4c61 100644
--- a/src/runtime/q3dsscenemanager.cpp
+++ b/src/runtime/q3dsscenemanager.cpp
@@ -47,6 +47,7 @@
#include "q3dslogging_p.h"
#include "q3dsviewportsettings_p.h"
#include "q3dstextmesh_p.h"
+#include "q3dsimagemanager_p.h"
#if QT_CONFIG(q3ds_profileui)
#include "profileui/q3dsprofileui_p.h"
#include "q3dsconsolecommands_p.h"
@@ -58,6 +59,7 @@
#include <qmath.h>
#include <QTextLayout>
#include <QOpenGLContext>
+#include <QTimer>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
@@ -447,7 +449,7 @@ static const int LAYER_CACHING_THRESHOLD = 4;
*/
-Q3DSSceneManager::Q3DSSceneManager()
+Q3DSSceneManager::Q3DSSceneManager(ParentChildPairs *pairs)
: m_gfxLimits(Q3DS::graphicsLimits()),
m_matGen(new Q3DSDefaultMaterialGenerator),
m_customMaterialGen(new Q3DSCustomMaterialGenerator),
@@ -459,7 +461,8 @@ Q3DSSceneManager::Q3DSSceneManager()
m_glyphCacheManager(nullptr),
#endif
m_profiler(new Q3DSProfiler),
- m_inputManager(new Q3DSInputManager(this))
+ m_inputManager(new Q3DSInputManager(this)),
+ m_pairs(pairs)
{
if (Q3DSSlideUtils::useDragonWings())
m_slidePlayer = new Q3DSSlidePlayerNg(this);
@@ -648,7 +651,10 @@ void Q3DSSceneManager::uncacheLayers()
void Q3DSSceneManager::prepareAnimators()
{
- m_slidePlayer->sceneReady();
+ if (!m_animatorsInitialized) {
+ m_slidePlayer->sceneReady();
+ m_animatorsInitialized = true;
+ }
}
QDebug operator<<(QDebug dbg, const Q3DSSceneManager::SceneBuilderParams &p)
@@ -668,6 +674,8 @@ void Q3DSSceneManager::prepareEngineReset()
delete m_slidePlayer;
m_slidePlayer = nullptr;
+ m_animatorsInitialized = false;
+
#if QT_CONFIG(q3ds_profileui)
if (m_profileUi)
m_profileUi->releaseResources();
@@ -813,6 +821,10 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen
Qt3DLogic::QFrameAction *nodeUpdater = new Qt3DLogic::QFrameAction;
m_frameUpdater = new Q3DSFrameUpdater(this);
QObject::connect(nodeUpdater, &Qt3DLogic::QFrameAction::triggered, m_frameUpdater, &Q3DSFrameUpdater::frameAction);
+ if (m_pairs) {
+ m_pairs->addPair(m_rootEntity, nodeUpdater);
+ m_pairs->addPair(m_rootEntity, m_frameUpdater);
+ }
m_rootEntity->addComponent(nodeUpdater);
Qt3DRender::QRenderSettings *frameGraphComponent;
@@ -854,14 +866,19 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen
// Do some pre-processing and early setup for some objects.
initSubTree(m_scene);
+ m_engine->loadSlideResources(m_masterSlide, m_presentation);
+ m_engine->loadSlideResources(m_currentSlide, m_presentation);
+
// Build the (offscreen) Qt3D scene
m_layerContainerFg = new Qt3DRender::QFrameGraphNode(frameGraphRoot);
new Qt3DRender::QNoDraw(m_layerContainerFg); // in case there are no layers at all
+
Q3DSUipPresentation::forAllLayers(m_scene, [=](Q3DSLayerNode *layer3DS) {
if (layer3DS->sourcePath().isEmpty())
buildLayer(layer3DS, m_layerContainerFg, m_outputPixelSize);
else
buildSubPresentationLayer(layer3DS, m_outputPixelSize);
+
});
// The Scene object may have non-layer children.
@@ -874,7 +891,6 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen
// Onscreen (or not) compositor (still offscreen when this is a subpresentation)
buildCompositor(frameGraphRoot, m_rootEntity);
-
// Profiling UI (main presentation only)
#if QT_CONFIG(q3ds_profileui)
if (!m_flags.testFlag(SubPresentation)) {
@@ -981,16 +997,17 @@ void Q3DSSceneManager::finalizeMainScene()
#endif
}
-void Q3DSSceneManager::loadSubUipPresentation(Q3DSSubPresentation *sp)
+void Q3DSSceneManager::finalizeSubpresentationLoading(Q3DSSubPresentation *sp)
{
- const QString id = sp->id;
- Q_ASSERT(!id.isEmpty());
- *sp = m_engine->loadSubUipPresentation(sp->id);
- sp->id = id;
if (sp->colorTex && sp->sceneManager) {
+ for (int i = 0; i < m_subPresentations.size(); ++i) {
+ if (m_subPresentations[i].id == sp->id) {
+ m_subPresentations[i] = *sp;
+ break;
+ }
+ }
sp->sceneManager->setSubPresentations(m_subPresentations);
if (!m_inSetSubPresentations) {
- sp->sceneManager->prepareAnimators();
m_profiler->registerSubPresentationProfiler(sp->sceneManager->m_profiler);
Q3DSUipPresentation::forAllObjectsOfType(sp->sceneManager->m_presentation->scene(),
Q3DSGraphObject::Behavior,
@@ -1004,6 +1021,12 @@ void Q3DSSceneManager::loadSubUipPresentation(Q3DSSubPresentation *sp)
}
}
+static bool isAsyncSubpresentationLoadingEnabled()
+{
+ static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING") & 8) > 0;
+ return enabled;
+}
+
void Q3DSSceneManager::updateSubPresentationHosts()
{
for (Q3DSLayerNode *layer3DS : m_pendingSubPresLayers) {
@@ -1012,25 +1035,39 @@ void Q3DSSceneManager::updateSubPresentationHosts()
auto it = std::find_if(m_subPresentations.begin(), m_subPresentations.end(),
[subPresId](const Q3DSSubPresentation &sp) { return sp.id == subPresId; });
if (it != m_subPresentations.end()) {
- if (!it->colorTex)
- loadSubUipPresentation(it);
- if (it->colorTex) {
- qCDebug(lcScene, "Directing subpresentation %s to layer %s", qPrintable(it->id), layer3DS->id().constData());
- Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
- layerData->eyeMono->layerTexture = it->colorTex;
+
+ auto setSubpresentationTolayer = [this](Q3DSLayerNode *layer, Q3DSSubPresentation *sp){
+ qCDebug(lcScene, "Directing subpresentation %s to layer %s", qPrintable(sp->id), layer->id().constData());
+ Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer->attached());
+ layerData->eyeMono->layerTexture = sp->colorTex;
layerData->eyeMono->compositorSourceParam->setValue(QVariant::fromValue(layerData->eyeMono->layerTexture));
- layerData->eyeLeft->layerTexture = it->colorTex;
- layerData->eyeRight->layerTexture = it->colorTex;
+ layerData->eyeLeft->layerTexture = sp->colorTex;
+ layerData->eyeRight->layerTexture = sp->colorTex;
layerData->eyeLeft->compositorSourceParam->setValue(QVariant::fromValue(layerData->eyeLeft->layerTexture));
layerData->eyeRight->compositorSourceParam->setValue(QVariant::fromValue(layerData->eyeRight->layerTexture));
layerData->updateSubPresentationSize();
+ };
+
+ if (!it->colorTex) {
+ if (isAsyncSubpresentationLoadingEnabled()) {
+ m_engine->loadSubUipPresentation(subPresId, [this, setSubpresentationTolayer](
+ Q3DSSubPresentation *sp, Q3DSLayerNode *layer) {
+ finalizeSubpresentationLoading(sp);
+ setSubpresentationTolayer(layer, sp);
+ }, layer3DS);
+ } else {
+ *it = m_engine->loadSubUipPresentation(subPresId);
+ finalizeSubpresentationLoading(it);
+ setSubpresentationTolayer(layer3DS, it);
+ }
+ } else {
+ setSubpresentationTolayer(layer3DS, it);
}
} else {
qCDebug(lcScene, "Subpresentation %s for layer %s not found",
qPrintable(subPresId), layer3DS->id().constData());
}
}
-
for (auto p : m_pendingSubPresImages)
setImageTextureFromSubPresentation(p.first, p.second);
@@ -1563,7 +1600,9 @@ void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS,
// Generate Qt3D material components.
Q3DSUipPresentation::forAllModels(layer3DS->firstChild(),
- [this](Q3DSModelNode *model3DS) { buildModelMaterial(model3DS); },
+ [this](Q3DSModelNode *model3DS) {
+ buildModelMaterial(model3DS);
+ },
true); // include hidden ones too
// Set up effects.
@@ -2152,7 +2191,7 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS)
if (layer3DS->lightProbe()) {
// initialize light probe parameters if necessary
if (!data->iblProbeData.lightProbeTexture) {
- data->iblProbeData.lightProbeTexture = Q3DSImageManager::instance().newTextureForImage(
+ data->iblProbeData.lightProbeTexture = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
layer3DS->lightProbe()->id(),
m_profiler, "iblProbe texture for image %s", layer3DS->lightProbe()->id().constData());
@@ -2175,59 +2214,75 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS)
// Update light probe parameter values
// also sets min/mag and generates mipmaps
- Q3DSImageManager::instance().setSource(data->iblProbeData.lightProbeTexture,
- QUrl::fromLocalFile(layer3DS->lightProbe()->sourcePath()),
- m_presentation->preferKtx());
- data->iblProbeData.lightProbeSampler->setValue(QVariant::fromValue(data->iblProbeData.lightProbeTexture));
+ data->iblProbeData.lightProbeTexture->setSource(m_presentation->imageUrl(
+ layer3DS->lightProbe()->sourcePath()));
+ data->iblProbeData.lightProbeTexture->onLoad([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSLayerNode *layer3DS) {
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbeSampler->setValue(QVariant::fromValue(texture));
- Qt3DRender::QTextureWrapMode wrapMode;
+ Qt3DRender::QTextureWrapMode wrapMode;
- switch (layer3DS->lightProbe()->horizontalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
+ switch (layer3DS->lightProbe()->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- switch (layer3DS->lightProbe()->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
+ switch (layer3DS->lightProbe()->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- Qt3DRender::QAbstractTexture *texture = data->iblProbeData.lightProbeTexture;
- texture->setWrapMode(wrapMode);
+ texture->setWrapMode(wrapMode);
- const QMatrix4x4 &textureTransform = layer3DS->lightProbe()->textureTransform();
- const float *m = textureTransform.constData();
+ const QMatrix4x4 &textureTransform = layer3DS->lightProbe()->textureTransform();
+ const float *m = textureTransform.constData();
- // offsets.w = max mip level
- const QSize texSize = Q3DSImageManager::instance().size(texture);
- float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
- QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
- data->iblProbeData.lightProbeOffset->setValue(offsets);
+ const QSize texSize = Q3DSImageManager::instance().size(texture);
+ float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
+ QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
+ data->iblProbeData.lightProbeOffset->setValue(offsets);
+
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ data->iblProbeData.lightProbeRotation->setValue(rotations);
+ }, layer3DS);
+
+ data->iblProbeData.lightProbeTexture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSLayerNode *layer3DS) {
+ Q_UNUSED(texture);
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbeSampler->setValue({});
+ QVector4D offsets(0.f, 0.f, 0.f, 0.f);
+ data->iblProbeData.lightProbeOffset->setValue(offsets);
+
+ QVector4D rotations(0.f, 0.f, 0.f, 0.f);
+ data->iblProbeData.lightProbeRotation->setValue(rotations);
+ }, layer3DS);
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- data->iblProbeData.lightProbeRotation->setValue(rotations);
if (layer3DS->lightProbe2()) {
// Initialize light probe 2 parameters
if (!data->iblProbeData.lightProbe2Texture) {
- data->iblProbeData.lightProbe2Texture = Q3DSImageManager::instance().newTextureForImage(
+ data->iblProbeData.lightProbe2Texture
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
layer3DS->lightProbe2()->id(),
- m_profiler, "iblProbe2 texture for image %s", layer3DS->lightProbe2()->id().constData());
+ m_profiler, "iblProbe2 texture for image %s",
+ layer3DS->lightProbe2()->id().constData());
}
if (!data->iblProbeData.lightProbe2Sampler) {
@@ -2238,22 +2293,63 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS)
// Update light probe 2 parameter values
// also sets min/mag and generates mipmaps
- Q3DSImageManager::instance().setSource(data->iblProbeData.lightProbe2Texture,
- QUrl::fromLocalFile(layer3DS->lightProbe2()->sourcePath()),
- m_presentation->preferKtx());
- data->iblProbeData.lightProbe2Sampler->setValue(QVariant::fromValue(data->iblProbeData.lightProbe2Texture));
+ data->iblProbeData.lightProbe2Texture->setSource(
+ m_presentation->imageUrl(layer3DS->lightProbe2()->sourcePath()));
+ data->iblProbeData.lightProbe2Texture->onLoad([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSLayerNode *layer3DS) {
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbe2Sampler->setValue(QVariant::fromValue(texture));
+
+ Qt3DRender::QTextureWrapMode wrapMode;
+
+ switch (layer3DS->lightProbe()->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- data->iblProbeData.lightProbe2Texture->setWrapMode(wrapMode);
+ switch (layer3DS->lightProbe()->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- QVector4D probe2Props(layer3DS->probe2Window(), layer3DS->probe2Pos(), layer3DS->probe2Fade(), 1.0f);
- data->iblProbeData.lightProbe2Properties->setValue(probe2Props);
- const QMatrix4x4 &textureTransform = layer3DS->lightProbe2()->textureTransform();
- const float *m = textureTransform.constData();
- QVector4D probeProps(m[12], m[13], layer3DS->probeHorizon(), layer3DS->probeBright() * 0.01f);
- data->iblProbeData.lightProbeProperties->setValue(probeProps);
+ texture->setWrapMode(wrapMode);
+
+ QVector4D probe2Props(layer3DS->probe2Window(), layer3DS->probe2Pos(),
+ layer3DS->probe2Fade(), 1.0f);
+ data->iblProbeData.lightProbe2Properties->setValue(probe2Props);
+ const QMatrix4x4 &textureTransform = layer3DS->lightProbe2()->textureTransform();
+ const float *m = textureTransform.constData();
+ QVector4D probeProps(m[12], m[13], layer3DS->probeHorizon(),
+ layer3DS->probeBright() * 0.01f);
+ data->iblProbeData.lightProbeProperties->setValue(probeProps);
+ }, layer3DS);
+ data->iblProbeData.lightProbe2Texture->onUnload(
+ [](Qt3DRender::QAbstractTexture *texture, Q3DSLayerNode *layer3DS) {
+ Q_UNUSED(texture);
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbe2Sampler->setValue({});
+ data->iblProbeData.lightProbe2Properties->setValue({});
+ data->iblProbeData.lightProbeProperties->setValue({});
+ }, layer3DS);
} else {
data->iblProbeData.lightProbe2Properties->setValue(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
- data->iblProbeData.lightProbeProperties->setValue(QVector4D(0.0f, 0.0f, layer3DS->probeHorizon(), layer3DS->probeBright() * 0.01f));
+ data->iblProbeData.lightProbeProperties->setValue(
+ QVector4D(0.0f, 0.0f, layer3DS->probeHorizon(),
+ layer3DS->probeBright() * 0.01f));
}
}
}
@@ -4814,7 +4910,8 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
data->entity->setObjectName(QObject::tr("top level text %1").arg(QString::fromUtf8(text3DS->id())));
#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
- if (shouldDistanceFieldRender) {
+ bool useDistanceFields = shouldDistanceFieldRender && text3DS->wordWrap() != Q3DSTextNode::Clip;
+ if (useDistanceFields) {
data->distanceFieldText = true;
data->dropShadow = text3DS->shadow();
QVector2D boundingBox = text3DS->boundingBox();
@@ -5049,8 +5146,10 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
cache->processPendingGlyphs();
qreal fontPixelSize = glyphRun.rawFont().pixelSize();
- float shadowOffsetX = float(fontPixelSize) * text3DS->shadowOffsetX() / 1000.0f;
- float shadowOffsetY = float(fontPixelSize) * text3DS->shadowOffsetY() / 1000.0f;
+
+ float shadowOffsetX = float(cache->fontSize()) * text3DS->shadowOffsetX() / 1000.0f;
+ float shadowOffsetY = float(cache->fontSize()) * text3DS->shadowOffsetY() / 1000.0f;
+
qreal maxTexMargin = cache->distanceFieldRadius();
qreal fontScale = cache->fontScale(fontPixelSize);
qreal margin = 2;
@@ -5087,8 +5186,15 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
float cy2 = cy1 + float(metrics.height);
if (text3DS->shadow()) {
- cx2 += shadowOffsetX;
- cy2 += shadowOffsetY;
+ if (shadowOffsetX < 0.0)
+ cx1 += shadowOffsetX * fontScale;
+ else
+ cx2 += shadowOffsetX * fontScale;
+
+ if (shadowOffsetY < 0.0)
+ cy1 += shadowOffsetY * fontScale;
+ else
+ cy2 += shadowOffsetY * fontScale;
}
float tx1 = float(c.x + c.xMargin);
@@ -5096,12 +5202,22 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
float ty1 = float(c.y + c.yMargin);
float ty2 = ty1 + float(c.height);
+ // Preserve original bounds of glyphs
+ float ttx1 = tx1;
+ float tty1 = ty1;
float ttx2 = tx2;
float tty2 = ty2;
if (text3DS->shadow()) {
- tx2 += float(c.width) * shadowOffsetX / float(metrics.width);
- ty2 += float(c.height) * shadowOffsetY / float(metrics.height);
+ if (shadowOffsetX < 0.0)
+ tx1 += float(c.width) * shadowOffsetX * fontScale / float(metrics.width);
+ else
+ tx2 += float(c.width) * shadowOffsetX * fontScale / float(metrics.width);
+
+ if (shadowOffsetY < 0.0)
+ ty1 += float(c.height) * shadowOffsetY * fontScale / float(metrics.height);
+ else
+ ty2 += float(c.height) * shadowOffsetY * fontScale / float(metrics.height);
}
const QSGDistanceFieldGlyphCache::Texture *texture = cache->glyphTexture(glyphIndex);
@@ -5127,8 +5243,8 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
vertexes.append(ty1);
if (text3DS->shadow()) {
- vertexes.append(tx1);
- vertexes.append(ty1);
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
vertexes.append(ttx2);
vertexes.append(tty2);
}
@@ -5140,8 +5256,8 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
vertexes.append(ty1);
if (text3DS->shadow()) {
- vertexes.append(tx1);
- vertexes.append(ty1);
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
vertexes.append(ttx2);
vertexes.append(tty2);
}
@@ -5153,8 +5269,8 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
vertexes.append(ty2);
if (text3DS->shadow()) {
- vertexes.append(tx1);
- vertexes.append(ty1);
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
vertexes.append(ttx2);
vertexes.append(tty2);
}
@@ -5166,8 +5282,8 @@ Qt3DCore::QEntity *Q3DSSceneManager::buildText(Q3DSTextNode *text3DS, Q3DSLayerN
vertexes.append(ty2);
if (text3DS->shadow()) {
- vertexes.append(tx1);
- vertexes.append(ty1);
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
vertexes.append(ttx2);
vertexes.append(tty2);
}
@@ -5380,7 +5496,6 @@ void Q3DSSceneManager::updateText(Q3DSTextNode *text3DS, bool needsNewImage)
Qt3DCore::QEntity *parentEntity = data->entity->parentEntity();
buildText(text3DS, data->layer3DS, parentEntity);
- setNodeVisibility(text3DS, text3DS->flags().testFlag(Q3DSNode::Active));
}
} else
#endif
@@ -5434,6 +5549,10 @@ void Q3DSSceneManager::updateText(Q3DSTextNode *text3DS, bool needsNewImage)
if (textOffsetX != 0 || textOffsetY != 0) {
QVector3D pos = text3DS->position();
+ QVector3D rot = text3DS->rotation();
+ // Apply text rotation to the translation amount
+ textOffsetX *= qCos(qDegreesToRadians(rot.y()));
+ textOffsetY *= qCos(qDegreesToRadians(rot.x()));
data->transform->setTranslation(QVector3D(pos.x() + textOffsetX,
pos.y() + textOffsetY,
pos.z()));
@@ -5757,7 +5876,6 @@ static void addShadowSsaoParams(Q3DSLayerAttached *layerData, QVector<Qt3DRender
void Q3DSSceneManager::buildModelMaterial(Q3DSModelNode *model3DS)
{
// Scene building phase 2: all lights are known -> generate actual Qt3D materials
-
Q3DSModelAttached *modelData = static_cast<Q3DSModelAttached *>(model3DS->attached());
if (!modelData)
return;
@@ -5785,7 +5903,6 @@ void Q3DSSceneManager::buildModelMaterial(Q3DSModelNode *model3DS)
// matters so much.
lightNodes.resize(m_gfxLimits.maxLightsPerLayer);
}
-
for (Q3DSModelAttached::SubMesh &sm : modelData->subMeshes) {
if (sm.resolvedMaterial && !sm.materialComponent && sm.resolvedMaterial->type() != Q3DSGraphObject::ReferencedMaterial) {
Q_ASSERT(sm.resolvedMaterial->attached());
@@ -5867,7 +5984,7 @@ void Q3DSSceneManager::buildModelMaterial(Q3DSModelNode *model3DS)
param->setParent(layerData->entity);
}
- sm.materialComponent = m_matGen->generateMaterial(defaultMaterial, sm.referencingMaterial, params, lightNodes, modelData->layer3DS);
+ sm.materialComponent = m_matGen->generateMaterial(m_pairs, defaultMaterial, sm.referencingMaterial, params, lightNodes, modelData->layer3DS);
sm.entity->addComponent(sm.materialComponent);
} else if (sm.resolvedMaterial->type() == Q3DSGraphObject::CustomMaterial) {
Q3DSCustomMaterialInstance *customMaterial = static_cast<Q3DSCustomMaterialInstance *>(sm.resolvedMaterial);
@@ -5948,7 +6065,8 @@ void Q3DSSceneManager::buildModelMaterial(Q3DSModelNode *model3DS)
if (!passes.isEmpty()) {
// ### TODO support more than one pass
auto pass = passes.first();
- sm.materialComponent = m_customMaterialGen->generateMaterial(customMaterial, sm.referencingMaterial, params, lightNodes, modelData->layer3DS, pass);
+ bool gles = m_gfxLimits.format.renderableType() == QSurfaceFormat::OpenGLES;
+ sm.materialComponent = m_customMaterialGen->generateMaterial(m_pairs, customMaterial, sm.referencingMaterial, params, lightNodes, modelData->layer3DS, pass, gles);
} else {
qCDebug(lcScene, "Custom material %s has no passes. Using dummy material. Object %s will not show.",
customMaterial->id().constData(), model3DS->id().constData());
@@ -5972,6 +6090,7 @@ void Q3DSSceneManager::rebuildModelMaterial(Q3DSModelNode *model3DS)
for (Q3DSModelAttached::SubMesh &sm : modelData->subMeshes) {
if (sm.resolvedMaterial && sm.materialComponent) {
qCDebug(lcPerf, "Rebuilding material for %s (entity %p)", model3DS->id().constData(), sm.entity);
+
delete sm.materialComponent;
sm.materialComponent = nullptr;
}
@@ -6062,15 +6181,58 @@ void Q3DSSceneManager::prepareTextureParameters(Q3DSTextureParameters &texturePa
textureParameters.size = new Qt3DRender::QParameter;
textureParameters.size->setName(name + QLatin1String("_size"));
-
- textureParameters.texture = Q3DSImageManager::instance().newTextureForImage(
- m_rootEntity, 0, image3DS->id(), m_profiler, "Texture for image %s", image3DS->id().constData());
}
void Q3DSSceneManager::updateTextureParameters(Q3DSTextureParameters &textureParameters, Q3DSImage *image)
{
// note that this function is called frequently (whenever any Image parameter changes)
+ auto setupTexture = [](Q3DSTextureParameters &textureParameters, Q3DSImage *image) {
+ Qt3DRender::QTextureWrapMode wrapMode;
+ switch (image->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+ switch (image->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ Qt3DRender::QAbstractTexture *texture
+ = textureParameters.sampler->value().value<Qt3DRender::QAbstractTexture *>();
+ Q_ASSERT(texture || !image->subPresentation().isEmpty());
+ if (!texture)
+ return;
+
+ texture->setWrapMode(wrapMode);
+ // min/mag are already set at this point
+
+ const QMatrix4x4 &textureTransform = image->textureTransform();
+ const float *m = textureTransform.constData();
+
+ QVector3D offsets(m[12], m[13], image->hasPremultipliedAlpha() ? 1 : 0);
+ textureParameters.offsets->setValue(offsets);
+
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ textureParameters.rotations->setValue(rotations);
+ const QSize size = Q3DSImageManager::instance().size(texture);
+ textureParameters.size->setValue(QVector2D(size.width(), size.height()));
+ };
+
if (!image->subPresentation().isEmpty()) {
if (textureParameters.subPresId != image->subPresentation()) {
textureParameters.subPresId = image->subPresentation();
@@ -6082,60 +6244,40 @@ void Q3DSSceneManager::updateTextureParameters(Q3DSTextureParameters &texturePar
setImageTextureFromSubPresentation(textureParameters.sampler, image);
}
}
+ setupTexture(textureParameters, image);
} else if (!image->sourcePath().isEmpty()) {
- Q3DSImageManager::instance().setSource(textureParameters.texture, QUrl::fromLocalFile(image->sourcePath()),
- m_presentation->preferKtx());
- textureParameters.sampler->setValue(QVariant::fromValue(textureParameters.texture));
+ if (!textureParameters.textureSource) {
+ textureParameters.textureSource
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
+ m_rootEntity, 0, image->id(), m_profiler, "Texture for image %s",
+ image->id().constData());
+ }
+ textureParameters.textureSource->setSource(m_presentation->imageUrl(image->sourcePath()));
+ textureParameters.textureSource->onLoad(
+ [setupTexture](Qt3DRender::QAbstractTexture *texture,
+ Q3DSTextureParameters *param, Q3DSImage *image) {
+ param->sampler->setValue(QVariant::fromValue(texture));
+ setupTexture(*param, image);
+ }, &textureParameters, image);
+ textureParameters.textureSource->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSTextureParameters *param) {
+ Q_UNUSED(texture);
+ param->sampler->setValue({});
+ param->offsets->setValue({});
+ param->rotations->setValue({});
+ param->size->setValue({});
+ }, &textureParameters);
} else if (!image->customImage().isNull()) {
+ textureParameters.texture
+ = Q3DSImageManager::instance().newTextureForImage(m_rootEntity, 0, image->id(),
+ m_profiler, QStringLiteral("Texture for image %1").arg(image->id().constData()));
Q3DSImageManager::instance().setSource(textureParameters.texture, image->customImage());
textureParameters.sampler->setValue(QVariant::fromValue(textureParameters.texture));
+ setupTexture(textureParameters, image);
} else {
textureParameters.sampler->setValue(QVariant::fromValue(dummyTexture(image->id())));
+ setupTexture(textureParameters, image);
}
-
- Qt3DRender::QTextureWrapMode wrapMode;
- switch (image->horizontalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
- switch (image->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
-
- Qt3DRender::QAbstractTexture *texture = textureParameters.sampler->value().value<Qt3DRender::QAbstractTexture *>();
- Q_ASSERT(texture || !image->subPresentation().isEmpty());
- if (!texture)
- return;
-
- texture->setWrapMode(wrapMode);
- // min/mag are already set at this point
-
- const QMatrix4x4 &textureTransform = image->textureTransform();
- const float *m = textureTransform.constData();
-
- QVector3D offsets(m[12], m[13], image->hasPremultipliedAlpha() ? 1 : 0);
- textureParameters.offsets->setValue(offsets);
-
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- textureParameters.rotations->setValue(rotations);
-
- const QSize size = Q3DSImageManager::instance().size(texture);
- textureParameters.size->setValue(QVector2D(size.width(), size.height()));
}
void Q3DSSceneManager::setImageTextureFromSubPresentation(Qt3DRender::QParameter *sampler, Q3DSImage *image)
@@ -6143,17 +6285,36 @@ void Q3DSSceneManager::setImageTextureFromSubPresentation(Qt3DRender::QParameter
auto it = std::find_if(m_subPresentations.begin(), m_subPresentations.end(),
[image](const Q3DSSubPresentation &sp) { return sp.id == image->subPresentation(); });
if (it != m_subPresentations.cend()) {
- if (!it->colorTex)
- loadSubUipPresentation(it);
- qCDebug(lcScene, "Directing subpresentation %s to image %s",
- qPrintable(image->subPresentation()), image->id().constData());
- sampler->setValue(QVariant::fromValue(it->colorTex));
- qCDebug(lcPerf, "Using a subpresentation as texture map (not as layer) makes layer caching in main presentation less efficient");
- // QML subpresentations will not have a scenemanager
- if (it->sceneManager)
- m_layerCacheDeps.insert(it->sceneManager);
- else
- m_hasQmlSubPresAsTextureMap = true;
+ auto handleSubpresentation = [this](
+ Q3DSSubPresentation *sp, Qt3DRender::QParameter *sampler, Q3DSImage *image) {
+ qCDebug(lcScene, "Directing subpresentation %s to image %s",
+ qPrintable(image->subPresentation()), image->id().constData());
+ sampler->setValue(QVariant::fromValue(sp->colorTex));
+ qCDebug(lcPerf, "Using a subpresentation as texture map (not as layer) makes layer caching in main presentation less efficient");
+ // QML subpresentations will not have a scenemanager
+ if (sp->sceneManager)
+ m_layerCacheDeps.insert(sp->sceneManager);
+ else
+ m_hasQmlSubPresAsTextureMap = true;
+ };
+ if (!it->colorTex) {
+ if (isAsyncSubpresentationLoadingEnabled()) {
+ m_engine->loadSubUipPresentation(image->subPresentation(),
+ [this, handleSubpresentation]
+ (Q3DSSubPresentation *sp,
+ Qt3DRender::QParameter *sampler,
+ Q3DSImage *image) {
+ finalizeSubpresentationLoading(sp);
+ handleSubpresentation(sp, sampler, image);
+ }, sampler, image);
+ } else {
+ *it = m_engine->loadSubUipPresentation(image->subPresentation());
+ finalizeSubpresentationLoading(it);
+ handleSubpresentation(it, sampler, image);
+ }
+ } else {
+ handleSubpresentation(it, sampler, image);
+ }
} else {
qCDebug(lcScene, "Subpresentation %s for image %s not found",
qPrintable(image->subPresentation()), image->id().constData());
@@ -6389,10 +6550,11 @@ QVector<Qt3DRender::QParameter *> Q3DSSceneManager::prepareDefaultMaterial(Q3DSD
iblOverrideImage = m->lightProbe();
if (iblOverrideImage) {
if (!data->lightProbeOverrideTexture) {
- data->lightProbeOverrideTexture = Q3DSImageManager::instance().newTextureForImage(
+ data->lightProbeOverrideTexture
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
- iblOverrideImage->id(),
- m_profiler, "Texture for image %s", iblOverrideImage->id().constData());
+ iblOverrideImage->id(), m_profiler,
+ "Texture for image %s", iblOverrideImage->id().constData());
data->lightProbeSampler = new Qt3DRender::QParameter;
data->lightProbeSampler->setName(QLatin1String("light_probe"));
@@ -6534,6 +6696,48 @@ void Q3DSSceneManager::updateDefaultMaterial(Q3DSDefaultMaterial *m, Q3DSReferen
// IBL
if (iblOverride) {
+ auto setupTexture = [](Qt3DRender::QAbstractTexture *texture,
+ Q3DSDefaultMaterialAttached *data, Q3DSImage *iblOverride) {
+ Qt3DRender::QTextureWrapMode wrapMode;
+
+ switch (iblOverride->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ switch (iblOverride->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ texture->setWrapMode(wrapMode);
+
+ const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
+ const float *m = textureTransform.constData();
+
+ // offsets.w = max mip level
+ const QSize texSize = Q3DSImageManager::instance().size(texture);
+ float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
+ QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
+ data->lightProbeOffset->setValue(offsets);
+
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ data->lightProbeRotation->setValue(rotations);
+ };
// also sets min/mag and generates mipmaps
if (!iblOverride->subPresentation().isEmpty()) {
if (m_subPresentations.isEmpty())
@@ -6541,52 +6745,23 @@ void Q3DSSceneManager::updateDefaultMaterial(Q3DSDefaultMaterial *m, Q3DSReferen
else
setImageTextureFromSubPresentation(data->lightProbeSampler, iblOverride);
} else {
- Q3DSImageManager::instance().setSource(data->lightProbeOverrideTexture,
- QUrl::fromLocalFile(iblOverride->sourcePath()),
- m_presentation->preferKtx());
- data->lightProbeSampler->setValue(QVariant::fromValue(data->lightProbeOverrideTexture));
- }
-
- Qt3DRender::QTextureWrapMode wrapMode;
-
- switch (iblOverride->horizontalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
-
- switch (iblOverride->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
+ data->lightProbeOverrideTexture->setSource(
+ m_presentation->imageUrl(iblOverride->sourcePath()));
+ data->lightProbeOverrideTexture->onLoad([setupTexture](
+ Qt3DRender::QAbstractTexture *texture,
+ Q3DSDefaultMaterialAttached *data,
+ Q3DSImage *iblOverride) {
+ data->lightProbeSampler->setValue(QVariant::fromValue(texture));
+ setupTexture(texture, data, iblOverride);
+ }, data, iblOverride);
+ data->lightProbeOverrideTexture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSDefaultMaterialAttached *data) {
+ Q_UNUSED(texture);
+ data->lightProbeSampler->setValue({});
+ data->lightProbeOffset->setValue({});
+ data->lightProbeRotation->setValue({});
+ }, data);
}
-
- Q_ASSERT(data->lightProbeOverrideTexture);
- data->lightProbeOverrideTexture->setWrapMode(wrapMode);
-
- const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
- const float *m = textureTransform.constData();
-
- // offsets.w = max mip level
- const QSize texSize = Q3DSImageManager::instance().size(data->lightProbeOverrideTexture);
- float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
- QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
- data->lightProbeOffset->setValue(offsets);
-
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- data->lightProbeRotation->setValue(rotations);
}
}
@@ -6614,71 +6789,91 @@ static inline void forAllCustomProperties(Q3DSEffectInstance *eff3DS, CustomProp
iterateCustomProperties(eff3DS->dynamicProperties(), eff3DS->effect()->properties(), callback);
}
-Qt3DRender::QAbstractTexture *Q3DSSceneManager::createCustomPropertyTexture(const Q3DSCustomPropertyParameter &p, const QByteArray &id)
+void Q3DSSceneManager::createCustomPropertyTexture(Q3DSCustomPropertyParameter &p,
+ const QByteArray &id)
{
+ // now override the defaults set in setSource() with whatever the metadata specifies
+ auto setupTexture = [](Q3DSCustomPropertyParameter *p, Qt3DRender::QAbstractTexture *texture) {
+ switch (p->meta.magFilterType) {
+ case Q3DSMaterial::Nearest:
+ texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Nearest);
+ break;
+ default:
+ texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ break;
+ }
+
+ switch (p->meta.minFilterType) {
+ case Q3DSMaterial::Nearest:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Nearest);
+ break;
+ case Q3DSMaterial::Linear:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ break;
+ case Q3DSMaterial::NearestMipmapNearest:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapNearest);
+ texture->setGenerateMipMaps(true);
+ break;
+ case Q3DSMaterial::NearestMipmapLinear:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapLinear);
+ texture->setGenerateMipMaps(true);
+ break;
+ case Q3DSMaterial::LinearMipmapNearest:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapNearest);
+ texture->setGenerateMipMaps(true);
+ break;
+ default:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
+ texture->setGenerateMipMaps(true);
+ break;
+ }
+
+ Qt3DRender::QTextureWrapMode wrapMode;
+ switch (p->meta.clampType) {
+ case Q3DSMaterial::Repeat:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+ texture->setWrapMode(wrapMode);
+ };
+
const QString source = p.inputValue.toString();
Qt3DRender::QAbstractTexture *texture;
if (source.isEmpty()) {
texture = dummyTexture(id);
+ setupTexture(&p, texture);
+ p.param->setValue(QVariant::fromValue(texture));
} else {
- texture = Q3DSImageManager::instance().newTextureForImage(m_rootEntity, 0, id, m_profiler,
- "Custom property texture %s", qPrintable(source));
+ p.texture = Q3DSImageManager::instance().newReloadableTextureForImage(
+ m_rootEntity, 0, id, m_profiler, "Custom property texture %s",
+ qPrintable(source));
qCDebug(lcScene, "Creating custom property texture %s", qPrintable(source));
- Q3DSImageManager::instance().setSource(texture, QUrl::fromLocalFile(source),
- m_presentation->preferKtx());
- }
-
- // now override the defaults set in setSource() with whatever the metadata specifies
-
- switch (p.meta.magFilterType) {
- case Q3DSMaterial::Nearest:
- texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Nearest);
- break;
- default:
- texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
- break;
- }
-
- switch (p.meta.minFilterType) {
- case Q3DSMaterial::Nearest:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Nearest);
- break;
- case Q3DSMaterial::Linear:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
- break;
- case Q3DSMaterial::NearestMipmapNearest:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapNearest);
- texture->setGenerateMipMaps(true);
- break;
- case Q3DSMaterial::NearestMipmapLinear:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapLinear);
- texture->setGenerateMipMaps(true);
- break;
- case Q3DSMaterial::LinearMipmapNearest:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapNearest);
- texture->setGenerateMipMaps(true);
- break;
- default:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
- texture->setGenerateMipMaps(true);
- break;
-
- }
-
- Qt3DRender::QTextureWrapMode wrapMode;
- switch (p.meta.clampType) {
- case Q3DSMaterial::Repeat:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
+ p.texture->setSource(m_presentation->imageUrl(source));
+ p.texture->onLoad([setupTexture](Qt3DRender::QAbstractTexture *texture,
+ Q3DSCustomPropertyParameter *p) {
+ p->param->setValue(QVariant::fromValue(texture));
+ if (p->texFlagParam)
+ p->texFlagParam->setValue(1);
+ const QSize size = Q3DSImageManager::instance().size(texture);
+ const bool isPremultiplied = false;
+ if (p->texInfoParam) {
+ p->texInfoParam->setValue(QVector4D(size.width(), size.height(),
+ isPremultiplied ? 1 : 0, 0));
+ }
+ setupTexture(p, texture);
+ }, &p);
+ p.texture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSCustomPropertyParameter *p) {
+ Q_UNUSED(texture);
+ p->param->setValue({});
+ p->texInfoParam->setValue({});
+ }, &p);
}
- texture->setWrapMode(wrapMode);
-
- return texture;
}
QVector<Qt3DRender::QParameter *> Q3DSSceneManager::prepareCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS)
@@ -6770,7 +6965,8 @@ QVector<Qt3DRender::QParameter *> Q3DSSceneManager::prepareCustomMaterial(Q3DSCu
iblOverrideImage = m->lightProbe();
if (iblOverrideImage) {
if (!data->lightProbeOverrideTexture) {
- data->lightProbeOverrideTexture = Q3DSImageManager::instance().newTextureForImage(
+ data->lightProbeOverrideTexture
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
iblOverrideImage->id(),
m_profiler, "Texture for image %s", iblOverrideImage->id().constData());
@@ -6818,7 +7014,7 @@ void Q3DSSceneManager::updateCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSR
// point whereas we need a proper Qt 3D texture.
switch (p.meta.type) {
case Q3DS::Texture:
- p.param->setValue(QVariant::fromValue(createCustomPropertyTexture(p, m->id())));
+ createCustomPropertyTexture(p, m->id());
break;
// Buffer, Image2D, etc. are not used for custom materials
@@ -6868,52 +7064,61 @@ void Q3DSSceneManager::updateCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSR
// IBL
if (iblOverride) {
// also sets min/mag and generates mipmaps
- Q3DSImageManager::instance().setSource(data->lightProbeOverrideTexture,
- QUrl::fromLocalFile(iblOverride->sourcePath()),
- m_presentation->preferKtx());
- data->lightProbeSampler->setValue(QVariant::fromValue(data->lightProbeOverrideTexture));
-
- Qt3DRender::QTextureWrapMode wrapMode;
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
-
- switch (iblOverride->horizontalTiling()) {
- case Q3DSImage::Tiled:
+ data->lightProbeOverrideTexture->setSource(
+ m_presentation->imageUrl(iblOverride->sourcePath()));
+ data->lightProbeOverrideTexture->onLoad([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSImage *iblOverride,
+ Q3DSCustomMaterialAttached *data) {
+ data->lightProbeSampler->setValue(QVariant::fromValue(texture));
+
+ Qt3DRender::QTextureWrapMode wrapMode;
wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
- switch (iblOverride->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
+ switch (iblOverride->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ switch (iblOverride->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- Q_ASSERT(data->lightProbeOverrideTexture);
- data->lightProbeOverrideTexture->setWrapMode(wrapMode);
+ texture->setWrapMode(wrapMode);
- const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
- const float *m = textureTransform.constData();
+ const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
+ const float *m = textureTransform.constData();
- // offsets.w = max mip level
- const QSize texSize = Q3DSImageManager::instance().size(data->lightProbeOverrideTexture);
- float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
- QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
- data->lightProbeOffset->setValue(offsets);
+ // offsets.w = max mip level
+ const QSize texSize = Q3DSImageManager::instance().size(texture);
+ float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
+ QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
+ data->lightProbeOffset->setValue(offsets);
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- data->lightProbeRotation->setValue(rotations);
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ data->lightProbeRotation->setValue(rotations);
+ }, iblOverride, data);
+ data->lightProbeOverrideTexture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSCustomMaterialAttached *data) {
+ Q_UNUSED(texture);
+ data->lightProbeSampler->setValue({});
+ data->lightProbeOffset->setValue({});
+ data->lightProbeRotation->setValue({});
+ }, data);
}
}
@@ -7896,10 +8101,7 @@ void Q3DSSceneManager::updateEffect(Q3DSEffectInstance *eff3DS)
switch (p.meta.type) {
case Q3DS::Texture:
{
- Qt3DRender::QAbstractTexture *tex = createCustomPropertyTexture(p, eff3DS->id());
- p.param->setValue(QVariant::fromValue(tex));
- setTextureInfoUniform(p.texInfoParam, tex);
- p.texFlagParam->setValue(1);
+ createCustomPropertyTexture(p, eff3DS->id());
}
break;
@@ -8397,7 +8599,8 @@ void Q3DSSceneManager::syncScene()
for (Q3DSModelNode *model3DS : needsRebuild)
rebuildModelMaterial(model3DS);
- Q3DSImageManager::instance().finishAsyncLoad();
+ m_engine->finishAsyncLoad(false);
+ Q3DSImageManager::instance().finishAsyncLoad(false);
}
void Q3DSSceneManager::setPendingVisibilities()
@@ -9073,6 +9276,11 @@ void Q3DSFrameUpdater::frameAction(float dt)
// the time includes all subpresentations too)
m_sceneManager->m_profiler->reportTotalParseBuildTime(m_sceneManager->m_engine->totalLoadTimeMsecs());
}
+
+ // At this point do synchronized start of engine animations
+ QTimer::singleShot(0, [this]() {
+ m_sceneManager->engine()->startAnimations();
+ });
}
m_sceneManager->m_profiler->reportBehaviorStats(m_sceneManager->m_engine->behaviorLoadTimeMsecs(),
m_sceneManager->m_engine->behaviorHandles().count());
diff --git a/src/runtime/q3dsscenemanager_p.h b/src/runtime/q3dsscenemanager_p.h
index ce189e5..b9e9450 100644
--- a/src/runtime/q3dsscenemanager_p.h
+++ b/src/runtime/q3dsscenemanager_p.h
@@ -51,6 +51,7 @@
#include <QQueue>
#include <QElapsedTimer>
#include <QMutex>
+#include <QFuture>
QT_BEGIN_NAMESPACE
@@ -74,6 +75,7 @@ class Q3DSConsoleCommands;
class Q3DSScenePicker;
struct Q3DSAnimator;
class Q3DSDataInput;
+struct ParentChildPairs;
namespace Qt3DCore {
class QEntity;
@@ -113,6 +115,9 @@ namespace Qt3DExtras {
class QPlaneMesh;
}
+class ReloadableTexture;
+typedef QSharedPointer<ReloadableTexture> ReloadableTexturePtr;
+
struct Q3DSEyeData
{
Qt3DRender::QCameraSelector *cameraSelector = nullptr;
@@ -403,8 +408,8 @@ public:
} advBlend;
struct IBLProbeData {
- Qt3DRender::QAbstractTexture *lightProbeTexture = nullptr;
- Qt3DRender::QAbstractTexture *lightProbe2Texture = nullptr;
+ ReloadableTexturePtr lightProbeTexture = nullptr;
+ ReloadableTexturePtr lightProbe2Texture = nullptr;
Qt3DRender::QParameter *lightProbeSampler = nullptr;
Qt3DRender::QParameter *lightProbeRotation = nullptr;
@@ -509,6 +514,7 @@ struct Q3DSTextureParameters
QVector<Qt3DRender::QParameter *> parameters() const { return { sampler, offsets, rotations, size }; }
Qt3DRender::QAbstractTexture *texture = nullptr;
+ ReloadableTexturePtr textureSource = nullptr;
QString subPresId;
};
@@ -563,7 +569,7 @@ public:
Q3DSTextureParameters lightmapShadowParams;
// IBL
QMetaObject::Connection updateOffsetConnection;
- Qt3DRender::QAbstractTexture *lightProbeOverrideTexture = nullptr;
+ ReloadableTexturePtr lightProbeOverrideTexture = nullptr;
Qt3DRender::QParameter *lightProbeSampler = nullptr;
Qt3DRender::QParameter *lightProbeRotation = nullptr;
Qt3DRender::QParameter *lightProbeOffset = nullptr;
@@ -576,6 +582,7 @@ struct Q3DSCustomPropertyParameter {
meta(meta_)
{ }
Q3DSCustomPropertyParameter() { }
+ ReloadableTexturePtr texture = nullptr;
Qt3DRender::QParameter *param = nullptr;
QVariant inputValue; // e.g. Texture: inputValue is a string whereas param->value is a QAbstractTexture*
Q3DSMaterial::PropertyElement meta;
@@ -598,7 +605,7 @@ public:
// IBL
QMetaObject::Connection updateOffsetConnection;
- Qt3DRender::QAbstractTexture *lightProbeOverrideTexture = nullptr;
+ ReloadableTexturePtr lightProbeOverrideTexture = nullptr;
Qt3DRender::QParameter *lightProbeSampler = nullptr;
Qt3DRender::QParameter *lightProbeRotation = nullptr;
Qt3DRender::QParameter *lightProbeOffset = nullptr;
@@ -734,7 +741,7 @@ public:
Qt3DRender::QViewport *viewportRight = nullptr;
};
- Q3DSSceneManager();
+ Q3DSSceneManager(ParentChildPairs *pairs = nullptr);
~Q3DSSceneManager();
Scene buildScene(Q3DSUipPresentation *presentation, const SceneBuilderParams &params);
@@ -804,6 +811,8 @@ public:
Q3DSInputManager *inputManager() { return m_inputManager; }
Q3DSTextRenderer *textRenderer() const { return m_textRenderer; }
+ Q3DSEngine *engine() const { return m_engine; }
+ Q3DSUipPresentation *presentation() const { return m_presentation; }
void setEyeDepthTextureEnabled(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData, bool enabled);
void setDepthTextureEnabled(Q3DSLayerNode *layer3DS, bool enabled);
@@ -828,8 +837,9 @@ public:
private:
Q_DISABLE_COPY(Q3DSSceneManager)
+
void updateSubPresentationHosts();
- void loadSubUipPresentation(Q3DSSubPresentation *sp);
+ void finalizeSubpresentationLoading(Q3DSSubPresentation *sp);
void initSubTree(Q3DSGraphObject *subTreeRoot);
void updateStereoRenderTree(Q3DSLayerAttached *layerData);
Q3DSEyeData *buildEye(Q3DSLayerNode *layer3DS, Qt3DRender::QViewport *viewport);
@@ -895,7 +905,7 @@ private:
bool checkImageTransparency(Q3DSImage *image) const;
void prepareTextureParameters(Q3DSTextureParameters &textureParameters, const QString &name, Q3DSImage *image3DS);
QVector<Qt3DRender::QParameter *> prepareDefaultMaterial(Q3DSDefaultMaterial *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS);
- Qt3DRender::QAbstractTexture *createCustomPropertyTexture(const Q3DSCustomPropertyParameter &p, const QByteArray &id);
+ void createCustomPropertyTexture(Q3DSCustomPropertyParameter &p, const QByteArray &id);
QVector<Qt3DRender::QParameter *> prepareCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS);
void setImageTextureFromSubPresentation(Qt3DRender::QParameter *sampler, Q3DSImage *image);
void updateTextureParameters(Q3DSTextureParameters &textureParameters, Q3DSImage *image);
@@ -1019,6 +1029,7 @@ private:
bool m_inSetSubPresentations = false;
bool m_layerCaching = true;
bool m_layerUncachePending = false;
+ bool m_animatorsInitialized = false;
QSet<Q3DSSceneManager *> m_layerCacheDeps;
bool m_hasQmlSubPresAsTextureMap = false;
Q3DSViewportData m_viewportData;
@@ -1029,6 +1040,7 @@ private:
QVector<Qt3DCore::QEntity *> m_compositorEntities;
bool m_compositorEnabled = true;
QVector<ViewportSet> m_viewports;
+ ParentChildPairs *m_pairs = nullptr;
friend class Q3DSFrameUpdater;
friend class Q3DSProfiler;
friend class Q3DSSlidePlayerNg;
diff --git a/src/runtime/q3dsslideplayer.cpp b/src/runtime/q3dsslideplayer.cpp
index 0bf850f..aad3ca1 100644
--- a/src/runtime/q3dsslideplayer.cpp
+++ b/src/runtime/q3dsslideplayer.cpp
@@ -34,6 +34,7 @@
#include <QtCore/qmetaobject.h>
#include "q3dsscenemanager_p.h"
#include "q3dsanimationmanager_p.h"
+#include "q3dsengine_p.h"
#include "q3dslogging_p.h"
#include <Qt3DAnimation/qclipanimator.h>
@@ -518,6 +519,19 @@ void Q3DSSlidePlayer::setSlideDeck(Q3DSSlideDeck *slideDeck)
forAllSlides(slideDeck);
+ m_sceneManager->engine()->loadSlideResources(slideDeck->masterSlide(),
+ m_sceneManager->presentation());
+ for (auto object : slideDeck->masterSlide()->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->currentSlide(),
+ m_sceneManager->presentation());
+
+ }
+ }
setInternalState(PlayerState::Ready);
Q_EMIT slideDeckChanged(m_data.slideDeck);
}
@@ -597,6 +611,17 @@ void Q3DSSlidePlayer::setInternalState(Q3DSSlidePlayer::PlayerState state)
{
m_data.pendingState = state;
Q3DSSlide *currentSlide = m_data.slideDeck->currentSlide();
+ m_sceneManager->engine()->loadSlideResources(currentSlide, m_sceneManager->presentation());
+ for (auto object : currentSlide->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ }
+ }
qCDebug(lcSlidePlayer, "Setting internal state for %s from %s to %s", getSlideId(currentSlide).constData(), getEnumName(m_data.state), getEnumName(m_data.pendingState));
@@ -617,6 +642,17 @@ void Q3DSSlidePlayer::setInternalState(Q3DSSlidePlayer::PlayerState state)
const bool forceUpdate = (!slideChanged && (state == PlayerState::Ready));
if (slideChanged || forceUpdate)
handleCurrentSlideChanged(currentSlide, previousSlide, forceUpdate);
+ if (slideChanged) {
+ currentSlide->setActive(true);
+ m_sceneManager->engine()->loadSlideResources(currentSlide, m_sceneManager->presentation());
+ if (previousSlide) {
+ previousSlide->setActive(false);
+ if (previousSlide->unloadSlide()) {
+ m_sceneManager->engine()->unloadSlideResources(previousSlide,
+ m_sceneManager->presentation());
+ }
+ }
+ }
if (state == PlayerState::Playing) {
const bool restart = (m_mode == PlayerMode::Viewer)
@@ -1099,4 +1135,9 @@ void Q3DSSlidePlayer::objectRemovedFromSlide(Q3DSGraphObject *obj, Q3DSSlide *sl
evaluateDynamicObjectVisibility(obj);
}
+float Q3DSSlidePlayer::playbackRate() const
+{
+ return m_data.playbackRate;
+}
+
QT_END_NAMESPACE
diff --git a/src/runtime/q3dsslideplayer_p.h b/src/runtime/q3dsslideplayer_p.h
index e3f0b82..6178884 100644
--- a/src/runtime/q3dsslideplayer_p.h
+++ b/src/runtime/q3dsslideplayer_p.h
@@ -61,36 +61,38 @@ class Q3DSV_PRIVATE_EXPORT Q3DSSlidePlayer : public Q3DSAbstractSlidePlayer
public:
Q3DSSlidePlayer(Q3DSSceneManager *sceneManager,
Q3DSAbstractSlidePlayer *parent = nullptr);
- ~Q3DSSlidePlayer();
+ ~Q3DSSlidePlayer() override;
- Q3DSSlideDeck *slideDeck() const;
- void advanceFrame(float);
- void sceneReady();
+ Q3DSSlideDeck *slideDeck() const override;
+ void advanceFrame(float) override;
+ void sceneReady() override;
- float duration() const;
- float position() const;
- PlayerState state() const { return m_data.state; }
+ float duration() const override;
+ float position() const override;
+ PlayerState state() const override { return m_data.state; }
- void setMode(PlayerMode mode);
- PlayerMode mode() const { return m_mode; }
+ void setMode(PlayerMode mode) override;
+ PlayerMode mode() const override { return m_mode; }
- void objectAboutToBeAddedToScene(Q3DSGraphObject *obj);
- void objectAboutToBeRemovedFromScene(Q3DSGraphObject *obj);
+ void objectAboutToBeAddedToScene(Q3DSGraphObject *obj) override;
+ void objectAboutToBeRemovedFromScene(Q3DSGraphObject *obj) override;
- void objectAddedToSlide(Q3DSGraphObject *obj, Q3DSSlide *slide);
- void objectRemovedFromSlide(Q3DSGraphObject *obj, Q3DSSlide *slide);
+ void objectAddedToSlide(Q3DSGraphObject *obj, Q3DSSlide *slide) override;
+ void objectRemovedFromSlide(Q3DSGraphObject *obj, Q3DSSlide *slide) override;
+
+ float playbackRate() const override;
public Q_SLOTS:
- void play();
- void stop();
- void pause();
- void seek(float);
- void setSlideDeck(Q3DSSlideDeck *slideDeck);
- void setPlaybackRate(float rate);
- void nextSlide();
- void previousSlide();
- void precedingSlide();
- void reload();
+ void play() override;
+ void stop() override;
+ void pause() override;
+ void seek(float) override;
+ void setSlideDeck(Q3DSSlideDeck *slideDeck) override;
+ void setPlaybackRate(float rate) override;
+ void nextSlide() override;
+ void previousSlide() override;
+ void precedingSlide() override;
+ void reload() override;
private:
Q3DSSlidePlayer(QSharedPointer<Q3DSAnimationManager> animationManager,
diff --git a/src/runtime/q3dssourceinfo_p.h b/src/runtime/q3dssourceinfo_p.h
index 2e153d6..0c0ae01 100644
--- a/src/runtime/q3dssourceinfo_p.h
+++ b/src/runtime/q3dssourceinfo_p.h
@@ -53,13 +53,31 @@ QT_BEGIN_NAMESPACE
class Q3DSV_PRIVATE_EXPORT Q3DSSourceInfo {
public:
- Q3DSSourceInfo() { }
+ Q3DSSourceInfo()
+ {
+ m_variantFilter.reset(new QHash<QStringRef, QVector<QStringRef>>);
+ m_internalVariantList.reset(new QVector<QString>);
+ }
~Q3DSSourceInfo()
{
- for (auto str : qAsConst(m_internalVariantList))
- delete str;
- m_internalVariantList.clear();
+ m_variantFilter.reset();
+ m_internalVariantList.reset();
+ }
+
+ Q3DSSourceInfo(const Q3DSSourceInfo &other)
+ {
+ operator=(other);
+ }
+
+ inline Q3DSSourceInfo &operator=(const Q3DSSourceInfo &other)
+ {
+ m_url = other.m_url;
+ m_fileName = other.m_fileName;
+ m_variantList = other.m_variantList;
+ m_internalVariantList = other.m_internalVariantList;
+ m_variantFilter = other.m_variantFilter;
+ return *this;
}
inline bool operator==(const Q3DSSourceInfo &other)
@@ -97,34 +115,37 @@ public:
// Store the QStringList to be returned from the API
m_variantList = variantList;
- for (auto str : qAsConst(m_internalVariantList))
- delete str;
- m_internalVariantList.clear();
+ // Note that all instances that share the filter lists will get updated
+ m_variantFilter->clear();
+ m_internalVariantList->clear();
// Build a fixed (in mem location) list of the variant strings
- for (auto tag: variantList)
- m_internalVariantList.append(new QString(tag));
+ for (const auto &tag : variantList)
+ m_internalVariantList->append(tag);
// Parse the variantGroup:variant list to map using the fixed list
- m_variantFilter.clear();
- for (auto tag : qAsConst(m_internalVariantList)) {
- QStringRef refTag = QStringRef(tag);
+ const auto &internalList = *m_internalVariantList.data();
+ for (const auto &tag : internalList) {
+ QStringRef refTag = QStringRef(&tag);
int separatorIdx = refTag.indexOf(QLatin1Char(':'));
QStringRef group = refTag.left(separatorIdx);
QStringRef variant = refTag.mid(separatorIdx + 1);
- m_variantFilter[group].append(variant);
+ (*m_variantFilter)[group].append(variant);
}
}
inline const QStringList &variantList() const { return m_variantList; }
- inline const QHash<QStringRef, QVector<QStringRef>> &variantMap() const { return m_variantFilter; }
+ inline const QHash<QStringRef, QVector<QStringRef>> &variantMap() const
+ {
+ return *m_variantFilter.data();
+ }
private:
QUrl m_url;
- QStringList m_variantList;
- QList<QString*> m_internalVariantList;
QString m_fileName;
- QHash<QStringRef, QVector<QStringRef>> m_variantFilter;
+ QStringList m_variantList;
+ QSharedPointer<QVector<QString>> m_internalVariantList;
+ QSharedPointer<QHash<QStringRef, QVector<QStringRef>>> m_variantFilter;
};
QT_END_NAMESPACE
diff --git a/src/runtime/q3dstextrenderer.cpp b/src/runtime/q3dstextrenderer.cpp
index cddca08..443a50c 100644
--- a/src/runtime/q3dstextrenderer.cpp
+++ b/src/runtime/q3dstextrenderer.cpp
@@ -195,6 +195,7 @@ void Q3DSTextRenderer::renderText(QPainter *painter, Q3DSTextNode *text3DS)
{
Q3DSTextRenderer::Font *font = findFont(text3DS->font());
Q_ASSERT(font);
+ int horizontalAlignmentFlag = Qt::AlignLeft;
updateFontInfo(font, text3DS);
@@ -229,9 +230,11 @@ void Q3DSTextRenderer::renderText(QPainter *painter, Q3DSTextNode *text3DS)
qreal tracking = 0.0;
switch (text3DS->horizontalAlignment()) {
case Q3DSTextNode::Center:
+ horizontalAlignmentFlag = Qt::AlignHCenter;
tracking += qreal(text3DS->tracking()) / 2.0;
break;
case Q3DSTextNode::Right:
+ horizontalAlignmentFlag = Qt::AlignRight;
tracking += qreal(text3DS->tracking());
break;
case Q3DSTextNode::Left:
@@ -303,12 +306,12 @@ void Q3DSTextRenderer::renderText(QPainter *painter, Q3DSTextNode *text3DS)
qreal(lineWidths.at(i)), lineHeight);
// shadow is a darker shade of the given font color
painter->setPen(QColor(shadowRgb, shadowRgb, shadowRgb));
- painter->drawText(boundShadow, mapVertAlign(text3DS) | wordWrapFlags | Qt::AlignLeft,
- line, &actualBound);
+ painter->drawText(boundShadow, mapVertAlign(text3DS) | wordWrapFlags
+ | horizontalAlignmentFlag, line, &actualBound);
painter->setPen(Qt::white); // coloring is done in the shader
}
- painter->drawText(bound, mapVertAlign(text3DS) | wordWrapFlags | Qt::AlignLeft, line,
- &actualBound);
+ painter->drawText(bound, mapVertAlign(text3DS) | wordWrapFlags | horizontalAlignmentFlag,
+ line, &actualBound);
nextHeight += lineHeight + qreal(text3DS->leading());
}
}
diff --git a/src/runtime/q3dsuipparser.cpp b/src/runtime/q3dsuipparser.cpp
index 3130aee..ecb79f9 100644
--- a/src/runtime/q3dsuipparser.cpp
+++ b/src/runtime/q3dsuipparser.cpp
@@ -648,6 +648,12 @@ void Q3DSUipParser::parseAddSet(Q3DSSlide *slide, bool isSet, bool isMaster)
ha.value = attr.value().trimmed().toString();
}
}
+ if (ha.value.isEmpty() && ha.type == Q3DS::Vector) {
+ // With all-zero vector values (0,0,0) Studio doesn't currently
+ // store "value" parameter at all into uip. So empty vector
+ // type values are handled as "0 0 0", see QT3DS-3143.
+ ha.value = QStringLiteral("0 0 0");
+ }
action.handlerArgs.append(ha);
}
r->skipCurrentElement();
diff --git a/src/runtime/q3dsuippresentation.cpp b/src/runtime/q3dsuippresentation.cpp
index 0e654c5..5ab3fd6 100644
--- a/src/runtime/q3dsuippresentation.cpp
+++ b/src/runtime/q3dsuippresentation.cpp
@@ -691,6 +691,20 @@ void Q3DSGraphObject::notifyPropertyChanges(int changeFlags, const QSet<QString>
}
}
+void Q3DSGraphObject::listCustomPropertyResources(
+ QSet<QString> &resources, const QMap<QString, Q3DSMaterial::PropertyElement> &meta) const
+{
+ auto properties = dynamicProperties();
+ for (auto it = properties.cbegin(), itEnd = properties.cend(); it != itEnd; ++it) {
+ const QString &propName(it.key());
+ const Q3DSMaterial::PropertyElement &propMeta(meta[propName]);
+ if (propMeta.type == Q3DS::Texture) {
+ const QVariant &propValue(it.value());
+ resources.insert(propValue.toString());
+ }
+ }
+}
+
// Setters return a change object that can be passed straight to
// notifyPropertyChange (via the changelists' intializer list even). When the
// value does not change, the returned change object has isValid()==false, these
@@ -1646,6 +1660,16 @@ Q3DSPropertyChange Q3DSSlide::setPlayThroughValue(const QVariant &v)
return createPropSetter(m_playThroughValue, v, "playthroughto");
}
+void Q3DSSlide::generateResourceSet(Q3DSUipPresentation *presentation)
+{
+ m_resources.clear();
+ QSet<QString> resources;
+ for (const auto *object : qAsConst(m_objects))
+ object->listResources(resources);
+ for (auto res : qAsConst(resources))
+ m_resources << presentation->imageUrl(res);
+}
+
Q3DSImage::Q3DSImage()
: Q3DSGraphObject(Image)
{
@@ -1721,6 +1745,12 @@ void Q3DSImage::resolveReferences(Q3DSUipPresentation &presentation)
}
}
+void Q3DSImage::listResources(QSet<QString> &resources) const
+{
+ if (!m_sourcePath.isEmpty() && !resources.contains(m_sourcePath))
+ resources.insert(m_sourcePath);
+}
+
namespace {
#if 0
bool scanImageForAlpha(const uchar *data, int width, int height, unsigned pixelSizeInBytes, unsigned alphaSizeInBits, bool isAlphaFirst = false)
@@ -3502,7 +3532,8 @@ Q3DSPropertyChange Q3DSModelNode::setCustomMesh(Q3DSGeometry *geom)
{
if (m_customMesh != geom)
delete m_customMesh;
-
+ // Update attributes so that qt3d has up-to-date parameters.
+ Q3DSMeshLoader::loadMesh(*geom, &m_customMeshMapping);
m_customMesh = geom; // takes ownership
// like setMesh but note that the m_mesh_unresolved value does not change
@@ -3936,6 +3967,24 @@ Q3DSGraphObject *Q3DSUipPresentation::getObjectByName(const QString &name) const
return nullptr;
}
+QUrl Q3DSUipPresentation::imageUrl(const QString &sourcePath)
+{
+ QUrl ret;
+ if (d->preferKtx) {
+ QString ktxSource = sourcePath;
+ ktxSource = ktxSource.left(ktxSource.lastIndexOf(QLatin1Char('.')));
+ ktxSource.append(QLatin1String(".ktx"));
+ QFileInfo info(ktxSource);
+ if (info.exists())
+ ret = QUrl::fromLocalFile(ktxSource);
+ else
+ ret = QUrl::fromLocalFile(sourcePath);
+ } else {
+ ret = QUrl::fromLocalFile(sourcePath);
+ }
+ return ret;
+}
+
namespace {
struct ClonedObject {
Q3DSGraphObject *original;
diff --git a/src/runtime/q3dsuippresentation_p.h b/src/runtime/q3dsuippresentation_p.h
index de1be1e..fde8233 100644
--- a/src/runtime/q3dsuippresentation_p.h
+++ b/src/runtime/q3dsuippresentation_p.h
@@ -406,8 +406,14 @@ public:
State state() const { return m_state; }
+ virtual void listResources(QSet<QString> &resources) const
+ {
+ Q_UNUSED(resources);
+ }
protected:
void destroyGraph();
+ void listCustomPropertyResources(QSet<QString> &resources, const QMap<QString,
+ Q3DSMaterial::PropertyElement> &meta) const;
QByteArray m_id;
QString m_name;
@@ -739,6 +745,23 @@ public:
Q3DSPropertyChange setPlayThrough(PlayThrough v);
Q3DSPropertyChange setPlayThroughValue(const QVariant &v);
+ void generateResourceSet(Q3DSUipPresentation *presentation);
+ QSet<QUrl> resourceSet() const { return m_resources; }
+
+ void setUnloadSlide(bool unload)
+ {
+ m_unload = unload;
+ }
+ void setActive(bool active)
+ {
+ m_active = active;
+ // The slide must be explicitly unloaded by the user every time
+ if (active)
+ m_unload = false;
+ }
+ bool unloadSlide() const { return m_unload; }
+ bool active() const { return m_active; }
+
private:
Q_DISABLE_COPY(Q3DSSlide)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -756,6 +779,9 @@ private:
QVector<Q3DSAction> m_actions;
QVector<SlideGraphChangeCallback> m_slideGraphChangeCallbacks; // master only
QVector<SlideObjectChangeCallback> m_slideObjectChangeCallbacks;
+ QSet<QUrl> m_resources;
+ bool m_unload = false;
+ bool m_active = false;
friend class Q3DSUipParser;
friend class Q3DSGraphObject;
@@ -842,6 +868,8 @@ public:
Q3DSPropertyChange setCustomImage(const QImage &v); // alternative setter
QImage customImage() const { return m_customImage; }
+ void listResources(QSet<QString> &resources) const override;
+
private:
Q_DISABLE_COPY(Q3DSImage)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -1956,6 +1984,10 @@ public:
const Q3DSCustomMaterial *material() const { return &m_material; }
+ void listResources(QSet<QString> &resources) const override
+ {
+ listCustomPropertyResources(resources, m_material.properties());
+ }
private:
Q_DISABLE_COPY(Q3DSCustomMaterialInstance)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -2005,6 +2037,11 @@ public:
const Q3DSEffect *effect() const { return &m_effect; }
+ void listResources(QSet<QString> &resources) const override
+ {
+ listCustomPropertyResources(resources, m_effect.properties());
+ }
+
private:
Q_DISABLE_COPY(Q3DSEffectInstance)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -2200,6 +2237,8 @@ public:
void addImplicitPropertyChanges();
QHash<QString, bool> &imageTransparencyHash();
+ QUrl imageUrl(const QString &sourcePath);
+
private:
Q_DISABLE_COPY(Q3DSUipPresentation)
diff --git a/src/runtime/shadergenerator/q3dsshadermanager.cpp b/src/runtime/shadergenerator/q3dsshadermanager.cpp
index b0ca6ce..a9bfcee 100644
--- a/src/runtime/shadergenerator/q3dsshadermanager.cpp
+++ b/src/runtime/shadergenerator/q3dsshadermanager.cpp
@@ -56,6 +56,7 @@ Qt3DRender::QShaderProgram *Q3DSShaderManager::generateShaderProgram(Q3DSDefault
bool hasTransparency,
const Q3DSShaderFeatureSet &featureSet)
{
+ QMutexLocker locker(&m_generatorMutex);
Q3DSSubsetMaterialVertexPipeline pipeline(*m_materialShaderGenerator, *m_shaderProgramGenerator, false);
return m_materialShaderGenerator->generateShader(material, referencedMaterial, pipeline, featureSet, lights, hasTransparency,
QString(QLatin1String("default material %1")).arg(QString::fromLatin1(material.id())));
@@ -68,6 +69,7 @@ Qt3DRender::QShaderProgram *Q3DSShaderManager::generateShaderProgram(Q3DSCustomM
const Q3DSShaderFeatureSet &featureSet,
const QString &shaderName)
{
+ QMutexLocker locker(&m_generatorMutex);
Q3DSCustomMaterialVertexPipeline pipeline(*m_materialShaderGenerator, *m_shaderProgramGenerator, false);
return m_customMaterialShaderGenerator->generateShader(material, referencedMaterial, pipeline, featureSet, lights, hasTransparency, shaderName);
}
diff --git a/src/runtime/shadergenerator/q3dsshadermanager_p.h b/src/runtime/shadergenerator/q3dsshadermanager_p.h
index e38ae69..cd7baaa 100644
--- a/src/runtime/shadergenerator/q3dsshadermanager_p.h
+++ b/src/runtime/shadergenerator/q3dsshadermanager_p.h
@@ -47,6 +47,7 @@
#include "q3dsuippresentation_p.h"
#include <Qt3DRender/QShaderProgram>
#include <QMap>
+#include <QMutex>
QT_BEGIN_NAMESPACE
@@ -121,6 +122,7 @@ private:
QMap<int, Qt3DRender::QShaderProgram *> m_blendOverlayShader;
QMap<int, Qt3DRender::QShaderProgram *> m_blendColorBurnShader;
QMap<int, Qt3DRender::QShaderProgram *> m_blendColorDodgeShader;
+ QMutex m_generatorMutex;
};
QT_END_NAMESPACE
diff --git a/src/runtime/shaders/distancefieldtext_dropshadow.frag b/src/runtime/shaders/distancefieldtext_dropshadow.frag
index da51c5c..94b277c 100644
--- a/src/runtime/shaders/distancefieldtext_dropshadow.frag
+++ b/src/runtime/shaders/distancefieldtext_dropshadow.frag
@@ -15,7 +15,7 @@ void main()
clamp(shadowSampleCoord,
normalizedTextureBounds.xy,
normalizedTextureBounds.zw)).a);
- highp vec4 shadowPixel = shadowColor * shadowAlpha;
+ highp vec4 shadowPixel = color * shadowColor * shadowAlpha;
highp float textAlpha = smoothstep(alphas.x,
alphas.y,
diff --git a/src/runtime/shaders/distancefieldtext_dropshadow_core.frag b/src/runtime/shaders/distancefieldtext_dropshadow_core.frag
index 46a1194..8daa915 100644
--- a/src/runtime/shaders/distancefieldtext_dropshadow_core.frag
+++ b/src/runtime/shaders/distancefieldtext_dropshadow_core.frag
@@ -20,7 +20,7 @@ void main()
clamp(shadowSampleCoord,
normalizedTextureBounds.xy,
normalizedTextureBounds.zw)).r);
- vec4 shadowPixel = shadowColor * shadowAlpha;
+ vec4 shadowPixel = color * shadowColor * shadowAlpha;
float textAlpha = smoothstep(alphas.x,
alphas.y,
diff --git a/src/runtime/slideplayerng/q3dsslideplayerng.cpp b/src/runtime/slideplayerng/q3dsslideplayerng.cpp
index ac24c30..77a6194 100644
--- a/src/runtime/slideplayerng/q3dsslideplayerng.cpp
+++ b/src/runtime/slideplayerng/q3dsslideplayerng.cpp
@@ -34,6 +34,7 @@
#include <QtCore/qmetaobject.h>
#include "q3dsscenemanager_p.h"
#include "q3dsanimationmanagerng_p.h"
+#include "q3dsengine_p.h"
#include "q3dslogging_p.h"
#include "animator/q3dsanimator_p.h"
@@ -390,6 +391,18 @@ void Q3DSSlidePlayerNg::setSlideDeck(Q3DSSlideDeck *slideDeck)
};
forAllSlides(slideDeck);
+ m_sceneManager->engine()->loadSlideResources(slideDeck->masterSlide(),
+ m_sceneManager->presentation());
+ for (auto object : slideDeck->masterSlide()->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->currentSlide(),
+ m_sceneManager->presentation());
+ }
+ }
setInternalState(PlayerState::Ready);
Q_EMIT slideDeckChanged(m_data.slideDeck);
}
@@ -533,8 +546,28 @@ void Q3DSSlidePlayerNg::setInternalState(Q3DSSlidePlayerNg::PlayerState newState
m_sceneManager->queueEvent(ev);
};
+ Q3DSSlide *oldSlide = m_currentSlide;
Q3DSSlide *currentSlide = m_data.slideDeck->currentSlide();
Q_ASSERT(currentSlide);
+ m_sceneManager->engine()->loadSlideResources(currentSlide, m_sceneManager->presentation());
+ if (m_currentSlide && m_currentSlide != currentSlide) {
+ m_currentSlide->setActive(false);
+ if (m_currentSlide->unloadSlide())
+ m_sceneManager->engine()->unloadSlideResources(m_currentSlide,
+ m_sceneManager->presentation());
+ }
+ m_currentSlide = currentSlide;
+ m_currentSlide->setActive(true);
+ for (auto object : currentSlide->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->currentSlide(),
+ m_sceneManager->presentation());
+ }
+ }
Q3DSSlide *activeSlide = (m_type == PlayerType::Scene) ? m_sceneManager->currentSlide()
: m_component->currentSlide();
@@ -612,6 +645,8 @@ void Q3DSSlidePlayerNg::setInternalState(Q3DSSlidePlayerNg::PlayerState newState
m_sceneManager->syncScene();
if (newState != PlayerState::Idle) {
+ if (oldSlide)
+ queueSlideEvent(oldSlide, Q3DSGraphObjectEvents::slideExitEvent());
queueSlideEvent(currentSlide, Q3DSGraphObjectEvents::slideEnterEvent());
Q_EMIT slideChanged(currentSlide);
}
@@ -906,4 +941,9 @@ void Q3DSSlidePlayerNg::objectRemovedFromSlide(Q3DSGraphObject *obj, Q3DSSlide *
Q3DSAnimationManagerNg::removeObject(slide, obj);
}
+float Q3DSSlidePlayerNg::playbackRate() const
+{
+ return m_data.playbackRate;
+}
+
QT_END_NAMESPACE
diff --git a/src/runtime/slideplayerng/q3dsslideplayerng_p.h b/src/runtime/slideplayerng/q3dsslideplayerng_p.h
index cdec113..9ab9773 100644
--- a/src/runtime/slideplayerng/q3dsslideplayerng_p.h
+++ b/src/runtime/slideplayerng/q3dsslideplayerng_p.h
@@ -58,36 +58,38 @@ class Q3DSV_PRIVATE_EXPORT Q3DSSlidePlayerNg : public Q3DSAbstractSlidePlayer
public:
Q3DSSlidePlayerNg(Q3DSSceneManager *sceneManager,
Q3DSAbstractSlidePlayer *parent = nullptr);
- ~Q3DSSlidePlayerNg();
+ ~Q3DSSlidePlayerNg() override;
- Q3DSSlideDeck *slideDeck() const;
- void advanceFrame(float dt);
- void sceneReady();
+ Q3DSSlideDeck *slideDeck() const override;
+ void advanceFrame(float dt) override;
+ void sceneReady() override;
- float duration() const;
- float position() const;
- PlayerState state() const { return m_data.state; }
+ float duration() const override;
+ float position() const override;
+ PlayerState state() const override { return m_data.state; }
- void setMode(PlayerMode mode);
- PlayerMode mode() const { return m_mode; }
+ void setMode(PlayerMode mode) override;
+ PlayerMode mode() const override { return m_mode; }
- void objectAboutToBeAddedToScene(Q3DSGraphObject *obj);
- void objectAboutToBeRemovedFromScene(Q3DSGraphObject *obj);
+ void objectAboutToBeAddedToScene(Q3DSGraphObject *obj) override;
+ void objectAboutToBeRemovedFromScene(Q3DSGraphObject *obj) override;
- void objectAddedToSlide(Q3DSGraphObject *obj, Q3DSSlide *slide);
- void objectRemovedFromSlide(Q3DSGraphObject *obj, Q3DSSlide *slide);
+ void objectAddedToSlide(Q3DSGraphObject *obj, Q3DSSlide *slide) override;
+ void objectRemovedFromSlide(Q3DSGraphObject *obj, Q3DSSlide *slide) override;
+
+ float playbackRate() const override;
public Q_SLOTS:
- void play();
- void stop();
- void pause();
- void seek(float);
- void setSlideDeck(Q3DSSlideDeck *slideDeck);
- void setPlaybackRate(float rate);
- void nextSlide();
- void previousSlide();
- void precedingSlide();
- void reload();
+ void play() override;
+ void stop() override;
+ void pause() override;
+ void seek(float) override;
+ void setSlideDeck(Q3DSSlideDeck *slideDeck) override;
+ void setPlaybackRate(float rate) override;
+ void nextSlide() override;
+ void previousSlide() override;
+ void precedingSlide() override;
+ void reload() override;
private:
Q3DSSlidePlayerNg(Q3DSSceneManager *sceneManager,
@@ -118,6 +120,7 @@ private:
float playbackRate = 1.0f;
} m_data;
+ Q3DSSlide *m_currentSlide = nullptr;
Q3DSSceneManager *m_sceneManager;
Q3DSComponentNode *m_component = nullptr;
PlayerMode m_mode = PlayerMode::Viewer;
diff --git a/tests/auto/slideplayer/tst_q3dsslideplayer.cpp b/tests/auto/slideplayer/tst_q3dsslideplayer.cpp
index e0ee1f3..85e033e 100644
--- a/tests/auto/slideplayer/tst_q3dsslideplayer.cpp
+++ b/tests/auto/slideplayer/tst_q3dsslideplayer.cpp
@@ -216,6 +216,49 @@ void tst_Q3DSSlidePlayer::tst_playModes()
QCOMPARE(slideChangedSpy.count(), 0);
QCOMPARE(player->slideDeck()->currentSlide(), m_stopAtEnd);
+ // STOP AT END (Already at the end, +rate)
+ // The player is now in paused state (StopAtEnd), changing the state to Playing should move it
+ // back into paused state (StopAtEnd).
+ stateChangeSpy.clear();
+ slideChangedSpy.clear();
+
+ QCOMPARE(player->position(), player->duration());
+
+ player->play();
+ QTRY_COMPARE(player->state(), Q3DSSlidePlayer::PlayerState::Playing);
+ QTRY_COMPARE(player->state(), Q3DSSlidePlayer::PlayerState::Paused);
+
+ // paused -> playing -> paused
+ QCOMPARE(stateChangeSpy.count(), 2);
+ QCOMPARE(slideChangedSpy.count(), 0);
+ QCOMPARE(player->position(), player->duration());
+ QCOMPARE(player->slideDeck()->currentSlide(), m_stopAtEnd);
+
+ // STOP AT END (Already at the end(start), -rate)
+ // Seek to the start and set the rate to a negative value, changing the state to Playing should
+ // move it back into paused state (StopAtEnd).
+ stateChangeSpy.clear();
+ slideChangedSpy.clear();
+
+ player->seek(0.0f);
+ player->setPlaybackRate(-0.5f);
+ QCOMPARE(player->playbackRate(), -0.5f);
+ QCOMPARE(player->position(), 0.0f);
+
+ player->play();
+ QTRY_COMPARE(player->state(), Q3DSSlidePlayer::PlayerState::Playing);
+ QTRY_COMPARE(player->state(), Q3DSSlidePlayer::PlayerState::Paused);
+
+ // paused -> playing -> paused
+ QCOMPARE(stateChangeSpy.count(), 2);
+ QCOMPARE(slideChangedSpy.count(), 0);
+ QCOMPARE(player->position(), 0.0f);
+ QCOMPARE(player->slideDeck()->currentSlide(), m_stopAtEnd);
+
+ // Reset the playback rate!!!
+ player->setPlaybackRate(1.0f);
+ QCOMPARE(player->playbackRate(), 1.0f);
+
// PLAY TO PREVIOUS
player->stop();
QTRY_COMPARE(player->state(), Q3DSSlidePlayer::PlayerState::Stopped);
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/dynamicloading.uia b/tests/manual/dynamicloadingapp/dynamicloading/dynamicloading.uia
new file mode 100644
index 0000000..67a2a18
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/dynamicloading.uia
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<application xmlns="http://qt.io/qt3dstudio/uia">
+ <assets initial="dynamicloading">
+ <presentation id="dynamicloading" src="presentations/dynamicloading.uip"/>
+ </assets>
+ <statemachine ref="#logic">
+ <visual-states>
+ <state ref="Initial">
+ <enter>
+ <goto-slide rel="next" element="main:Scene"/>
+ </enter>
+ </state>
+ </visual-states>
+ </statemachine>
+</application>
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/maps/Abstract_001_COLOR.jpg b/tests/manual/dynamicloadingapp/dynamicloading/maps/Abstract_001_COLOR.jpg
new file mode 100644
index 0000000..7fa51e6
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/maps/Abstract_001_COLOR.jpg
Binary files differ
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/maps/Blue_Marble_002_COLOR.jpg b/tests/manual/dynamicloadingapp/dynamicloading/maps/Blue_Marble_002_COLOR.jpg
new file mode 100644
index 0000000..bd84795
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/maps/Blue_Marble_002_COLOR.jpg
Binary files differ
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/maps/Ice_002_COLOR.jpg b/tests/manual/dynamicloadingapp/dynamicloading/maps/Ice_002_COLOR.jpg
new file mode 100644
index 0000000..d3d3106
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/maps/Ice_002_COLOR.jpg
Binary files differ
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/maps/Moon_001_COLOR.jpg b/tests/manual/dynamicloadingapp/dynamicloading/maps/Moon_001_COLOR.jpg
new file mode 100644
index 0000000..9ddd871
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/maps/Moon_001_COLOR.jpg
Binary files differ
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/materials/Material.materialdef b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material.materialdef
new file mode 100644
index 0000000..9c312b5
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material.materialdef
@@ -0,0 +1,44 @@
+<MaterialData version="1.0">
+ <Property name="shaderlighting">Pixel</Property>
+ <Property name="blendmode">Normal</Property>
+ <Property name="diffuse">1 1 1</Property>
+ <Property name="diffusemap"><![CDATA[./maps/Abstract_001_COLOR.jpg]]></Property>
+ <Property name="specularamount">0</Property>
+ <Property name="specularroughness">0</Property>
+ <Property name="opacity">100</Property>
+ <Property name="emissivecolor">1 1 1</Property>
+ <Property name="emissivepower">0</Property>
+ <Property name="bumpamount">0.5</Property>
+ <Property name="displaceamount">20</Property>
+ <Property name="translucentfalloff">1</Property>
+ <Property name="diffuselightwrap">0</Property>
+ <Property name="specularmodel">Default</Property>
+ <Property name="speculartint">1 1 1</Property>
+ <Property name="ior">1.5</Property>
+ <Property name="fresnelPower">0</Property>
+ <Property name="vertexcolors">False</Property>
+ <Property name="sourcepath"></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Material</Property>
+ <Property name="name"><![CDATA[materials/Material]]></Property>
+ <Property name="path"><![CDATA[C:/dev/dynamicloadingapp/dynamicloading/materials/Material.materialdef]]></Property>
+ <TextureData name="diffusemap">
+ <Property name="scaleu">1</Property>
+ <Property name="scalev">1</Property>
+ <Property name="mappingmode">UV Mapping</Property>
+ <Property name="tilingmodehorz">No Tiling</Property>
+ <Property name="tilingmodevert">No Tiling</Property>
+ <Property name="rotationuv">0</Property>
+ <Property name="positionu">0</Property>
+ <Property name="positionv">0</Property>
+ <Property name="pivotu">0</Property>
+ <Property name="pivotv">0</Property>
+ <Property name="subpresentation"></Property>
+ <Property name="sourcepath"><![CDATA[./maps/Abstract_001_COLOR.jpg]]></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Image</Property>
+ <Property name="name"><![CDATA[Image]]></Property>
+ </TextureData>
+</MaterialData> \ No newline at end of file
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/materials/Material2.materialdef b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material2.materialdef
new file mode 100644
index 0000000..1e46acf
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material2.materialdef
@@ -0,0 +1,44 @@
+<MaterialData version="1.0">
+ <Property name="shaderlighting">Pixel</Property>
+ <Property name="blendmode">Normal</Property>
+ <Property name="diffuse">1 1 1</Property>
+ <Property name="diffusemap"><![CDATA[./maps/Blue_Marble_002_COLOR.jpg]]></Property>
+ <Property name="specularamount">0</Property>
+ <Property name="specularroughness">0</Property>
+ <Property name="opacity">100</Property>
+ <Property name="emissivecolor">1 1 1</Property>
+ <Property name="emissivepower">0</Property>
+ <Property name="bumpamount">0.5</Property>
+ <Property name="displaceamount">20</Property>
+ <Property name="translucentfalloff">1</Property>
+ <Property name="diffuselightwrap">0</Property>
+ <Property name="specularmodel">Default</Property>
+ <Property name="speculartint">1 1 1</Property>
+ <Property name="ior">1.5</Property>
+ <Property name="fresnelPower">0</Property>
+ <Property name="vertexcolors">False</Property>
+ <Property name="sourcepath"></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Material</Property>
+ <Property name="name"><![CDATA[materials/Material2]]></Property>
+ <Property name="path"><![CDATA[C:/dev/dynamicloadingapp/dynamicloading/materials/Material2.materialdef]]></Property>
+ <TextureData name="diffusemap">
+ <Property name="scaleu">1</Property>
+ <Property name="scalev">1</Property>
+ <Property name="mappingmode">UV Mapping</Property>
+ <Property name="tilingmodehorz">No Tiling</Property>
+ <Property name="tilingmodevert">No Tiling</Property>
+ <Property name="rotationuv">0</Property>
+ <Property name="positionu">0</Property>
+ <Property name="positionv">0</Property>
+ <Property name="pivotu">0</Property>
+ <Property name="pivotv">0</Property>
+ <Property name="subpresentation"></Property>
+ <Property name="sourcepath"><![CDATA[./maps/Blue_Marble_002_COLOR.jpg]]></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Image</Property>
+ <Property name="name"><![CDATA[Image]]></Property>
+ </TextureData>
+</MaterialData> \ No newline at end of file
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/materials/Material3.materialdef b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material3.materialdef
new file mode 100644
index 0000000..93e8ef9
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material3.materialdef
@@ -0,0 +1,44 @@
+<MaterialData version="1.0">
+ <Property name="shaderlighting">Pixel</Property>
+ <Property name="blendmode">Normal</Property>
+ <Property name="diffuse">1 1 1</Property>
+ <Property name="diffusemap"><![CDATA[./maps/Ice_002_COLOR.jpg]]></Property>
+ <Property name="specularamount">0</Property>
+ <Property name="specularroughness">0</Property>
+ <Property name="opacity">100</Property>
+ <Property name="emissivecolor">1 1 1</Property>
+ <Property name="emissivepower">0</Property>
+ <Property name="bumpamount">0.5</Property>
+ <Property name="displaceamount">20</Property>
+ <Property name="translucentfalloff">1</Property>
+ <Property name="diffuselightwrap">0</Property>
+ <Property name="specularmodel">Default</Property>
+ <Property name="speculartint">1 1 1</Property>
+ <Property name="ior">1.5</Property>
+ <Property name="fresnelPower">0</Property>
+ <Property name="vertexcolors">False</Property>
+ <Property name="sourcepath"></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Material</Property>
+ <Property name="name"><![CDATA[materials/Material3]]></Property>
+ <Property name="path"><![CDATA[C:/dev/dynamicloadingapp/dynamicloading/materials/Material3.materialdef]]></Property>
+ <TextureData name="diffusemap">
+ <Property name="scaleu">1</Property>
+ <Property name="scalev">1</Property>
+ <Property name="mappingmode">UV Mapping</Property>
+ <Property name="tilingmodehorz">No Tiling</Property>
+ <Property name="tilingmodevert">No Tiling</Property>
+ <Property name="rotationuv">0</Property>
+ <Property name="positionu">0</Property>
+ <Property name="positionv">0</Property>
+ <Property name="pivotu">0</Property>
+ <Property name="pivotv">0</Property>
+ <Property name="subpresentation"></Property>
+ <Property name="sourcepath"><![CDATA[./maps/Ice_002_COLOR.jpg]]></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Image</Property>
+ <Property name="name"><![CDATA[Image]]></Property>
+ </TextureData>
+</MaterialData> \ No newline at end of file
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/materials/Material4.materialdef b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material4.materialdef
new file mode 100644
index 0000000..be318da
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/materials/Material4.materialdef
@@ -0,0 +1,44 @@
+<MaterialData version="1.0">
+ <Property name="shaderlighting">Pixel</Property>
+ <Property name="blendmode">Normal</Property>
+ <Property name="diffuse">1 1 1</Property>
+ <Property name="diffusemap"><![CDATA[./maps/Moon_001_COLOR.jpg]]></Property>
+ <Property name="specularamount">0</Property>
+ <Property name="specularroughness">0</Property>
+ <Property name="opacity">100</Property>
+ <Property name="emissivecolor">1 1 1</Property>
+ <Property name="emissivepower">0</Property>
+ <Property name="bumpamount">0.5</Property>
+ <Property name="displaceamount">20</Property>
+ <Property name="translucentfalloff">1</Property>
+ <Property name="diffuselightwrap">0</Property>
+ <Property name="specularmodel">Default</Property>
+ <Property name="speculartint">1 1 1</Property>
+ <Property name="ior">1.5</Property>
+ <Property name="fresnelPower">0</Property>
+ <Property name="vertexcolors">False</Property>
+ <Property name="sourcepath"></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Material</Property>
+ <Property name="name"><![CDATA[materials/Material4]]></Property>
+ <Property name="path"><![CDATA[C:/dev/dynamicloadingapp/dynamicloading/materials/Material4.materialdef]]></Property>
+ <TextureData name="diffusemap">
+ <Property name="scaleu">1</Property>
+ <Property name="scalev">1</Property>
+ <Property name="mappingmode">UV Mapping</Property>
+ <Property name="tilingmodehorz">No Tiling</Property>
+ <Property name="tilingmodevert">No Tiling</Property>
+ <Property name="rotationuv">0</Property>
+ <Property name="positionu">0</Property>
+ <Property name="positionv">0</Property>
+ <Property name="pivotu">0</Property>
+ <Property name="pivotv">0</Property>
+ <Property name="subpresentation"></Property>
+ <Property name="sourcepath"><![CDATA[./maps/Moon_001_COLOR.jpg]]></Property>
+ <Property name="importid"></Property>
+ <Property name="importfile"></Property>
+ <Property name="type">Image</Property>
+ <Property name="name"><![CDATA[Image]]></Property>
+ </TextureData>
+</MaterialData> \ No newline at end of file
diff --git a/tests/manual/dynamicloadingapp/dynamicloading/presentations/dynamicloading.uip b/tests/manual/dynamicloadingapp/dynamicloading/presentations/dynamicloading.uip
new file mode 100644
index 0000000..1f9e93b
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloading/presentations/dynamicloading.uip
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<UIP version="5" >
+ <Project >
+ <ProjectSettings author="" company="" presentationWidth="800" presentationHeight="600" maintainAspect="False" preferKtx="False" >
+ <CustomColors count="16" >#ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff #ffffff</CustomColors>
+ </ProjectSettings>
+ <Graph >
+ <Scene id="Scene" >
+ <Layer id="Layer" variants="" >
+ <Camera id="Camera" />
+ <Light id="Light" />
+ <Model id="Rectangle" >
+ <ReferencedMaterial id="Material" name="Default" />
+ </Model>
+ <Model id="Rectangle2" >
+ <ReferencedMaterial id="Material2" name="Default" />
+ </Model>
+ <Model id="Rectangle3" >
+ <ReferencedMaterial id="Material3" name="Default" />
+ </Model>
+ <Model id="Rectangle4" >
+ <ReferencedMaterial id="Material4" name="Default" />
+ </Model>
+ </Layer>
+ <Material id="__Container" >
+ <Material id="materials/Material" >
+ <Image id="materials/Material_diffusemap" />
+ </Material>
+ <Material id="materials/Material2" >
+ <Image id="materials/Material2_diffusemap" />
+ </Material>
+ <Material id="materials/Material3" >
+ <Image id="materials/Material3_diffusemap" />
+ </Material>
+ <Material id="materials/Material4" >
+ <Image id="materials/Material4_diffusemap" />
+ </Material>
+ </Material>
+ </Scene>
+ </Graph>
+ <Logic >
+ <State name="Master Slide" component="#Scene" >
+ <Add ref="#Layer" />
+ <Add ref="#Camera" />
+ <Add ref="#Light" />
+ <Add ref="#__Container" name="__Container" />
+ <Add ref="#materials/Material" name="materials/Material" diffusemap="#materials/Material_diffusemap" importid="" />
+ <Add ref="#materials/Material2" name="materials/Material2" diffusemap="#materials/Material2_diffusemap" importid="" />
+ <Add ref="#materials/Material3" name="materials/Material3" diffusemap="#materials/Material3_diffusemap" importid="" />
+ <Add ref="#materials/Material4" name="materials/Material4" diffusemap="#materials/Material4_diffusemap" importid="" />
+ <State id="Scene-Slide1" name="Slide1" playmode="Play Through To..." >
+ <Set ref="#Layer" endtime="2000" />
+ <Set ref="#Camera" endtime="2000" />
+ <Set ref="#Light" endtime="2000" />
+ <Add ref="#Rectangle" name="Rectangle" endtime="2000" scale="6.11742 5.19718 1" sourcepath="#Rectangle" />
+ <Add ref="#Material" name="Material" endtime="2000" referencedmaterial="#materials/Material" sourcepath="../materials/Material.materialdef" />
+ <Add ref="#materials/Material_diffusemap" importid="" sourcepath="../maps/Abstract_001_COLOR.jpg" subpresentation="" />
+ </State>
+ <State id="Scene-Slide2" name="Slide2" playmode="Play Through To..." playthroughto="Next" >
+ <Set ref="#Layer" endtime="2000" />
+ <Set ref="#Camera" endtime="5000" />
+ <Set ref="#Light" endtime="2000" />
+ <Add ref="#Rectangle2" name="Rectangle2" endtime="2000" scale="6.11742 5.19718 1" sourcepath="#Rectangle" />
+ <Add ref="#Material2" name="Material2" endtime="2000" referencedmaterial="#materials/Material2" sourcepath="../materials/Material2.materialdef" />
+ <Add ref="#materials/Material2_diffusemap" importid="" sourcepath="../maps/Blue_Marble_002_COLOR.jpg" subpresentation="" />
+ </State>
+ <State id="Scene-Slide3" name="Slide3" playmode="Play Through To..." playthroughto="Next" >
+ <Set ref="#Layer" endtime="2000" />
+ <Set ref="#Camera" endtime="2000" />
+ <Set ref="#Light" endtime="2000" />
+ <Add ref="#Rectangle3" name="Rectangle3" endtime="2000" scale="6.11742 5.19718 1" sourcepath="#Rectangle" />
+ <Add ref="#Material3" name="Material3" endtime="2000" referencedmaterial="#materials/Material3" sourcepath="../materials/Material3.materialdef" />
+ <Add ref="#materials/Material3_diffusemap" importid="" sourcepath="../maps/Ice_002_COLOR.jpg" subpresentation="" />
+ </State>
+ <State id="Scene-Slide4" name="Slide4" playmode="Play Through To..." playthroughto="#Scene-Slide1" >
+ <Set ref="#Layer" endtime="2000" />
+ <Set ref="#Camera" endtime="2000" />
+ <Set ref="#Light" endtime="2000" />
+ <Add ref="#Rectangle4" name="Rectangle4" endtime="2000" scale="6.11742 5.19718 1" sourcepath="#Rectangle" />
+ <Add ref="#Material4" name="Material4" endtime="2000" referencedmaterial="#materials/Material4" sourcepath="../materials/Material4.materialdef" />
+ <Add ref="#materials/Material4_diffusemap" importid="" sourcepath="../maps/Moon_001_COLOR.jpg" subpresentation="" />
+ </State>
+ </State>
+ </Logic>
+ </Project>
+</UIP>
diff --git a/tests/manual/dynamicloadingapp/dynamicloadingapp.pro b/tests/manual/dynamicloadingapp/dynamicloadingapp.pro
new file mode 100644
index 0000000..6c114af
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/dynamicloadingapp.pro
@@ -0,0 +1,29 @@
+QT += quick widgets qml 3dstudioruntime2
+CONFIG += c++11
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Refer to the documentation for the
+# deprecated API to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += qml.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+# Additional import path used to resolve QML modules just for Qt Quick Designer
+QML_DESIGNER_IMPORT_PATH =
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
diff --git a/tests/manual/dynamicloadingapp/main.cpp b/tests/manual/dynamicloadingapp/main.cpp
new file mode 100644
index 0000000..713595f
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets/QApplication>
+#include <QtQuick/QQuickView>
+#include <q3dsruntimeglobal.h>
+#include "q3dsdatainput.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QSurfaceFormat::setDefaultFormat(Q3DS::surfaceFormat());
+ QQuickView viewer;
+ viewer.setSource(QUrl("qrc:/main.qml"));
+
+ viewer.setTitle(QStringLiteral("Qt 3D Studio Example"));
+ viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+ viewer.resize(1280, 720);
+ viewer.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/dynamicloadingapp/main.qml b/tests/manual/dynamicloadingapp/main.qml
new file mode 100644
index 0000000..0e056a5
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/main.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtStudio3D 2.3
+
+Item {
+ id: mainview
+ width: 1280
+ height: 768
+ visible: true
+
+ Studio3D {
+ id: studio3D
+ anchors.fill: parent
+ height: 900
+ width: 1280
+
+ Presentation {
+ property string slide1 : "Slide1"
+ property string slide2 : "Slide2"
+ property string slide3 : "Slide3"
+ property string slide4 : "Slide4"
+ id: presentation
+ profilingEnabled: true
+ source: "qrc:/dynamicloading/dynamicloading.uia"
+
+ onSlideEntered: {
+ var slideName = name
+ var slideIndex = index
+ var slidePath = elementPath
+
+ console.log("Entered: " + name)
+
+ if (slideName === "Slide1")
+ preloadSlide(slide2)
+ else if (slideName === "Slide2")
+ preloadSlide(slide3)
+ else if (slideName === "Slide3")
+ preloadSlide(slide4)
+ else if (slideName === "Slide4")
+ preloadSlide(slide1)
+ }
+
+ onSlideExited: {
+ console.log("Exited: " + name)
+ unloadSlide(name)
+ }
+ }
+ }
+}
diff --git a/tests/manual/dynamicloadingapp/qml.qrc b/tests/manual/dynamicloadingapp/qml.qrc
new file mode 100644
index 0000000..7fc37fa
--- /dev/null
+++ b/tests/manual/dynamicloadingapp/qml.qrc
@@ -0,0 +1,16 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>dynamicloading/maps/Abstract_001_COLOR.jpg</file>
+ <file>dynamicloading/maps/Blue_Marble_002_COLOR.jpg</file>
+ <file>dynamicloading/maps/Ice_002_COLOR.jpg</file>
+ <file>dynamicloading/maps/Moon_001_COLOR.jpg</file>
+ <file>dynamicloading/materials/Material.materialdef</file>
+ <file>dynamicloading/materials/Material2.materialdef</file>
+ <file>dynamicloading/materials/Material3.materialdef</file>
+ <file>dynamicloading/materials/Material4.materialdef</file>
+ <file>dynamicloading/presentations/dynamicloading.uip</file>
+ <file>dynamicloading/dynamicloading.uia</file>
+ <file>main.cpp</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 234578b..36adbb5 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -9,5 +9,6 @@ qtHaveModule(quick) {
SUBDIRS += \
qml3dapi \
pureqml3d \
- manylayersinquick
+ manylayersinquick \
+ dynamicloadingapp
}
diff --git a/tools/q3dsviewer/main.cpp b/tools/q3dsviewer/main.cpp
index 7f341f9..0e8852a 100644
--- a/tools/q3dsviewer/main.cpp
+++ b/tools/q3dsviewer/main.cpp
@@ -48,13 +48,16 @@ QT_BEGIN_NAMESPACE
class Q3DStudioMainWindow;
QT_END_NAMESPACE
+#define STRINGIFY(x) STRINGIFY2(x)
+#define STRINGIFY2(x) QStringLiteral(#x)
+
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setOrganizationName(QStringLiteral("The Qt Company"));
QCoreApplication::setOrganizationDomain(QStringLiteral("qt.io"));
QCoreApplication::setApplicationName(QStringLiteral("Qt 3D Viewer"));
- QCoreApplication::setApplicationVersion(QStringLiteral("2.2"));
+ QCoreApplication::setApplicationVersion(STRINGIFY(STUDIO_VERSION));
#ifdef Q3DSVIEWER_WIDGETS
QApplication app(argc, argv);
diff --git a/tools/q3dsviewer/q3dsviewer.pro b/tools/q3dsviewer/q3dsviewer.pro
index d4ffae0..eff030b 100644
--- a/tools/q3dsviewer/q3dsviewer.pro
+++ b/tools/q3dsviewer/q3dsviewer.pro
@@ -31,3 +31,5 @@ CONFIG += app_bundle
RESOURCES += \
resources.qrc
+DEFINES += STUDIO_VERSION=$$MODULE_VERSION
+