summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2018-07-11 14:03:42 +0200
committerAndy Nichols <andy.nichols@qt.io>2018-07-16 08:16:13 +0000
commitf548947c3780285d103f9a4a947376596b8e41d1 (patch)
tree8ba20c10f8012786a832c4069e621f130b84f777
parent39b8fbca67348b2c078b4e52b432e8979cf383ef (diff)
Enablers for adding and removing layers to the scene at run time
Adding, removing and reordering layers is almost in place now. Some things are still missing when it comes to cameras since the mvp in the vertex shaders for layers introduced on-the-fly is incorrect. Extend also setparent() in the console to allow inserting new subtrees at arbitrary places. Q3DSGraphObject already has the necessary APIs (insertChildNodeAfter and friends). Therefore, doing object(newlayer, newlayer, Layer, null, Slide1) object(newlayer_cam, newlayer_cam, Camera, newlayer, Slide1) ... setparent(newlayer, Scene) Adds the new layer after all existing layers (and thus to the bottom of the stack since the 3D Studio object tree (and the editor's visualization of the tree) assumes front-to-back ordering for the layers which is weird but that's what it is). To instead add to the top (i.e. before the existing Layer children of Scene), we can now do: setparent(newlayer, Scene, null) This also matches what clicking the "Add new layer" button in the editor would do. To implement moving layers around (to change their ordering), one can now simply do setparent(newlayer, Scene, SomeOtherLayer) (or the equivalent using the Q3DSGraphObject APIs) in order to move newlayer after SomeOtherLayer. Change-Id: I9c832e4299cb7398d97a799dce5eaa3cd5518a77 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
-rw-r--r--src/runtime/q3dsconsolecommands.cpp25
-rw-r--r--src/runtime/q3dsscenemanager.cpp200
-rw-r--r--src/runtime/q3dsscenemanager_p.h9
-rw-r--r--src/runtime/q3dsuippresentation.cpp8
-rw-r--r--src/runtime/q3dsuippresentation_p.h1
-rw-r--r--tests/scripts/newlayer.drgscr17
-rw-r--r--tests/scripts/newlayer_direct.drgscr13
7 files changed, 205 insertions, 68 deletions
diff --git a/src/runtime/q3dsconsolecommands.cpp b/src/runtime/q3dsconsolecommands.cpp
index 1e65c5b..49fdbb3 100644
--- a/src/runtime/q3dsconsolecommands.cpp
+++ b/src/runtime/q3dsconsolecommands.cpp
@@ -148,7 +148,7 @@ void Q3DSConsoleCommands::setupConsole(Q3DSConsole *console)
"\n"
"object(id, name, type, parentObj, slide) - Creates a new object. (type == Model, DefaultMaterial, ...; parent and slide may be null) [R]\n"
"primitive(id, name, source, parentObj, slide) - Shortcut to add a model with a default material. (source == #Cube, #Cone, etc.) [R]\n"
- "setparent(obj, parentObj) - Sets the parent (may be null). [R]\n"
+ "setparent(obj, parentObj, [insertAfterObj]) - Sets the parent (may be null). Appends to child list, unless insertAfterObj is set. [R]\n"
"kill(obj) - Removes a node from the scene graph (and from the slides' object list). [R]\n"
"objslideadd(obj, slide) - Associates the object with the given slide. [R]\n"
"objslideremove(obj, slide) - Removes the object from the slide's objject list. [R]\n"
@@ -442,14 +442,29 @@ void Q3DSConsoleCommands::setupConsole(Q3DSConsole *console)
if (splitArgs.count() >= 2) {
Q3DSGraphObject *obj = resolveObj(splitArgs[0]);
Q3DSGraphObject *parent = resolveObj(splitArgs[1]);
+ Q3DSGraphObject *insertAfterObj = nullptr;
+ bool insertAfterRequested = false;
+ if (splitArgs.count() >= 3) {
+ insertAfterRequested = true;
+ insertAfterObj = resolveObj(splitArgs[2]); // can be null
+ }
if (obj) {
if (parent) {
- if (obj->parent() != parent) {
- if (obj->parent())
- obj->parent()->removeChildNode(obj);
+ if (obj->parent())
+ obj->parent()->removeChildNode(obj);
+ // A typical setParent operation only allows appending
+ // to the children list. This is often too restrictive,
+ // so add a way to insert after a specified child
+ // (which may be null, when inserting as first child).
+ if (!insertAfterRequested) {
parent->appendChildNode(obj);
- m_console->addMessageFmt(responseColor, "Reparented");
+ } else {
+ if (insertAfterObj)
+ parent->insertChildNodeAfter(obj, insertAfterObj);
+ else
+ parent->prependChildNode(obj);
}
+ m_console->addMessageFmt(responseColor, "Reparented");
} else {
if (obj->parent()) {
obj->parent()->removeChildNode(obj);
diff --git a/src/runtime/q3dsscenemanager.cpp b/src/runtime/q3dsscenemanager.cpp
index 9c0a730..127a25d 100644
--- a/src/runtime/q3dsscenemanager.cpp
+++ b/src/runtime/q3dsscenemanager.cpp
@@ -468,6 +468,11 @@ Q3DSSceneManager::~Q3DSSceneManager()
delete m_frameUpdater;
delete m_profiler;
delete m_inputManager;
+
+ if (m_scene && m_sceneChangeObserverIndex >= 0)
+ m_scene->removeSceneChangeObserver(m_sceneChangeObserverIndex);
+ if (m_masterSlide && m_slideGraphChangeObserverIndex >= 0)
+ m_masterSlide->removeSlideGraphChangeObserver(m_slideGraphChangeObserverIndex);
}
void Q3DSSceneManager::updateSizes(const QSize &size, qreal dpr, const QRect &viewport, bool forceSynchronous)
@@ -754,15 +759,12 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen
m_fsQuadTag = new Qt3DRender::QLayer(m_rootEntity);
m_fsQuadTag->setObjectName(QLatin1String("Fullscreen quad pass"));
- // Prepare image objects. This must be done up-front.
- m_presentation->forAllImages([this](Q3DSImage *image) { initImage(image); });
-
// Build the (offscreen) Qt3D scene
- Qt3DRender::QFrameGraphNode *layerContainerFg = new Qt3DRender::QFrameGraphNode(frameGraphRoot);
- new Qt3DRender::QNoDraw(layerContainerFg); // in case there are no layers at all
+ 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, layerContainerFg, m_outputPixelSize);
+ buildLayer(layer3DS, m_layerContainerFg, m_outputPixelSize);
else
buildSubPresentationLayer(layer3DS, m_outputPixelSize);
});
@@ -833,12 +835,12 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen
setPendingVisibilities();
// Listen to future changes to the scene graph.
- m_scene->addSceneChangeObserver(std::bind(&Q3DSSceneManager::handleSceneChange,
- this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ m_sceneChangeObserverIndex = m_scene->addSceneChangeObserver(
+ std::bind(&Q3DSSceneManager::handleSceneChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
// And the "slide graph" (more like slide list).
- m_masterSlide->addSlideGraphChangeObserver(std::bind(&Q3DSSceneManager::handleSlideGraphChange,
- this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ m_slideGraphChangeObserverIndex = m_masterSlide->addSlideGraphChangeObserver(
+ std::bind(&Q3DSSceneManager::handleSlideGraphChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
// And for events too.
m_scene->addEventHandler(QString(), std::bind(&Q3DSSceneManager::handleEvent, this, std::placeholders::_1));
@@ -1189,6 +1191,12 @@ void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS,
layer3DS->setAttached(layerData);
+ // Prepare image objects. This must be done up-front.
+ Q3DSUipPresentation::forAllObjectsInSubTree(layer3DS, [this](Q3DSGraphObject *obj) {
+ if (obj->type() == Q3DSGraphObject::Image)
+ initImage(static_cast<Q3DSImage *>(obj));
+ });
+
// Now add the scene contents.
Q3DSGraphObject *obj = layer3DS->firstChild();
m_componentNodeStack.clear();
@@ -3281,6 +3289,14 @@ static void setLayerBlending(Qt3DRender::QBlendEquation *blendFunc,
void Q3DSSceneManager::buildLayerQuadEntity(Q3DSLayerNode *layer3DS, Qt3DCore::QEntity *parentEntity,
Qt3DRender::QLayer *tag, BuildLayerQuadFlags flags, int layerDepth)
{
+ // Generates the layer's compositorEntity and the components on it.
+
+ // Watch out for parenting: this entity will get destroyed and then
+ // recreated eventually, therefore it and its components must not take
+ // ownership of longer living objects.
+
+ qCDebug(lcScene, "Generating compositor quad for %s with logical depth %d", layer3DS->id().constData(), layerDepth);
+
Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
Q_ASSERT(data);
Qt3DCore::QEntity *layerQuadEntity = new Qt3DCore::QEntity;
@@ -3340,6 +3356,10 @@ void Q3DSSceneManager::buildLayerQuadEntity(Q3DSLayerNode *layer3DS, Qt3DCore::Q
if (!flags.testFlag(LayerQuadCustomShader)) {
updateLayerCompositorProgram(layer3DS);
+
+ if (!data->compositorSourceParam->parent())
+ data->compositorSourceParam->setParent(data->layerSceneRootEntity);
+
renderPass->addParameter(data->compositorSourceParam);
}
@@ -3406,10 +3426,11 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
// MatteRoot - NoDraw
// - [Clear - NoDraw]
// Viewport - CameraSelector - [Scissor] - ClearBuffers - NoDraw
- // - LayerFilter for layer quad entity 0
- // - LayerFilter for layer quad entity 1
- // ...
- // With standard blend modes only this is simplified to a single LayerFilter and QLayer tag.
+ // - "m_compositorRoot" (destroyed and recreated on-the-fly when needed)
+ // - LayerFilter for layer quad entity 0
+ // - LayerFilter for layer quad entity 1
+ // ...
+ // With standard blend modes only the list is simplified to a single LayerFilter and QLayer tag.
// If Matte is enabled in the ViewerSettings, then we need clear with the matte color first
// then later restrict the scene clear color to the vieport rect with a scissor test
@@ -3454,6 +3475,29 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
new Qt3DRender::QNoDraw(clearBuffers);
+ m_compositorFgContainer = cameraSelector;
+ m_compositorParentEntity = parentEntity;
+ rebuildCompositorLayerChain();
+}
+
+void Q3DSSceneManager::rebuildCompositorLayerChain()
+{
+ // The order of layers in the object tree is front-to-back which is somewhat
+ // unintuitive. Anyways, while it might seem that the Z value in the quad's
+ // transform (based on a unique layerDepth) is sufficient to ensure the
+ // correct ordering during composition (and so a full rebuild of the frame
+ // and scene graph for the compositor quads is not necessary after the
+ // initial buildCompositor() run), that isn't true for advanced blend mode
+ // emulation because that depends heavily on back-to-front order in the
+ // framegraph. Therefore we'll keep rebuilding the layer quads and
+ // corresponding framegraph entries whenever the list of layers change.
+
+ delete m_compositorRoot;
+ qDeleteAll(m_compositorEntities);
+ m_compositorEntities.clear();
+
+ m_compositorRoot = new Qt3DRender::QFrameGraphNode(m_compositorFgContainer);
+
QVarLengthArray<Q3DSLayerNode *, 16> layers;
Q3DSUipPresentation::forAllLayers(m_scene, [&layers](Q3DSLayerNode *layer3DS) {
layers.append(layer3DS);
@@ -3478,10 +3522,13 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
}
if (!needsAdvanced) {
- Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(cameraSelector);
- Qt3DRender::QLayer *tag = new Qt3DRender::QLayer(parentEntity);
+ // standard case: 1 LayerFilter leaf to the framegraph, N entities (with quad mesh) to the scenegraph
+ Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(m_compositorRoot);
+ Qt3DRender::QLayer *tag = new Qt3DRender::QLayer; // just let it be parented to the LayerFilter
tag->setObjectName(QLatin1String("Compositor quad pass"));
layerFilter->addLayer(tag);
+ // This works also when 'layers' is empty since we have a single leaf
+ // (the LayerFilter) that will get no match for its filter.
int layerDepth = 1;
for (Q3DSLayerNode *layer3DS : layers) {
@@ -3489,7 +3536,8 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
if (layer3DS->layerBackground() == Q3DSLayerNode::Transparent)
flags |= LayerQuadBlend;
- buildLayerQuadEntity(layer3DS, parentEntity, tag, flags, layerDepth++);
+ buildLayerQuadEntity(layer3DS, m_compositorParentEntity, tag, flags, layerDepth++);
+ m_compositorEntities.append(layer3DS->attached<Q3DSLayerAttached>()->compositorEntity);
}
} else {
qCDebug(lcPerf, "Some layers use an advanced blend mode in presentation %s. "
@@ -3542,7 +3590,7 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
data->advBlend.tempRt->addOutput(tempTexOutput);
}
- Qt3DRender::QBlitFramebuffer *bgBlit = new Qt3DRender::QBlitFramebuffer(cameraSelector);
+ Qt3DRender::QBlitFramebuffer *bgBlit = new Qt3DRender::QBlitFramebuffer(m_compositorRoot);
bgBlit->setDestination(data->advBlend.tempRt);
new Qt3DRender::QNoDraw(bgBlit);
@@ -3566,14 +3614,15 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
// Set some initial sizes.
setSizeDependentValues(layer3DS);
- Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(cameraSelector);
- Qt3DRender::QLayer *tag = new Qt3DRender::QLayer(parentEntity);
+ Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(m_compositorRoot);
+ Qt3DRender::QLayer *tag = new Qt3DRender::QLayer;
tag->setObjectName(QLatin1String("Adv. blend mode compositor quad pass"));
layerFilter->addLayer(tag);
// Now we do not need normal blending and will provide a custom shader program.
BuildLayerQuadFlags flags = LayerQuadCustomShader;
- buildLayerQuadEntity(layer3DS, parentEntity, tag, flags, layerDepth++);
+ buildLayerQuadEntity(layer3DS, m_compositorParentEntity, tag, flags, layerDepth++);
+ m_compositorEntities.append(data->compositorEntity);
Qt3DRender::QRenderPass *renderPass = data->compositorRenderPass;
switch (layer3DS->blendType()) {
@@ -3602,8 +3651,8 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
} else {
data->usesDefaultCompositorProgram = true;
- Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(cameraSelector);
- Qt3DRender::QLayer *tag = new Qt3DRender::QLayer(parentEntity);
+ Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(m_compositorRoot);
+ Qt3DRender::QLayer *tag = new Qt3DRender::QLayer;
tag->setObjectName(QLatin1String("Compositor quad pass (adv.blend path)"));
layerFilter->addLayer(tag);
@@ -3611,10 +3660,17 @@ void Q3DSSceneManager::buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3D
if (layer3DS->layerBackground() == Q3DSLayerNode::Transparent)
flags |= LayerQuadBlend;
- buildLayerQuadEntity(layer3DS, parentEntity, tag, flags, layerDepth++);
+ buildLayerQuadEntity(layer3DS, m_compositorParentEntity, tag, flags, layerDepth++);
+ m_compositorEntities.append(data->compositorEntity);
}
}
}
+
+ // When dynamically introducing a new layer, it is essential to re-run the
+ // transform calculations for all layers because the logical depth
+ // (layerDepth) based on which Z is generated may change for some of them.
+ for (Q3DSLayerNode *layer3DS : layers)
+ layer3DS->attached<Q3DSLayerAttached>()->updateCompositorCalculations();
}
void Q3DSSceneManager::buildGuiPass(Qt3DRender::QFrameGraphNode *parent, Qt3DCore::QEntity *parentEntity)
@@ -7026,27 +7082,28 @@ void Q3DSSceneManager::setPendingVisibilities()
Q3DSEffectInstance *effect = static_cast<Q3DSEffectInstance *>(it.key());
Q3DSEffectAttached *data = effect->attached<Q3DSEffectAttached>();
if (data) {
- it.key()->attached()->visibilityTag = (it.value() && effect->eyeballEnabled()) ? Q3DSGraphObjectAttached::Visible
- : Q3DSGraphObjectAttached::Hidden;
+ data->visibilityTag = (it.value() && effect->eyeballEnabled()) ? Q3DSGraphObjectAttached::Visible
+ : Q3DSGraphObjectAttached::Hidden;
updateEffectStatus(effect->attached<Q3DSEffectAttached>()->layer3DS);
}
} else if (it.key()->type() == Q3DSGraphObject::Layer) {
Q3DSLayerNode *layer3DS = static_cast<Q3DSLayerNode *>(it.key());
Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
- if (data && data->compositorEntity) { // may not exist if this is still buildLayer()
+ if (data) {
const bool enabled = it.value() && layer3DS->flags().testFlag(Q3DSNode::Active);
- it.key()->attached()->visibilityTag = enabled ? Q3DSGraphObjectAttached::Visible
- : Q3DSGraphObjectAttached::Hidden;
+ data->visibilityTag = enabled ? Q3DSGraphObjectAttached::Visible
+ : Q3DSGraphObjectAttached::Hidden;
updateGlobals(layer3DS, UpdateGlobalsRecursively | UpdateGlobalsSkipTransform);
- data->compositorEntity->setEnabled(enabled);
+ if (data->compositorEntity) // may not exist if this is still buildLayer()
+ data->compositorEntity->setEnabled(enabled);
}
} else if (it.key()->type() == Q3DSGraphObject::Camera) {
Q3DSCameraNode *cam3DS = static_cast<Q3DSCameraNode *>(it.key());
Q3DSCameraAttached *data = static_cast<Q3DSCameraAttached *>(cam3DS->attached());
if (data) {
const bool visible = (it.value() && cam3DS->flags().testFlag(Q3DSNode::Active));
- it.key()->attached()->visibilityTag = visible ? Q3DSGraphObjectAttached::Visible
- : Q3DSGraphObjectAttached::Hidden;
+ data->visibilityTag = visible ? Q3DSGraphObjectAttached::Visible
+ : Q3DSGraphObjectAttached::Hidden;
updateGlobals(cam3DS, UpdateGlobalsRecursively | UpdateGlobalsSkipTransform);
// leave rechoosing the camera to handleNodeGlobalChange()
}
@@ -8089,37 +8146,43 @@ void Q3DSSceneManager::handleSceneChange(Q3DSScene *, Q3DSGraphObject::DirtyFlag
Q3DSLayerNode *layer3DS = findLayerForObjectInScene(obj);
if (layer3DS)
addLayerContent(obj, obj->parent(), layer3DS);
+ } else if (obj->type() == Q3DSGraphObject::Layer) {
+ addLayer(static_cast<Q3DSLayerNode *>(obj));
}
- // ### layers need further considerations, not handled yet
}
} else if (change == Q3DSGraphObject::DirtyNodeRemoved) {
if (obj->isNode()) {
- if (obj->type() != Q3DSGraphObject::Layer) {
- Q_ASSERT(obj->parent());
+ Q_ASSERT(obj->parent());
+ const bool isLayer = obj->type() == Q3DSGraphObject::Layer;
+ if (isLayer)
+ qCDebug(lcScene) << "Dyn.removing layer" << obj->id();
+ else
qCDebug(lcScene) << "Dyn.removing" << obj->attached()->entity;
- Q3DSUipPresentation::forAllObjectsInSubTree(obj, [this](Q3DSGraphObject *objOrChild) {
- Q3DSGraphObjectAttached *data = objOrChild->attached();
- if (!data)
- return;
- Q3DSSlidePlayer *slidePlayer = m_slidePlayer;
- if (data->component)
- slidePlayer = data->component->masterSlide()->attached<Q3DSSlideAttached>()->slidePlayer;
- slidePlayer->objectAboutToBeRemovedFromScene(objOrChild);
- if (data->propertyChangeObserverIndex >= 0) {
- objOrChild->removePropertyChangeObserver(data->propertyChangeObserverIndex);
- data->propertyChangeObserverIndex = -1;
- }
- if (data->eventObserverIndex >= 0) {
- objOrChild->removeEventHandler(QString(), data->eventObserverIndex);
- data->eventObserverIndex = -1;
- }
- m_pendingObjectVisibility.remove(objOrChild);
- });
+ Q3DSUipPresentation::forAllObjectsInSubTree(obj, [this](Q3DSGraphObject *objOrChild) {
+ Q3DSGraphObjectAttached *data = objOrChild->attached();
+ if (!data)
+ return;
+ Q3DSSlidePlayer *slidePlayer = m_slidePlayer;
+ if (data->component)
+ slidePlayer = data->component->masterSlide()->attached<Q3DSSlideAttached>()->slidePlayer;
+ slidePlayer->objectAboutToBeRemovedFromScene(objOrChild);
+ if (data->propertyChangeObserverIndex >= 0) {
+ objOrChild->removePropertyChangeObserver(data->propertyChangeObserverIndex);
+ data->propertyChangeObserverIndex = -1;
+ }
+ if (data->eventObserverIndex >= 0) {
+ objOrChild->removeEventHandler(QString(), data->eventObserverIndex);
+ data->eventObserverIndex = -1;
+ }
+ m_pendingObjectVisibility.remove(objOrChild);
+ });
+ if (!isLayer) {
delete obj->attached()->entity;
-
Q3DSLayerNode *layer3DS = findLayerForObjectInScene(obj);
if (layer3DS)
removeLayerContent(obj, layer3DS);
+ } else {
+ rebuildCompositorLayerChain();
}
}
// bye bye attached; it will get recreated in case obj gets added back later on
@@ -8176,6 +8239,13 @@ void Q3DSSceneManager::addLayerContent(Q3DSGraphObject *obj, Q3DSGraphObject *pa
case Q3DSGraphObject::Effect:
needsEffectUpdate = true;
break;
+
+ case Q3DSGraphObject::Scene:
+ Q_FALLTHROUGH();
+ case Q3DSGraphObject::Layer:
+ qWarning("addLayerContent: Invalid child object %s", objOrChild->id().constData());
+ return;
+
default:
break;
}
@@ -8200,6 +8270,28 @@ void Q3DSSceneManager::removeLayerContent(Q3DSGraphObject *obj, Q3DSLayerNode *l
markLayerAsContentChanged(layer3DS, Q3DSLayerNode::LayerContentSubTreeLightsChange);
}
+void Q3DSSceneManager::addLayer(Q3DSLayerNode *layer3DS)
+{
+ // Simply build the layer like buildScene() would. The order in the
+ // framegraph does not matter here (unlike in the composition step), so we
+ // can just append the new subtree root to m_LayerContainerFg.
+
+ if (layer3DS->sourcePath().isEmpty())
+ buildLayer(layer3DS, m_layerContainerFg, m_outputPixelSize);
+ else
+ buildSubPresentationLayer(layer3DS, m_outputPixelSize);
+
+ Q3DSUipPresentation::forAllObjectsInSubTree(layer3DS, [this, layer3DS](Q3DSGraphObject *objOrChild) {
+ Q3DSSlidePlayer *slidePlayer = m_slidePlayer;
+ if (objOrChild->attached() && objOrChild->attached()->component)
+ slidePlayer = objOrChild->attached()->component->masterSlide()->attached<Q3DSSlideAttached>()->slidePlayer;
+
+ slidePlayer->objectAboutToBeAddedToScene(objOrChild);
+ });
+
+ rebuildCompositorLayerChain();
+}
+
void Q3DSSceneManager::handleSlideGraphChange(Q3DSSlide *master, Q3DSGraphObject::DirtyFlag change, Q3DSSlide *slide)
{
// called when a slide (a child of the master slide) is added or removed
diff --git a/src/runtime/q3dsscenemanager_p.h b/src/runtime/q3dsscenemanager_p.h
index 58c3f38..7d82d55 100644
--- a/src/runtime/q3dsscenemanager_p.h
+++ b/src/runtime/q3dsscenemanager_p.h
@@ -850,6 +850,7 @@ private:
void buildLayerQuadEntity(Q3DSLayerNode *layer3DS, Qt3DCore::QEntity *parentEntity, Qt3DRender::QLayer *tag,
BuildLayerQuadFlags flags, int layerDepth = 0);
void updateLayerCompositorProgram(Q3DSLayerNode *layer3DS);
+ void rebuildCompositorLayerChain();
void buildCompositor(Qt3DRender::QFrameGraphNode *parent, Qt3DCore::QEntity *parentEntity);
void buildGuiPass(Qt3DRender::QFrameGraphNode *parent, Qt3DCore::QEntity *parentEntity);
@@ -878,6 +879,7 @@ private:
void handleSceneChange(Q3DSScene *scene, Q3DSGraphObject::DirtyFlag change, Q3DSGraphObject *obj);
void addLayerContent(Q3DSGraphObject *obj, Q3DSGraphObject *parent, Q3DSLayerNode *layer3DS);
void removeLayerContent(Q3DSGraphObject *obj, Q3DSLayerNode *layer3DS);
+ void addLayer(Q3DSLayerNode *layer3DS);
void handleSlideGraphChange(Q3DSSlide *master, Q3DSGraphObject::DirtyFlag change, Q3DSSlide *slide);
void handleSlideObjectChange(Q3DSSlide *slide, const Q3DSSlide::SlideObjectChange &change);
@@ -893,7 +895,9 @@ private:
Q3DSUipPresentation *m_presentation;
QSize m_presentationSize;
Q3DSScene *m_scene;
+ int m_sceneChangeObserverIndex = -1;
Q3DSSlide *m_masterSlide;
+ int m_slideGraphChangeObserverIndex = -1;
Q3DSSlide *m_currentSlide;
Qt3DCore::QEntity *m_rootEntity;
Q3DSFrameUpdater *m_frameUpdater = nullptr;
@@ -901,6 +905,7 @@ private:
Q3DSCustomMaterialGenerator *m_customMaterialGen;
Q3DSTextMaterialGenerator *m_textMatGen;
Q3DSTextRenderer *m_textRenderer;
+ Qt3DRender::QFrameGraphNode *m_layerContainerFg;
using SubTreeWithDirtyLight = QPair<Q3DSGraphObject *, bool>;
QSet<SubTreeWithDirtyLight> m_subTreesWithDirtyLights;
QSet<Q3DSDefaultMaterial *> m_pendingDefMatRebuild;
@@ -929,6 +934,10 @@ private:
QSet<Q3DSSceneManager *> m_layerCacheDeps;
bool m_hasQmlSubPresAsTextureMap = false;
Q3DSViewportData m_viewportData;
+ Qt3DRender::QFrameGraphNode *m_compositorFgContainer = nullptr;
+ Qt3DRender::QFrameGraphNode *m_compositorRoot = nullptr;
+ Qt3DCore::QEntity *m_compositorParentEntity = nullptr;
+ QVector<Qt3DCore::QEntity *> m_compositorEntities;
friend class Q3DSFrameUpdater;
friend class Q3DSProfiler;
diff --git a/src/runtime/q3dsuippresentation.cpp b/src/runtime/q3dsuippresentation.cpp
index 749b6f0..1b5add2 100644
--- a/src/runtime/q3dsuippresentation.cpp
+++ b/src/runtime/q3dsuippresentation.cpp
@@ -4091,14 +4091,6 @@ void Q3DSUipPresentation::forAllModels(Q3DSGraphObject *obj,
}
}
-void Q3DSUipPresentation::forAllImages(std::function<void (Q3DSImage *)> f)
-{
- for (Q3DSGraphObject *obj : qAsConst(d->objects)) {
- if (obj->type() == Q3DSGraphObject::Image)
- f(static_cast<Q3DSImage *>(obj));
- }
-}
-
Q3DSGraphObject *Q3DSUipPresentation::newObject(const char *type, const QByteArray &id)
{
Q3DSGraphObject *obj = nullptr;
diff --git a/src/runtime/q3dsuippresentation_p.h b/src/runtime/q3dsuippresentation_p.h
index 4601e15..c444c73 100644
--- a/src/runtime/q3dsuippresentation_p.h
+++ b/src/runtime/q3dsuippresentation_p.h
@@ -2009,7 +2009,6 @@ public:
std::function<void(Q3DSNode *)> f);
static void forAllLayers(Q3DSScene *scene, std::function<void(Q3DSLayerNode*)> f, bool reverse = false);
static void forAllModels(Q3DSGraphObject *obj, std::function<void(Q3DSModelNode *)> f, bool includeHidden = false);
- void forAllImages(std::function<void(Q3DSImage *)> f);
void notifyPropertyChanges(const Q3DSSlide::PropertyChanges &changeList) const;
void applyPropertyChanges(const Q3DSSlide::PropertyChanges &changeList) const;
diff --git a/tests/scripts/newlayer.drgscr b/tests/scripts/newlayer.drgscr
new file mode 100644
index 0000000..ae7e556
--- /dev/null
+++ b/tests/scripts/newlayer.drgscr
@@ -0,0 +1,17 @@
+object(newlayer, newlayer, Layer, null, Slide1)
+
+object(newlayer_cam, newlayer_cam, Camera, newlayer, Slide1)
+// the default position does not work since the property is shared with nodes and those use 0 0 0 as the default,
+// unlike the expected 0 0 -600 for cameras. so must specify it explicitly.
+set(newlayer_cam, position, 0 0 -600)
+
+// must have a light to see something
+object(newlayer_light, newlayer_light, Light, newlayer, Slide1)
+
+// add some text
+object(newlayer_text, newlayer_text, Text, newlayer, Slide1)
+set(newlayer_text, textstring, New Layer!)
+set(newlayer_text, textcolor, 0 1 0)
+
+// use prependChildNode, i.e. add as the top-most (front) layer
+setparent(newlayer, Scene, null)
diff --git a/tests/scripts/newlayer_direct.drgscr b/tests/scripts/newlayer_direct.drgscr
new file mode 100644
index 0000000..4688a2b
--- /dev/null
+++ b/tests/scripts/newlayer_direct.drgscr
@@ -0,0 +1,13 @@
+// Here the layer and its children are connected one by one to the scene right away as they are created.
+// will end up as the bottom layer (last child of Scene)
+
+object(newlayer, newlayer, Layer, Scene, Slide1)
+
+object(newlayer_cam, newlayer_cam, Camera, newlayer, Slide1)
+set(newlayer_cam, position, 0 0 -600)
+
+object(newlayer_light, newlayer_light, Light, newlayer, Slide1)
+
+object(newlayer_text, newlayer_text, Text, newlayer, Slide1)
+set(newlayer_text, textstring, New Layer!)
+set(newlayer_text, textcolor, 0 1 0)