QmlDesigner: Add support for multiple views to 3D edit view

Any subtree of Nodes can be shown in 3D edit view.
Selecting any Node or their children, or View3D changes the
view in 3D edit view to the relevant scene.

Fixes: QDS-1494
Fixes: QDS-1565
Change-Id: I2d5a6f88bab2a20b74c347351235f79fb530519b
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2020-01-28 10:40:44 +02:00
parent 22b64369c0
commit 3890bf4efb
11 changed files with 375 additions and 166 deletions

View File

@@ -40,7 +40,7 @@ DirectionalDraggable {
_targetStartPos.x + sceneRelativeDistance.x,
_targetStartPos.y + sceneRelativeDistance.y,
_targetStartPos.z + sceneRelativeDistance.z);
return targetNode.parent.mapPositionFromScene(newScenePos);
return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos;
}
onDragged: {

View File

@@ -37,9 +37,9 @@ View3D {
Node {
OrthographicCamera {
id: axisHelperCamera
rotation: editCameraCtrl.camera.rotation
position: editCameraCtrl.camera.position.minus(editCameraCtrl._lookAtPoint)
.normalized().times(600)
rotation: editCameraCtrl.camera ? editCameraCtrl.camera.rotation : Qt.vector3d(0, 0, 0)
position: editCameraCtrl.camera ? editCameraCtrl.camera.position.minus(editCameraCtrl._lookAtPoint)
.normalized().times(600) : Qt.vector3d(0, 0, 0)
}
AutoScaleHelper {

View File

@@ -46,6 +46,9 @@ Item {
property Camera _prevCamera: null
function restoreCameraState(cameraState) {
if (!camera)
return;
_lookAtPoint = cameraState[0];
_zoomFactor = cameraState[1];
camera.position = cameraState[2];
@@ -55,6 +58,9 @@ Item {
}
function storeCameraState(delay) {
if (!camera)
return;
var cameraState = [];
cameraState[0] = _lookAtPoint;
cameraState[1] = _zoomFactor;
@@ -66,6 +72,9 @@ Item {
function focusObject(targetObject, rotation, updateZoom)
{
if (!camera)
return;
camera.rotation = rotation;
var newLookAtAndZoom = _generalHelper.focusObjectToCamera(
camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, updateZoom);
@@ -76,16 +85,19 @@ Item {
function zoomRelative(distance)
{
if (!camera)
return;
_zoomFactor = _generalHelper.zoomCamera(camera, distance, _defaultCameraLookAtDistance,
_lookAtPoint, _zoomFactor, true);
}
Component.onCompleted: {
cameraCtrl._defaultCameraLookAtDistance = cameraCtrl.camera.position.length();
cameraCtrl._defaultCameraLookAtDistance = Qt.vector3d(0, 600, -600).length();
}
onCameraChanged: {
if (_prevCamera) {
if (camera && _prevCamera) {
// Reset zoom on previous camera to ensure it's properties are good to copy to new cam
_generalHelper.zoomCamera(_prevCamera, 0, _defaultCameraLookAtDistance, _lookAtPoint,
1, false);
@@ -106,7 +118,7 @@ Item {
hoverEnabled: false
anchors.fill: parent
onPositionChanged: {
if (mouse.modifiers === Qt.AltModifier && cameraCtrl._dragging) {
if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier && cameraCtrl._dragging) {
var currentPoint = Qt.vector3d(mouse.x, mouse.y, 0);
if (cameraCtrl._button == Qt.LeftButton) {
_generalHelper.orbitCamera(cameraCtrl.camera, cameraCtrl._startRotation,
@@ -124,7 +136,7 @@ Item {
}
}
onPressed: {
if (mouse.modifiers === Qt.AltModifier) {
if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier) {
cameraCtrl._dragging = true;
cameraCtrl._startRotation = cameraCtrl.camera.rotation;
cameraCtrl._startPosition = cameraCtrl.camera.position;
@@ -147,9 +159,11 @@ Item {
onCanceled: handleRelease()
onWheel: {
if (cameraCtrl.camera) {
// Emprically determined divisor for nice zoom
cameraCtrl.zoomRelative(wheel.angleDelta.y / -40);
cameraCtrl.storeCameraState(500);
}
}
}
}

View File

@@ -41,7 +41,8 @@ Window {
// need all those flags otherwise the title bar disappears after setting WindowStaysOnTopHint flag later
flags: Qt.Window | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint
property alias scene: editView.importScene
property Node activeScene: null
property View3D editView: null
property alias showEditLight: btnEditViewLight.toggled
property alias usePerspective: btnPerspective.toggled
@@ -63,6 +64,31 @@ Window {
onShowEditLightChanged: _generalHelper.storeToolState("showEditLight", showEditLight)
onGlobalOrientationChanged: _generalHelper.storeToolState("globalOrientation", globalOrientation)
onActiveSceneChanged: {
// importScene cannot be updated after initial set, so we need to reconstruct entire View3D
var component = Qt.createComponent("SceneView3D.qml");
if (component.status === Component.Ready) {
var oldView = editView;
if (editView)
editView.visible = false;
editView = component.createObject(viewRect,
{"usePerspective": usePerspective,
"showSceneLight": showEditLight,
"importScene": activeScene,
"z": 1});
editView.usePerspective = Qt.binding(function() {return usePerspective;});
editView.showSceneLight = Qt.binding(function() {return showEditLight;});
selectionBoxes.length = 0;
ensureSelectionBoxes(1);
if (oldView)
oldView.destroy();
}
}
function updateToolStates(toolStates) {
// Init the stored state so we don't unnecessarily reflect changes back to creator
_generalHelper.initToolStates(toolStates);
@@ -103,9 +129,13 @@ Window {
if (component.status === Component.Ready) {
for (var i = 0; i < needMore; ++i) {
var geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry");
var box = component.createObject(mainSceneHelpers, {"view3D": editView,
var boxParent = null;
if (editView)
boxParent = editView.sceneHelpers;
var box = component.createObject(boxParent, {"view3D": editView,
"geometryName": geometryName});
selectionBoxes[selectionBoxes.length] = box;
box.view3D = Qt.binding(function() {return editView;});
}
}
}
@@ -133,7 +163,7 @@ Window {
function handleObjectClicked(object, multi) {
var theObject = object;
if (btnSelectGroup.selected) {
while (theObject && theObject.parent !== scene)
while (theObject && theObject.parent !== activeScene)
theObject = theObject.parent;
}
// Object selection logic:
@@ -162,11 +192,12 @@ Window {
selectionChanged(newSelection);
}
function addLightGizmo(obj)
function addLightGizmo(scene, obj)
{
// Insert into first available gizmo
for (var i = 0; i < lightGizmos.length; ++i) {
if (!lightGizmos[i].targetNode) {
lightGizmos[i].scene = scene;
lightGizmos[i].targetNode = obj;
return;
}
@@ -177,18 +208,21 @@ Window {
if (component.status === Component.Ready) {
var gizmo = component.createObject(overlayScene,
{"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes});
"selectedNodes": selectedNodes, "scene": scene,
"activeScene": activeScene});
lightGizmos[lightGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
gizmo.activeScene = Qt.binding(function() {return activeScene;});
}
}
function addCameraGizmo(obj)
function addCameraGizmo(scene, obj)
{
// Insert into first available gizmo
for (var i = 0; i < cameraGizmos.length; ++i) {
if (!cameraGizmos[i].targetNode) {
cameraGizmos[i].scene = scene;
cameraGizmos[i].targetNode = obj;
return;
}
@@ -200,11 +234,13 @@ Window {
var gizmo = component.createObject(
overlayScene,
{"view3D": overlayView, "targetNode": obj, "geometryName": geometryName,
"viewPortRect": viewPortRect, "selectedNodes": selectedNodes});
"viewPortRect": viewPortRect, "selectedNodes": selectedNodes,
"scene": scene, "activeScene": activeScene});
cameraGizmos[cameraGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;});
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
gizmo.activeScene = Qt.binding(function() {return activeScene;});
}
}
@@ -233,19 +269,19 @@ Window {
PerspectiveCamera {
id: overlayPerspectiveCamera
clipFar: editPerspectiveCamera.clipFar
clipNear: editPerspectiveCamera.clipNear
position: editPerspectiveCamera.position
rotation: editPerspectiveCamera.rotation
clipFar: viewWindow.editView ? viewWindow.editView.perpectiveCamera.clipFar : 1000
clipNear: viewWindow.editView ? viewWindow.editView.perpectiveCamera.clipNear : 1
position: viewWindow.editView ? viewWindow.editView.perpectiveCamera.position : Qt.vector3d(0, 0, 0)
rotation: viewWindow.editView ? viewWindow.editView.perpectiveCamera.rotation : Qt.vector3d(0, 0, 0)
}
OrthographicCamera {
id: overlayOrthoCamera
clipFar: editOrthoCamera.clipFar
clipNear: editOrthoCamera.clipNear
position: editOrthoCamera.position
rotation: editOrthoCamera.rotation
scale: editOrthoCamera.scale
clipFar: viewWindow.editView ? viewWindow.editView.orthoCamera.clipFar : 1000
clipNear: viewWindow.editView ? viewWindow.editView.orthoCamera.clipNear : 1
position: viewWindow.editView ? viewWindow.editView.orthoCamera.position : Qt.vector3d(0, 0, 0)
rotation: viewWindow.editView ? viewWindow.editView.orthoCamera.rotation : Qt.vector3d(0, 0, 0)
scale: viewWindow.editView ? viewWindow.editView.orthoCamera.scale : Qt.vector3d(0, 0, 0)
}
MouseArea3D {
@@ -349,6 +385,7 @@ Window {
}
Rectangle {
id: viewRect
anchors.fill: parent
focus: true
@@ -361,69 +398,26 @@ Window {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onClicked: {
var pickResult = editView.pick(mouse.x, mouse.y);
if (viewWindow.editView) {
var pickResult = viewWindow.editView.pick(mouse.x, mouse.y);
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
mouse.modifiers & Qt.ControlModifier);
if (!pickResult.objectHit)
mouse.accepted = false;
}
}
}
DropArea {
anchors.fill: parent
}
View3D {
id: editView
anchors.fill: parent
camera: usePerspective ? editPerspectiveCamera : editOrthoCamera
Node {
id: mainSceneHelpers
HelperGrid {
id: helperGrid
lines: 50
step: 50
}
PointLight {
id: editLight
visible: showEditLight
position: usePerspective ? editPerspectiveCamera.position
: editOrthoCamera.position
quadraticFade: 0
linearFade: 0
}
// Initial camera position and rotation should be such that they look at origin.
// Otherwise EditCameraController._lookAtPoint needs to be initialized to correct
// point.
PerspectiveCamera {
id: editPerspectiveCamera
z: -600
y: 600
rotation.x: 45
clipFar: 100000
clipNear: 1
}
OrthographicCamera {
id: editOrthoCamera
z: -600
y: 600
rotation.x: 45
clipFar: 100000
clipNear: -10000
}
}
}
View3D {
id: overlayView
anchors.fill: parent
camera: usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera
importScene: overlayScene
z: 2
}
Overlay2D {
@@ -431,6 +425,7 @@ Window {
targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo
targetView: overlayView
visible: targetNode.dragging
z: 3
Rectangle {
color: "white"
@@ -463,9 +458,9 @@ Window {
EditCameraController {
id: cameraControl
camera: editView.camera
camera: viewWindow.editView ? viewWindow.editView.camera : null
anchors.fill: parent
view3d: editView
view3d: viewWindow.editView
}
}
@@ -557,10 +552,10 @@ Window {
togglable: false
onSelectedChanged: {
if (selected) {
if (viewWindow.editView && selected) {
var targetNode = viewWindow.selectedNodes.length > 0
? selectionBoxes[0].model : null;
cameraControl.focusObject(targetNode, editView.camera.rotation, true);
cameraControl.focusObject(targetNode, viewWindow.editView.camera.rotation, true);
}
}
}

View File

@@ -30,6 +30,8 @@ import QtGraphicalEffects 1.12
Node {
id: iconGizmo
property Node activeScene: null
property Node scene: null
property View3D view3D
property bool highlightOnHover: true
property Node targetNode: null
@@ -50,7 +52,7 @@ Node {
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
rotation: targetNode ? targetNode.sceneRotation : Qt.vector3d(0, 0, 0)
visible: targetNode ? targetNode.visible : false
visible: activeScene === scene && (targetNode ? targetNode.visible : false)
Overlay2D {
id: iconOverlay

View File

@@ -40,7 +40,7 @@ PlanarDraggable {
_targetStartPos.x + sceneRelativeDistance.x,
_targetStartPos.y + sceneRelativeDistance.y,
_targetStartPos.z + sceneRelativeDistance.z);
return targetNode.parent.mapPositionFromScene(newScenePos);
return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos;
}
onDragged: {

View File

@@ -0,0 +1,80 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QtQuick 2.12
import QtQuick3D 1.14
View3D {
id: sceneView
anchors.fill: parent
property bool usePerspective: false
property bool showSceneLight: false
property alias sceneHelpers: sceneHelpers
property alias perpectiveCamera: scenePerspectiveCamera
property alias orthoCamera: sceneOrthoCamera
camera: usePerspective ? scenePerspectiveCamera : sceneOrthoCamera
Node {
id: sceneHelpers
HelperGrid {
id: helperGrid
lines: 50
step: 50
}
PointLight {
id: sceneLight
visible: showSceneLight
position: usePerspective ? scenePerspectiveCamera.position
: sceneOrthoCamera.position
quadraticFade: 0
linearFade: 0
}
// Initial camera position and rotation should be such that they look at origin.
// Otherwise EditCameraController._lookAtPoint needs to be initialized to correct
// point.
PerspectiveCamera {
id: scenePerspectiveCamera
z: -600
y: 600
rotation.x: 45
clipFar: 100000
clipNear: 1
}
OrthographicCamera {
id: sceneOrthoCamera
z: -600
y: 600
rotation.x: 45
clipFar: 100000
clipNear: -10000
}
}
}

View File

@@ -154,8 +154,11 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
// Explicitly set local transform of root node to target node parent's global transform
// to avoid having to reparent the selection box. This has to be done directly on render
// nodes.
QMatrix4x4 m;
if (targetRN->parent) {
targetRN->parent->calculateGlobalVariables();
QMatrix4x4 m = targetRN->parent->globalTransform;
m = targetRN->parent->globalTransform;
}
rootRN->localTransform = m;
rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty);
rootRN->calculateGlobalVariables();

View File

@@ -81,6 +81,12 @@
#include <QQmlContext>
#include <QQmlEngine>
#ifdef QUICK3D_MODULE
#include <QtQuick3D/private/qquick3dnode_p.h>
#include <QtQuick3D/private/qquick3dviewport_p.h>
#include <QtQuick3D/private/qquick3dscenerootnode_p.h>
#endif
namespace QmlDesigner {
static QVariant objectToVariant(QObject *object)
@@ -255,12 +261,12 @@ void Qt5InformationNodeInstanceServer::modifyVariantValue(
const PropertyName &propertyName,
ValuesModifiedCommand::TransactionOption option)
{
PropertyName targetPopertyName;
PropertyName targetPropertyName;
// Position is a special case, because the position can be 'position.x 'or simply 'x'.
// We prefer 'x'.
if (propertyName != "position")
targetPopertyName = propertyName;
targetPropertyName = propertyName;
auto *obj = node.value<QObject *>();
@@ -275,7 +281,7 @@ void Qt5InformationNodeInstanceServer::modifyVariantValue(
// We do have to split position into position.x, position.y, position.z
ValuesModifiedCommand command = createValuesModifiedCommand(vectorToPropertyValue(
instance,
targetPopertyName,
targetPropertyName,
obj->property(propertyName)));
command.transactionOption = option;
@@ -318,14 +324,32 @@ void Qt5InformationNodeInstanceServer::handleToolStateChanged(const QString &too
QVariant::fromValue(data)});
}
void Qt5InformationNodeInstanceServer::updateViewPortRect()
void Qt5InformationNodeInstanceServer::handleView3DSizeChange()
{
QRectF viewPortrect(0, 0, m_viewPortInstance.internalObject()->property("width").toDouble(),
m_viewPortInstance.internalObject()->property("height").toDouble());
QObject *view3D = sender();
if (view3D == m_active3DView.internalObject())
updateView3DRect(view3D);
}
void Qt5InformationNodeInstanceServer::updateView3DRect(QObject *view3D)
{
QRectF viewPortrect(0., 0., 1000., 1000.);
if (view3D) {
viewPortrect = QRectF(0., 0., view3D->property("width").toDouble(),
view3D->property("height").toDouble());
}
QQmlProperty viewPortProperty(m_editView3D, "viewPortRect", context());
viewPortProperty.write(viewPortrect);
}
void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D()
{
QQmlProperty sceneProperty(m_editView3D, "activeScene", context());
sceneProperty.write(objectToVariant(m_active3DScene));
if (m_active3DView.isValid())
updateView3DRect(m_active3DView.internalObject());
}
Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) :
Qt5NodeInstanceServer(nodeInstanceClient)
{
@@ -425,65 +449,147 @@ void Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout()
changeSelection(m_pendingSelectionChangeCommand);
}
QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport(
const QList<ServerNodeInstance> &instanceList) const
{
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DViewport")) {
QObject *rootObj = nullptr;
int viewChildCount = 0;
for (const ServerNodeInstance &child : instanceList) { /* Look for scene node */
/* The QQuick3DViewport always creates a root node.
* This root node contains the complete scene. */
if (child.isSubclassOf("QQuick3DNode") && child.parent() == instance) {
// Implicit root node is not visible in editor, so there is often another node
// added below it that serves as the actual scene root node.
// If the found root is the only node child of the view, assume that is the case.
++viewChildCount;
if (!rootObj)
rootObj = child.internalObject();
}
}
if (viewChildCount == 1)
return rootObj;
else if (rootObj)
return rootObj->property("parent").value<QObject *>();
}
}
return nullptr;
}
void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
const QList<ServerNodeInstance> &instanceList) const
{
QObjectList cameras;
QObjectList lights;
QHash<QObject *, QObjectList> cameras;
QHash<QObject *, QObjectList> lights;
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DCamera"))
cameras << instance.internalObject();
cameras[find3DSceneRoot(instance)] << instance.internalObject();
else if (instance.isSubclassOf("QQuick3DAbstractLight"))
lights << instance.internalObject();
lights[find3DSceneRoot(instance)] << instance.internalObject();
}
for (auto &obj : qAsConst(cameras)) {
auto cameraIt = cameras.constBegin();
while (cameraIt != cameras.constEnd()) {
const auto cameraObjs = cameraIt.value();
for (auto &obj : cameraObjs) {
QMetaObject::invokeMethod(m_editView3D, "addCameraGizmo",
Q_ARG(QVariant, objectToVariant(cameraIt.key())),
Q_ARG(QVariant, objectToVariant(obj)));
}
for (auto &obj : qAsConst(lights)) {
++cameraIt;
}
auto lightIt = lights.constBegin();
while (lightIt != lights.constEnd()) {
const auto lightObjs = lightIt.value();
for (auto &obj : lightObjs) {
QMetaObject::invokeMethod(m_editView3D, "addLightGizmo",
Q_ARG(QVariant, objectToVariant(lightIt.key())),
Q_ARG(QVariant, objectToVariant(obj)));
}
++lightIt;
}
}
ServerNodeInstance Qt5InformationNodeInstanceServer::findViewPort(
const QList<ServerNodeInstance> &instanceList)
void Qt5InformationNodeInstanceServer::addViewPorts(const QList<ServerNodeInstance> &instanceList)
{
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DViewport"))
return instance;
if (instance.isSubclassOf("QQuick3DViewport")) {
m_view3Ds << instance;
QObject *obj = instance.internalObject();
QObject::connect(obj, SIGNAL(widthChanged()), this, SLOT(handleView3DSizeChange()));
QObject::connect(obj, SIGNAL(heightChanged()), this, SLOT(handleView3DSizeChange()));
}
return ServerNodeInstance();
}
}
ServerNodeInstance Qt5InformationNodeInstanceServer::findView3DForInstance(const ServerNodeInstance &instance) const
{
#ifdef QUICK3D_MODULE
if (!instance.isValid())
return {};
// View3D of an instance is one of the following, in order of priority:
// - Any direct ancestor View3D of the instance
// - Any View3D that specifies the instance's scene as importScene
ServerNodeInstance checkInstance = instance;
while (checkInstance.isValid()) {
if (checkInstance.isSubclassOf("QQuick3DViewport"))
return checkInstance;
else
checkInstance = checkInstance.parent();
}
// If no ancestor View3D was found, check if the scene root is specified as importScene in
// some View3D.
QObject *sceneRoot = find3DSceneRoot(instance);
for (const auto &view3D : qAsConst(m_view3Ds)) {
auto view = qobject_cast<QQuick3DViewport *>(view3D.internalObject());
if (sceneRoot == view->importScene())
return view3D;
}
#endif
return {};
}
QObject *Qt5InformationNodeInstanceServer::find3DSceneRoot(const ServerNodeInstance &instance) const
{
#ifdef QUICK3D_MODULE
// The root of a 3D scene is any QQuick3DNode that doesn't have QQuick3DNode as parent.
// One exception is QQuick3DSceneRootNode that has only a single child QQuick3DNode (not
// a subclass of one, but exactly QQuick3DNode). In that case we consider the single child node
// to be the scene root (as QQuick3DSceneRootNode is not visible in the navigator scene graph).
if (!instance.isValid())
return nullptr;
QQuick3DNode *childNode = nullptr;
auto countChildNodes = [&childNode](QQuick3DViewport *view) -> int {
QQuick3DNode *sceneNode = view->scene();
QList<QQuick3DObject *> children = sceneNode->childItems();
int nodeCount = 0;
for (const auto &child : children) {
auto nodeChild = qobject_cast<QQuick3DNode *>(child);
if (nodeChild) {
++nodeCount;
childNode = nodeChild;
}
}
return nodeCount;
};
// In case of View3D is selected, the root scene is whatever is contained in View3D, or
// importScene, in case there is no content in View3D
QObject *obj = instance.internalObject();
auto view = qobject_cast<QQuick3DViewport *>(obj);
if (view) {
int nodeCount = countChildNodes(view);
if (nodeCount == 0)
return view->importScene();
else if (nodeCount == 1)
return childNode;
else
return view->scene();
}
ServerNodeInstance checkInstance = instance;
bool foundNode = checkInstance.isSubclassOf("QQuick3DNode");
while (checkInstance.isValid()) {
ServerNodeInstance parentInstance = checkInstance.parent();
if (parentInstance.isSubclassOf("QQuick3DViewport")) {
view = qobject_cast<QQuick3DViewport *>(parentInstance.internalObject());
int nodeCount = countChildNodes(view);
if (nodeCount == 1)
return checkInstance.internalObject();
else
return view->scene();
} else if (parentInstance.isSubclassOf("QQuick3DNode")) {
foundNode = true;
checkInstance = parentInstance;
} else {
if (!foundNode) {
// We haven't found any node yet, continue the search
checkInstance = parentInstance;
} else {
return checkInstance.internalObject();
}
}
}
#endif
return nullptr;
}
void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeInstance> &instanceList,
@@ -491,38 +597,28 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
{
ServerNodeInstance root = rootNodeInstance();
bool showCustomLight = false;
addViewPorts(instanceList);
if (root.isSubclassOf("QQuick3DNode")) {
m_rootNode = root.internalObject();
showCustomLight = true; // Pure node scene we should add a custom light
} else {
m_rootNode = findRootNodeOf3DViewport(instanceList);
// Find any scene to show
for (const auto &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DNode")) {
m_active3DScene = find3DSceneRoot(instance);
m_active3DView = findView3DForInstance(instance);
break;
}
}
if (m_rootNode) { // If we found a scene we create the edit view
if (m_active3DScene) {
m_editView3D = createEditView3D(engine());
if (!m_editView3D)
if (!m_editView3D) {
m_active3DScene = nullptr;
m_active3DView = {};
return;
QQmlProperty sceneProperty(m_editView3D, "scene", context());
m_rootNode->setParent(m_editView3D);
sceneProperty.write(objectToVariant(m_rootNode));
QQmlProperty parentProperty(m_rootNode, "parent", context());
parentProperty.write(objectToVariant(m_editView3D));
QQmlProperty showLightProperty(m_editView3D, "showLight", context());
showLightProperty.write(showCustomLight);
m_viewPortInstance = findViewPort(instanceList);
if (m_viewPortInstance.internalObject()) {
QObject::connect(m_viewPortInstance.internalObject(), SIGNAL(widthChanged()),
this, SLOT(updateViewPortRect()));
QObject::connect(m_viewPortInstance.internalObject(), SIGNAL(heightChanged()),
this, SLOT(updateViewPortRect()));
updateViewPortRect();
}
updateActiveSceneToEditView3D();
createCameraAndLightGizmos(instanceList);
QMetaObject::invokeMethod(m_editView3D, "updateToolStates", Q_ARG(QVariant, toolStates));
@@ -711,19 +807,33 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
return;
}
// Find a scene root of the selection to update active scene shown
const QVector<qint32> instanceIds = command.instanceIds();
QVariantList selectedObjs;
QObject *firstSceneRoot = nullptr;
ServerNodeInstance firstInstance;
for (qint32 id : instanceIds) {
if (hasInstanceForId(id)) {
ServerNodeInstance instance = instanceForId(id);
QObject *sceneRoot = find3DSceneRoot(instance);
if (!firstSceneRoot && sceneRoot) {
firstSceneRoot = sceneRoot;
firstInstance = instance;
}
QObject *object = nullptr;
if (instance.isSubclassOf("QQuick3DNode"))
if (firstSceneRoot && sceneRoot == firstSceneRoot && instance.isSubclassOf("QQuick3DNode"))
object = instance.internalObject();
if (object && object != m_rootNode)
if (object && (firstSceneRoot != object || instance.isSubclassOf("QQuick3DModel")))
selectedObjs << objectToVariant(object);
}
}
if (firstSceneRoot && m_active3DScene != firstSceneRoot) {
m_active3DScene = firstSceneRoot;
m_active3DView = findView3DForInstance(firstInstance);
updateActiveSceneToEditView3D();
}
// Ensure the UI has enough selection box items. If it doesn't yet have them, which can be the
// case when the first selection processed is a multiselection, we wait a bit as
// using the new boxes immediately leads to visual glitches.

View File

@@ -62,7 +62,7 @@ private slots:
void handleObjectPropertyCommit(const QVariant &object, const QVariant &propName);
void handleObjectPropertyChange(const QVariant &object, const QVariant &propName);
void handleToolStateChanged(const QString &tool, const QVariant &toolState);
void updateViewPortRect();
void handleView3DSizeChange();
protected:
void collectItemChangesAndSendChangeCommands() override;
@@ -81,9 +81,10 @@ private:
QObject *createEditView3D(QQmlEngine *engine);
void setup3DEditView(const QList<ServerNodeInstance> &instanceList,
const QVariantMap &toolStates);
QObject *findRootNodeOf3DViewport(const QList<ServerNodeInstance> &instanceList) const;
void createCameraAndLightGizmos(const QList<ServerNodeInstance> &instanceList) const;
ServerNodeInstance findViewPort(const QList<ServerNodeInstance> &instanceList);
void addViewPorts(const QList<ServerNodeInstance> &instanceList);
ServerNodeInstance findView3DForInstance(const ServerNodeInstance &instance) const;
QObject *find3DSceneRoot(const ServerNodeInstance &instance) const;
QVector<InstancePropertyValueTriple> vectorToPropertyValue(const ServerNodeInstance &instance,
const PropertyName &propertyName,
const QVariant &variant);
@@ -91,8 +92,13 @@ private:
const PropertyName &propertyName,
ValuesModifiedCommand::TransactionOption option);
bool dropAcceptable(QDragMoveEvent *event) const;
void updateView3DRect(QObject *view3D);
void updateActiveSceneToEditView3D();
QObject *m_editView3D = nullptr;
QVector<ServerNodeInstance> m_view3Ds;
ServerNodeInstance m_active3DView;
QObject *m_active3DScene = nullptr;
QSet<ServerNodeInstance> m_parentChangedSet;
QList<ServerNodeInstance> m_completedComponentList;
QList<TokenCommand> m_tokenList;
@@ -100,8 +106,6 @@ private:
QTimer m_selectionChangeTimer;
QVariant m_changedNode;
PropertyName m_changedProperty;
ServerNodeInstance m_viewPortInstance;
QObject *m_rootNode = nullptr;
ChangeSelectionCommand m_pendingSelectionChangeCommand;
};

View File

@@ -27,6 +27,7 @@
<file>mockfiles/ToggleButton.qml</file>
<file>mockfiles/RotateGizmo.qml</file>
<file>mockfiles/RotateRing.qml</file>
<file>mockfiles/SceneView3D.qml</file>
<file>mockfiles/SelectionBox.qml</file>
<file>mockfiles/AxisHelper.qml</file>
<file>mockfiles/AxisHelperArm.qml</file>