diff --git a/src/quick3d/qquick3ditem2d.cpp b/src/quick3d/qquick3ditem2d.cpp index 3650f1d870d85ded9db8e6ad137b514373bd592a..74edc3f0c648a9cc75700be67995448d1be4c227 100644 --- a/src/quick3d/qquick3ditem2d.cpp +++ b/src/quick3d/qquick3ditem2d.cpp @@ -179,11 +179,12 @@ QSSGRenderGraphObject *QQuick3DItem2D::updateSpatialNode(QSSGRenderGraphObject * m_layer->setSize(textureSize); - itemNode->qsgTexture = m_layer; + itemNode->zOrder = float(m_sourceItem->z()); if (m_sourceItem->isVisible()) itemNode->combinedOpacity = itemNode->globalOpacity * float(m_sourceItem->opacity()); else itemNode->combinedOpacity = 0.0f; + itemNode->qsgTexture = m_layer; return node; } diff --git a/src/runtimerender/graphobjects/qssgrenderitem2d_p.h b/src/runtimerender/graphobjects/qssgrenderitem2d_p.h index 39e25f1e8a797a706de0558a9da0e3701f0d5632..258da7b682cd39a0dee8ddaaba47a71b167f704a 100644 --- a/src/runtimerender/graphobjects/qssgrenderitem2d_p.h +++ b/src/runtimerender/graphobjects/qssgrenderitem2d_p.h @@ -54,7 +54,8 @@ struct Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRenderItem2D : public QSSGRenderNode QSGTexture *qsgTexture = nullptr; QMatrix4x4 MVP; - float combinedOpacity; + float combinedOpacity = 1.0; + float zOrder = 0; QSSGRenderItem2D(); ~QSSGRenderItem2D(); diff --git a/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderdata.cpp b/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderdata.cpp index 01d16069247f5970986dee232adbe8ce7339c1ae..be2eb648167140914e3b6e5b8ac2a5ab992cbefe 100644 --- a/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderdata.cpp +++ b/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderdata.cpp @@ -839,6 +839,22 @@ void QSSGLayerRenderData::runRenderPass(TRenderRenderableFunction inRenderFn, inRenderFn(*this, *theObject, theCameraProps, getShaderFeatureSet(), indexLight, inCamera); } + // Render Quick items + for (auto theNodeEntry : getRenderableItem2Ds()) { + QSSGRenderItem2D *item2D = static_cast<QSSGRenderItem2D *>(theNodeEntry.node); + // Fast-path to avoid rendering totally transparent items + if (item2D->combinedOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) + continue; + // Don't try rendering until texture exists + if (!item2D->qsgTexture) + continue; + QVector2D dimensions = QVector2D(item2D->qsgTexture->textureSize().width(), + item2D->qsgTexture->textureSize().height()); + QSSGRenderTexture2D tex(renderer->context(), item2D->qsgTexture); + + renderer->renderFlippedQuad(dimensions, item2D->MVP, tex, item2D->combinedOpacity); + } + // transparent objects if (inEnableBlending || !layer.flags.testFlag(QSSGRenderLayer::Flag::LayerEnableDepthTest)) { theRenderContext->setBlendingEnabled(inEnableBlending); @@ -880,20 +896,6 @@ void QSSGLayerRenderData::render(QSSGResourceFrameBuffer *theFB) renderer->beginLayerRender(*this); runRenderPass(renderRenderable, true, !layer.flags.testFlag(QSSGRenderLayer::Flag::LayerEnableDepthPrePass), false, true, 0, *camera, theFB); - for (auto theNodeEntry : getRenderableItem2Ds()) { - QSSGRenderItem2D *item2D = static_cast<QSSGRenderItem2D *>(theNodeEntry.node); - // Fast-path to avoid rendering totally transparent items - if (item2D->combinedOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) - continue; - // Don't try rendering until texture exists - if (!item2D->qsgTexture) - continue; - QVector2D dimensions = QVector2D(item2D->qsgTexture->textureSize().width(), - item2D->qsgTexture->textureSize().height()); - QSSGRenderTexture2D tex(renderer->context(), item2D->qsgTexture); - - renderer->renderFlippedQuad(dimensions, item2D->MVP, tex, item2D->combinedOpacity); - } renderer->endLayerRender(); } diff --git a/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata.cpp b/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata.cpp index d87b599e4989f36d705c7e9438dc1607d532412e..446a543f45d26c0c1d60052edfb5631e87270e48 100644 --- a/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata.cpp +++ b/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata.cpp @@ -212,7 +212,44 @@ const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderPreparationData::getTr const QVector<QSSGRenderableNodeEntry> &QSSGLayerRenderPreparationData::getRenderableItem2Ds() { - return renderableItem2Ds; + + if (!renderedItem2Ds.isEmpty() || camera == nullptr) + return renderedItem2Ds; + + renderedItem2Ds = renderableItem2Ds; + + const QVector3D cameraDirection(getCameraDirection()); + const QVector3D cameraPosition = camera->getGlobalPos(); + + const auto isItemNodeDistanceGreatThan = [cameraDirection, cameraPosition] + (const QSSGRenderableNodeEntry &lhs, const QSSGRenderableNodeEntry &rhs) { + if (!lhs.node->parent || !rhs.node->parent) + return false; + const QVector3D lhsDifference = lhs.node->parent->position - cameraPosition; + const float lhsCameraDistanceSq = QVector3D::dotProduct(lhsDifference, cameraDirection); + const QVector3D rhsDifference = rhs.node->parent->position - cameraPosition; + const float rhsCameraDistanceSq = QVector3D::dotProduct(rhsDifference, cameraDirection); + return lhsCameraDistanceSq > rhsCameraDistanceSq; + }; + + const auto isItemZOrderLessThan = [] + (const QSSGRenderableNodeEntry &lhs, const QSSGRenderableNodeEntry &rhs) { + if (lhs.node->parent && rhs.node->parent && lhs.node->parent == rhs.node->parent) { + // Same parent nodes, so sort with item z-ordering + QSSGRenderItem2D *lhsItem = static_cast<QSSGRenderItem2D *>(lhs.node); + QSSGRenderItem2D *rhsItem = static_cast<QSSGRenderItem2D *>(rhs.node); + return lhsItem->zOrder < rhsItem->zOrder; + } + return false; + }; + + // Render furthest to nearest items (parent nodes). + std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemNodeDistanceGreatThan); + // Render items inside same node by item z-order. + // Note: stable_sort so item order in QML file is respected. + std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemZOrderLessThan); + + return renderedItem2Ds; } /** @@ -810,7 +847,8 @@ bool QSSGLayerRenderPreparationData::prepareRenderablesForRender(const QMatrix4x theItem2D->calculateGlobalVariables(); if (theItem2D->flags.testFlag(QSSGRenderModel::Flag::GloballyActive)) { theItem2D->MVP = inViewProjection * theItem2D->globalTransform; - renderableItem2Ds.push_back(theNodeEntry); + // Pushing front to keep item order inside QML file + renderableItem2Ds.push_front(theNodeEntry); } } break; default: @@ -1158,6 +1196,7 @@ void QSSGLayerRenderPreparationData::resetForFrame() lightDirections.clear(); renderedOpaqueObjects.clear(); renderedTransparentObjects.clear(); + renderedItem2Ds.clear(); } QT_END_NAMESPACE diff --git a/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata_p.h b/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata_p.h index 6aa05eb687fd668e429194be4430f81a42344736..57fd1fa4e75882d63cfda95e5f060f1c85104013 100644 --- a/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata_p.h +++ b/src/runtimerender/rendererimpl/qssgrendererimpllayerrenderpreparationdata_p.h @@ -231,6 +231,7 @@ struct QSSGLayerRenderPreparationData // TNodeLightEntryPoolType m_RenderableNodeLightEntryPool; QVector<QSSGRenderableNodeEntry> renderableNodes; QVector<QSSGRenderableNodeEntry> renderableItem2Ds; + QVector<QSSGRenderableNodeEntry> renderedItem2Ds; TLightToNodeMap lightToNodeMap; // map of lights to nodes to cache if we have looked up a // given scoped light yet. // Built at the same time as the renderable nodes map. diff --git a/tests/auto/quick3d_lancelot/data/quick/RenderingOrder.qml b/tests/auto/quick3d_lancelot/data/quick/RenderingOrder.qml new file mode 100644 index 0000000000000000000000000000000000000000..f3e00b14bcf71ba579bd8a22055f30f8e631595e --- /dev/null +++ b/tests/auto/quick3d_lancelot/data/quick/RenderingOrder.qml @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick3D 1.15 +import QtQuick 2.15 + +Rectangle { + width: 480 + height: 480 + color: Qt.rgba(1, 1, 0.5, 1) + + View3D { + id: layer + anchors.fill: parent + environment: SceneEnvironment { + clearColor: Qt.rgba(0, 0, 0, 1) + aoDither: true + depthPrePassEnabled: true + depthTestEnabled: true + } + + PerspectiveCamera { + position: Qt.vector3d(0, 0, 800) + } + + DirectionalLight { + } + + DirectionalLight { + eulerRotation.x: -30 + eulerRotation.y: -70 + } + + // Model behind of everything else + Model { + position: Qt.vector3d(0, 100, -400) + eulerRotation: Qt.vector3d(45, 45, 45) + scale: Qt.vector3d(3.5, 3.5, 3.5) + source: "#Cube" + materials: DefaultMaterial { + diffuseColor: Qt.rgba(1, 0, 1, 1) + } + } + + // Model in front of everything else + Model { + position: Qt.vector3d(0, -100, 450) + eulerRotation: Qt.vector3d(45, 45, 45) + scale: Qt.vector3d(1.0, 1.0, 1.0) + source: "#Cube" + opacity: 1.0 + materials: DefaultMaterial { + diffuseColor: Qt.rgba(0, 1, 1, 1) + } + } + + // Pile #1 + Node { + x: -200 + y: 250 + Rectangle { + width: 200 + height: 200 + color: "red" + opacity: 0.8 + } + } + Node { + x: -200 + y: 250 + z: 1 + Rectangle { + width: 150 + height: 150 + color: "green" + opacity: 0.8 + } + } + Node { + x: -200 + y: 250 + z: 2 + Rectangle { + width: 100 + height: 100 + color: "blue" + opacity: 0.8 + } + } + + // Pile #2 + Node { + x: 200 + y: 250 + Rectangle { + width: 200 + height: 200 + color: "red" + opacity: 0.8 + } + } + Node { + x: 200 + y: 250 + z: 2 + Rectangle { + width: 100 + height: 100 + color: "blue" + opacity: 0.8 + } + } + Node { + x: 200 + y: 250 + z: 1 + Rectangle { + width: 150 + height: 150 + color: "green" + opacity: 0.8 + } + } + + // Pile #3 + Node { + x: -200 + y: 0 + z: -1 + Rectangle { + width: 100 + height: 100 + color: "blue" + opacity: 0.8 + } + } + Node { + x: -200 + y: 0 + z: -2 + Rectangle { + width: 150 + height: 150 + color: "green" + opacity: 0.8 + } + } + Node { + x: -200 + y: 0 + z: -3 + Rectangle { + width: 200 + height: 200 + color: "red" + opacity: 0.8 + } + } + + // Pile #4 + Node { + x: 200 + y: 0 + z: -1 + Rectangle { + width: 100 + height: 100 + color: "blue" + opacity: 0.8 + } + } + Node { + x: 200 + y: 0 + z: 20 + NumberAnimation on z { + to: -2 + duration: 200 + } + Rectangle { + width: 150 + height: 150 + color: "green" + opacity: 0.8 + } + } + Node { + x: 200 + y: 0 + z: 3 + Component.onCompleted: { + z = -3; + } + Rectangle { + width: 200 + height: 200 + color: "red" + opacity: 0.8 + } + } + + // Pile #5 with 3D models + Model { + position: Qt.vector3d(-200, -250, -20) + eulerRotation: Qt.vector3d(45, 45, 45) + scale: Qt.vector3d(0.4, 0.4, 0.4) + opacity: 0.8 + source: "#Cube" + Component.onCompleted: { + z = 20; + } + materials: DefaultMaterial { + diffuseColor: Qt.rgba(1, 1, 1, 1) + } + } + Model { + position: Qt.vector3d(-200, -250, -1) + scale: Qt.vector3d(2, 2, 0.01) + opacity: 0.8 + source: "#Cube" + materials: DefaultMaterial { + diffuseColor: Qt.rgba(1, 0, 0, 1) + } + } + Node { + x: -200 + y: -250 + opacity: 0.8 + Rectangle { + width: 150 + height: 150 + color: "green" + opacity: 0.8 + } + } + Model { + position: Qt.vector3d(-200, -250, 1) + //eulerRotation: Qt.vector3d(-10,-10,-10) + scale: Qt.vector3d(1, 1, 0.01) + opacity: 0.8 + source: "#Cube" + materials: DefaultMaterial { + diffuseColor: Qt.rgba(0, 0, 1, 1) + } + } + + // Pile #6 with 3D models + Node { + x: 200 + y: -250 + z: 10 + Rectangle { + width: 200 + height: 200 + color: "red" + opacity: 0.8 + } + } + Node { + x: 200 + y: -250 + z: 10 + Model { + scale: Qt.vector3d(1.5, 1.5, 0.01) + opacity: 0.8 + source: "#Cube" + materials: DefaultMaterial { + diffuseColor: Qt.rgba(0, 1, 0, 1) + } + } + } + Node { + x: 200 + y: -250 + z: 12 + Rectangle { + width: 100 + height: 100 + color: "blue" + opacity: 0.8 + } + } + Model { + position: Qt.vector3d(200, -250, -20) + eulerRotation: Qt.vector3d(45, 45, 45) + scale: Qt.vector3d(0.4, 0.4, 0.4) + source: "#Cube" + NumberAnimation on z { + to: 20 + duration: 200 + } + materials: DefaultMaterial { + diffuseColor: Qt.rgba(1, 1, 1, 1) + } + } + } +}