diff options
author | Kaj Grönholm <kaj.gronholm@qt.io> | 2018-09-13 08:14:49 +0300 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2018-10-24 16:18:24 +0000 |
commit | 3db4c4b180b3afb105f6f531f2f070adf0c2ec64 (patch) | |
tree | 6de4ac16e0f33eb51f5be438754a225f41d1cd90 | |
parent | 5d6a5f0238c31f48c9ffac335783116a40cf6a4e (diff) |
Support stereoscopic rendering
This patch provides stereoscopic rendering support for Qt Studio
qt3d-runtime.
Contains API to switch between stereo modes (mono, top-bottom,
left-right, analyph red-cyan & green-magenta) and to control
eye (camera) separation. Adds menu into Q3DSViewer for these.
Task-number: QT3DS-1023
Change-Id: I3da3d34606a07178978eba83236ca6cb6360e893
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
-rw-r--r-- | src/runtime/profileui/q3dsprofileui.cpp | 4 | ||||
-rw-r--r-- | src/runtime/q3dsengine.cpp | 29 | ||||
-rw-r--r-- | src/runtime/q3dsengine_p.h | 3 | ||||
-rw-r--r-- | src/runtime/q3dsres.qrc | 6 | ||||
-rw-r--r-- | src/runtime/q3dsscenemanager.cpp | 1045 | ||||
-rw-r--r-- | src/runtime/q3dsscenemanager_p.h | 176 | ||||
-rw-r--r-- | src/runtime/q3dsviewportsettings.cpp | 26 | ||||
-rw-r--r-- | src/runtime/q3dsviewportsettings_p.h | 19 | ||||
-rw-r--r-- | src/runtime/shaders/compositor_ms2_stereoscopic.frag | 49 | ||||
-rw-r--r-- | src/runtime/shaders/compositor_ms2_stereoscopic_core.frag | 48 | ||||
-rw-r--r-- | src/runtime/shaders/compositor_ms4_stereoscopic.frag | 49 | ||||
-rw-r--r-- | src/runtime/shaders/compositor_ms4_stereoscopic_core.frag | 48 | ||||
-rw-r--r-- | src/runtime/shaders/compositor_stereoscopic.frag | 43 | ||||
-rw-r--r-- | src/runtime/shaders/compositor_stereoscopic_core.frag | 45 | ||||
-rw-r--r-- | tests/auto/slides/tst_q3dsslides.cpp | 4 | ||||
-rw-r--r-- | tools/q3dsviewer/q3dsmainwindow.cpp | 124 |
16 files changed, 1323 insertions, 395 deletions
diff --git a/src/runtime/profileui/q3dsprofileui.cpp b/src/runtime/profileui/q3dsprofileui.cpp index cdb5e21..85f5ac5 100644 --- a/src/runtime/profileui/q3dsprofileui.cpp +++ b/src/runtime/profileui/q3dsprofileui.cpp @@ -675,8 +675,8 @@ void Q3DSProfileView::addLayerWindow() + QByteArrayLiteral("\nTAA: ") + (layer3DS->layerFlags().testFlag(Q3DSLayerNode::TemporalAA) ? "Yes": "No"); ImGui::Text("%s", aa.constData()); ImGui::NextColumn(); - const bool timeDep = data->effectActive && data->effectData.combinedEffectFlags.testFlag(Q3DSEffect::ReliesOnTime); - ImGui::Text("%d active%s", data->effectActive ? data->effectData.activeEffectCount : 0, timeDep ? " [TD]" : ""); + const bool timeDep = data->effectActive && data->eyeMono->effectData.combinedEffectFlags.testFlag(Q3DSEffect::ReliesOnTime); + ImGui::Text("%d active%s", data->effectActive ? data->eyeMono->effectData.activeEffectCount : 0, timeDep ? " [TD]" : ""); ImGui::NextColumn(); ImGui::Text("%s", data->wasDirty ? "true" : "false"); ImGui::NextColumn(); diff --git a/src/runtime/q3dsengine.cpp b/src/runtime/q3dsengine.cpp index 2405184..b05c911 100644 --- a/src/runtime/q3dsengine.cpp +++ b/src/runtime/q3dsengine.cpp @@ -1246,6 +1246,28 @@ void Q3DSEngine::setViewportSettings(Q3DSViewportSettings *viewportSettings) m_uipPresentations[0].sceneManager->setMatteColor(m_viewportSettings->matteColor()); } }); + + connect(m_viewportSettings, &Q3DSViewportSettings::stereoModeChanged, [this] { + if (!m_uipPresentations.isEmpty()) { + m_uipPresentations[0].sceneManager->updateViewportRects(); + + if (isStereoscopic() != m_wasStereoscopic) + emit stereoscopicChanged(); + + m_wasStereoscopic = isStereoscopic(); + + // Force a refresh + resize(m_size, m_dpr); + } + }); + + connect(m_viewportSettings, &Q3DSViewportSettings::stereoEyeSeparationChanged, [this] { + if (!m_uipPresentations.isEmpty()) { + // Force a refresh + resize(m_size, m_dpr); + } + }); + } void Q3DSEngine::setOnDemandRendering(bool enabled) @@ -1312,6 +1334,11 @@ void Q3DSEngine::resize(const QSize &size, qreal dpr, bool forceSynchronous) } } +bool Q3DSEngine::isStereoscopic() const +{ + return m_viewportSettings->stereoMode() != Q3DSViewportSettings::StereoModeMono; +} + bool Q3DSEngine::isProfileUiVisible() const { Q3DSSceneManager *sm = !m_uipPresentations.isEmpty() ? m_uipPresentations[0].sceneManager : nullptr; @@ -1903,7 +1930,7 @@ Qt3DCore::QNodeId Q3DSEngine::layerTextureNodeId(Q3DSLayerNode *layer3DS) const if (!data) return Qt3DCore::QNodeId(); - Qt3DRender::QAbstractTexture *t = data->effectActive ? data->effLayerTexture : data->layerTexture; + Qt3DRender::QAbstractTexture *t = data->effectActive ? data->eyeMono->effLayerTexture : data->eyeMono->layerTexture; if (!t) return Qt3DCore::QNodeId(); diff --git a/src/runtime/q3dsengine_p.h b/src/runtime/q3dsengine_p.h index d434a7f..d349dec 100644 --- a/src/runtime/q3dsengine_p.h +++ b/src/runtime/q3dsengine_p.h @@ -186,6 +186,7 @@ public: void setAutoToggleProfileUi(bool enabled) { m_autoToggleProfileUi = enabled; } void setProfileUiVisible(bool visible, bool openLogAndConsole = false); + bool isStereoscopic() const; bool isProfileUiVisible() const; void configureProfileUi(float scale); float profileUiScaleFactor() const { return m_profileUiScale; } @@ -229,6 +230,7 @@ Q_SIGNALS: void slideExited(Q3DSGraphObject *context, int index, const QString &name); void layerResized(Q3DSLayerNode *layer3DS); void layerTextureNodeChanged(Q3DSLayerNode *layer3DS); + void stereoscopicChanged(); private: Q_DISABLE_COPY(Q3DSEngine) @@ -313,6 +315,7 @@ private: QMutex m_renderLoopStatsMutex; float m_quickDeltaSum = 0; int m_quickDeltaCount = 0; + bool m_wasStereoscopic = false; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSEngine::Flags) diff --git a/src/runtime/q3dsres.qrc b/src/runtime/q3dsres.qrc index 9b44feb..94c6a4e 100644 --- a/src/runtime/q3dsres.qrc +++ b/src/runtime/q3dsres.qrc @@ -13,6 +13,12 @@ <file>shaders/text_core.frag</file> <file>shaders/text.vert</file> <file>shaders/text.frag</file> + <file>shaders/compositor_stereoscopic_core.frag</file> + <file>shaders/compositor_stereoscopic.frag</file> + <file>shaders/compositor_ms2_stereoscopic.frag</file> + <file>shaders/compositor_ms2_stereoscopic_core.frag</file> + <file>shaders/compositor_ms4_stereoscopic.frag</file> + <file>shaders/compositor_ms4_stereoscopic_core.frag</file> <file alias="res/DataModelMetadata/en-us/MetaData.xml">../../res/DataModelMetadata/en-us/MetaData.xml</file> <file alias="res/effectlib/abbeNumberIOR.glsllib">../../res/effectlib/abbeNumberIOR.glsllib</file> <file alias="res/effectlib/anisotropyConversion.glsllib">../../res/effectlib/anisotropyConversion.glsllib</file> diff --git a/src/runtime/q3dsscenemanager.cpp b/src/runtime/q3dsscenemanager.cpp index 4748e8e..250ac0d 100644 --- a/src/runtime/q3dsscenemanager.cpp +++ b/src/runtime/q3dsscenemanager.cpp @@ -39,6 +39,7 @@ #include "q3dsslideplayer_p.h" #include "q3dsimagemanager_p.h" #include "q3dslogging_p.h" +#include "q3dsviewportsettings_p.h" #if QT_CONFIG(q3ds_profileui) #include "profileui/q3dsprofileui_p.h" #include "q3dsconsolecommands_p.h" @@ -543,6 +544,9 @@ void Q3DSSceneManager::updateSizes(const QSize &size, qreal dpr, const QRect &vi }); if (forceTreeVisit) syncScene(); + + // Setup scene viewport sizes + updateViewportRects(); } void Q3DSSceneManager::setCurrentSlide(Q3DSSlide *newSlide, bool flush) @@ -722,6 +726,8 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } + QObject::disconnect(m_engine, &Q3DSEngine::stereoscopicChanged, 0, 0); + // Kick off the Qt3D scene. m_rootEntity = new Qt3DCore::QEntity; m_rootEntity->setObjectName(QString(QLatin1String("non-layer root for presentation %1")).arg(m_presentation->name())); @@ -957,8 +963,12 @@ void Q3DSSceneManager::updateSubPresentationHosts() 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->layerTexture = it->colorTex; - layerData->compositorSourceParam->setValue(QVariant::fromValue(layerData->layerTexture)); + layerData->eyeMono->layerTexture = it->colorTex; + layerData->eyeMono->compositorSourceParam->setValue(QVariant::fromValue(layerData->eyeMono->layerTexture)); + layerData->eyeLeft->layerTexture = it->colorTex; + layerData->eyeRight->layerTexture = it->colorTex; + layerData->eyeLeft->compositorSourceParam->setValue(QVariant::fromValue(layerData->eyeLeft->layerTexture)); + layerData->eyeRight->compositorSourceParam->setValue(QVariant::fromValue(layerData->eyeRight->layerTexture)); layerData->updateSubPresentationSize(); } } else { @@ -1125,93 +1135,106 @@ void Q3DSSceneManager::initSubTree(Q3DSGraphObject *subTreeRoot) }); } -void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS, - Qt3DRender::QFrameGraphNode *parent, - const QSize &parentSize) +void Q3DSSceneManager::updateViewportRects() { - Qt3DRender::QFrameGraphNode *layerFgRoot = new Qt3DRender::QFrameGraphNode(parent); - - // main passes, generating the layer texture - Qt3DRender::QRenderTargetSelector *rtSelector = new Qt3DRender::QRenderTargetSelector(layerFgRoot); + if (m_viewports.isEmpty()) + return; - int ssaaScaleFactor = 1; - if (layer3DS->multisampleAA() == Q3DSLayerNode::SSAA) { - ssaaScaleFactor = 2; - qCDebug(lcPerf, "Layer %s uses %dx SSAA", layer3DS->id().constData(), ssaaScaleFactor); + for (ViewportSet viewports : m_viewports) { + // Note: updateCamerasStatus() gets called when switching between mono <-> stereo + // and it handles properly hiding views. + switch (m_engine->viewportSettings()->stereoMode()) { + case Q3DSViewportSettings::StereoModeMono: + viewports.viewportMono->setNormalizedRect(QRectF(0, 0, 1, 1)); + // Not sure if hiding Qt3D viewports currently affects anything, + // but shouldn't harm either + viewports.viewportMono->setEnabled(true); + viewports.viewportLeft->setEnabled(false); + viewports.viewportRight->setEnabled(false); + break; + case Q3DSViewportSettings::StereoModeTopBottom: + viewports.viewportLeft->setNormalizedRect(QRectF(0, 0, 1, 0.5)); + viewports.viewportRight->setNormalizedRect(QRectF(0, 0.5, 1, 0.5)); + viewports.viewportMono->setEnabled(false); + viewports.viewportLeft->setEnabled(true); + viewports.viewportRight->setEnabled(true); + break; + case Q3DSViewportSettings::StereoModeLeftRight: + viewports.viewportLeft->setNormalizedRect(QRectF(0, 0, 0.5, 1)); + viewports.viewportRight->setNormalizedRect(QRectF(0.5, 0, 0.5, 1)); + viewports.viewportMono->setEnabled(false); + viewports.viewportLeft->setEnabled(true); + viewports.viewportRight->setEnabled(true); + break; + case Q3DSViewportSettings::StereoModeAnaglyphRedCyan: + Q_FALLTHROUGH(); + case Q3DSViewportSettings::StereoModeAnaglyphGreenMagenta: + viewports.viewportLeft->setNormalizedRect(QRectF(0, 0, 1, 1)); + viewports.viewportRight->setNormalizedRect(QRectF(0, 0, 1, 1)); + viewports.viewportMono->setEnabled(false); + viewports.viewportLeft->setEnabled(true); + viewports.viewportRight->setEnabled(true); + break; + default: + Q_UNREACHABLE(); + break; + } } - int msaaSampleCount = 0; - switch (layer3DS->multisampleAA()) { - case Q3DSLayerNode::MSAA2x: - msaaSampleCount = 2; - break; - case Q3DSLayerNode::MSAA4x: - msaaSampleCount = 4; - break; - default: - break; - } - if (m_flags.testFlag(Force4xMSAA)) - msaaSampleCount = 4; + // Update mode into shader parameter + m_stereoMode->setValue(m_engine->viewportSettings()->stereoMode()); +} - // Layer MSAA is only available through multisample textures (GLES 3.1+ or GL 3.2+) at the moment. (QTBUG-63382) - // Revert to no-MSAA when this is not supported. - if (msaaSampleCount > 1 && !m_gfxLimits.multisampleTextureSupported) { - qCDebug(lcScene, "Layer MSAA requested but not supported; ignoring request"); - msaaSampleCount = 0; +void Q3DSSceneManager::updateCamerasStatus(Q3DSLayerAttached *layerData) +{ + Q_ASSERT(layerData); + if (m_engine->isStereoscopic()) { + layerData->eyeMono->cameraSelector->setEnabled(false); + layerData->eyeLeft->cameraSelector->setEnabled(true); + layerData->eyeRight->cameraSelector->setEnabled(true); + } else { + layerData->eyeMono->cameraSelector->setEnabled(true); + layerData->eyeLeft->cameraSelector->setEnabled(false); + layerData->eyeRight->cameraSelector->setEnabled(false); } +} - if (msaaSampleCount > 1) - qCDebug(lcPerf, "Layer %s uses multisample texture", layer3DS->id().constData()); - - // parentSize could well be (0, 0) at this stage still, nevermind that - const QSize layerSize = calculateLayerSize(layer3DS, parentSize); - const QSize layerPixelSize = safeLayerPixelSize(layerSize, ssaaScaleFactor); - - // Create color and depth-stencil buffers for this layer - Qt3DRender::QAbstractTexture *colorTex; - Qt3DRender::QAbstractTexture *dsTexOrRb; - Qt3DRender::QRenderTarget *rt = newLayerRenderTarget(layerPixelSize, msaaSampleCount, &colorTex, &dsTexOrRb, layerFgRoot, layer3DS); - m_profiler->trackNewObject(rt, Q3DSProfiler::RenderTargetObject, - "RT for layer %s", layer3DS->id().constData()); - rtSelector->setTarget(rt); - - Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport(rtSelector); - viewport->setNormalizedRect(QRectF(0, 0, 1, 1)); - - Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(viewport); - Qt3DRender::QTechniqueFilter *mainTechniqueSelector = new Qt3DRender::QTechniqueFilter(cameraSelector); +Q3DSEyeData *Q3DSSceneManager::buildEye(Q3DSLayerNode *layer3DS, Qt3DRender::QViewport *viewport) +{ + Q3DSEyeData *eyeData = new Q3DSEyeData; + eyeData->cameraSelector = new Qt3DRender::QCameraSelector(viewport); + Qt3DRender::QTechniqueFilter *mainTechniqueSelector = new Qt3DRender::QTechniqueFilter(eyeData->cameraSelector); Qt3DRender::QFilterKey *techniqueFilterKey = new Qt3DRender::QFilterKey; techniqueFilterKey->setName(QLatin1String("type")); techniqueFilterKey->setValue(QLatin1String("main")); mainTechniqueSelector->addMatch(techniqueFilterKey); - Qt3DRender::QLayer *opaqueTag = new Qt3DRender::QLayer(m_rootEntity); - opaqueTag->setObjectName(QLatin1String("Opaque pass")); - Qt3DRender::QLayer *transparentTag = new Qt3DRender::QLayer(m_rootEntity); - transparentTag->setObjectName(QLatin1String("Transparent pass")); + eyeData->opaqueTag = new Qt3DRender::QLayer(m_rootEntity); + eyeData->opaqueTag->setObjectName(QLatin1String("Opaque pass")); + eyeData->transparentTag = new Qt3DRender::QLayer(m_rootEntity); + eyeData->transparentTag->setObjectName(QLatin1String("Transparent pass")); // Depth texture pass, optional, with its own rendertarget and clear. Just // a placeholder for now since it is disabled by default. - Qt3DRender::QRenderTargetSelector *depthRtSelector = new Qt3DRender::QRenderTargetSelector(mainTechniqueSelector); + eyeData->depthRtSelector = new Qt3DRender::QRenderTargetSelector(mainTechniqueSelector); // Having a NoDraw leaf is essential while the depth texture is not // generated (since we must not make draw calls from this leaf), and will // [hopefully] not cause any harm later on, when depthRtSelector gets // another child, either. - new Qt3DRender::QNoDraw(depthRtSelector); + new Qt3DRender::QNoDraw(eyeData->depthRtSelector); // ditto for SSAO - Qt3DRender::QRenderTargetSelector *ssaoRtSelector = new Qt3DRender::QRenderTargetSelector(mainTechniqueSelector); - new Qt3DRender::QNoDraw(ssaoRtSelector); + eyeData->ssaoRtSelector = new Qt3DRender::QRenderTargetSelector(mainTechniqueSelector); + new Qt3DRender::QNoDraw(eyeData->ssaoRtSelector); // ditto for shadow maps but here there will be multiple rendertargetselectors later, so use a dummy node as root - Qt3DRender::QFrameGraphNode *shadowRoot = new Qt3DRender::QFrameGraphNode(mainTechniqueSelector); - new Qt3DRender::QNoDraw(shadowRoot); + eyeData->shadowRoot = new Qt3DRender::QFrameGraphNode(mainTechniqueSelector); + new Qt3DRender::QNoDraw(eyeData->shadowRoot); // Clear op for depth pre, opaque, transparent passes. - Qt3DRender::QClearBuffers *clearBuffers = new Qt3DRender::QClearBuffers(mainTechniqueSelector); - clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthStencilBuffer); - new Qt3DRender::QNoDraw(clearBuffers); + eyeData->clearBuffers = new Qt3DRender::QClearBuffers(mainTechniqueSelector); + eyeData->clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthStencilBuffer); + new Qt3DRender::QNoDraw(eyeData->clearBuffers); // Depth pre-pass, optional Qt3DRender::QRenderPassFilter *depthPreFilter = new Qt3DRender::QRenderPassFilter(mainTechniqueSelector); @@ -1224,7 +1247,7 @@ void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS, const bool depthPrePassEnabled = !layer3DS->layerFlags().testFlag(Q3DSLayerNode::DisableDepthPrePass) && !layer3DS->layerFlags().testFlag(Q3DSLayerNode::DisableDepthTest); if (depthPrePassEnabled) - depthPreLayerFilter->addLayer(opaqueTag); // opaque only, transparent objects must not be present + depthPreLayerFilter->addLayer(eyeData->opaqueTag); // opaque only, transparent objects must not be present const bool transparentPassOnly = layer3DS->layerFlags().testFlag(Q3DSLayerNode::DisableDepthTest); @@ -1237,7 +1260,7 @@ void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS, opaqueFilter->addMatch(opaqueFilterKey); Qt3DRender::QSortPolicy *opaqueSortPolicy = opaquePassSortPolicy(opaqueFilter); Qt3DRender::QLayerFilter *opaqueLayerFilter = new Qt3DRender::QLayerFilter(opaqueSortPolicy); - opaqueLayerFilter->addLayer(opaqueTag); + opaqueLayerFilter->addLayer(eyeData->opaqueTag); } // Transparent pass, sort back to front @@ -1248,19 +1271,109 @@ void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS, transFilter->addMatch(transFilterKey); Qt3DRender::QSortPolicy *transSortPolicy = transparentPassSortPolicy(transFilter); Qt3DRender::QLayerFilter *transLayerFilter = new Qt3DRender::QLayerFilter(transSortPolicy); - transLayerFilter->addLayer(transparentTag); + transLayerFilter->addLayer(eyeData->transparentTag); if (transparentPassOnly) - transLayerFilter->addLayer(opaqueTag); + transLayerFilter->addLayer(eyeData->opaqueTag); // Post-processing effect passes - Qt3DRender::QFrameGraphNode *effectRoot = new Qt3DRender::QFrameGraphNode(mainTechniqueSelector); - new Qt3DRender::QNoDraw(effectRoot); + eyeData->effectRoot = new Qt3DRender::QFrameGraphNode(mainTechniqueSelector); + new Qt3DRender::QNoDraw(eyeData->effectRoot); + + return eyeData; +} + +void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS, + Qt3DRender::QFrameGraphNode *parent, + const QSize &parentSize) +{ + Qt3DRender::QFrameGraphNode *layerFgRoot = new Qt3DRender::QFrameGraphNode(parent); - Qt3DCore::QEntity *layerSceneRootEntity = new Qt3DCore::QEntity(rtSelector); + // main passes, generating the layer texture + Qt3DRender::QRenderTargetSelector *rtSelector = + new Qt3DRender::QRenderTargetSelector(layerFgRoot); + Qt3DRender::QRenderTargetSelector *rtSelectorLeft = + new Qt3DRender::QRenderTargetSelector(layerFgRoot); + Qt3DRender::QRenderTargetSelector *rtSelectorRight = + new Qt3DRender::QRenderTargetSelector(layerFgRoot); + + int ssaaScaleFactor = 1; + if (layer3DS->multisampleAA() == Q3DSLayerNode::SSAA) { + ssaaScaleFactor = 2; + qCDebug(lcPerf, "Layer %s uses %dx SSAA", layer3DS->id().constData(), ssaaScaleFactor); + } + + int msaaSampleCount = 0; + switch (layer3DS->multisampleAA()) { + case Q3DSLayerNode::MSAA2x: + msaaSampleCount = 2; + break; + case Q3DSLayerNode::MSAA4x: + msaaSampleCount = 4; + break; + default: + break; + } + if (m_flags.testFlag(Force4xMSAA)) + msaaSampleCount = 4; + + // Layer MSAA is only available through multisample textures (GLES 3.1+ or GL 3.2+) at the moment. (QTBUG-63382) + // Revert to no-MSAA when this is not supported. + if (msaaSampleCount > 1 && !m_gfxLimits.multisampleTextureSupported) { + qCDebug(lcScene, "Layer MSAA requested but not supported; ignoring request"); + msaaSampleCount = 0; + } + + if (msaaSampleCount > 1) + qCDebug(lcPerf, "Layer %s uses multisample texture", layer3DS->id().constData()); + + // parentSize could well be (0, 0) at this stage still, nevermind that + const QSize layerSize = calculateLayerSize(layer3DS, parentSize); + const QSize layerPixelSize = safeLayerPixelSize(layerSize, ssaaScaleFactor); + + Q3DSLayerAttached *layerData = new Q3DSLayerAttached; + ViewportSet viewports; + + // Create color and depth-stencil buffers for this layer + // Normal mono viewport + Qt3DRender::QAbstractTexture *colorTex; + Qt3DRender::QAbstractTexture *dsTexOrRb; + Qt3DRender::QRenderTarget *rt = newLayerRenderTarget(layerPixelSize, msaaSampleCount, &colorTex, &dsTexOrRb, layerFgRoot, layer3DS); + m_profiler->trackNewObject(rt, Q3DSProfiler::RenderTargetObject, + "RT for layer %s", layer3DS->id().constData()); + rtSelector->setTarget(rt); + + viewports.viewportMono = new Qt3DRender::QViewport(rtSelector); + layerData->eyeMono = buildEye(layer3DS, viewports.viewportMono); + + // Create color and depth-stencil buffers for this layer + // Stereoscopic left-eye + Qt3DRender::QAbstractTexture *colorTexLeft; + Qt3DRender::QAbstractTexture *dsTexOrRbLeft; + Qt3DRender::QRenderTarget *rtLeft = newLayerRenderTarget(layerPixelSize, msaaSampleCount, &colorTexLeft, &dsTexOrRbLeft, layerFgRoot, layer3DS); + m_profiler->trackNewObject(rtLeft, Q3DSProfiler::RenderTargetObject, + "RT for layer %s", layer3DS->id().constData()); + rtSelectorLeft->setTarget(rtLeft); + + viewports.viewportLeft = new Qt3DRender::QViewport(rtSelectorLeft); + layerData->eyeLeft = buildEye(layer3DS, viewports.viewportLeft); + + // Create color and depth-stencil buffers for this layer + // Stereoscopic right-eye + Qt3DRender::QAbstractTexture *colorTexRight; + Qt3DRender::QAbstractTexture *dsTexOrRbRight; + Qt3DRender::QRenderTarget *rtRight = newLayerRenderTarget(layerPixelSize, msaaSampleCount, &colorTexRight, &dsTexOrRbRight, layerFgRoot, layer3DS); + m_profiler->trackNewObject(rtRight, Q3DSProfiler::RenderTargetObject, + "RT for layer %s", layer3DS->id().constData()); + rtSelectorRight->setTarget(rtRight); + + viewports.viewportRight = new Qt3DRender::QViewport(rtSelectorRight); + layerData->eyeRight = buildEye(layer3DS, viewports.viewportRight); + + // Note: Moved layerSceneRootEntity to be child of layerFgRoot instead of rtSelector + Qt3DCore::QEntity *layerSceneRootEntity = new Qt3DCore::QEntity(layerFgRoot); layerSceneRootEntity->setObjectName(QObject::tr("root for %1").arg(QString::fromUtf8(layer3DS->id()))); m_profiler->reportQt3DSceneGraphRoot(layerSceneRootEntity); - Q3DSLayerAttached *layerData = new Q3DSLayerAttached; // Must set an entity to make Q3DSLayerNode properties animatable. // also, this must be the general purpose "root" (### why?) layerData->entity = m_rootEntity; @@ -1272,39 +1385,88 @@ void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS, layerData->layerFgRoot = layerFgRoot; layerData->layerFgRootParent = layerFgRoot->parentNode(); layerData->layerFgDummyParent = new Qt3DCore::QNode(layerData->entity); - layerData->cameraSelector = cameraSelector; - layerData->clearBuffers = clearBuffers; - layerData->rtSelector = rtSelector; - layerData->layerTexture = colorTex; - layerData->layerDS = dsTexOrRb; - layerData->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("tex"), layerData->layerTexture); + layerData->eyeMono->rtSelector = rtSelector; + layerData->eyeMono->layerTexture = colorTex; + layerData->eyeMono->layerDS = dsTexOrRb; + layerData->eyeMono->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("tex"), layerData->eyeMono->layerTexture); layerData->layerSize = layerSize; layerData->parentSize = parentSize; layerData->msaaSampleCount = msaaSampleCount; layerData->ssaaScaleFactor = ssaaScaleFactor; - layerData->opaqueTag = opaqueTag; - layerData->transparentTag = transparentTag; layerData->cameraPropertiesParam = new Qt3DRender::QParameter(QLatin1String("camera_properties"), QVector2D(10, 5000), m_rootEntity); - layerData->depthTextureData.rtSelector = depthRtSelector; - layerData->ssaoTextureData.rtSelector = ssaoRtSelector; - layerData->shadowMapData.shadowRoot = shadowRoot; - layerData->effectData.effectRoot = effectRoot; + layerData->eyeMono->depthTextureData.rtSelector = layerData->eyeMono->depthRtSelector; + layerData->ssaoTextureData.rtSelector = layerData->eyeMono->ssaoRtSelector; + layerData->shadowMapData.shadowRoot = layerData->eyeMono->shadowRoot; + layerData->eyeMono->effectData.effectRoot = layerData->eyeMono->effectRoot; // textures that are resized automatically to match the layer's dimensions layerData->sizeManagedTextures << colorTex << dsTexOrRb; + // Setup for stereo left / eye + layerData->eyeLeft->layerTexture = colorTexLeft; + layerData->eyeRight->layerTexture = colorTexRight; + layerData->eyeLeft->layerDS = dsTexOrRbLeft; + layerData->eyeRight->layerDS = dsTexOrRbRight; + layerData->eyeLeft->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("texLeft"), layerData->eyeLeft->layerTexture); + layerData->eyeRight->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("texRight"), layerData->eyeRight->layerTexture); + layerData->eyeLeft->rtSelector = rtSelectorLeft; + layerData->eyeRight->rtSelector = rtSelectorRight; + layerData->eyeLeft->depthTextureData.rtSelector = layerData->eyeLeft->depthRtSelector; + layerData->eyeRight->depthTextureData.rtSelector = layerData->eyeRight->depthRtSelector; + + layerData->sizeManagedTextures << colorTexLeft << dsTexOrRbLeft; + layerData->sizeManagedTextures << colorTexRight << dsTexOrRbRight; + + layerData->eyeLeft->effectData.effectRoot = layerData->eyeLeft->effectRoot; + layerData->eyeRight->effectData.effectRoot = layerData->eyeRight->effectRoot; + + // Enable/disable correct cameras depending on mode + updateCamerasStatus(layerData); + + m_stereoMode = new Qt3DRender::QParameter(QLatin1String("stereoMode"), + m_engine->viewportSettings()->stereoMode()); + // The effective texture for the layer can change over time (for example // when a postproc effect or progaa becomes active or inactive). Keep the // engine notified about this. - QObject::connect(layerData->compositorSourceParam, &Qt3DRender::QParameter::valueChanged, + QObject::connect(layerData->eyeMono->compositorSourceParam, &Qt3DRender::QParameter::valueChanged, + [this, layer3DS](const QVariant &value) + { + if (value.isValid()) + emit m_engine->layerTextureNodeChanged(layer3DS); + }); + + QObject::connect(layerData->eyeLeft->compositorSourceParam, &Qt3DRender::QParameter::valueChanged, + [this, layer3DS](const QVariant &value) + { + if (value.isValid()) + emit m_engine->layerTextureNodeChanged(layer3DS); + }); + + QObject::connect(layerData->eyeRight->compositorSourceParam, &Qt3DRender::QParameter::valueChanged, [this, layer3DS](const QVariant &value) { if (value.isValid()) emit m_engine->layerTextureNodeChanged(layer3DS); }); + QObject::connect(m_engine, &Q3DSEngine::stereoscopicChanged, + [this, layer3DS, layerData]() + { + // When switching between stereo <-> non-stereo, update used shader + // TODO: Here we disable effects when rendering stereo as msaa+effect currently doesn't work for stereo + int activeEffectCount = layerData->eyeMono->effectData.activeEffectCount; + layerData->effectActive = m_engine->isStereoscopic() ? false : activeEffectCount > 0; + + updateLayerCompositorProgram(layer3DS); + // And enable/disable related cameras + updateCamerasStatus(layerData); + }); + layer3DS->setAttached(layerData); + m_viewports.append(viewports); + // Add the scene contents. Q3DSGraphObject *obj = layer3DS->firstChild(); m_componentNodeStack.clear(); @@ -1318,8 +1480,9 @@ void Q3DSSceneManager::buildLayer(Q3DSLayerNode *layer3DS, if (layer3DS->firstChild()) { auto rayCaster = new Qt3DRender::QRayCaster(layerSceneRootEntity); rayCaster->setFilterMode(Qt3DRender::QAbstractRayCaster::AcceptAnyMatchingLayers); - rayCaster->addLayer(layerData->opaqueTag); - rayCaster->addLayer(layerData->transparentTag); + // Note: picking is done always with eyeMono layers, doesn't change if switching to stereo + rayCaster->addLayer(layerData->eyeMono->opaqueTag); + rayCaster->addLayer(layerData->eyeMono->transparentTag); rayCaster->setRunMode(Qt3DRender::QAbstractRayCaster::SingleShot); layerSceneRootEntity->addComponent(rayCaster); layerData->layerRayCaster = rayCaster; @@ -1376,10 +1539,17 @@ void Q3DSSceneManager::buildSubPresentationLayer(Q3DSLayerNode *layer3DS, const data->layerSize = calculateLayerSize(layer3DS, parentSize); data->parentSize = parentSize; + // Initialize empty datasets for sub presentations + data->eyeMono = new Q3DSEyeData; + data->eyeLeft = new Q3DSEyeData; + data->eyeRight = new Q3DSEyeData; + // camera and stuff stays null, no such thing for subpresentation layers // leave compositorSourceParam dummy for now, we don't know the actual texture yet - data->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("tex"), dummyTexture(layer3DS->id())); + data->eyeMono->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("tex"), dummyTexture(layer3DS->id())); + data->eyeLeft->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("texLeft"), dummyTexture(layer3DS->id())); + data->eyeRight->compositorSourceParam = new Qt3DRender::QParameter(QLatin1String("texRight"), dummyTexture(layer3DS->id())); m_pendingSubPresLayers.insert(layer3DS); // subpresentations associated with layers follow the size of the layer @@ -1740,14 +1910,8 @@ static bool isVerticalAdjust(Q3DSCameraNode *cam3DS, float presentationAspect, f || cam3DS->scaleMode() == Q3DSCameraNode::FitVertical; } -void Q3DSSceneManager::setLayerCameraSizeProperties(Q3DSLayerNode *layer3DS, const QVector2D &offset) +void Q3DSSceneManager::setSingleCameraSizeProperties(Q3DSLayerAttached *data, Qt3DRender::QCamera *camera, const QVector2D &offset) { - Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); - if (!data->cam3DS) - return; - - Qt3DRender::QCamera *camera = static_cast<Qt3DRender::QCamera *>(data->cameraSelector->camera()); - // The aspect ratio is not simply based on the viewport (layer) const float layerAspect = data->layerSize.height() ? data->layerSize.width() / float(data->layerSize.height()) : 0.0f; // ...but may take the presentation design size into account as well. @@ -1811,6 +1975,34 @@ void Q3DSSceneManager::setLayerCameraSizeProperties(Q3DSLayerNode *layer3DS, con } } +void Q3DSSceneManager::setLayerCameraSizeProperties(Q3DSLayerNode *layer3DS, const QVector2D &offset) +{ + Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); + if (!data->cam3DS) + return; + + Qt3DRender::QCamera *camera = static_cast<Qt3DRender::QCamera *>(data->eyeMono->cameraSelector->camera()); + setSingleCameraSizeProperties(data, camera, offset); + if (m_engine->isStereoscopic()) { + Qt3DRender::QCamera *cameraLeft = static_cast<Qt3DRender::QCamera *>(data->eyeLeft->cameraSelector->camera()); + setSingleCameraSizeProperties(data, cameraLeft, offset); + Qt3DRender::QCamera *cameraRight = static_cast<Qt3DRender::QCamera *>(data->eyeRight->cameraSelector->camera()); + setSingleCameraSizeProperties(data, cameraRight, offset); + + // Adjust left & right camera eyeseparation + const float eyeSeparation = m_engine->viewportSettings()->stereoEyeSeparation(); + cameraLeft->setPosition(camera->position()); + cameraRight->setPosition(camera->position()); + cameraLeft->setViewCenter(camera->viewCenter()); + cameraRight->setViewCenter(camera->viewCenter()); + QVector3D leftMovement(-eyeSeparation, 0, 0); + QVector3D rightMovement(eyeSeparation, 0, 0); + cameraLeft->translate(leftMovement, Qt3DRender::QCamera::TranslateViewCenter); + cameraRight->translate(rightMovement, Qt3DRender::QCamera::TranslateViewCenter); + } + +} + void Q3DSSceneManager::setLayerSizeProperties(Q3DSLayerNode *layer3DS) { Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); @@ -1865,8 +2057,13 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS) Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); Q_ASSERT(data); - if (data->clearBuffers) // not available for subpresentation layers - setClearColorForClearBuffers(data->clearBuffers, layer3DS); + // not available for subpresentation layers + if (data->eyeMono->clearBuffers) + setClearColorForClearBuffers(data->eyeMono->clearBuffers, layer3DS); + if (data->eyeLeft->clearBuffers) + setClearColorForClearBuffers(data->eyeLeft->clearBuffers, layer3DS); + if (data->eyeRight->clearBuffers) + setClearColorForClearBuffers(data->eyeRight->clearBuffers, layer3DS); if (data->compositorEntity) // may not exist if this is still buildLayer() data->compositorEntity->setEnabled(layer3DS->flags().testFlag(Q3DSNode::Active) && (data->visibilityTag == Q3DSGraphObjectAttached::Visible)); @@ -1988,25 +2185,39 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS) } } -Qt3DRender::QCamera *Q3DSSceneManager::buildCamera(Q3DSCameraNode *cam3DS, Q3DSLayerNode *layer3DS, Qt3DCore::QEntity *parent) +QVector<Qt3DRender::QCamera *> Q3DSSceneManager::buildCameras(Q3DSCameraNode *cam3DS, + Q3DSLayerNode *layer3DS, + Qt3DCore::QEntity *parent) { + QVector<Qt3DRender::QCamera *> cameras; Qt3DRender::QCamera *camera = new Qt3DRender::QCamera(parent); + cameras << camera; camera->setObjectName(QObject::tr("camera %1 for %2").arg(QString::fromUtf8(cam3DS->id())).arg(QString::fromUtf8(layer3DS->id()))); + Qt3DRender::QCamera *cameraLeft = new Qt3DRender::QCamera(parent); + cameras << cameraLeft; + cameraLeft->setObjectName(QObject::tr("left camera %1 for %2").arg(QString::fromUtf8(cam3DS->id())).arg(QString::fromUtf8(layer3DS->id()))); + Qt3DRender::QCamera *cameraRight = new Qt3DRender::QCamera(parent); + cameras << cameraRight; + cameraRight->setObjectName(QObject::tr("right camera %1 for %2").arg(QString::fromUtf8(cam3DS->id())).arg(QString::fromUtf8(layer3DS->id()))); + Q3DSCameraAttached *data = new Q3DSCameraAttached; data->transform = new Qt3DCore::QTransform; data->camera = camera; + data->cameraLeft = cameraLeft; + data->cameraRight = cameraRight; data->layer3DS = layer3DS; cam3DS->setAttached(data); // Make sure data->entity, globalTransform, etc. are usable. Note however // that during the initial scene building globalVisibility will typically // be false at this stage due to the slideplayer only generating visibility // changes later on. + // TODO: Need to handle separate cameras here somehow? setNodeProperties(cam3DS, camera, data->transform, NodePropUpdateAttached | NodePropUpdateGlobalsRecursively); setCameraProperties(cam3DS, Q3DSNode::TransformChanges); data->propertyChangeObserverIndex = cam3DS->addPropertyChangeObserver( std::bind(&Q3DSSceneManager::handlePropertyChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); data->eventObserverIndex = cam3DS->addEventHandler(QString(), std::bind(&Q3DSSceneManager::handleEvent, this, std::placeholders::_1)); - return camera; + return cameras; } static QMatrix4x4 composeTransformMatrix(const QVector3D &position, const QVector3D &rotation, const QVector3D &scale) @@ -2040,12 +2251,8 @@ static void adjustRotationLeftToRight(QMatrix4x4 *m) p[8 + 1] *= -1; } -void Q3DSSceneManager::setCameraProperties(Q3DSCameraNode *camNode, int changeFlags) +void Q3DSSceneManager::setEyeCameraProperties(Q3DSCameraNode *camNode, Qt3DRender::QCamera *camera) { - Q3DSCameraAttached *data = static_cast<Q3DSCameraAttached *>(camNode->attached()); - Q_ASSERT(data); - Qt3DRender::QCamera *camera = data->camera; - camera->setProjectionType(camNode->orthographic() ? Qt3DRender::QCameraLens::OrthographicProjection : Qt3DRender::QCameraLens::PerspectiveProjection); if (camNode->orthographic()) { @@ -2058,6 +2265,24 @@ void Q3DSSceneManager::setCameraProperties(Q3DSCameraNode *camNode, int changeFl } camera->setNearPlane(camNode->clipNear()); camera->setFarPlane(camNode->clipFar()); +} + +void Q3DSSceneManager::setCameraProperties(Q3DSCameraNode *camNode, int changeFlags) +{ + Q3DSCameraAttached *data = static_cast<Q3DSCameraAttached *>(camNode->attached()); + Q_ASSERT(data); + + // Main camera + Qt3DRender::QCamera *camera = data->camera; + setEyeCameraProperties(camNode, camera); + + // Left camera + Qt3DRender::QCamera *cameraLeft = data->cameraLeft; + setEyeCameraProperties(camNode, cameraLeft); + + // Right camera + Qt3DRender::QCamera *cameraRight = data->cameraRight; + setEyeCameraProperties(camNode, cameraRight); if (!(changeFlags & Q3DSNode::TransformChanges)) return; @@ -2071,16 +2296,22 @@ void Q3DSSceneManager::setCameraProperties(Q3DSCameraNode *camNode, int changeFl adjustRotationLeftToRight(&t); const QVector3D pos(t(0, 3), t(1, 3), lhFactor * t(2, 3)); camera->setPosition(pos); + cameraLeft->setPosition(pos); + cameraRight->setPosition(pos); // For the viewCenter make up some point in the correct direction. const QVector3D d = directionFromTransform(t, leftHanded); const QVector3D center(pos + d); camera->setViewCenter(center); + cameraLeft->setViewCenter(center); + cameraRight->setViewCenter(center); // roll is handled in the up vector QVector3D upVec(0, 1, 0); QMatrix4x4 rotZ; rotZ.rotate(camNode->rotation().z(), 0, 0, -lhFactor); upVec = rotZ * upVec; camera->setUpVector(upVec); + cameraLeft->setUpVector(upVec); + cameraRight->setUpVector(upVec); } // Returns true if the camera actually changed @@ -2091,9 +2322,13 @@ bool Q3DSSceneManager::setActiveLayerCamera(Q3DSCameraNode *cam3DS, Q3DSLayerNod layerData->cam3DS = cam3DS; if (cam3DS) { Q3DSCameraAttached *activeCameraData = static_cast<Q3DSCameraAttached *>(cam3DS->attached()); - layerData->cameraSelector->setCamera(activeCameraData->camera); + layerData->eyeMono->cameraSelector->setCamera(activeCameraData->camera); + layerData->eyeLeft->cameraSelector->setCamera(activeCameraData->cameraLeft); + layerData->eyeRight->cameraSelector->setCamera(activeCameraData->cameraRight); } else { - layerData->cameraSelector->setCamera(nullptr); + layerData->eyeMono->cameraSelector->setCamera(nullptr); + layerData->eyeLeft->cameraSelector->setCamera(nullptr); + layerData->eyeRight->cameraSelector->setCamera(nullptr); } if (cam3DS) { @@ -2160,17 +2395,15 @@ static void prepareSizeDependentTexture(Qt3DRender::QAbstractTexture *texture, data->sizeManagedTextures << Q3DSLayerAttached::SizeManagedTexture(texture, callback, flags); } -void Q3DSSceneManager::setDepthTextureEnabled(Q3DSLayerNode *layer3DS, bool enabled) +void Q3DSSceneManager::setEyeDepthTextureEnabled(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData, bool enabled) { - Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); - Q_ASSERT(data); - if (enabled == data->depthTextureData.enabled) + if (enabled == eyeData->depthTextureData.enabled) return; - data->depthTextureData.enabled = enabled; + eyeData->depthTextureData.enabled = enabled; qCDebug(lcPerf, "Depth texture enabled for layer %s is now %d", layer3DS->id().constData(), enabled); if (enabled) { - if (!data->depthTextureData.depthTexture) { + if (!eyeData->depthTextureData.depthTexture) { Qt3DRender::QTexture2D *depthTex = new Qt3DRender::QTexture2D; m_profiler->trackNewObject(depthTex, Q3DSProfiler::Texture2DObject, "Depth texture for layer %s", layer3DS->id().constData()); @@ -2178,45 +2411,54 @@ void Q3DSSceneManager::setDepthTextureEnabled(Q3DSLayerNode *layer3DS, bool enab depthTex->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear); depthTex->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear); prepareSizeDependentTexture(depthTex, layer3DS); - data->depthTextureData.depthTexture = depthTex; + eyeData->depthTextureData.depthTexture = depthTex; Qt3DRender::QRenderTarget *depthRt = new Qt3DRender::QRenderTarget; m_profiler->trackNewObject(depthRt, Q3DSProfiler::RenderTargetObject, "Depth texture RT for layer %s", layer3DS->id().constData()); Qt3DRender::QRenderTargetOutput *depthRtOutput = new Qt3DRender::QRenderTargetOutput; depthRtOutput->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Depth); - depthRtOutput->setTexture(data->depthTextureData.depthTexture); + depthRtOutput->setTexture(eyeData->depthTextureData.depthTexture); depthRt->addOutput(depthRtOutput); - data->depthTextureData.rtSelector->setTarget(depthRt); + eyeData->depthTextureData.rtSelector->setTarget(depthRt); - data->depthTextureData.clearBuffers = new Qt3DRender::QClearBuffers(data->depthTextureData.rtSelector); - data->depthTextureData.clearBuffers->setBuffers(Qt3DRender::QClearBuffers::DepthBuffer); - new Qt3DRender::QNoDraw(data->depthTextureData.clearBuffers); + eyeData->depthTextureData.clearBuffers = new Qt3DRender::QClearBuffers(eyeData->depthTextureData.rtSelector); + eyeData->depthTextureData.clearBuffers->setBuffers(Qt3DRender::QClearBuffers::DepthBuffer); + new Qt3DRender::QNoDraw(eyeData->depthTextureData.clearBuffers); - Qt3DRender::QRenderPassFilter *depthTexFilter = new Qt3DRender::QRenderPassFilter(data->depthTextureData.rtSelector); + Qt3DRender::QRenderPassFilter *depthTexFilter = new Qt3DRender::QRenderPassFilter(eyeData->depthTextureData.rtSelector); Qt3DRender::QFilterKey *depthFilterKey = new Qt3DRender::QFilterKey; depthFilterKey->setName(QLatin1String("pass")); depthFilterKey->setValue(QLatin1String("depth")); depthTexFilter->addMatch(depthFilterKey); Qt3DRender::QSortPolicy *sortPolicy = opaquePassSortPolicy(depthTexFilter); - data->depthTextureData.layerFilterOpaque = new Qt3DRender::QLayerFilter(sortPolicy); + eyeData->depthTextureData.layerFilterOpaque = new Qt3DRender::QLayerFilter(sortPolicy); - depthTexFilter = new Qt3DRender::QRenderPassFilter(data->depthTextureData.rtSelector); + depthTexFilter = new Qt3DRender::QRenderPassFilter(eyeData->depthTextureData.rtSelector); depthTexFilter->addMatch(depthFilterKey); sortPolicy = transparentPassSortPolicy(depthTexFilter); - data->depthTextureData.layerFilterTransparent = new Qt3DRender::QLayerFilter(sortPolicy); + eyeData->depthTextureData.layerFilterTransparent = new Qt3DRender::QLayerFilter(sortPolicy); } - data->depthTextureData.clearBuffers->setEnabled(true); - data->depthTextureData.layerFilterOpaque->addLayer(data->opaqueTag); - data->depthTextureData.layerFilterTransparent->addLayer(data->transparentTag); - } else if (data->depthTextureData.depthTexture) { - data->depthTextureData.clearBuffers->setEnabled(false); - data->depthTextureData.layerFilterOpaque->removeLayer(data->opaqueTag); - data->depthTextureData.layerFilterTransparent->removeLayer(data->transparentTag); + eyeData->depthTextureData.clearBuffers->setEnabled(true); + eyeData->depthTextureData.layerFilterOpaque->addLayer(eyeData->opaqueTag); + eyeData->depthTextureData.layerFilterTransparent->addLayer(eyeData->transparentTag); + } else if (eyeData->depthTextureData.depthTexture) { + eyeData->depthTextureData.clearBuffers->setEnabled(false); + eyeData->depthTextureData.layerFilterOpaque->removeLayer(eyeData->opaqueTag); + eyeData->depthTextureData.layerFilterTransparent->removeLayer(eyeData->transparentTag); } } +void Q3DSSceneManager::setDepthTextureEnabled(Q3DSLayerNode *layer3DS, bool enabled) +{ + Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); + Q_ASSERT(data); + setEyeDepthTextureEnabled(layer3DS, data->eyeMono, enabled); + setEyeDepthTextureEnabled(layer3DS, data->eyeLeft, enabled); + setEyeDepthTextureEnabled(layer3DS, data->eyeRight, enabled); +} + void Q3DSSceneManager::setSsaoTextureEnabled(Q3DSLayerNode *layer3DS, bool enabled) { Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); @@ -2266,7 +2508,8 @@ void Q3DSSceneManager::setSsaoTextureEnabled(Q3DSLayerNode *layer3DS, bool enabl data->ssaoTextureData.depthSampler = new Qt3DRender::QParameter; data->ssaoTextureData.depthSampler->setName(QLatin1String("depth_sampler")); - data->ssaoTextureData.depthSampler->setValue(QVariant::fromValue(data->depthTextureData.depthTexture)); + // TODO: Duplicate this method if ssaoTextureData is also duplicated and do for all eyes + data->ssaoTextureData.depthSampler->setValue(QVariant::fromValue(data->eyeMono->depthTextureData.depthTexture)); data->ssaoTextureData.aoDataBuf = new Qt3DRender::QBuffer(data->entity); data->ssaoTextureData.aoDataBuf->setObjectName(QLatin1String("ambient occlusion pass constant buffer")); @@ -2321,8 +2564,8 @@ void Q3DSSceneManager::updateAoParameters(Q3DSLayerNode *layer3DS) layer3DS->shadowBias()); float R2 = layer3DS->aoDistance() * layer3DS->aoDistance() * 0.16f; - float rw = data->depthTextureData.depthTexture->width(); - float rh = data->depthTextureData.depthTexture->height(); + float rw = data->eyeMono->depthTextureData.depthTexture->width(); + float rh = data->eyeMono->depthTextureData.depthTexture->height(); float fov = data->cam3DS ? qDegreesToRadians(data->cam3DS->verticalFov(rw / rh)) : 1.0f; float tanHalfFovY = qTan(0.5f * fov * (rh / rw)); float invFocalLenX = tanHalfFovY * (rw / rh); @@ -2432,8 +2675,11 @@ void Q3DSSceneManager::updateCubeShadowCam(Q3DSLayerAttached::PerLightShadowMapD shadowCam->setAspectRatio(1); } -void Q3DSSceneManager::genCubeBlurPassFg(Q3DSLayerAttached::PerLightShadowMapData *d, Qt3DRender::QAbstractTexture *inTex, - Qt3DRender::QAbstractTexture *outTex, const QString &passName, Q3DSLightNode *light3DS) +void Q3DSSceneManager::genCubeBlurPassFg(Q3DSLayerAttached::PerLightShadowMapData *d, + Qt3DRender::QAbstractTexture *inTex, + Qt3DRender::QAbstractTexture *outTex, + const QString &passName, + Q3DSLightNode *light3DS) { Qt3DRender::QRenderTargetSelector *rtSelector = new Qt3DRender::QRenderTargetSelector(d->subTreeRoot); Qt3DRender::QRenderTarget *rt = new Qt3DRender::QRenderTarget; @@ -2815,8 +3061,12 @@ void Q3DSSceneManager::updateShadowMapStatus(Q3DSLayerNode *layer3DS, bool *smDi } // verify no globally used parameters get parented to this volatile framegraph subtree - Q_ASSERT(layerData->opaqueTag->parent()); - Q_ASSERT(layerData->transparentTag->parent()); + Q_ASSERT(layerData->eyeMono->opaqueTag->parent()); + Q_ASSERT(layerData->eyeMono->transparentTag->parent()); + Q_ASSERT(layerData->eyeLeft->opaqueTag->parent()); + Q_ASSERT(layerData->eyeLeft->transparentTag->parent()); + Q_ASSERT(layerData->eyeRight->opaqueTag->parent()); + Q_ASSERT(layerData->eyeRight->transparentTag->parent()); Q_ASSERT(layerData->cameraPropertiesParam->parent()); Q_ASSERT(m_fsQuadTag->parent()); @@ -2864,7 +3114,9 @@ void Q3DSSceneManager::updateShadowMapStatus(Q3DSLayerNode *layer3DS, bool *smDi shadowFilter->addMatch(shadowFilterKey); Qt3DRender::QSortPolicy *sortPolicyOpaque = opaquePassSortPolicy(shadowFilter); Qt3DRender::QLayerFilter *layerFilterOpaque = new Qt3DRender::QLayerFilter(sortPolicyOpaque); - layerFilterOpaque->addLayer(layerData->opaqueTag); + layerFilterOpaque->addLayer(layerData->eyeMono->opaqueTag); + layerFilterOpaque->addLayer(layerData->eyeLeft->opaqueTag); + layerFilterOpaque->addLayer(layerData->eyeRight->opaqueTag); shadowFilter->addParameter(d->cameraPositionParam); shadowFilter->addParameter(layerData->cameraPropertiesParam); @@ -2872,16 +3124,19 @@ void Q3DSSceneManager::updateShadowMapStatus(Q3DSLayerNode *layer3DS, bool *smDi shadowFilter->addMatch(shadowFilterKey); Qt3DRender::QSortPolicy *sortPolicyTransparent = transparentPassSortPolicy(shadowFilter); Qt3DRender::QLayerFilter *layerFilterTransparent = new Qt3DRender::QLayerFilter(sortPolicyTransparent); - layerFilterTransparent->addLayer(layerData->transparentTag); + layerFilterTransparent->addLayer(layerData->eyeMono->transparentTag); + layerFilterTransparent->addLayer(layerData->eyeLeft->transparentTag); + layerFilterTransparent->addLayer(layerData->eyeRight->transparentTag); shadowFilter->addParameter(d->cameraPositionParam); shadowFilter->addParameter(layerData->cameraPropertiesParam); } - // Now two blur passes that output to the final texture, play ping pong. if (m_gfxLimits.maxDrawBuffers >= 6) { // ### // Draws a fullscreen quad into the 6 faces of the cubemap texture (COLOR0..5), with the other texture as input. - genCubeBlurPassFg(d, d->shadowMapTexture, d->shadowMapTextureTemp, QLatin1String("shadowCubeBlurX"), light3DS); - genCubeBlurPassFg(d, d->shadowMapTextureTemp, d->shadowMapTexture, QLatin1String("shadowCubeBlurY"), light3DS); + genCubeBlurPassFg(d, d->shadowMapTexture, d->shadowMapTextureTemp, + QLatin1String("shadowCubeBlurX"), light3DS); + genCubeBlurPassFg(d, d->shadowMapTextureTemp, d->shadowMapTexture, + QLatin1String("shadowCubeBlurY"), light3DS); } // set QParameter names and values @@ -2925,18 +3180,21 @@ void Q3DSSceneManager::updateShadowMapStatus(Q3DSLayerNode *layer3DS, bool *smDi shadowFilter->addMatch(shadowFilterKey); Qt3DRender::QSortPolicy *sortPolicyOpaque = opaquePassSortPolicy(shadowFilter); Qt3DRender::QLayerFilter *layerFilterOpaque = new Qt3DRender::QLayerFilter(sortPolicyOpaque); - layerFilterOpaque->addLayer(layerData->opaqueTag); + layerFilterOpaque->addLayer(layerData->eyeMono->opaqueTag); + layerFilterOpaque->addLayer(layerData->eyeLeft->opaqueTag); + layerFilterOpaque->addLayer(layerData->eyeRight->opaqueTag); shadowFilter = new Qt3DRender::QRenderPassFilter(camSel); shadowFilter->addMatch(shadowFilterKey); Qt3DRender::QSortPolicy *sortPolicyTransparent = transparentPassSortPolicy(shadowFilter); Qt3DRender::QLayerFilter *layerFilterTransparent = new Qt3DRender::QLayerFilter(sortPolicyTransparent); - layerFilterTransparent->addLayer(layerData->transparentTag); + layerFilterTransparent->addLayer(layerData->eyeMono->transparentTag); + layerFilterTransparent->addLayer(layerData->eyeLeft->transparentTag); + layerFilterTransparent->addLayer(layerData->eyeRight->transparentTag); // 2 blur passes genOrthoBlurPassFg(d, d->shadowMapTexture, d->shadowMapTextureTemp, QLatin1String("shadowOrthoBlurX"), light3DS); genOrthoBlurPassFg(d, d->shadowMapTextureTemp, d->shadowMapTexture, QLatin1String("shadowOrthoBlurY"), light3DS); - // set QParameter names and values updateOrthoShadowMapParams(d, light3DS, lightIndexStr); custMatOrthoShadowMaps.append(QVariant::fromValue(d->shadowMapTexture)); @@ -3046,7 +3304,7 @@ static QVector2D adjustedProgressiveTemporalAAVertexOffset(const QVector2D &vert vertexOffset.y() / (layerPixelSize.height() / 2.0f) * camZ); } -void Q3DSSceneManager::stealLayerRenderTarget(Qt3DRender::QAbstractTexture **stolenColorBuf, Q3DSLayerNode *layer3DS) +void Q3DSSceneManager::stealLayerRenderTarget(Qt3DRender::QAbstractTexture **stolenColorBuf, Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData) { Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); if (*stolenColorBuf) { @@ -3054,7 +3312,7 @@ void Q3DSSceneManager::stealLayerRenderTarget(Qt3DRender::QAbstractTexture **sto data->sizeManagedTextures.removeOne(*stolenColorBuf); delete *stolenColorBuf; } - *stolenColorBuf = data->effectActive ? data->effLayerTexture : data->layerTexture; + *stolenColorBuf = data->effectActive ? eyeData->effLayerTexture : eyeData->layerTexture; // create a whole new render target for the layer data->sizeManagedTextures.removeOne(*stolenColorBuf); const QSize layerPixelSize = safeLayerPixelSize(data); @@ -3065,16 +3323,16 @@ void Q3DSSceneManager::stealLayerRenderTarget(Qt3DRender::QAbstractTexture **sto &colorTex, &dsTexOrRb, data->layerFgRoot, layer3DS, - data->layerDS); // keep using the existing depth-stencil buffer - Qt3DRender::QRenderTarget *oldRt = data->rtSelector->target(); - data->rtSelector->setTarget(rt); + eyeData->layerDS); // keep using the existing depth-stencil buffer + Qt3DRender::QRenderTarget *oldRt = eyeData->rtSelector->target(); + eyeData->rtSelector->setTarget(rt); delete oldRt; if (data->effectActive) { data->sizeManagedTextures.append(colorTex); - data->effLayerTexture = colorTex; + eyeData->effLayerTexture = colorTex; } else { data->sizeManagedTextures.insert(0, colorTex); - data->layerTexture = colorTex; + eyeData->layerTexture = colorTex; } // update descriptions for profiler @@ -3099,26 +3357,16 @@ Qt3DRender::QAbstractTexture *Q3DSSceneManager::createProgressiveTemporalAAExtra static const int MAX_AA_LEVELS = 8; -// Called once per frame (in the preparation step from the frame action) for -// each progressive AA enabled layer. -bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) +// Called once per eye (mono, left, right) for each progressive AA enabled layer. +bool Q3DSSceneManager::updateEyeProgressiveAA(Q3DSLayerNode *layer3DS, Q3DSLayerAttached *data, Q3DSEyeData *eyeData) { - Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); - if (!data || !data->cam3DS) - return false; - - // No prog.aa for msaa/ssaa layers - 3DS1 supports this (would need the - // usual resolve step with blitframebuffer) but we'll live with this - // limitation for now. - if (data->msaaSampleCount > 1 || data->ssaaScaleFactor > 1) - return false; - // When a frame applies an offset to the camera's matrix, the next frame // must reset it. This must happen regardless of having PAA active in the // next frame. - if (data->progAA.cameraAltered) { - data->progAA.cameraAltered = false; - setLayerCameraSizeProperties(layer3DS); + if (eyeData->progAA.cameraAltered) { + eyeData->progAA.cameraAltered = false; + Qt3DRender::QCamera *camera = static_cast<Qt3DRender::QCamera *>(eyeData->cameraSelector->camera()); + setSingleCameraSizeProperties(data, camera); } if (data->layerSize.isEmpty()) @@ -3129,7 +3377,7 @@ bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) // the pass index otherwise. #ifndef PAA_ALWAYS_ON if (data->wasDirty) - data->progAA.pass = 0; + eyeData->progAA.pass = 0; #endif // Do not start accumulating before at least 2 non-dirty frames. This is @@ -3154,32 +3402,31 @@ bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) break; } - if (data->progAA.pass > maxPass) { + if (eyeData->progAA.pass > maxPass) { // State is Idle. Keep displaying the output in currentOutputTexture until // wasDirty becomes true and so pass gets reset. - if (data->progAA.layerFilter->layers().contains(m_fsQuadTag)) - data->progAA.layerFilter->removeLayer(m_fsQuadTag); + if (eyeData->progAA.layerFilter->layers().contains(m_fsQuadTag)) + eyeData->progAA.layerFilter->removeLayer(m_fsQuadTag); #ifdef PAA_ALWAYS_ON - data->progAA.pass = 0; + eyeData->progAA.pass = 0; #endif return true; } - if (data->progAA.pass < 1 + PROGAA_FRAME_DELAY) { + if (eyeData->progAA.pass < 1 + PROGAA_FRAME_DELAY) { // State is Inactive. Must make sure no progAA output is shown. - ++data->progAA.pass; - if (data->progAA.enabled) { + ++eyeData->progAA.pass; + if (eyeData->progAA.enabled) { qCDebug(lcScene, "Stopping progressive AA for layer %s", layer3DS->id().constData()); - data->progAA.enabled = false; - data->compositorSourceParam->setValue(QVariant::fromValue(data->effectActive ? data->effLayerTexture : data->layerTexture)); - + eyeData->progAA.enabled = false; + eyeData->compositorSourceParam->setValue(QVariant::fromValue(data->effectActive ? eyeData->effLayerTexture : eyeData->layerTexture)); // Do not delete and then recreate the framegraph subtree since // that is likely way too expensive. Keep it around instead and // disable it by making sure it has no renderable entities. This // could have already been done in the > maxPass branch above but // won't hurt to repeat since we may get a dirty frame before // reaching the maximum accumulation level. - data->progAA.layerFilter->removeLayer(m_fsQuadTag); + eyeData->progAA.layerFilter->removeLayer(m_fsQuadTag); } return false; } @@ -3208,21 +3455,22 @@ bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) QVector2D(0.111111f, 0.888889f), // 8x }; - const int factorsIdx = data->progAA.pass - (1 + PROGAA_FRAME_DELAY); + const int factorsIdx = eyeData->progAA.pass - (1 + PROGAA_FRAME_DELAY); const QVector2D vertexOffset = adjustedProgressiveTemporalAAVertexOffset(vertexOffsets[factorsIdx], data); const QVector2D blendFactor = blendFactors[factorsIdx]; - if (!data->progAA.enabled && data->progAA.fg && data->progAA.layerFilter) - data->progAA.layerFilter->addLayer(m_fsQuadTag); + if (!eyeData->progAA.enabled && eyeData->progAA.fg && eyeData->progAA.layerFilter) + eyeData->progAA.layerFilter->addLayer(m_fsQuadTag); - data->progAA.enabled = true; + eyeData->progAA.enabled = true; if (factorsIdx == 0) qCDebug(lcPerf, "Kicking off progressive AA for layer %s", layer3DS->id().constData()); // Alter the camera's matrix by a little movement based on the current // vertexOffset. This applies to the camera used by the main layer passes. - setLayerCameraSizeProperties(layer3DS, vertexOffset); - data->progAA.cameraAltered = true; + Qt3DRender::QCamera *camera = static_cast<Qt3DRender::QCamera *>(eyeData->cameraSelector->camera()); + setSingleCameraSizeProperties(data, camera, vertexOffset); + eyeData->progAA.cameraAltered = true; // data->layerTexture is the original contents (albeit with jiggled // camera), generated by the main layer passes. Have two additional @@ -3230,24 +3478,24 @@ bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) // be used as output in the next frame). The layer compositor will be // switched over to use always the one that is the (blended) output. - if (!data->progAA.extraColorBuf) - data->progAA.extraColorBuf = createProgressiveTemporalAAExtraBuffer(layer3DS); + if (!eyeData->progAA.extraColorBuf) + eyeData->progAA.extraColorBuf = createProgressiveTemporalAAExtraBuffer(layer3DS); // ### depth, ssao, shadow passes in the main layer framegraph subtree should be disabled when pass > 0 // For the other buffer there is no new texture needed - instead, // steal data->(eff)layerTexture. (note: already in sizeManagedTextures) if (factorsIdx == 0) { - stealLayerRenderTarget(&data->progAA.stolenColorBuf, layer3DS); + stealLayerRenderTarget(&eyeData->progAA.stolenColorBuf, layer3DS, eyeData); // start with the existing color buffer as the accumulator - data->progAA.currentAccumulatorTexture = data->progAA.stolenColorBuf; - data->progAA.currentOutputTexture = data->progAA.extraColorBuf; + eyeData->progAA.currentAccumulatorTexture = eyeData->progAA.stolenColorBuf; + eyeData->progAA.currentOutputTexture = eyeData->progAA.extraColorBuf; } // ### have to do this on every new PAA run since accumulatorTexture changes above. // It is an overkill though since the framegraph could be generated just once. if (factorsIdx == 0) { - delete data->progAA.fg; + delete eyeData->progAA.fg; // set up a framegraph subtree to render a quad @@ -3256,8 +3504,8 @@ bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) // ones. This is probably more efficient anyways (since it results in // binding a different FBO, without altering attachments). - data->progAA.curTarget = 0; - for (Qt3DRender::QAbstractTexture *t : { data->progAA.currentOutputTexture, data->progAA.currentAccumulatorTexture }) { + eyeData->progAA.curTarget = 0; + for (Qt3DRender::QAbstractTexture *t : { eyeData->progAA.currentOutputTexture, eyeData->progAA.currentAccumulatorTexture }) { Qt3DRender::QRenderTargetOutput *rtOutput = new Qt3DRender::QRenderTargetOutput; rtOutput->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Color0); rtOutput->setTexture(t); @@ -3265,39 +3513,39 @@ bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) m_profiler->trackNewObject(rt, Q3DSProfiler::RenderTargetObject, layer3DS->id(), "Progressive AA RT for layer %s", layer3DS->id().constData()); rt->addOutput(rtOutput); - data->progAA.rts[data->progAA.curTarget++] = rt; + eyeData->progAA.rts[eyeData->progAA.curTarget++] = rt; } - data->progAA.curTarget = 0; + eyeData->progAA.curTarget = 0; auto rtSel = createProgressiveTemporalAAFramegraph(data->layerFgRoot, - data->progAA.rts[data->progAA.curTarget], + eyeData->progAA.rts[eyeData->progAA.curTarget], m_fsQuadTag, - &data->progAA.layerFilter, - &data->progAA.accumTexParam, - &data->progAA.lastTexParam, - &data->progAA.blendFactorsParam); - data->progAA.fg = data->progAA.rtSel = rtSel; + &eyeData->progAA.layerFilter, + &eyeData->progAA.accumTexParam, + &eyeData->progAA.lastTexParam, + &eyeData->progAA.blendFactorsParam); + eyeData->progAA.fg = eyeData->progAA.rtSel = rtSel; } // Input - data->progAA.accumTexParam->setValue(QVariant::fromValue(data->progAA.currentAccumulatorTexture)); - data->progAA.lastTexParam->setValue(QVariant::fromValue(data->effectActive ? data->effLayerTexture : data->layerTexture)); - data->progAA.blendFactorsParam->setValue(blendFactor); + eyeData->progAA.accumTexParam->setValue(QVariant::fromValue(eyeData->progAA.currentAccumulatorTexture)); + eyeData->progAA.lastTexParam->setValue(QVariant::fromValue(data->effectActive ? eyeData->effLayerTexture : eyeData->layerTexture)); + eyeData->progAA.blendFactorsParam->setValue(blendFactor); // Output - data->progAA.rtSel->setTarget(data->progAA.rts[data->progAA.curTarget]); + eyeData->progAA.rtSel->setTarget(eyeData->progAA.rts[eyeData->progAA.curTarget]); // have the compositor use the blended results instead of the layer texture - data->compositorSourceParam->setValue(QVariant::fromValue(data->progAA.currentOutputTexture)); + eyeData->compositorSourceParam->setValue(QVariant::fromValue(eyeData->progAA.currentOutputTexture)); // In the next PAA round (i.e. the frame after the next one) reuse accumTex // as the output and the current output as accumTex... - std::swap(data->progAA.currentAccumulatorTexture, data->progAA.currentOutputTexture); + std::swap(eyeData->progAA.currentAccumulatorTexture, eyeData->progAA.currentOutputTexture); // ...whereas the output of the next frame will be used as input, so flip the // index to use the correct render target. - data->progAA.curTarget = 1 - data->progAA.curTarget; + eyeData->progAA.curTarget = 1 - eyeData->progAA.curTarget; - ++data->progAA.pass; + ++eyeData->progAA.pass; // Make sure layer caching does not interfere and the layer's framegraph subtree // is still active as long as PAA accumulation is active. @@ -3306,26 +3554,42 @@ bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) return true; } -void Q3DSSceneManager::updateTemporalAA(Q3DSLayerNode *layer3DS) +// Called once per frame (in the preparation step from the frame action) for +// each progressive AA enabled layer. +bool Q3DSSceneManager::updateProgressiveAA(Q3DSLayerNode *layer3DS) { - static const int MAX_TEMPORAL_AA_LEVELS = 2; - - Q3DSLayerAttached *data = layer3DS->attached<Q3DSLayerAttached>(); + Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); if (!data || !data->cam3DS) - return; + return false; - // No TempAA for MSAA/SSAA layers for now. + // No prog.aa for msaa/ssaa layers - 3DS1 supports this (would need the + // usual resolve step with blitframebuffer) but we'll live with this + // limitation for now. if (data->msaaSampleCount > 1 || data->ssaaScaleFactor > 1) - return; + return false; + + bool returnValue = false; + if (!m_engine->isStereoscopic()) { + returnValue = updateEyeProgressiveAA(layer3DS, data, data->eyeMono); + } else { + updateEyeProgressiveAA(layer3DS, data, data->eyeLeft); + returnValue = updateEyeProgressiveAA(layer3DS, data, data->eyeRight); + } + return returnValue; +} + +void Q3DSSceneManager::updateEyeTemporalAA(Q3DSLayerNode *layer3DS, Q3DSLayerAttached *data, Q3DSEyeData *eyeData) +{ + static const int MAX_TEMPORAL_AA_LEVELS = 2; // Temporal AA is like a 2x progressive AA while the layer has movement (is // dirty). Accumulation stops after 2 non-dirty frames but apart from that // it is "always on". if (data->wasDirty) - data->tempAA.nonDirtyPass = 0; + eyeData->tempAA.nonDirtyPass = 0; - if (data->tempAA.nonDirtyPass >= MAX_TEMPORAL_AA_LEVELS) + if (eyeData->tempAA.nonDirtyPass >= MAX_TEMPORAL_AA_LEVELS) return; static const QVector2D vertexOffsets[MAX_TEMPORAL_AA_LEVELS] = { @@ -3334,18 +3598,18 @@ void Q3DSSceneManager::updateTemporalAA(Q3DSLayerNode *layer3DS) }; static const QVector2D blendFactor(0.5f, 0.5f); - const QVector2D vertexOffset = adjustedProgressiveTemporalAAVertexOffset(vertexOffsets[data->tempAA.passIndex], data); + const QVector2D vertexOffset = adjustedProgressiveTemporalAAVertexOffset(vertexOffsets[eyeData->tempAA.passIndex], data); - if (!data->tempAA.fg) { - data->tempAA.extraColorBuf = createProgressiveTemporalAAExtraBuffer(layer3DS); + if (!eyeData->tempAA.fg) { + eyeData->tempAA.extraColorBuf = createProgressiveTemporalAAExtraBuffer(layer3DS); - stealLayerRenderTarget(&data->tempAA.stolenColorBuf, layer3DS); + stealLayerRenderTarget(&eyeData->tempAA.stolenColorBuf, layer3DS, eyeData); // start with the existing color buffer as the accumulator - data->tempAA.currentAccumulatorTexture = data->tempAA.stolenColorBuf; - data->tempAA.currentOutputTexture = data->tempAA.extraColorBuf; + eyeData->tempAA.currentAccumulatorTexture = eyeData->tempAA.stolenColorBuf; + eyeData->tempAA.currentOutputTexture = eyeData->tempAA.extraColorBuf; int i = 0; - for (Qt3DRender::QAbstractTexture *t : { data->tempAA.currentOutputTexture, data->tempAA.currentAccumulatorTexture }) { + for (Qt3DRender::QAbstractTexture *t : { eyeData->tempAA.currentOutputTexture, eyeData->tempAA.currentAccumulatorTexture }) { Qt3DRender::QRenderTargetOutput *rtOutput = new Qt3DRender::QRenderTargetOutput; rtOutput->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Color0); rtOutput->setTexture(t); @@ -3353,44 +3617,63 @@ void Q3DSSceneManager::updateTemporalAA(Q3DSLayerNode *layer3DS) m_profiler->trackNewObject(rt, Q3DSProfiler::RenderTargetObject, layer3DS->id(), "Temporal AA RT for layer %s", layer3DS->id().constData()); rt->addOutput(rtOutput); - data->tempAA.rts[i++] = rt; + eyeData->tempAA.rts[i++] = rt; } auto rtSel = createProgressiveTemporalAAFramegraph(data->layerFgRoot, - data->tempAA.rts[0], + eyeData->tempAA.rts[0], m_fsQuadTag, - &data->tempAA.layerFilter, - &data->tempAA.accumTexParam, - &data->tempAA.lastTexParam, - &data->tempAA.blendFactorsParam); - data->tempAA.fg = data->tempAA.rtSel = rtSel; + &eyeData->tempAA.layerFilter, + &eyeData->tempAA.accumTexParam, + &eyeData->tempAA.lastTexParam, + &eyeData->tempAA.blendFactorsParam); + eyeData->tempAA.fg = eyeData->tempAA.rtSel = rtSel; - data->tempAA.layerFilter->addLayer(m_fsQuadTag); + eyeData->tempAA.layerFilter->addLayer(m_fsQuadTag); } // Jiggle - setLayerCameraSizeProperties(layer3DS, vertexOffset); + Qt3DRender::QCamera *camera = static_cast<Qt3DRender::QCamera *>(eyeData->cameraSelector->camera()); + setSingleCameraSizeProperties(data, camera, vertexOffset); // Input - data->tempAA.accumTexParam->setValue(QVariant::fromValue(data->tempAA.currentAccumulatorTexture)); - data->tempAA.lastTexParam->setValue(QVariant::fromValue(data->effectActive ? data->effLayerTexture : data->layerTexture)); - data->tempAA.blendFactorsParam->setValue(blendFactor); + eyeData->tempAA.accumTexParam->setValue(QVariant::fromValue(eyeData->tempAA.currentAccumulatorTexture)); + eyeData->tempAA.lastTexParam->setValue(QVariant::fromValue(data->effectActive ? eyeData->effLayerTexture : eyeData->layerTexture)); + eyeData->tempAA.blendFactorsParam->setValue(blendFactor); // Output - data->tempAA.rtSel->setTarget(data->tempAA.rts[data->tempAA.passIndex]); + eyeData->tempAA.rtSel->setTarget(eyeData->tempAA.rts[eyeData->tempAA.passIndex]); // have the compositor use the blended results instead of the layer texture - data->compositorSourceParam->setValue(QVariant::fromValue(data->tempAA.currentOutputTexture)); + eyeData->compositorSourceParam->setValue(QVariant::fromValue(eyeData->tempAA.currentOutputTexture)); // play ping-pong with the buffers - std::swap(data->tempAA.currentAccumulatorTexture, data->tempAA.currentOutputTexture); + std::swap(eyeData->tempAA.currentAccumulatorTexture, eyeData->tempAA.currentOutputTexture); // Make sure layer caching does not interfere and the layer's framegraph subtree // is still active as long as TAA accumulation is active. m_layerUncachePending = true; - data->tempAA.passIndex = (data->tempAA.passIndex + 1) % MAX_TEMPORAL_AA_LEVELS; - ++data->tempAA.nonDirtyPass; + eyeData->tempAA.passIndex = (eyeData->tempAA.passIndex + 1) % MAX_TEMPORAL_AA_LEVELS; + ++eyeData->tempAA.nonDirtyPass; +} + +void Q3DSSceneManager::updateTemporalAA(Q3DSLayerNode *layer3DS) +{ + Q3DSLayerAttached *data = layer3DS->attached<Q3DSLayerAttached>(); + if (!data || !data->cam3DS) + return; + + // No TempAA for MSAA/SSAA layers for now. + if (data->msaaSampleCount > 1 || data->ssaaScaleFactor > 1) + return; + + if (!m_engine->isStereoscopic()) { + updateEyeTemporalAA(layer3DS, data, data->eyeMono); + } else { + updateEyeTemporalAA(layer3DS, data, data->eyeLeft); + updateEyeTemporalAA(layer3DS, data, data->eyeRight); + } } static void setLayerBlending(Qt3DRender::QBlendEquation *blendFunc, @@ -3516,10 +3799,23 @@ void Q3DSSceneManager::buildLayerQuadEntity(Q3DSLayerNode *layer3DS, Qt3DCore::Q if (!flags.testFlag(LayerQuadCustomShader)) { updateLayerCompositorProgram(layer3DS); - if (!data->compositorSourceParam->parent()) - data->compositorSourceParam->setParent(data->layerSceneRootEntity); + if (!data->eyeMono->compositorSourceParam->parent()) + data->eyeMono->compositorSourceParam->setParent(data->layerSceneRootEntity); + + renderPass->addParameter(data->eyeMono->compositorSourceParam); + + if (!data->eyeLeft->compositorSourceParam->parent()) + data->eyeLeft->compositorSourceParam->setParent(data->layerSceneRootEntity); + + renderPass->addParameter(data->eyeLeft->compositorSourceParam); - renderPass->addParameter(data->compositorSourceParam); + if (!data->eyeRight->compositorSourceParam->parent()) + data->eyeRight->compositorSourceParam->setParent(data->layerSceneRootEntity); + + renderPass->addParameter(data->eyeRight->compositorSourceParam); + + // Add stereo parameter + renderPass->addParameter(m_stereoMode); } technique->addRenderPass(renderPass); @@ -3563,14 +3859,18 @@ void Q3DSSceneManager::updateLayerCompositorProgram(Q3DSLayerNode *layer3DS) fragSuffix = QLatin1String("_core.frag"); } + QString stereoString; + if (m_engine->isStereoscopic()) + stereoString = QLatin1String("_stereoscopic"); + const QString vertSrc = QLatin1String("qrc:/q3ds/shaders/compositor") + vertSuffix; shaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(vertSrc))); QString fragSrc; if (msaa) - fragSrc = QLatin1String("qrc:/q3ds/shaders/compositor_ms") + QString::number(data->msaaSampleCount) + fragSuffix; + fragSrc = QLatin1String("qrc:/q3ds/shaders/compositor_ms") + QString::number(data->msaaSampleCount) + stereoString + fragSuffix; else - fragSrc = QLatin1String("qrc:/q3ds/shaders/compositor") + fragSuffix; + fragSrc = QLatin1String("qrc:/q3ds/shaders/compositor") + stereoString + fragSuffix; shaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(fragSrc))); @@ -3815,7 +4115,8 @@ void Q3DSSceneManager::rebuildCompositorLayerChain() baseLayerParam->setValue(QVariant::fromValue(data->advBlend.tempTexture)); Qt3DRender::QParameter *blendLayerParam = new Qt3DRender::QParameter; blendLayerParam->setName(QLatin1String("blend_layer")); - blendLayerParam->setValue(QVariant::fromValue(data->effectActive ? data->effLayerTexture : data->layerTexture)); + // TODO: Needs to be duplicated for all eyes? + blendLayerParam->setValue(QVariant::fromValue(data->effectActive ? data->eyeMono->effLayerTexture : data->eyeMono->layerTexture)); renderPass->addParameter(baseLayerParam); renderPass->addParameter(blendLayerParam); } else { @@ -4034,6 +4335,7 @@ void Q3DSSceneManager::buildLayerScene(Q3DSGraphObject *obj, Q3DSLayerNode *laye } Qt3DCore::QEntity *newEntity = nullptr; + QVector<Qt3DRender::QCamera *> cameras; switch (obj->type()) { case Q3DSGraphObject::Light: @@ -4080,8 +4382,10 @@ void Q3DSSceneManager::buildLayerScene(Q3DSGraphObject *obj, Q3DSLayerNode *laye } break; case Q3DSGraphObject::Camera: - newEntity = buildCamera(static_cast<Q3DSCameraNode *>(obj), layer3DS, parent); - addChildren(obj, newEntity); + cameras = buildCameras(static_cast<Q3DSCameraNode *>(obj), layer3DS, parent); + addChildren(obj, cameras.at(0)); + addChildren(obj, cameras.at(1)); + addChildren(obj, cameras.at(2)); break; case Q3DSGraphObject::Alias: { @@ -5057,12 +5361,24 @@ void Q3DSSceneManager::retagSubMeshes(Q3DSModelNode *model3DS) } if (data->globalEffectiveVisibility) { - Qt3DRender::QLayer *newTag = sm.hasTransparency ? layerData->transparentTag : layerData->opaqueTag; + Qt3DRender::QLayer *newTag = sm.hasTransparency ? layerData->eyeMono->transparentTag : layerData->eyeMono->opaqueTag; if (!sm.entity->components().contains(newTag)) { - Qt3DRender::QLayer *prevTag = newTag == layerData->transparentTag ? layerData->opaqueTag : layerData->transparentTag; + Qt3DRender::QLayer *prevTag = newTag == layerData->eyeMono->transparentTag ? layerData->eyeMono->opaqueTag : layerData->eyeMono->transparentTag; sm.entity->removeComponent(prevTag); sm.entity->addComponent(newTag); } + Qt3DRender::QLayer *newTagLeft = sm.hasTransparency ? layerData->eyeLeft->transparentTag : layerData->eyeLeft->opaqueTag; + if (!sm.entity->components().contains(newTagLeft)) { + Qt3DRender::QLayer *prevTagLeft = newTagLeft == layerData->eyeLeft->transparentTag ? layerData->eyeLeft->opaqueTag : layerData->eyeLeft->transparentTag; + sm.entity->removeComponent(prevTagLeft); + sm.entity->addComponent(newTagLeft); + } + Qt3DRender::QLayer *newTagRight = sm.hasTransparency ? layerData->eyeRight->transparentTag : layerData->eyeRight->opaqueTag; + if (!sm.entity->components().contains(newTagRight)) { + Qt3DRender::QLayer *prevTagRight = newTagRight == layerData->eyeRight->transparentTag ? layerData->eyeRight->opaqueTag : layerData->eyeRight->transparentTag; + sm.entity->removeComponent(prevTagRight); + sm.entity->addComponent(newTagRight); + } } profData.needsBlending = sm.hasTransparency; @@ -5915,26 +6231,26 @@ void Q3DSSceneManager::updateCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSR } } -void Q3DSSceneManager::initEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS) +void Q3DSSceneManager::initEyeEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData) { Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); Q3DSEffectAttached *effData = new Q3DSEffectAttached; effData->entity = layerData->entity; effData->layer3DS = layer3DS; eff3DS->setAttached(effData); - layerData->effectData.effects.append(eff3DS); + eyeData->effectData.effects.append(eff3DS); - if (!layerData->effLayerTexture) { + if (!eyeData->effLayerTexture) { // The effect's output texture is never multisampled. If the layer // (i.e. the effect's input) uses MSAA or SSAA then an extra resolve // step will be applied via BlitFramebuffer. From that point on // everything behaves like non-MSAA. const QSize sz = safeLayerPixelSize(layerData); - layerData->effLayerTexture = newColorBuffer(sz, 1); - layerData->effLayerTexture->setParent(layerData->entity); - m_profiler->trackNewObject(layerData->effLayerTexture, Q3DSProfiler::Texture2DObject, + eyeData->effLayerTexture = newColorBuffer(sz, 1); + eyeData->effLayerTexture->setParent(layerData->entity); + m_profiler->trackNewObject(eyeData->effLayerTexture, Q3DSProfiler::Texture2DObject, "Effect buffer for layer %s", layer3DS->id().constData()); - layerData->sizeManagedTextures.append(layerData->effLayerTexture); + layerData->sizeManagedTextures.append(eyeData->effLayerTexture); } effData->propertyChangeObserverIndex = eff3DS->addPropertyChangeObserver( @@ -5942,6 +6258,18 @@ void Q3DSSceneManager::initEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *lay effData->eventObserverIndex = eff3DS->addEventHandler(QString(), std::bind(&Q3DSSceneManager::handleEvent, this, std::placeholders::_1)); } +void Q3DSSceneManager::initEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS) +{ + Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); + initEyeEffect(eff3DS, layer3DS, layerData->eyeMono); +#if 0 + // Note: Effects currently disabled for stereoscopic views + // TODO: Should we here duplicate eff3DS to make this work? + initEyeEffect(eff3DS, layer3DS, layerData->eyeLeft); + initEyeEffect(eff3DS, layer3DS, layerData->eyeRight); +#endif +} + static inline void setTextureInfoUniform(Qt3DRender::QParameter *param, Qt3DRender::QAbstractTexture *texture) { const QSize size = Q3DSImageManager::instance().size(texture); @@ -6070,10 +6398,9 @@ static inline void setupStencilTest(Qt3DRender::QStencilTest *stencilTest, const // The main entry point for activating/deactivating effects on a layer. Called // upon scene building and every time an effect gets hidden/shown (eyeball). -void Q3DSSceneManager::updateEffectStatus(Q3DSLayerNode *layer3DS, bool force) +void Q3DSSceneManager::updateEyeEffectStatus(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData, bool force) { - Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); - const int count = layerData->effectData.effects.count(); + const int count = eyeData->effectData.effects.count(); int activeEffectCount = 0; int firstActiveIndex = 0; int lastActiveIndex = 0; @@ -6084,7 +6411,7 @@ void Q3DSSceneManager::updateEffectStatus(Q3DSLayerNode *layer3DS, bool force) // by nature. So reverse the iteration in order to match the 3DS1 // results. for (int i = count - 1; i >= 0; --i) { - Q3DSEffectInstance *eff3DS = layerData->effectData.effects[i]; + Q3DSEffectInstance *eff3DS = eyeData->effectData.effects[i]; Q3DSEffectAttached *effData = static_cast<Q3DSEffectAttached *>(eff3DS->attached()); if (eff3DS->eyeballEnabled() && effData->visibilityTag == Q3DSGraphObjectAttached::Visible) { ++activeEffectCount; @@ -6099,7 +6426,7 @@ void Q3DSSceneManager::updateEffectStatus(Q3DSLayerNode *layer3DS, bool force) } } - layerData->effectData.activeEffectCount = activeEffectCount; + eyeData->effectData.activeEffectCount = activeEffectCount; if (!change && !force) return; // nothing has changed @@ -6113,69 +6440,88 @@ void Q3DSSceneManager::updateEffectStatus(Q3DSLayerNode *layer3DS, bool force) activeEffectCount, count, layer3DS->id().constData()); for (int i = 0; i < count; ++i) { - Q3DSEffectInstance *eff3DS = layerData->effectData.effects[i]; + Q3DSEffectInstance *eff3DS = eyeData->effectData.effects[i]; deactivateEffect(eff3DS, layer3DS); } - layerData->effectData.combinedEffectFlags = 0; + eyeData->effectData.combinedEffectFlags = 0; - cleanupEffectSource(layer3DS); + cleanupEffectSource(layer3DS, eyeData); if (activeEffectCount) { - ensureEffectSource(layer3DS); + ensureEffectSource(layer3DS, eyeData); Qt3DRender::QAbstractTexture *prevOutput = nullptr; for (int i = count - 1; i >= 0; --i) { - Q3DSEffectInstance *eff3DS = layerData->effectData.effects[i]; + Q3DSEffectInstance *eff3DS = eyeData->effectData.effects[i]; if (eff3DS->eyeballEnabled() && eff3DS->attached()->visibilityTag == Q3DSGraphObjectAttached::Visible) { Q3DSSceneManager::EffectActivationFlags flags = 0; if (i == firstActiveIndex) flags |= Q3DSSceneManager::EffIsFirst; if (i == lastActiveIndex) flags |= Q3DSSceneManager::EffIsLast; - activateEffect(eff3DS, layer3DS, flags, prevOutput); - layerData->effectData.combinedEffectFlags |= eff3DS->effect()->flags(); + activateEffect(eff3DS, layer3DS, flags, prevOutput, eyeData); + eyeData->effectData.combinedEffectFlags |= eff3DS->effect()->flags(); prevOutput = eff3DS->attached<Q3DSEffectAttached>()->outputTexture; } } } + if (activeEffectCount) { + // The layer compositor must use the output of the effect passes from now on. + eyeData->compositorSourceParam->setValue(QVariant::fromValue(eyeData->effLayerTexture)); + } else { + eyeData->compositorSourceParam->setValue(QVariant::fromValue(eyeData->layerTexture)); + } +} + +void Q3DSSceneManager::updateEffectStatus(Q3DSLayerNode *layer3DS, bool force) +{ + Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); + updateEyeEffectStatus(layer3DS, layerData->eyeMono, force); +#if 0 + // Note: Effects currently disabled for stereoscopic views + updateEyeEffectStatus(layer3DS, layerData->eyeLeft, force); + updateEyeEffectStatus(layer3DS, layerData->eyeRight, force); +#endif + const bool wasActive = layerData->effectActive; + int activeEffectCount = layerData->eyeMono->effectData.activeEffectCount; + if (activeEffectCount) { // The layer compositor must use the output of the effect passes from now on. - layerData->compositorSourceParam->setValue(QVariant::fromValue(layerData->effLayerTexture)); - layerData->effectActive = true; + // TODO: Effects are disabled for stereoscopic views as msaa+effect currently doesn't work for stereo + layerData->effectActive = m_engine->isStereoscopic() ? false : true; if (!wasActive && layerData->msaaSampleCount > 1) updateLayerCompositorProgram(layer3DS); } else { - layerData->compositorSourceParam->setValue(QVariant::fromValue(layerData->layerTexture)); layerData->effectActive = false; if (wasActive && layerData->msaaSampleCount > 1) updateLayerCompositorProgram(layer3DS); } } -void Q3DSSceneManager::ensureEffectSource(Q3DSLayerNode *layer3DS) +void Q3DSSceneManager::ensureEffectSource(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData) { Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); // MSAA/SSAA layers need an additional resolve step since effects can only // work with non-MSAA (and 1:1 sized) textures as input. const bool needsResolve = layerData->msaaSampleCount > 1 || layerData->ssaaScaleFactor > 1; if (needsResolve) { - layerData->effectData.sourceTexture = new Qt3DRender::QTexture2D(m_rootEntity); - m_profiler->trackNewObject(layerData->effectData.sourceTexture, Q3DSProfiler::Texture2DObject, layer3DS->id(), + eyeData->effectData.sourceTexture = new Qt3DRender::QTexture2D(m_rootEntity); + m_profiler->trackNewObject(eyeData->effectData.sourceTexture, Q3DSProfiler::Texture2DObject, layer3DS->id(), "Resolve buffer for effects"); - layerData->effectData.sourceTexture->setFormat(Qt3DRender::QAbstractTexture::RGBA8_UNorm); - layerData->effectData.sourceTexture->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear); - layerData->effectData.sourceTexture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear); - layerData->effectData.ownsSourceTexture = true; + eyeData->effectData.sourceTexture->setFormat(Qt3DRender::QAbstractTexture::RGBA8_UNorm); + eyeData->effectData.sourceTexture->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear); + eyeData->effectData.sourceTexture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear); + eyeData->effectData.ownsSourceTexture = true; Qt3DRender::QRenderTarget *rtSrc = new Qt3DRender::QRenderTarget; m_profiler->trackNewObject(rtSrc, Q3DSProfiler::RenderTargetObject, layer3DS->id(), "Src resolve RT for effects"); Qt3DRender::QRenderTargetOutput *rtSrcOutput = new Qt3DRender::QRenderTargetOutput; rtSrcOutput->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Color0); - rtSrcOutput->setTexture(layerData->layerTexture); + rtSrcOutput->setTexture(eyeData->layerTexture); rtSrc->addOutput(rtSrcOutput); Qt3DRender::QRenderTarget *rtDst = new Qt3DRender::QRenderTarget; @@ -6183,11 +6529,11 @@ void Q3DSSceneManager::ensureEffectSource(Q3DSLayerNode *layer3DS) "Dst resolve RT for effects"); Qt3DRender::QRenderTargetOutput *rtDstOutput = new Qt3DRender::QRenderTargetOutput; rtDstOutput->setAttachmentPoint(Qt3DRender::QRenderTargetOutput::Color0); - rtDstOutput->setTexture(layerData->effectData.sourceTexture); + rtDstOutput->setTexture(eyeData->effectData.sourceTexture); rtDst->addOutput(rtDstOutput); - Qt3DRender::QBlitFramebuffer *resolve = new Qt3DRender::QBlitFramebuffer(layerData->effectData.effectRoot); - layerData->effectData.resolve = resolve; + Qt3DRender::QBlitFramebuffer *resolve = new Qt3DRender::QBlitFramebuffer(eyeData->effectData.effectRoot); + eyeData->effectData.resolve = resolve; new Qt3DRender::QNoDraw(resolve); resolve->setSource(rtSrc); resolve->setDestination(rtDst); @@ -6198,33 +6544,34 @@ void Q3DSSceneManager::ensureEffectSource(Q3DSLayerNode *layer3DS) }; // must track layer size, but without the SSAA scale - prepareSizeDependentTexture(layerData->effectData.sourceTexture, layer3DS, blitResizer, + prepareSizeDependentTexture(eyeData->effectData.sourceTexture, layer3DS, blitResizer, Q3DSLayerAttached::SizeManagedTexture::IgnoreSSAA); // set initial blit rects blitResizer(layer3DS); } else { - layerData->effectData.sourceTexture = layerData->layerTexture; - layerData->effectData.ownsSourceTexture = false; - layerData->effectData.resolve = nullptr; + eyeData->effectData.sourceTexture = eyeData->layerTexture; + eyeData->effectData.ownsSourceTexture = false; + eyeData->effectData.resolve = nullptr; } } -void Q3DSSceneManager::cleanupEffectSource(Q3DSLayerNode *layer3DS) +void Q3DSSceneManager::cleanupEffectSource(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData) { Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); - delete layerData->effectData.resolve; - layerData->effectData.resolve = nullptr; - if (layerData->effectData.ownsSourceTexture) { - layerData->effectData.ownsSourceTexture = false; - layerData->sizeManagedTextures.removeOne(layerData->effectData.sourceTexture); - delete layerData->effectData.sourceTexture; + delete eyeData->effectData.resolve; + eyeData->effectData.resolve = nullptr; + if (eyeData->effectData.ownsSourceTexture) { + eyeData->effectData.ownsSourceTexture = false; + layerData->sizeManagedTextures.removeOne(eyeData->effectData.sourceTexture); + delete eyeData->effectData.sourceTexture; } } void Q3DSSceneManager::activateEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS, EffectActivationFlags flags, - Qt3DRender::QAbstractTexture *prevOutput) + Qt3DRender::QAbstractTexture *prevOutput, + Q3DSEyeData *eyeData) { Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); @@ -6293,15 +6640,15 @@ void Q3DSSceneManager::activateEffect(Q3DSEffectInstance *eff3DS, // effLayerTexture. Effects in-between have their own output textures while // the source refers to the output of the previous effect. if (flags.testFlag(EffIsFirst)) { - Q_ASSERT(layerData->effectData.sourceTexture); - effData->sourceTexture = layerData->effectData.sourceTexture; + Q_ASSERT(eyeData->effectData.sourceTexture); + effData->sourceTexture = eyeData->effectData.sourceTexture; } else { Q_ASSERT(prevOutput); effData->sourceTexture = prevOutput; } if (flags.testFlag(EffIsLast)) { - Q_ASSERT(layerData->effLayerTexture); - effData->outputTexture = layerData->effLayerTexture; + Q_ASSERT(eyeData->effLayerTexture); + effData->outputTexture = eyeData->effLayerTexture; effData->ownsOutputTexture = false; } else { effData->outputTexture = new Qt3DRender::QTexture2D(m_rootEntity); @@ -6404,7 +6751,7 @@ void Q3DSSceneManager::activateEffect(Q3DSEffectInstance *eff3DS, if (valid) { Qt3DRender::QParameter *texParam = new Qt3DRender::QParameter; texParam->setName(samplerName); - texParam->setValue(QVariant::fromValue(layerData->depthTextureData.depthTexture)); + texParam->setValue(QVariant::fromValue(eyeData->depthTextureData.depthTexture)); paramList.append(texParam); // Have the usual Info and flag uniforms. Qt3DRender::QParameter *texInfoParam = new Qt3DRender::QParameter; @@ -6624,7 +6971,7 @@ void Q3DSSceneManager::activateEffect(Q3DSEffectInstance *eff3DS, effData->quadEntity = buildFsQuad(quadInfo); // framegraph - Qt3DRender::QRenderTargetSelector *rtSel = new Qt3DRender::QRenderTargetSelector(layerData->effectData.effectRoot); + Qt3DRender::QRenderTargetSelector *rtSel = new Qt3DRender::QRenderTargetSelector(eyeData->effectData.effectRoot); effData->passFgRoots.append(rtSel); Qt3DRender::QRenderTarget *rt = new Qt3DRender::QRenderTarget; m_profiler->trackNewObject(rt, Q3DSProfiler::RenderTargetObject, "RT for effect %s pass %d", @@ -7467,8 +7814,18 @@ void Q3DSSceneManager::prepareNextFrame() // Post-processing effects have uniforms that need to be updated on every frame. Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); if (layerData) { - for (Q3DSEffectInstance *eff3DS : qAsConst(layerData->effectData.effects)) - updateEffectForNextFrame(eff3DS, nextFrameNo); + if (!m_engine->isStereoscopic()) + for (Q3DSEffectInstance *eff3DS : qAsConst(layerData->eyeMono->effectData.effects)) + updateEffectForNextFrame(eff3DS, nextFrameNo); +#if 0 + // Note: Effects currently disabled for stereoscopic views + if (m_engine->isStereoscopic()) { + for (Q3DSEffectInstance *eff3DS : qAsConst(layerData->eyeLeft->effectData.effects)) + updateEffectForNextFrame(eff3DS, nextFrameNo); + for (Q3DSEffectInstance *eff3DS : qAsConst(layerData->eyeRight->effectData.effects)) + updateEffectForNextFrame(eff3DS, nextFrameNo); + } +#endif } }); @@ -7497,8 +7854,9 @@ void Q3DSSceneManager::prepareNextFrame() // the AppFrame uniform) cannot be cached since the effect needs // continuous updates (and in 3DS2 the effect's output is part of the // layer's final texture that is "cached"). + // Note: Testing just mono eye should be enough, effects among eyes are same const bool hasTimeDependentEffect = layerData->effectActive - && layerData->effectData.combinedEffectFlags.testFlag(Q3DSEffect::ReliesOnTime); + && (layerData->eyeMono->effectData.combinedEffectFlags.testFlag(Q3DSEffect::ReliesOnTime)); if (!layerData->wasDirty && !m_layerUncachePending && !subDirty && !hasTimeDependentEffect) { ++layerData->nonDirtyRenderCount; @@ -7657,8 +8015,19 @@ void Q3DSSceneManager::updateSubTreeRecursive(Q3DSGraphObject *obj) // (orthographic) shadow maps, ssao texture, effects all rely on camera properties like clip range updateShadowMapStatus(data->layer3DS); updateSsaoStatus(data->layer3DS); - for (Q3DSEffectInstance *eff3DS : data->layer3DS->attached<Q3DSLayerAttached>()->effectData.effects) - updateEffect(eff3DS); + Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(data->layer3DS->attached()); + if (!m_engine->isStereoscopic()) + for (Q3DSEffectInstance *eff3DS : layerData->eyeMono->effectData.effects) + updateEffect(eff3DS); +#if 0 + // Note: Effects currently disabled for stereoscopic views + if (m_engine->isStereoscopic()) { + for (Q3DSEffectInstance *eff3DS : layerData->eyeLeft->effectData.effects) + updateEffect(eff3DS); + for (Q3DSEffectInstance *eff3DS : layerData->eyeRight->effectData.effects) + updateEffect(eff3DS); + } +#endif m_wasDirty = true; markLayerForObjectDirty(cam3DS); } @@ -7779,6 +8148,7 @@ void Q3DSSceneManager::updateSubTreeRecursive(Q3DSGraphObject *obj) if (activeFlagChanges || sourceChanges) // active/deactivate effects by rebuilding the chain updateEffectStatus(data->layer3DS, sourceChanges); // send changed parameter values to the shader + // TODO: Should we here handle & update effect for all eyes? updateEffect(eff3DS); m_wasDirty = true; markLayerForObjectDirty(eff3DS); @@ -7913,18 +8283,31 @@ void Q3DSSceneManager::setNodeVisibility(Q3DSNode *node, bool visible) "Attempted to manage visibility of a node with non-dedicated entity by QLayer. This should not happen."); Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(data->layer3DS->attached()); - if (!layerData->opaqueTag || !layerData->transparentTag) // bail out for subpresentation layers + if (!layerData->eyeMono->opaqueTag || !layerData->eyeMono->transparentTag || + !layerData->eyeLeft->opaqueTag || !layerData->eyeLeft->transparentTag || + !layerData->eyeRight->opaqueTag || !layerData->eyeRight->transparentTag) { + // bail out for subpresentation layers return; + } if (!visible) { data->visibilityTag = Q3DSGraphObjectAttached::Hidden; - data->entity->removeComponent(layerData->opaqueTag); - data->entity->removeComponent(layerData->transparentTag); + data->entity->removeComponent(layerData->eyeMono->opaqueTag); + data->entity->removeComponent(layerData->eyeMono->transparentTag); + data->entity->removeComponent(layerData->eyeLeft->opaqueTag); + data->entity->removeComponent(layerData->eyeLeft->transparentTag); + data->entity->removeComponent(layerData->eyeRight->opaqueTag); + data->entity->removeComponent(layerData->eyeRight->transparentTag); } else { data->visibilityTag = Q3DSGraphObjectAttached::Visible; - if (node->type() != Q3DSGraphObject::Text) - data->entity->addComponent(layerData->opaqueTag); - data->entity->addComponent(layerData->transparentTag); + if (node->type() != Q3DSGraphObject::Text) { + data->entity->addComponent(layerData->eyeMono->opaqueTag); + data->entity->addComponent(layerData->eyeLeft->opaqueTag); + data->entity->addComponent(layerData->eyeRight->opaqueTag); + } + data->entity->addComponent(layerData->eyeMono->transparentTag); + data->entity->addComponent(layerData->eyeLeft->transparentTag); + data->entity->addComponent(layerData->eyeRight->transparentTag); } // For models the model root entity is tagged with both opaque and @@ -7933,12 +8316,20 @@ void Q3DSSceneManager::setNodeVisibility(Q3DSNode *node, bool visible) if (node->type() == Q3DSGraphObject::Model) { Q3DSModelAttached *mdata = static_cast<Q3DSModelAttached *>(node->attached()); for (Q3DSModelAttached::SubMesh &sm : mdata->subMeshes) { - Qt3DRender::QLayer *tag = sm.hasTransparency ? layerData->transparentTag : layerData->opaqueTag; + Qt3DRender::QLayer *tag = sm.hasTransparency ? layerData->eyeMono->transparentTag : layerData->eyeMono->opaqueTag; + Qt3DRender::QLayer *tagLeft = sm.hasTransparency ? layerData->eyeLeft->transparentTag : layerData->eyeLeft->opaqueTag; + Qt3DRender::QLayer *tagRight = sm.hasTransparency ? layerData->eyeRight->transparentTag : layerData->eyeRight->opaqueTag; if (!visible) { - sm.entity->removeComponent(layerData->opaqueTag); - sm.entity->removeComponent(layerData->transparentTag); + sm.entity->removeComponent(layerData->eyeMono->opaqueTag); + sm.entity->removeComponent(layerData->eyeMono->transparentTag); + sm.entity->removeComponent(layerData->eyeLeft->opaqueTag); + sm.entity->removeComponent(layerData->eyeLeft->transparentTag); + sm.entity->removeComponent(layerData->eyeRight->opaqueTag); + sm.entity->removeComponent(layerData->eyeRight->transparentTag); } else { sm.entity->addComponent(tag); + sm.entity->addComponent(tagLeft); + sm.entity->addComponent(tagRight); } } } @@ -8503,8 +8894,19 @@ void Q3DSSceneManager::handleNodeGlobalChange(Q3DSNode *node) // (orthographic) shadow maps, ssao texture, effects all rely on camera properties like clip range updateShadowMapStatus(data->layer3DS); updateSsaoStatus(data->layer3DS); - for (Q3DSEffectInstance *eff3DS : data->layer3DS->attached<Q3DSLayerAttached>()->effectData.effects) - updateEffect(eff3DS); + Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(data->layer3DS->attached()); + if (!m_engine->isStereoscopic()) + for (Q3DSEffectInstance *eff3DS : layerData->eyeMono->effectData.effects) + updateEffect(eff3DS); +#if 0 + // Note: Effects currently disabled for stereoscopic views + if (m_engine->isStereoscopic()) { + for (Q3DSEffectInstance *eff3DS : layerData->eyeLeft->effectData.effects) + updateEffect(eff3DS); + for (Q3DSEffectInstance *eff3DS : layerData->eyeRight->effectData.effects) + updateEffect(eff3DS); + } +#endif } } else if (node->type() != Q3DSGraphObject::Layer) { const bool isVisible = node->attached<Q3DSNodeAttached>()->globalEffectiveVisibility; @@ -8591,7 +8993,10 @@ void Q3DSSceneManager::handleSceneChange(Q3DSScene *, Q3DSGraphObject::DirtyFlag Q3DSEffectInstance *eff3DS = static_cast<Q3DSEffectInstance *>(objOrChild); Q3DSLayerNode *layer3DS = findLayerForObjectInScene(eff3DS); if (layer3DS) { - layer3DS->attached<Q3DSLayerAttached>()->effectData.effects.removeOne(eff3DS); + Q3DSLayerAttached *layerData = static_cast<Q3DSLayerAttached *>(layer3DS->attached()); + layerData->eyeMono->effectData.effects.removeOne(eff3DS); + layerData->eyeLeft->effectData.effects.removeOne(eff3DS); + layerData->eyeRight->effectData.effects.removeOne(eff3DS); needsEffectUpdate = true; } } diff --git a/src/runtime/q3dsscenemanager_p.h b/src/runtime/q3dsscenemanager_p.h index 7447489..5746776 100644 --- a/src/runtime/q3dsscenemanager_p.h +++ b/src/runtime/q3dsscenemanager_p.h @@ -106,6 +106,78 @@ namespace Qt3DExtras { class QPlaneMesh; } +struct Q3DSEyeData +{ + Qt3DRender::QCameraSelector *cameraSelector = nullptr; + Qt3DRender::QLayer *opaqueTag = nullptr; + Qt3DRender::QLayer *transparentTag = nullptr; + Qt3DRender::QRenderTargetSelector *depthRtSelector = nullptr; + Qt3DRender::QRenderTargetSelector *ssaoRtSelector = nullptr; + Qt3DRender::QFrameGraphNode *shadowRoot = nullptr; + Qt3DRender::QFrameGraphNode *effectRoot = nullptr; + Qt3DRender::QClearBuffers *clearBuffers = nullptr; + Qt3DRender::QAbstractTexture *effLayerTexture = nullptr; + Qt3DRender::QRenderTargetSelector *rtSelector = nullptr; + Qt3DRender::QAbstractTexture *layerDS = nullptr; + + Qt3DRender::QAbstractTexture *layerTexture = nullptr; + Qt3DRender::QParameter *compositorSourceParam = nullptr; + + struct DepthTextureData { + bool enabled = false; + Qt3DRender::QRenderTargetSelector *rtSelector = nullptr; + Qt3DRender::QAbstractTexture *depthTexture = nullptr; + Qt3DRender::QClearBuffers *clearBuffers = nullptr; + Qt3DRender::QLayerFilter *layerFilterOpaque = nullptr; + Qt3DRender::QLayerFilter *layerFilterTransparent = nullptr; + } depthTextureData; + + struct ProgAAData { + Qt3DRender::QFrameGraphNode *fg = nullptr; + Qt3DRender::QRenderTargetSelector *rtSel = nullptr; + Qt3DRender::QRenderTarget *rts[2]; + Qt3DRender::QAbstractTexture *currentAccumulatorTexture = nullptr; + Qt3DRender::QAbstractTexture *currentOutputTexture = nullptr; + Qt3DRender::QAbstractTexture *stolenColorBuf = nullptr; + Qt3DRender::QAbstractTexture *extraColorBuf = nullptr; + Qt3DRender::QParameter *accumTexParam = nullptr; + Qt3DRender::QParameter *lastTexParam = nullptr; + Qt3DRender::QParameter *blendFactorsParam = nullptr; + Qt3DRender::QLayerFilter *layerFilter = nullptr; + int pass = 0; + int curTarget = 0; + bool cameraAltered = false; + bool enabled = false; + } progAA; + + struct TempAAData { + int nonDirtyPass = 0; + int passIndex = 0; // wraps around + Qt3DRender::QFrameGraphNode *fg = nullptr; + Qt3DRender::QRenderTargetSelector *rtSel = nullptr; + Qt3DRender::QRenderTarget *rts[2]; + Qt3DRender::QAbstractTexture *currentAccumulatorTexture = nullptr; + Qt3DRender::QAbstractTexture *currentOutputTexture = nullptr; + Qt3DRender::QAbstractTexture *stolenColorBuf = nullptr; + Qt3DRender::QAbstractTexture *extraColorBuf = nullptr; + Qt3DRender::QParameter *accumTexParam = nullptr; + Qt3DRender::QParameter *lastTexParam = nullptr; + Qt3DRender::QParameter *blendFactorsParam = nullptr; + Qt3DRender::QLayerFilter *layerFilter = nullptr; + } tempAA; + + struct EffectData { + Qt3DRender::QFrameGraphNode *effectRoot = nullptr; + QVector<Q3DSEffectInstance *> effects; + int activeEffectCount = 0; + Q3DSEffect::Flags combinedEffectFlags; + Qt3DRender::QAbstractTexture *sourceTexture = nullptr; + bool ownsSourceTexture = false; + Qt3DRender::QFrameGraphNode *resolve = nullptr; + } effectData; + +}; + // The Qt 3D scene, once built, still has to react to property changes. // Therefore some extra bookkeeping is needed, so that the Qt 3D objects to // update are accessible in a sane manner. This is done via the *Attached @@ -208,15 +280,17 @@ public: // layers always have light data lightsData.reset(new Q3DSNodeAttached::LightsData); } + + Q3DSEyeData *eyeMono = nullptr; + Q3DSEyeData *eyeLeft = nullptr; + Q3DSEyeData *eyeRight = nullptr; + Qt3DCore::QEntity *layerSceneRootEntity = nullptr; Qt3DCore::QEntity *compositorEntity = nullptr; Qt3DRender::QFrameGraphNode *layerFgRoot = nullptr; Qt3DCore::QNode *layerFgRootParent = nullptr; Qt3DCore::QNode *layerFgDummyParent = nullptr; Q3DSCameraNode *cam3DS = nullptr; - Qt3DRender::QCameraSelector *cameraSelector = nullptr; - Qt3DRender::QClearBuffers *clearBuffers = nullptr; - Qt3DRender::QRenderTargetSelector *rtSelector = nullptr; typedef std::function<void(Q3DSLayerNode *)> SizeChangeCallback; struct SizeManagedTexture { enum Flag { @@ -235,10 +309,6 @@ public: QVector<SizeManagedTexture> sizeManagedTextures; QVector<SizeChangeCallback> layerSizeChangeCallbacks; - Qt3DRender::QAbstractTexture *layerTexture = nullptr; - Qt3DRender::QAbstractTexture *effLayerTexture = nullptr; - Qt3DRender::QAbstractTexture *layerDS = nullptr; - Qt3DRender::QParameter *compositorSourceParam = nullptr; Qt3DRender::QRenderPass *compositorRenderPass = nullptr; std::function<void()> updateCompositorCalculations = nullptr; std::function<void()> updateSubPresentationSize = nullptr; @@ -253,8 +323,6 @@ public: bool wasDirty = false; bool rayCasterBusy = false; Qt3DRender::QParameter *cameraPropertiesParam = nullptr; - Qt3DRender::QLayer *opaqueTag = nullptr; - Qt3DRender::QLayer *transparentTag = nullptr; Qt3DRender::QRayCaster *layerRayCaster = nullptr; struct RayCastQueueEntry { @@ -267,15 +335,6 @@ public: }; QQueue<RayCastQueueEntry> rayCastQueue; - struct DepthTextureData { - bool enabled = false; - Qt3DRender::QRenderTargetSelector *rtSelector = nullptr; - Qt3DRender::QAbstractTexture *depthTexture = nullptr; - Qt3DRender::QClearBuffers *clearBuffers = nullptr; - Qt3DRender::QLayerFilter *layerFilterOpaque = nullptr; - Qt3DRender::QLayerFilter *layerFilterTransparent = nullptr; - } depthTextureData; - struct SsaoTextureData { bool enabled = false; Qt3DRender::QRenderTargetSelector *rtSelector = nullptr; @@ -326,55 +385,11 @@ public: } custMatParams; } shadowMapData; - struct ProgAAData { - Qt3DRender::QFrameGraphNode *fg = nullptr; - Qt3DRender::QRenderTargetSelector *rtSel = nullptr; - Qt3DRender::QRenderTarget *rts[2]; - Qt3DRender::QAbstractTexture *currentAccumulatorTexture = nullptr; - Qt3DRender::QAbstractTexture *currentOutputTexture = nullptr; - Qt3DRender::QAbstractTexture *stolenColorBuf = nullptr; - Qt3DRender::QAbstractTexture *extraColorBuf = nullptr; - Qt3DRender::QParameter *accumTexParam = nullptr; - Qt3DRender::QParameter *lastTexParam = nullptr; - Qt3DRender::QParameter *blendFactorsParam = nullptr; - Qt3DRender::QLayerFilter *layerFilter = nullptr; - int pass = 0; - int curTarget = 0; - bool cameraAltered = false; - bool enabled = false; - } progAA; - - struct TempAAData { - int nonDirtyPass = 0; - int passIndex = 0; // wraps around - Qt3DRender::QFrameGraphNode *fg = nullptr; - Qt3DRender::QRenderTargetSelector *rtSel = nullptr; - Qt3DRender::QRenderTarget *rts[2]; - Qt3DRender::QAbstractTexture *currentAccumulatorTexture = nullptr; - Qt3DRender::QAbstractTexture *currentOutputTexture = nullptr; - Qt3DRender::QAbstractTexture *stolenColorBuf = nullptr; - Qt3DRender::QAbstractTexture *extraColorBuf = nullptr; - Qt3DRender::QParameter *accumTexParam = nullptr; - Qt3DRender::QParameter *lastTexParam = nullptr; - Qt3DRender::QParameter *blendFactorsParam = nullptr; - Qt3DRender::QLayerFilter *layerFilter = nullptr; - } tempAA; - struct AdvBlendData { Qt3DRender::QAbstractTexture *tempTexture = nullptr; Qt3DRender::QRenderTarget *tempRt = nullptr; } advBlend; - struct EffectData { - Qt3DRender::QFrameGraphNode *effectRoot = nullptr; - QVector<Q3DSEffectInstance *> effects; - int activeEffectCount = 0; - Q3DSEffect::Flags combinedEffectFlags; - Qt3DRender::QAbstractTexture *sourceTexture = nullptr; - bool ownsSourceTexture = false; - Qt3DRender::QFrameGraphNode *resolve = nullptr; - } effectData; - struct IBLProbeData { Qt3DRender::QAbstractTexture *lightProbeTexture = nullptr; Qt3DRender::QAbstractTexture *lightProbe2Texture = nullptr; @@ -393,8 +408,8 @@ public: Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSLayerAttached::SizeManagedTexture::Flags) // NB! Q3DSLayerAttached::SizeManagedTexture cannot be Q_MOVABLE_TYPE due to std::function in it Q_DECLARE_TYPEINFO(Q3DSLayerAttached::PerLightShadowMapData, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(Q3DSLayerAttached::ProgAAData, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(Q3DSLayerAttached::TempAAData, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Q3DSEyeData::ProgAAData, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Q3DSEyeData::TempAAData, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(Q3DSLayerAttached::RayCastQueueEntry, Q_MOVABLE_TYPE); // ensure a lookup based on a texture hits the entry regardless of the callback or flags @@ -412,6 +427,8 @@ class Q3DSCameraAttached : public Q3DSNodeAttached { public: Qt3DRender::QCamera *camera = nullptr; + Qt3DRender::QCamera *cameraLeft = nullptr; + Qt3DRender::QCamera *cameraRight = nullptr; }; class Q3DSGroupAttached : public Q3DSNodeAttached @@ -692,6 +709,12 @@ public: Qt3DLogic::QFrameAction *frameAction = nullptr; }; + struct ViewportSet { + Qt3DRender::QViewport *viewportMono = nullptr; + Qt3DRender::QViewport *viewportLeft = nullptr; + Qt3DRender::QViewport *viewportRight = nullptr; + }; + Q3DSSceneManager(); ~Q3DSSceneManager(); @@ -761,6 +784,7 @@ public: Q3DSInputManager *inputManager() { return m_inputManager; } + void setEyeDepthTextureEnabled(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData, bool enabled); void setDepthTextureEnabled(Q3DSLayerNode *layer3DS, bool enabled); void rebuildModelMaterial(Q3DSModelNode *model3DS); @@ -778,6 +802,7 @@ public: void goToTimeAction(Q3DSGraphObject *sceneOrComponent, float milliseconds, bool pause); void queueEvent(const Q3DSGraphObject::Event &e); + void updateViewportRects(); private: Q_DISABLE_COPY(Q3DSSceneManager) @@ -785,6 +810,8 @@ private: void updateSubPresentationHosts(); void loadSubUipPresentation(Q3DSSubPresentation *sp); void initSubTree(Q3DSGraphObject *subTreeRoot); + void updateCamerasStatus(Q3DSLayerAttached *layerData); + Q3DSEyeData *buildEye(Q3DSLayerNode *layer3DS, Qt3DRender::QViewport *viewport); void buildLayer(Q3DSLayerNode *layer3DS, Qt3DRender::QFrameGraphNode *parent, const QSize &parentSize); void buildSubPresentationLayer(Q3DSLayerNode *layer3DS, const QSize &parentSize); Qt3DRender::QRenderTarget *newLayerRenderTarget(const QSize &layerPixelSize, int msaaSampleCount, @@ -794,6 +821,8 @@ private: QSize calculateLayerSize(Q3DSLayerNode *layer3DS, const QSize &parentSize); QPointF calculateLayerPos(Q3DSLayerNode *layer3DS, const QSize &parentSize); void updateSizesForLayer(Q3DSLayerNode *layer3DS, const QSize &newParentSize); + void setSingleCameraSizeProperties(Q3DSLayerAttached *data, Qt3DRender::QCamera *camera, + const QVector2D &offset = QVector2D()); void setLayerCameraSizeProperties(Q3DSLayerNode *layer3DS, const QVector2D &offset = QVector2D()); void setLayerSizeProperties(Q3DSLayerNode *layer3DS); void setLayerProperties(Q3DSLayerNode *layer3DS); @@ -810,12 +839,16 @@ private: void updateOrthoShadowCam(Q3DSLayerAttached::PerLightShadowMapData *d, Q3DSLightNode *light3DS, Q3DSLayerAttached *layerData); void genOrthoBlurPassFg(Q3DSLayerAttached::PerLightShadowMapData *d, Qt3DRender::QAbstractTexture *inTex, Qt3DRender::QAbstractTexture *outTex, const QString &passName, Q3DSLightNode *light3DS); - void stealLayerRenderTarget(Qt3DRender::QAbstractTexture **stolenColorBuf, Q3DSLayerNode *layer3DS); + void stealLayerRenderTarget(Qt3DRender::QAbstractTexture **stolenColorBuf, Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData); Qt3DRender::QAbstractTexture *createProgressiveTemporalAAExtraBuffer(Q3DSLayerNode *layer3DS); + bool updateEyeProgressiveAA(Q3DSLayerNode *layer3DS, Q3DSLayerAttached *data, Q3DSEyeData *eyeData); bool updateProgressiveAA(Q3DSLayerNode *layer3DS); + void updateEyeTemporalAA(Q3DSLayerNode *layer3DS, Q3DSLayerAttached *data, Q3DSEyeData *eyeData); void updateTemporalAA(Q3DSLayerNode *layer3DS); - Qt3DRender::QCamera *buildCamera(Q3DSCameraNode *cam3DS, Q3DSLayerNode *layer3DS, Qt3DCore::QEntity *parent); + QVector<Qt3DRender::QCamera *> buildCameras(Q3DSCameraNode *cam3DS, Q3DSLayerNode *layer3DS, + Qt3DCore::QEntity *parent); + void setEyeCameraProperties(Q3DSCameraNode *camNode, Qt3DRender::QCamera *camera); void setCameraProperties(Q3DSCameraNode *camNode, int changeFlags); bool setActiveLayerCamera(Q3DSCameraNode *cam3DS, Q3DSLayerNode *layer3DS); Q3DSCameraNode *findFirstCamera(Q3DSLayerNode *layer3DS); @@ -847,10 +880,11 @@ private: void updateTextureParameters(Q3DSTextureParameters &textureParameters, Q3DSImage *image); void updateDefaultMaterial(Q3DSDefaultMaterial *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS); void updateCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS); + void updateEyeEffectStatus(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData, bool force = false); void updateEffectStatus(Q3DSLayerNode *layer3DS, bool force = false); - void ensureEffectSource(Q3DSLayerNode *layer3DS); - void cleanupEffectSource(Q3DSLayerNode *layer3DS); - void activateEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS, EffectActivationFlags flags, Qt3DRender::QAbstractTexture *prevOutput); + void ensureEffectSource(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData); + void cleanupEffectSource(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData); + void activateEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS, EffectActivationFlags flags, Qt3DRender::QAbstractTexture *prevOutput, Q3DSEyeData *eyeData); void deactivateEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS); void setupEffectTextureBuffer(Q3DSEffectAttached::TextureBuffer *tb, const Q3DSMaterial::PassBuffer &bufDesc, Q3DSLayerNode *layer3DS); void createEffectBuffers(Q3DSEffectInstance *eff3DS); @@ -863,6 +897,7 @@ private: QVector<Q3DSNodeAttached::LightsData *> getLightsDataForNode(Q3DSGraphObject *object); QVector<Qt3DRender::QParameter *> prepareSeparateLightUniforms(const QVector<Q3DSLightSource> &allLights, const QString &lightsUniformName); + void initEyeEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData); void initEffect(Q3DSEffectInstance *eff3DS, Q3DSLayerNode *layer3DS); void initBehaviorInstance(Q3DSBehaviorInstance *behaviorInstance); void initImage(Q3DSImage *image); @@ -964,7 +999,8 @@ private: Qt3DCore::QEntity *m_compositorParentEntity = nullptr; QVector<Qt3DCore::QEntity *> m_compositorEntities; bool m_compositorEnabled = true; - + QVector<ViewportSet> m_viewports; + Qt3DRender::QParameter *m_stereoMode; friend class Q3DSFrameUpdater; friend class Q3DSProfiler; friend class Q3DSSlidePlayer; diff --git a/src/runtime/q3dsviewportsettings.cpp b/src/runtime/q3dsviewportsettings.cpp index 3f45448..cdd67c3 100644 --- a/src/runtime/q3dsviewportsettings.cpp +++ b/src/runtime/q3dsviewportsettings.cpp @@ -56,6 +56,16 @@ Q3DSViewportSettings::ScaleMode Q3DSViewportSettings::scaleMode() const return m_scaleMode; } +Q3DSViewportSettings::StereoMode Q3DSViewportSettings::stereoMode() const +{ + return m_stereoMode; +} + +float Q3DSViewportSettings::stereoEyeSeparation() const +{ + return m_stereoEyeSeparation; +} + void Q3DSViewportSettings::setMatteEnabled(bool isEnabled) { if (m_matteEnabled != isEnabled) { @@ -88,4 +98,20 @@ void Q3DSViewportSettings::setScaleMode(Q3DSViewportSettings::ScaleMode mode) } } +void Q3DSViewportSettings::setStereoMode(Q3DSViewportSettings::StereoMode mode) +{ + if (m_stereoMode != mode) { + m_stereoMode = mode; + emit stereoModeChanged(); + } +} + +void Q3DSViewportSettings::setStereoEyeSeparation(float value) +{ + if (m_stereoEyeSeparation != value) { + m_stereoEyeSeparation = value; + emit stereoEyeSeparationChanged(); + } +} + QT_END_NAMESPACE diff --git a/src/runtime/q3dsviewportsettings_p.h b/src/runtime/q3dsviewportsettings_p.h index 93d3e33..067b43f 100644 --- a/src/runtime/q3dsviewportsettings_p.h +++ b/src/runtime/q3dsviewportsettings_p.h @@ -54,6 +54,8 @@ class Q3DSV_PRIVATE_EXPORT Q3DSViewportSettings : public QObject Q_PROPERTY(QColor matteColor READ matteColor WRITE setMatteColor NOTIFY matteColorChanged) Q_PROPERTY(bool showRenderStats READ isShowingRenderStats WRITE setShowRenderStats NOTIFY showRenderStatsChanged) Q_PROPERTY(ScaleMode scaleMode READ scaleMode WRITE setScaleMode NOTIFY scaleModeChanged) + Q_PROPERTY(StereoMode stereoMode READ stereoMode WRITE setStereoMode NOTIFY stereoModeChanged) + Q_PROPERTY(float stereoEyeSeparation READ stereoEyeSeparation WRITE setStereoEyeSeparation NOTIFY stereoEyeSeparationChanged) public: enum ScaleMode { @@ -63,24 +65,39 @@ public: }; Q_ENUM(ScaleMode) + enum StereoMode { + StereoModeMono, + StereoModeTopBottom, + StereoModeLeftRight, + StereoModeAnaglyphRedCyan, + StereoModeAnaglyphGreenMagenta + }; + Q_ENUM(StereoMode) + explicit Q3DSViewportSettings(QObject *parent = nullptr); bool matteEnabled() const; QColor matteColor() const; bool isShowingRenderStats() const; ScaleMode scaleMode() const; + StereoMode stereoMode() const; + float stereoEyeSeparation() const; public Q_SLOTS: void setMatteEnabled(bool isEnabled); void setMatteColor(const QColor &color); void setShowRenderStats(bool show); void setScaleMode(ScaleMode mode); + void setStereoMode(StereoMode mode); + void setStereoEyeSeparation(float value); Q_SIGNALS: void matteEnabledChanged(); void matteColorChanged(); void showRenderStatsChanged(); void scaleModeChanged(); + void stereoModeChanged(); + void stereoEyeSeparationChanged(); private: Q_DISABLE_COPY(Q3DSViewportSettings) @@ -89,6 +106,8 @@ private: QColor m_matteColor = QColor(51, 51, 51); bool m_showRenderStats = false; Q3DSViewportSettings::ScaleMode m_scaleMode = ScaleModeFill; + Q3DSViewportSettings::StereoMode m_stereoMode = StereoModeMono; + float m_stereoEyeSeparation = 0.4f; }; QT_END_NAMESPACE diff --git a/src/runtime/shaders/compositor_ms2_stereoscopic.frag b/src/runtime/shaders/compositor_ms2_stereoscopic.frag new file mode 100644 index 0000000..aa197a5 --- /dev/null +++ b/src/runtime/shaders/compositor_ms2_stereoscopic.frag @@ -0,0 +1,49 @@ +#version 310 es +precision highp float; + +in vec2 texCoord; + +uniform highp sampler2DMS texLeft; +uniform highp sampler2DMS texRight; +uniform highp int stereoMode; + +out vec4 fragColor; + +void main() +{ + ivec2 tc = ivec2(floor(vec2(textureSize(texLeft)) * texCoord)); + vec4 cLeft = vec4(0.0); + vec4 cRight = vec4(0.0); + // Fetch from correct texture + if (stereoMode == 1) { + if (texCoord.y > 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1); + } else if (stereoMode == 2) { + if (texCoord.x < 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1); + } else { + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1); + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1); + } + + cLeft /= 2.0; + cRight /= 2.0; + // This discard, while not necessarily ideal for some GPUs, is necessary to + // get correct results with certain layer blend modes for example. + if (cLeft.a == 0.0 && cRight.a == 0.0) + discard; + if (stereoMode == 3) { + cLeft.g = 0.0; + cLeft.b = 0.0; + cRight.r = 0.0; + } else if (stereoMode == 4) { + cLeft.r = 0.0; + cLeft.b = 0.0; + cRight.g = 0.0; + } + fragColor = cLeft + cRight; +} diff --git a/src/runtime/shaders/compositor_ms2_stereoscopic_core.frag b/src/runtime/shaders/compositor_ms2_stereoscopic_core.frag new file mode 100644 index 0000000..3ce4722 --- /dev/null +++ b/src/runtime/shaders/compositor_ms2_stereoscopic_core.frag @@ -0,0 +1,48 @@ +#version 330 core + +in vec2 texCoord; + +uniform sampler2DMS texLeft; +uniform sampler2DMS texRight; +uniform int stereoMode; + +out vec4 fragColor; + +void main() +{ + ivec2 tc = ivec2(floor(textureSize(texLeft) * texCoord)); + vec4 cLeft = vec4(0.0); + vec4 cRight = vec4(0.0); + // Fetch from correct texture + if (stereoMode == 1) { + if (texCoord.y > 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1); + } else if (stereoMode == 2) { + if (texCoord.x < 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1); + } else { + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1); + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1); + } + + cLeft /= 2.0; + cRight /= 2.0; + // This discard, while not necessarily ideal for some GPUs, is necessary to + // get correct results with certain layer blend modes for example. + if (cLeft.a == 0.0 && cRight.a == 0.0) + discard; + if (stereoMode == 3) { + cLeft.g = 0.0; + cLeft.b = 0.0; + cRight.r = 0.0; + } else if (stereoMode == 4) { + cLeft.r = 0.0; + cLeft.b = 0.0; + cRight.g = 0.0; + } + fragColor = cLeft + cRight; +} diff --git a/src/runtime/shaders/compositor_ms4_stereoscopic.frag b/src/runtime/shaders/compositor_ms4_stereoscopic.frag new file mode 100644 index 0000000..0ac9984 --- /dev/null +++ b/src/runtime/shaders/compositor_ms4_stereoscopic.frag @@ -0,0 +1,49 @@ +#version 310 es +precision highp float; + +in vec2 texCoord; + +uniform highp sampler2DMS texLeft; +uniform highp sampler2DMS texRight; +uniform highp int stereoMode; + +out vec4 fragColor; + +void main() +{ + ivec2 tc = ivec2(floor(vec2(textureSize(texLeft)) * texCoord)); + vec4 cLeft = vec4(0.0); + vec4 cRight = vec4(0.0); + // Fetch from correct texture + if (stereoMode == 1) { + if (texCoord.y > 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1) + texelFetch(texLeft, tc, 2) + texelFetch(texLeft, tc, 3); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1) + texelFetch(texRight, tc, 2) + texelFetch(texRight, tc, 3); + } else if (stereoMode == 2) { + if (texCoord.x < 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1) + texelFetch(texLeft, tc, 2) + texelFetch(texLeft, tc, 3); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1) + texelFetch(texRight, tc, 2) + texelFetch(texRight, tc, 3); + } else { + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1) + texelFetch(texLeft, tc, 2) + texelFetch(texLeft, tc, 3); + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1) + texelFetch(texRight, tc, 2) + texelFetch(texRight, tc, 3); + } + + cLeft /= 4.0; + cRight /= 4.0; + // This discard, while not necessarily ideal for some GPUs, is necessary to + // get correct results with certain layer blend modes for example. + if (cLeft.a == 0.0 && cRight.a == 0.0) + discard; + if (stereoMode == 3) { + cLeft.g = 0.0; + cLeft.b = 0.0; + cRight.r = 0.0; + } else if (stereoMode == 4) { + cLeft.r = 0.0; + cLeft.b = 0.0; + cRight.g = 0.0; + } + fragColor = cLeft + cRight; +} diff --git a/src/runtime/shaders/compositor_ms4_stereoscopic_core.frag b/src/runtime/shaders/compositor_ms4_stereoscopic_core.frag new file mode 100644 index 0000000..571ec89 --- /dev/null +++ b/src/runtime/shaders/compositor_ms4_stereoscopic_core.frag @@ -0,0 +1,48 @@ +#version 330 core + +in vec2 texCoord; + +uniform sampler2DMS texLeft; +uniform sampler2DMS texRight; +uniform int stereoMode; + +out vec4 fragColor; + +void main() +{ + ivec2 tc = ivec2(floor(textureSize(texLeft) * texCoord)); + vec4 cLeft = vec4(0.0); + vec4 cRight = vec4(0.0); + // Fetch from correct texture + if (stereoMode == 1) { + if (texCoord.y > 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1) + texelFetch(texLeft, tc, 2) + texelFetch(texLeft, tc, 3); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1) + texelFetch(texRight, tc, 2) + texelFetch(texRight, tc, 3); + } else if (stereoMode == 2) { + if (texCoord.x < 0.5) + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1) + texelFetch(texLeft, tc, 2) + texelFetch(texLeft, tc, 3); + else + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1) + texelFetch(texRight, tc, 2) + texelFetch(texRight, tc, 3); + } else { + cLeft = texelFetch(texLeft, tc, 0) + texelFetch(texLeft, tc, 1) + texelFetch(texLeft, tc, 2) + texelFetch(texLeft, tc, 3); + cRight = texelFetch(texRight, tc, 0) + texelFetch(texRight, tc, 1) + texelFetch(texRight, tc, 2) + texelFetch(texRight, tc, 3); + } + + cLeft /= 4.0; + cRight /= 4.0; + // This discard, while not necessarily ideal for some GPUs, is necessary to + // get correct results with certain layer blend modes for example. + if (cLeft.a == 0.0 && cRight.a == 0.0) + discard; + if (stereoMode == 3) { + cLeft.g = 0.0; + cLeft.b = 0.0; + cRight.r = 0.0; + } else if (stereoMode == 4) { + cLeft.r = 0.0; + cLeft.b = 0.0; + cRight.g = 0.0; + } + fragColor = cLeft + cRight; +} diff --git a/src/runtime/shaders/compositor_stereoscopic.frag b/src/runtime/shaders/compositor_stereoscopic.frag new file mode 100644 index 0000000..ab3aca5 --- /dev/null +++ b/src/runtime/shaders/compositor_stereoscopic.frag @@ -0,0 +1,43 @@ +precision highp float; + +varying vec2 texCoord; + +uniform sampler2D texLeft; +uniform sampler2D texRight; +uniform int stereoMode; + +void main() +{ + vec4 cLeft = vec4(0.0); + vec4 cRight = vec4(0.0); + // Fetch from correct texture + if (stereoMode == 1) { + if (texCoord.y > 0.5) + cLeft = texture2D(texLeft, texCoord); + else + cRight = texture2D(texRight, texCoord); + } else if (stereoMode == 2) { + if (texCoord.x < 0.5) + cLeft = texture2D(texLeft, texCoord); + else + cRight = texture2D(texRight, texCoord); + } else { + cLeft = texture2D(texLeft, texCoord); + cRight = texture2D(texRight, texCoord); + } + + // This discard, while not necessarily ideal for some GPUs, is necessary to + // get correct results with certain layer blend modes for example. + if (cLeft.a == 0.0 && cRight.a == 0.0) + discard; + if (stereoMode == 3) { + cLeft.g = 0.0; + cLeft.b = 0.0; + cRight.r = 0.0; + } else if (stereoMode == 4) { + cLeft.r = 0.0; + cLeft.b = 0.0; + cRight.g = 0.0; + } + gl_FragColor = cLeft + cRight; +} diff --git a/src/runtime/shaders/compositor_stereoscopic_core.frag b/src/runtime/shaders/compositor_stereoscopic_core.frag new file mode 100644 index 0000000..1cce56c --- /dev/null +++ b/src/runtime/shaders/compositor_stereoscopic_core.frag @@ -0,0 +1,45 @@ +#version 330 core + +in vec2 texCoord; + +uniform sampler2D texLeft; +uniform sampler2D texRight; +uniform int stereoMode; + +out vec4 fragColor; + +void main() +{ + vec4 cLeft = vec4(0.0); + vec4 cRight = vec4(0.0); + // Fetch from correct texture + if (stereoMode == 1) { + if (texCoord.y > 0.5) + cLeft = texture(texLeft, texCoord); + else + cRight = texture(texRight, texCoord); + } else if (stereoMode == 2) { + if (texCoord.x < 0.5) + cLeft = texture(texLeft, texCoord); + else + cRight = texture(texRight, texCoord); + } else { + cLeft = texture(texLeft, texCoord); + cRight = texture(texRight, texCoord); + } + + // This discard, while not necessarily ideal for some GPUs, is necessary to + // get correct results with certain layer blend modes for example. + if (cLeft.a == 0.0 && cRight.a == 0.0) + discard; + if (stereoMode == 3) { + cLeft.g = 0.0; + cLeft.b = 0.0; + cRight.r = 0.0; + } else if (stereoMode == 4) { + cLeft.r = 0.0; + cLeft.b = 0.0; + cRight.g = 0.0; + } + fragColor = cLeft + cRight; +} diff --git a/tests/auto/slides/tst_q3dsslides.cpp b/tests/auto/slides/tst_q3dsslides.cpp index 5863b8e..59d8809 100644 --- a/tests/auto/slides/tst_q3dsslides.cpp +++ b/tests/auto/slides/tst_q3dsslides.cpp @@ -814,11 +814,11 @@ bool tst_Q3DSSlides::isNodeVisible(Q3DSNode *node) return false; auto layerAttached = nodeAttached->layer3DS->attached<Q3DSLayerAttached>(); - Q_ASSERT(layerAttached->opaqueTag || layerAttached->transparentTag); + Q_ASSERT(layerAttached->eyeMono->opaqueTag || layerAttached->eyeMono->transparentTag); const bool visible = (nodeAttached->visibilityTag == Q3DSGraphObjectAttached::Visible); // These should be in sync, or we're in trouble. - Q_ASSERT(visible == (entity->components().contains(layerAttached->opaqueTag) || entity->components().contains(layerAttached->transparentTag))); + Q_ASSERT(visible == (entity->components().contains(layerAttached->eyeMono->opaqueTag) || entity->components().contains(layerAttached->eyeMono->transparentTag))); return visible; } diff --git a/tools/q3dsviewer/q3dsmainwindow.cpp b/tools/q3dsviewer/q3dsmainwindow.cpp index bd0f19e..3aed563 100644 --- a/tools/q3dsviewer/q3dsmainwindow.cpp +++ b/tools/q3dsviewer/q3dsmainwindow.cpp @@ -45,6 +45,8 @@ QT_BEGIN_NAMESPACE +static const float STEREO_SEPARATION_STEP = 0.05f; + QString Q3DStudioMainWindow::fileFilter() { return tr("All Supported Formats (*.uia *.uip);;Studio UI Presentation (*.uip);;Application File (*.uia);;All Files (*)"); @@ -195,6 +197,128 @@ Q3DStudioMainWindow::Q3DStudioMainWindow(Q3DSWindow *view, Q3DSRemoteDeploymentM }); viewMenu->addMenu(scaleModeMenu); + + // Stereoscopic menu + QAction *stereoModeAction = new QAction(tr("Stereo Mode")); + addAction(stereoModeAction); + stereoModeAction->setShortcut(QKeySequence(tr("Ctrl+Shift+T"))); + QMenu *stereoModeMenu = new QMenu(); + stereoModeAction->setMenu(stereoModeMenu); + + QAction *stereoModeMono = new QAction(tr("Mono")); + stereoModeMono->setCheckable(true); + stereoModeMono->setChecked(view->engine()->viewportSettings()->stereoMode() == Q3DSViewportSettings::StereoModeMono); + stereoModeMenu->addAction(stereoModeMono); + + QAction *stereoModeTopBottom = new QAction(tr("Top-Bottom")); + stereoModeTopBottom->setCheckable(true); + stereoModeTopBottom->setChecked(view->engine()->viewportSettings()->stereoMode() == Q3DSViewportSettings::StereoModeTopBottom); + stereoModeMenu->addAction(stereoModeTopBottom); + + QAction *stereoModeLeftRight = new QAction(tr("Left-Right")); + stereoModeLeftRight->setCheckable(true); + stereoModeLeftRight->setChecked(view->engine()->viewportSettings()->stereoMode() == Q3DSViewportSettings::StereoModeLeftRight); + stereoModeMenu->addAction(stereoModeLeftRight); + + QAction *stereoModeAnaglyphRedCyan = new QAction(tr("Anaglyph (Red-Cyan)")); + stereoModeAnaglyphRedCyan->setCheckable(true); + stereoModeAnaglyphRedCyan->setChecked(view->engine()->viewportSettings()->stereoMode() == Q3DSViewportSettings::StereoModeAnaglyphRedCyan); + stereoModeMenu->addAction(stereoModeAnaglyphRedCyan); + + QAction *stereoModeAnaglyphGreenMagenta = new QAction(tr("Anaglyph (Green-Magenta)")); + stereoModeAnaglyphGreenMagenta->setCheckable(true); + stereoModeAnaglyphGreenMagenta->setChecked(view->engine()->viewportSettings()->stereoMode() == Q3DSViewportSettings::StereoModeAnaglyphGreenMagenta); + stereoModeMenu->addAction(stereoModeAnaglyphGreenMagenta); + + QAction *stereoSeparationIncrease = new QAction(tr("Increase Separation")); + stereoSeparationIncrease->setShortcut(QKeySequence(tr("Ctrl+Shift++"))); + stereoModeMenu->addAction(stereoSeparationIncrease); + + QAction *stereoSeparationDecrease = new QAction(tr("Decrease Separation")); + stereoSeparationDecrease->setShortcut(QKeySequence(tr("Ctrl+Shift+-"))); + stereoModeMenu->addAction(stereoSeparationDecrease); + + connect(stereoModeMono, &QAction::triggered, [=]() { + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeMono); + stereoModeMono->setChecked(true); + stereoModeTopBottom->setChecked(false); + stereoModeLeftRight->setChecked(false); + stereoModeAnaglyphRedCyan->setChecked(false); + stereoModeAnaglyphGreenMagenta->setChecked(false); + }); + connect(stereoModeTopBottom, &QAction::triggered, [=]() { + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeTopBottom); + stereoModeMono->setChecked(false); + stereoModeTopBottom->setChecked(true); + stereoModeLeftRight->setChecked(false); + stereoModeAnaglyphRedCyan->setChecked(false); + stereoModeAnaglyphGreenMagenta->setChecked(false); + }); + connect(stereoModeLeftRight, &QAction::triggered, [=]() { + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeLeftRight); + stereoModeMono->setChecked(false); + stereoModeTopBottom->setChecked(false); + stereoModeLeftRight->setChecked(true); + stereoModeAnaglyphRedCyan->setChecked(false); + stereoModeAnaglyphGreenMagenta->setChecked(false); + }); + connect(stereoModeAnaglyphRedCyan, &QAction::triggered, [=]() { + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeAnaglyphRedCyan); + stereoModeMono->setChecked(false); + stereoModeTopBottom->setChecked(false); + stereoModeLeftRight->setChecked(false); + stereoModeAnaglyphRedCyan->setChecked(true); + stereoModeAnaglyphGreenMagenta->setChecked(false); + }); + connect(stereoModeAnaglyphGreenMagenta, &QAction::triggered, [=]() { + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeAnaglyphGreenMagenta); + stereoModeMono->setChecked(false); + stereoModeTopBottom->setChecked(false); + stereoModeLeftRight->setChecked(false); + stereoModeAnaglyphRedCyan->setChecked(false); + stereoModeAnaglyphGreenMagenta->setChecked(true); + }); + + connect(stereoModeAction, &QAction::triggered, [=]() { + // toggle between the stereo modes + if (stereoModeMono->isChecked()) { + stereoModeMono->setChecked(false); + stereoModeTopBottom->setChecked(true); + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeTopBottom); + } else if (stereoModeTopBottom->isChecked()) { + stereoModeTopBottom->setChecked(false); + stereoModeLeftRight->setChecked(true); + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeLeftRight); + } else if (stereoModeLeftRight->isChecked()) { + stereoModeLeftRight->setChecked(false); + stereoModeAnaglyphRedCyan->setChecked(true); + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeAnaglyphRedCyan); + } else if (stereoModeAnaglyphRedCyan->isChecked()) { + stereoModeAnaglyphRedCyan->setChecked(false); + stereoModeAnaglyphGreenMagenta->setChecked(true); + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeAnaglyphGreenMagenta); + } else { + stereoModeAnaglyphGreenMagenta->setChecked(false); + stereoModeMono->setChecked(true); + view->engine()->viewportSettings()->setStereoMode(Q3DSViewportSettings::StereoModeMono); + } + }); + + connect(stereoSeparationIncrease, &QAction::triggered, [=]() { + float separation = view->engine()->viewportSettings()->stereoEyeSeparation(); + separation += STEREO_SEPARATION_STEP; + separation = qMin(separation, 100.0f); + view->engine()->viewportSettings()->setStereoEyeSeparation(separation); + }); + connect(stereoSeparationDecrease, &QAction::triggered, [=]() { + float separation = view->engine()->viewportSettings()->stereoEyeSeparation(); + separation -= STEREO_SEPARATION_STEP; + separation = qMax(separation, 0.0f); + view->engine()->viewportSettings()->setStereoEyeSeparation(separation); + }); + + viewMenu->addMenu(stereoModeMenu); + QAction *fullscreenAction = new QAction(tr("Full Scree&n"), this); connect(fullscreenAction, &QAction::triggered, [this]() { if (!windowState().testFlag(Qt::WindowFullScreen)) { |