QmlDesigner: Don't display the camera, if it has no scene

Fixes: QDS-13490
Change-Id: I82bf07866f667dd39773f7e5e172ebd36c36e46e
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2024-08-30 16:23:19 +03:00
parent 52c7d2b0f0
commit 340f1ce19a
5 changed files with 156 additions and 91 deletions

View File

@@ -24,6 +24,7 @@
<file>mockfiles/qt6/AdjustableArrow.qml</file> <file>mockfiles/qt6/AdjustableArrow.qml</file>
<file>mockfiles/qt6/Arrow.qml</file> <file>mockfiles/qt6/Arrow.qml</file>
<file>mockfiles/qt6/AutoScaleHelper.qml</file> <file>mockfiles/qt6/AutoScaleHelper.qml</file>
<file>mockfiles/qt6/CameraDisplay.qml</file>
<file>mockfiles/qt6/CameraFrustum.qml</file> <file>mockfiles/qt6/CameraFrustum.qml</file>
<file>mockfiles/qt6/CameraGizmo.qml</file> <file>mockfiles/qt6/CameraGizmo.qml</file>
<file>mockfiles/qt6/CameraView.qml</file> <file>mockfiles/qt6/CameraView.qml</file>

View File

@@ -0,0 +1,82 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick3D
Item {
id: cameraDisplay
required property Camera camera
required property size preferredSize
required property size viewPortSize
required property var activeScene
required property var activeSceneEnvironment
implicitWidth: view3D.dispSize.width
implicitHeight: view3D.dispSize.height
clip: true
View3D {
id: view3D
readonly property bool isOrthographicCamera: _generalHelper.isOrthographicCamera(view3D.camera)
property size dispSize: calculateSize(cameraDisplay.viewPortSize, cameraDisplay.preferredSize)
readonly property real magnificationFactor: cameraDisplay.viewPortSize.width === 0
? 1.0
: (view3D.dispSize.width / cameraDisplay.viewPortSize.width)
transformOrigin: Item.Center
anchors.centerIn: parent
camera: cameraDisplay.camera
importScene: cameraDisplay.activeScene
environment: cameraDisplay.activeSceneEnvironment ?? defaultSceneEnvironment
function calculateSize(viewPortSize: size, preferredSize : size) {
if (_generalHelper.fuzzyCompare(viewPortSize.width, 0)
|| _generalHelper.fuzzyCompare(viewPortSize.height, 0)) {
return Qt.size(0, 0)
}
let aspectRatio = viewPortSize.height / viewPortSize.width
var calculatedHeight = preferredSize.width * aspectRatio
if (calculatedHeight <= preferredSize.height)
return Qt.size(preferredSize.width, calculatedHeight)
var calculatedWidth = preferredSize.height / aspectRatio;
return Qt.size(calculatedWidth, preferredSize.height);
}
}
SceneEnvironment {
id: defaultSceneEnvironment
antialiasingMode: SceneEnvironment.MSAA
antialiasingQuality: SceneEnvironment.High
}
states: [
State {
name: "orthoCamera"
when: view3D.isOrthographicCamera
PropertyChanges {
target: view3D
width: cameraDisplay.viewPortSize.width
height: cameraDisplay.viewPortSize.height
scale: view3D.magnificationFactor
}
},
State {
name: "nonOrthoCamera"
when: !view3D.isOrthographicCamera
PropertyChanges {
target: view3D
width: view3D.dispSize.width
height: view3D.dispSize.height
scale: 1.0
}
}
]
}

View File

@@ -3,7 +3,7 @@
import QtQuick import QtQuick
import QtQuick3D import QtQuick3D
Item { Rectangle {
id: cameraView id: cameraView
required property bool showCameraView required property bool showCameraView
@@ -16,44 +16,57 @@ Item {
property var activeSceneEnvironment property var activeSceneEnvironment
property var preferredCamera property var preferredCamera
width: loader.width width: priv.loaderSize.width + 2
height: loader.height height: priv.loaderSize.height + 2
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.margins: 10 anchors.margins: 10
visible: loader.active
onTargetNodeChanged: loader.updateCamera() visible: priv.cameraViewIsOn
onAlwaysOnChanged: loader.updateCamera()
onPreferredCameraChanged: loader.updateCamera() onTargetNodeChanged: priv.updateCamera()
onAlwaysOnChanged: priv.updateCamera()
onPreferredCameraChanged: priv.updateCamera()
onActiveSceneChanged: forceReload() onActiveSceneChanged: forceReload()
Component.onCompleted: loader.updateCamera() Component.onCompleted: priv.updateCamera()
border.width: 1
border.color: "lightslategray"
color: "black"
function forceReload() { function forceReload() {
loader.forceDeactive = true priv.forceDeactive = true
loader.oldCamera = null priv.oldCamera = null
loader.updateCamera() priv.updateCamera()
loader.forceDeactive = false priv.forceDeactive = false
} }
Loader { QtObject {
id: loader id: priv
property Camera camera property Camera camera
property Camera oldCamera property Camera oldCamera
property bool view3dRootNodeExists
property bool forceDeactive: false property bool forceDeactive: false
readonly property bool cameraViewIsOn: !forceDeactive && (cameraView.alwaysOn || cameraView.showCameraView) && priv.camera
active: !forceDeactive && (cameraView.alwaysOn || cameraView.showCameraView) && loader.camera readonly property bool cameraHasValidScene: priv.cameraViewIsOn && priv.view3dRootNodeExists
property Loader activeLoader
readonly property size loaderSize: activeLoader && activeLoader.active
? Qt.size(activeLoader.width, activeLoader.height)
: Qt.size(-2, -2)
function updateCamera() { function updateCamera() {
loader.camera = activeCamera() let activeCam = activeCamera()
priv.camera = activeCam
priv.view3dRootNodeExists = _generalHelper.view3dRootNode(activeCam)
} }
function activeCamera() { function activeCamera() {
if (cameraView.alwaysOn) { if (cameraView.alwaysOn) {
if (_generalHelper.isCamera(cameraView.targetNode)) if (_generalHelper.isCamera(cameraView.targetNode))
return cameraView.targetNode return cameraView.targetNode
else if (loader.oldCamera) else if (priv.oldCamera)
return loader.oldCamera return priv.oldCamera
else else
return cameraView.preferredCamera return cameraView.preferredCamera
} else if (_generalHelper.isCamera(cameraView.targetNode)) { } else if (_generalHelper.isCamera(cameraView.targetNode)) {
@@ -63,82 +76,36 @@ Item {
} }
onCameraChanged: { onCameraChanged: {
if (loader.camera) if (priv.camera)
loader.oldCamera = loader.camera priv.oldCamera = priv.camera
}
sourceComponent: Rectangle {
id: cameraRect
implicitWidth: view3D.dispSize.width + 2
implicitHeight: view3D.dispSize.height + 2
border.width: 1
border.color: "lightslategray"
color: "black"
View3D {
id: view3D
readonly property bool isOrthographicCamera: _generalHelper.isOrthographicCamera(view3D.camera)
property size dispSize: calculateSize(cameraView.viewPortSize, cameraView.preferredSize)
readonly property real magnificationFactor: cameraView.viewPortSize.width === 0
? 1.0
: (view3D.dispSize.width / cameraView.viewPortSize.width)
transformOrigin: Item.Center
anchors.centerIn: parent
camera: loader.camera
importScene: cameraView.activeScene
environment: cameraView.activeSceneEnvironment ?? defaultSceneEnvironment
function calculateSize(viewPortSize: size, preferredSize : size){
if (_generalHelper.fuzzyCompare(viewPortSize.width, 0)
|| _generalHelper.fuzzyCompare(viewPortSize.height, 0))
return Qt.size(0, 0)
let aspectRatio = viewPortSize.height / viewPortSize.width
var calculatedHeight = preferredSize.width * aspectRatio
if (calculatedHeight <= preferredSize.height)
return Qt.size(preferredSize.width, calculatedHeight)
var calculatedWidth = preferredSize.height / aspectRatio;
return Qt.size(calculatedWidth, preferredSize.height);
}
states: [
State {
name: "orthoCamera"
when: view3D.isOrthographicCamera
PropertyChanges {
target: view3D
width: cameraView.viewPortSize.width
height: cameraView.viewPortSize.height
scale: view3D.magnificationFactor
}
},
State {
name: "nonOrthoCamera"
when: !view3D.isOrthographicCamera
PropertyChanges {
target: view3D
width: view3D.dispSize.width
height: view3D.dispSize.height
scale: 1.0
}
}
]
}
} }
} }
SceneEnvironment { Loader {
id: defaultSceneEnvironment id: cameraLoader
antialiasingMode: SceneEnvironment.MSAA active: priv.cameraViewIsOn && priv.cameraHasValidScene
antialiasingQuality: SceneEnvironment.High onLoaded: priv.activeLoader = this
sourceComponent: CameraDisplay {
camera: priv.camera
preferredSize: cameraView.preferredSize
viewPortSize: cameraView.viewPortSize
activeScene: cameraView.activeScene
activeSceneEnvironment: cameraView.activeSceneEnvironment
}
}
Loader {
id: errorLoader
active: priv.cameraViewIsOn && !priv.cameraHasValidScene
onLoaded: priv.activeLoader = this
sourceComponent: Text {
font.pixelSize: 14
color: "yellow"
text: qsTr("Camera does not have a valid view")
padding: 10
}
} }
states: [ states: [

View File

@@ -692,6 +692,20 @@ bool GeneralHelper::isOrthographicCamera(QQuick3DNode *node) const
return node && qobject_cast<QQuick3DOrthographicCamera *>(node); return node && qobject_cast<QQuick3DOrthographicCamera *>(node);
} }
QQuick3DNode *GeneralHelper::view3dRootNode(QQuick3DNode *node) const
{
if (!node)
return nullptr;
QQuick3DNode *parentNode = node->parentNode();
while (parentNode) {
if (parentNode->inherits("QQuick3DSceneRootNode"))
return parentNode;
parentNode = parentNode->parentNode();
}
return nullptr;
}
// Emitter gizmo model creation is done in C++ as creating dynamic properties and // Emitter gizmo model creation is done in C++ as creating dynamic properties and
// assigning materials to dynamically created models is lot simpler in C++ // assigning materials to dynamically created models is lot simpler in C++
QQuick3DNode *GeneralHelper::createParticleEmitterGizmoModel(QQuick3DNode *emitter, QQuick3DNode *GeneralHelper::createParticleEmitterGizmoModel(QQuick3DNode *emitter,

View File

@@ -97,6 +97,7 @@ public:
Q_INVOKABLE bool isPickable(QQuick3DNode *node) const; Q_INVOKABLE bool isPickable(QQuick3DNode *node) const;
Q_INVOKABLE bool isCamera(QQuick3DNode *node) const; Q_INVOKABLE bool isCamera(QQuick3DNode *node) const;
Q_INVOKABLE bool isOrthographicCamera(QQuick3DNode *node) const; Q_INVOKABLE bool isOrthographicCamera(QQuick3DNode *node) const;
Q_INVOKABLE QQuick3DNode *view3dRootNode(QQuick3DNode *node) const;
Q_INVOKABLE QQuick3DNode *createParticleEmitterGizmoModel(QQuick3DNode *emitter, Q_INVOKABLE QQuick3DNode *createParticleEmitterGizmoModel(QQuick3DNode *emitter,
QQuick3DMaterial *material) const; QQuick3DMaterial *material) const;