forked from qt-creator/qt-creator
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:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user