QmlDesigner: Implement group selection boxes in 3D edit view

Object's selection box now includes the bounds of all of its
descendants. Selection boxes of immediate children of a selected
object are also drawn.
Individual/group selection buttons also now work as expected.

Change-Id: Ice7ef9a536e32c6bb6da70fe23bf0a38e72c14f8
Fixes: QDS-1210
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2019-11-21 17:37:09 +02:00
parent 9dac42f153
commit 389f96b0ee
7 changed files with 212 additions and 67 deletions

View File

@@ -55,9 +55,14 @@ Window {
selectedNode = object; selectedNode = object;
} }
function emitObjectClicked(object) { function handleObjectClicked(object) {
selectObject(object); var theObject = object;
objectClicked(object); if (btnSelectGroup.selected) {
while (theObject && theObject.parent !== scene)
theObject = theObject.parent;
}
selectObject(theObject);
objectClicked(theObject);
} }
function addLightGizmo(obj) function addLightGizmo(obj)
@@ -68,7 +73,7 @@ Window {
{"view3D": overlayView, "targetNode": obj, {"view3D": overlayView, "targetNode": obj,
"selectedNode": selectedNode}); "selectedNode": selectedNode});
lightGizmos[lightGizmos.length] = gizmo; lightGizmos[lightGizmos.length] = gizmo;
gizmo.clicked.connect(emitObjectClicked); gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNode = Qt.binding(function() {return selectedNode;}); gizmo.selectedNode = Qt.binding(function() {return selectedNode;});
} }
} }
@@ -83,7 +88,7 @@ Window {
{"view3D": overlayView, "targetNode": obj, "geometryName": geometryName, {"view3D": overlayView, "targetNode": obj, "geometryName": geometryName,
"viewPortRect": viewPortRect, "selectedNode": selectedNode}); "viewPortRect": viewPortRect, "selectedNode": selectedNode});
cameraGizmos[cameraGizmos.length] = gizmo; cameraGizmos[cameraGizmos.length] = gizmo;
gizmo.clicked.connect(emitObjectClicked); gizmo.clicked.connect(handleObjectClicked);
gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;}); gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;});
gizmo.selectedNode = Qt.binding(function() {return selectedNode;}); gizmo.selectedNode = Qt.binding(function() {return selectedNode;});
} }
@@ -178,7 +183,7 @@ Window {
onTapped: { onTapped: {
var pickResult = editView.pick(eventPoint.scenePosition.x, var pickResult = editView.pick(eventPoint.scenePosition.x,
eventPoint.scenePosition.y); eventPoint.scenePosition.y);
emitObjectClicked(pickResult.objectHit); handleObjectClicked(pickResult.objectHit);
} }
} }

View File

@@ -52,7 +52,7 @@ Node {
orientation: selectionBox.targetNode ? selectionBox.targetNode.orientation : Node.LeftHanded orientation: selectionBox.targetNode ? selectionBox.targetNode.orientation : Node.LeftHanded
rotationOrder: selectionBox.targetNode ? selectionBox.targetNode.rotationOrder : Node.YXZ rotationOrder: selectionBox.targetNode ? selectionBox.targetNode.rotationOrder : Node.YXZ
visible: selectionBox.targetNode && selectionBox.targetNode instanceof Model visible: selectionBox.targetNode && !selectionBoxGeometry.isEmpty
materials: [ materials: [
DefaultMaterial { DefaultMaterial {

View File

@@ -29,15 +29,17 @@
#include <QtQuick3D/private/qquick3dorthographiccamera_p.h> #include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
#include <QtQuick3D/private/qquick3dperspectivecamera_p.h> #include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
#include <QtQuick3D/private/qquick3dobject_p_p.h> #include <QtQuick3D/private/qquick3dobject_p_p.h>
#include <QtQuick3D/private/qquick3dcamera_p.h>
#include <QtQuick3D/private/qquick3dnode_p.h>
#include <QtQuick3D/private/qquick3dmodel_p.h> #include <QtQuick3D/private/qquick3dmodel_p.h>
#include <QtQuick3D/private/qquick3dviewport_p.h>
#include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h> #include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h> #include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h> #include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
#include <QtQuick3DUtils/private/qssgbounds3_p.h> #include <QtQuick3DUtils/private/qssgbounds3_p.h>
#include <QtQuick/qquickwindow.h> #include <QtQuick/qquickwindow.h>
#include <QtCore/qhash.h>
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
#include <QtGui/qmatrix4x4.h>
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {

View File

@@ -27,14 +27,21 @@
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
#include <QtQuick3D/private/qquick3dcamera_p.h>
#include <QtQuick3D/private/qquick3dnode_p.h>
#include <QtQuick3D/private/qquick3dviewport_p.h>
#include <QtCore/qobject.h> #include <QtCore/qobject.h>
#include <QtCore/qtimer.h> #include <QtCore/qtimer.h>
#include <QtCore/qhash.h>
#include <QtGui/qvector3d.h>
#include <QtGui/qmatrix4x4.h>
QT_BEGIN_NAMESPACE
class QQuick3DCamera;
class QQuick3DNode;
class QQuick3DViewport;
QT_END_NAMESPACE
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {
class GeneralHelper : public QObject class GeneralHelper : public QObject
{ {
Q_OBJECT Q_OBJECT

View File

@@ -35,6 +35,9 @@
#include <QtQuick3D/private/qquick3dmodel_p.h> #include <QtQuick3D/private/qquick3dmodel_p.h>
#include <QtQuick3D/private/qquick3dobject_p_p.h> #include <QtQuick3D/private/qquick3dobject_p_p.h>
#include <QtQuick/qquickwindow.h> #include <QtQuick/qquickwindow.h>
#include <QtCore/qvector.h>
#include <limits>
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {
@@ -46,6 +49,9 @@ SelectionBoxGeometry::SelectionBoxGeometry()
SelectionBoxGeometry::~SelectionBoxGeometry() SelectionBoxGeometry::~SelectionBoxGeometry()
{ {
for (auto &connection : qAsConst(m_connections))
QObject::disconnect(connection);
m_connections.clear();
} }
QQuick3DNode *SelectionBoxGeometry::targetNode() const QQuick3DNode *SelectionBoxGeometry::targetNode() const
@@ -63,6 +69,11 @@ QQuick3DViewport *SelectionBoxGeometry::view3D() const
return m_view3D; return m_view3D;
} }
bool QmlDesigner::Internal::SelectionBoxGeometry::isEmpty() const
{
return m_isEmpty;
}
void SelectionBoxGeometry::setTargetNode(QQuick3DNode *targetNode) void SelectionBoxGeometry::setTargetNode(QQuick3DNode *targetNode)
{ {
if (m_targetNode == targetNode) if (m_targetNode == targetNode)
@@ -111,13 +122,18 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
QSSGRenderGeometry *geometry = static_cast<QSSGRenderGeometry *>(node); QSSGRenderGeometry *geometry = static_cast<QSSGRenderGeometry *>(node);
geometry->clear(); geometry->clear();
for (auto &connection : qAsConst(m_connections))
QObject::disconnect(connection);
m_connections.clear();
QByteArray vertexData; QByteArray vertexData;
QByteArray indexData; QByteArray indexData;
QVector3D minBounds(-100.f, -100.f, -100.f); static const float floatMin = std::numeric_limits<float>::lowest();
QVector3D maxBounds(100.f, 100.f, 100.f); static const float floatMax = std::numeric_limits<float>::max();
QVector3D extents;
QVector3D minBounds = QVector3D(floatMax, floatMax, floatMax);
QVector3D maxBounds = QVector3D(floatMin, floatMin, floatMin);
if (m_targetNode) { if (m_targetNode) {
auto rootPriv = QQuick3DObjectPrivate::get(m_rootNode); auto rootPriv = QQuick3DObjectPrivate::get(m_rootNode);
@@ -134,32 +150,11 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty); rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty);
rootRN->calculateGlobalVariables(); rootRN->calculateGlobalVariables();
} }
if (auto modelNode = qobject_cast<QQuick3DModel *>(m_targetNode)) { getBounds(m_targetNode, vertexData, indexData, minBounds, maxBounds, QMatrix4x4());
auto nodePriv = QQuick3DObjectPrivate::get(m_targetNode); } else {
if (auto renderModel = static_cast<QSSGRenderModel *>(nodePriv->spatialNode)) { // Fill some dummy data so geometry won't get rejected
QWindow *window = static_cast<QWindow *>(m_view3D->window()); appendVertexData(vertexData, indexData, minBounds, maxBounds);
if (window) {
auto context = QSSGRenderContextInterface::getRenderContextInterface(
quintptr(window));
if (!context.isNull()) {
auto bufferManager = context->bufferManager();
QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager);
QVector3D center = bounds.center();
extents = bounds.extents();
minBounds = center - extents;
maxBounds = center + extents;
} }
}
}
}
}
// Adjust bounds to reduce targetNode pixels obscuring the selection box
extents /= 1000.f;
minBounds -= extents;
maxBounds += extents;
fillVertexData(vertexData, indexData, minBounds, maxBounds);
geometry->addAttribute(QSSGRenderGeometry::Attribute::PositionSemantic, 0, geometry->addAttribute(QSSGRenderGeometry::Attribute::PositionSemantic, 0,
QSSGRenderGeometry::Attribute::ComponentType::F32Type); QSSGRenderGeometry::Attribute::ComponentType::F32Type);
@@ -171,19 +166,138 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
geometry->setPrimitiveType(QSSGRenderGeometry::Lines); geometry->setPrimitiveType(QSSGRenderGeometry::Lines);
geometry->setBounds(minBounds, maxBounds); geometry->setBounds(minBounds, maxBounds);
bool empty = minBounds.isNull() && maxBounds.isNull();
if (m_isEmpty != empty) {
m_isEmpty = empty;
// Delay notification until we're done with spatial node updates
QTimer::singleShot(0, this, &SelectionBoxGeometry::isEmptyChanged);
}
return node; return node;
} }
void SelectionBoxGeometry::fillVertexData(QByteArray &vertexData, QByteArray &indexData, void SelectionBoxGeometry::getBounds(QQuick3DNode *node, QByteArray &vertexData,
QByteArray &indexData, QVector3D &minBounds,
QVector3D &maxBounds, const QMatrix4x4 &transform)
{
QMatrix4x4 fullTransform;
auto nodePriv = QQuick3DObjectPrivate::get(node);
auto renderNode = static_cast<QSSGRenderNode *>(nodePriv->spatialNode);
// All transforms are relative to targetNode transform, so its local transform is ignored
if (node != m_targetNode) {
if (renderNode) {
if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty))
renderNode->calculateLocalTransform();
fullTransform = transform * renderNode->localTransform;
}
m_connections << QObject::connect(node, &QQuick3DNode::scaleChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::rotationChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::positionChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::pivotChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::orientationChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::rotationOrderChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
}
QVector<QVector3D> minBoundsVec;
QVector<QVector3D> maxBoundsVec;
// Check for children
const auto children = node->childItems();
for (const auto child : children) {
if (auto childNode = qobject_cast<QQuick3DNode *>(child)) {
QVector3D newMinBounds = minBounds;
QVector3D newMaxBounds = maxBounds;
getBounds(childNode, vertexData, indexData, newMinBounds, newMaxBounds, fullTransform);
minBoundsVec << newMinBounds;
maxBoundsVec << newMaxBounds;
}
}
// Combine all child bounds
for (const auto &newBounds : qAsConst(minBoundsVec)) {
minBounds.setX(qMin(newBounds.x(), minBounds.x()));
minBounds.setY(qMin(newBounds.y(), minBounds.y()));
minBounds.setZ(qMin(newBounds.z(), minBounds.z()));
}
for (const auto &newBounds : qAsConst(maxBoundsVec)) {
maxBounds.setX(qMax(newBounds.x(), maxBounds.x()));
maxBounds.setY(qMax(newBounds.y(), maxBounds.y()));
maxBounds.setZ(qMax(newBounds.z(), maxBounds.z()));
}
if (auto modelNode = qobject_cast<QQuick3DModel *>(node)) {
if (auto renderModel = static_cast<QSSGRenderModel *>(renderNode)) {
QWindow *window = static_cast<QWindow *>(m_view3D->window());
if (window) {
auto context = QSSGRenderContextInterface::getRenderContextInterface(
quintptr(window));
if (!context.isNull()) {
auto bufferManager = context->bufferManager();
QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager);
QVector3D center = bounds.center();
QVector3D extents = bounds.extents();
QVector3D localMin = center - extents;
QVector3D localMax = center + extents;
// Transform all corners of the local bounding box to find final extent in
// in parent space
auto checkCorner = [&minBounds, &maxBounds, &fullTransform]
(const QVector3D &corner) {
QVector3D mappedCorner = fullTransform.map(corner);
minBounds.setX(qMin(mappedCorner.x(), minBounds.x()));
minBounds.setY(qMin(mappedCorner.y(), minBounds.y()));
minBounds.setZ(qMin(mappedCorner.z(), minBounds.z()));
maxBounds.setX(qMax(mappedCorner.x(), maxBounds.x()));
maxBounds.setY(qMax(mappedCorner.y(), maxBounds.y()));
maxBounds.setZ(qMax(mappedCorner.z(), maxBounds.z()));
};
checkCorner(localMin);
checkCorner(localMax);
checkCorner(QVector3D(localMin.x(), localMin.y(), localMax.z()));
checkCorner(QVector3D(localMin.x(), localMax.y(), localMin.z()));
checkCorner(QVector3D(localMax.x(), localMin.y(), localMin.z()));
checkCorner(QVector3D(localMin.x(), localMax.y(), localMax.z()));
checkCorner(QVector3D(localMax.x(), localMax.y(), localMin.z()));
checkCorner(QVector3D(localMax.x(), localMin.y(), localMax.z()));
}
}
}
}
// Target node and immediate children get selection boxes
if (transform.isIdentity()) {
// Adjust bounds to reduce targetNode pixels obscuring the selection box
QVector3D extents = (maxBounds - minBounds) / 1000.f;
QVector3D minAdjBounds = minBounds - extents;
QVector3D maxAdjBounds = maxBounds + extents;
appendVertexData(vertexData, indexData, minAdjBounds, maxAdjBounds);
}
}
void SelectionBoxGeometry::appendVertexData(QByteArray &vertexData, QByteArray &indexData,
const QVector3D &minBounds, const QVector3D &maxBounds) const QVector3D &minBounds, const QVector3D &maxBounds)
{ {
int initialVertexSize = vertexData.size();
int initialIndexSize = indexData.size();
const int vertexSize = int(sizeof(float)) * 8 * 3; // 8 vertices, 3 floats/vert const int vertexSize = int(sizeof(float)) * 8 * 3; // 8 vertices, 3 floats/vert
vertexData.resize(vertexSize); quint16 indexAdd = quint16(initialVertexSize / 12);
vertexData.resize(initialVertexSize + vertexSize);
const int indexSize = int(sizeof(quint16)) * 12 * 2; // 12 lines, 2 vert/line const int indexSize = int(sizeof(quint16)) * 12 * 2; // 12 lines, 2 vert/line
indexData.resize(indexSize); indexData.resize(initialIndexSize + indexSize);
auto dataPtr = reinterpret_cast<float *>(vertexData.data()); auto dataPtr = reinterpret_cast<float *>(vertexData.data() + initialVertexSize);
auto indexPtr = reinterpret_cast<quint16 *>(indexData.data()); auto indexPtr = reinterpret_cast<quint16 *>(indexData.data() + initialIndexSize);
*dataPtr++ = maxBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z(); *dataPtr++ = maxBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z(); *dataPtr++ = minBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z();
@@ -194,20 +308,20 @@ void SelectionBoxGeometry::fillVertexData(QByteArray &vertexData, QByteArray &in
*dataPtr++ = minBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z(); *dataPtr++ = minBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z();
*dataPtr++ = maxBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z(); *dataPtr++ = maxBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z();
*indexPtr++ = 0; *indexPtr++ = 1; *indexPtr++ = 0 + indexAdd; *indexPtr++ = 1 + indexAdd;
*indexPtr++ = 1; *indexPtr++ = 2; *indexPtr++ = 1 + indexAdd; *indexPtr++ = 2 + indexAdd;
*indexPtr++ = 2; *indexPtr++ = 3; *indexPtr++ = 2 + indexAdd; *indexPtr++ = 3 + indexAdd;
*indexPtr++ = 3; *indexPtr++ = 0; *indexPtr++ = 3 + indexAdd; *indexPtr++ = 0 + indexAdd;
*indexPtr++ = 0; *indexPtr++ = 4; *indexPtr++ = 0 + indexAdd; *indexPtr++ = 4 + indexAdd;
*indexPtr++ = 1; *indexPtr++ = 5; *indexPtr++ = 1 + indexAdd; *indexPtr++ = 5 + indexAdd;
*indexPtr++ = 2; *indexPtr++ = 6; *indexPtr++ = 2 + indexAdd; *indexPtr++ = 6 + indexAdd;
*indexPtr++ = 3; *indexPtr++ = 7; *indexPtr++ = 3 + indexAdd; *indexPtr++ = 7 + indexAdd;
*indexPtr++ = 4; *indexPtr++ = 5; *indexPtr++ = 4 + indexAdd; *indexPtr++ = 5 + indexAdd;
*indexPtr++ = 5; *indexPtr++ = 6; *indexPtr++ = 5 + indexAdd; *indexPtr++ = 6 + indexAdd;
*indexPtr++ = 6; *indexPtr++ = 7; *indexPtr++ = 6 + indexAdd; *indexPtr++ = 7 + indexAdd;
*indexPtr++ = 7; *indexPtr++ = 4; *indexPtr++ = 7 + indexAdd; *indexPtr++ = 4 + indexAdd;
} }
} }

View File

@@ -40,6 +40,7 @@ class SelectionBoxGeometry : public QQuick3DGeometry
Q_PROPERTY(QQuick3DNode *targetNode READ targetNode WRITE setTargetNode NOTIFY targetNodeChanged) Q_PROPERTY(QQuick3DNode *targetNode READ targetNode WRITE setTargetNode NOTIFY targetNodeChanged)
Q_PROPERTY(QQuick3DNode *rootNode READ rootNode WRITE setRootNode NOTIFY rootNodeChanged) Q_PROPERTY(QQuick3DNode *rootNode READ rootNode WRITE setRootNode NOTIFY rootNodeChanged)
Q_PROPERTY(QQuick3DViewport *view3D READ view3D WRITE setView3D NOTIFY view3DChanged) Q_PROPERTY(QQuick3DViewport *view3D READ view3D WRITE setView3D NOTIFY view3DChanged)
Q_PROPERTY(bool isEmpty READ isEmpty NOTIFY isEmptyChanged)
public: public:
SelectionBoxGeometry(); SelectionBoxGeometry();
@@ -48,6 +49,7 @@ public:
QQuick3DNode *targetNode() const; QQuick3DNode *targetNode() const;
QQuick3DNode *rootNode() const; QQuick3DNode *rootNode() const;
QQuick3DViewport *view3D() const; QQuick3DViewport *view3D() const;
bool isEmpty() const;
public Q_SLOTS: public Q_SLOTS:
void setTargetNode(QQuick3DNode *targetNode); void setTargetNode(QQuick3DNode *targetNode);
@@ -58,17 +60,22 @@ Q_SIGNALS:
void targetNodeChanged(); void targetNodeChanged();
void rootNodeChanged(); void rootNodeChanged();
void view3DChanged(); void view3DChanged();
void isEmptyChanged();
protected: protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override; QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override;
private: private:
void fillVertexData(QByteArray &vertexData, QByteArray &indexData, void getBounds(QQuick3DNode *node, QByteArray &vertexData, QByteArray &indexData,
QVector3D &minBounds, QVector3D &maxBounds, const QMatrix4x4 &transform);
void appendVertexData(QByteArray &vertexData, QByteArray &indexData,
const QVector3D &minBounds, const QVector3D &maxBounds); const QVector3D &minBounds, const QVector3D &maxBounds);
QQuick3DNode *m_targetNode = nullptr; QQuick3DNode *m_targetNode = nullptr;
QQuick3DViewport *m_view3D = nullptr; QQuick3DViewport *m_view3D = nullptr;
QQuick3DNode *m_rootNode = nullptr; QQuick3DNode *m_rootNode = nullptr;
bool m_isEmpty = true;
QVector<QMetaObject::Connection> m_connections;
}; };
} }

View File

@@ -340,13 +340,25 @@ QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport(
{ {
for (const ServerNodeInstance &instance : instanceList) { for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DViewport")) { if (instance.isSubclassOf("QQuick3DViewport")) {
QObject *rootObj = nullptr;
int viewChildCount = 0;
for (const ServerNodeInstance &child : instanceList) { /* Look for scene node */ for (const ServerNodeInstance &child : instanceList) { /* Look for scene node */
/* The QQuick3DViewport always creates a root node. /* The QQuick3DViewport always creates a root node.
* This root node contains the complete scene. */ * This root node contains the complete scene. */
if (child.isSubclassOf("QQuick3DNode") && child.parent() == instance) if (child.isSubclassOf("QQuick3DNode") && child.parent() == instance) {
return child.internalObject()->property("parent").value<QObject *>(); // 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; return nullptr;
} }
@@ -603,10 +615,8 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
if (hasInstanceForId(id)) { if (hasInstanceForId(id)) {
ServerNodeInstance instance = instanceForId(id); ServerNodeInstance instance = instanceForId(id);
QObject *object = nullptr; QObject *object = nullptr;
if (instance.isSubclassOf("QQuick3DModel") || instance.isSubclassOf("QQuick3DCamera") if (instance.isSubclassOf("QQuick3DNode"))
|| instance.isSubclassOf("QQuick3DAbstractLight")) {
object = instance.internalObject(); object = instance.internalObject();
}
QMetaObject::invokeMethod(m_editView3D, "selectObject", Q_ARG(QVariant, QMetaObject::invokeMethod(m_editView3D, "selectObject", Q_ARG(QVariant,
objectToVariant(object))); objectToVariant(object)));
return; // TODO: support multi-selection return; // TODO: support multi-selection