Merge remote-tracking branch 'origin/4.11'

Conflicts:
	src/plugins/designer/codemodelhelpers.cpp

Change-Id: I78906f2fbbfd27d254589a272ebca423b0b80699
This commit is contained in:
Eike Ziller
2019-11-28 08:24:10 +01:00
125 changed files with 2380 additions and 881 deletions

View File

@@ -1,78 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2019 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.
**
****************************************************************************/
#include "cameracontrolhelper.h"
#include <QHash>
namespace QmlDesigner {
namespace Internal {
CameraControlHelper::CameraControlHelper()
: QObject()
{
m_inputUpdateTimer.setInterval(16);
QObject::connect(&m_inputUpdateTimer, &QTimer::timeout,
this, &CameraControlHelper::handleUpdateTimer);
m_overlayUpdateTimer.setInterval(16);
m_overlayUpdateTimer.setSingleShot(true);
QObject::connect(&m_overlayUpdateTimer, &QTimer::timeout,
this, &CameraControlHelper::overlayUpdateNeeded);
}
bool CameraControlHelper::enabled()
{
return m_enabled;
}
void CameraControlHelper::handleUpdateTimer()
{
emit updateInputs();
}
void CameraControlHelper::setEnabled(bool enabled)
{
if (enabled)
m_inputUpdateTimer.start();
else
m_inputUpdateTimer.stop();
m_enabled = enabled;
}
void CameraControlHelper::requestOverlayUpdate()
{
if (!m_overlayUpdateTimer.isActive())
m_overlayUpdateTimer.start();
}
QString CameraControlHelper::generateUniqueName(const QString &nameRoot)
{
static QHash<QString, int> counters;
int count = counters[nameRoot]++;
return QStringLiteral("%1_%2").arg(nameRoot).arg(count);
}
}
}

View File

@@ -1,62 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2019 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.
**
****************************************************************************/
#pragma once
#include <QtCore/QObject>
#include <QtCore/QTimer>
namespace QmlDesigner {
namespace Internal {
class CameraControlHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
public:
CameraControlHelper();
bool enabled();
void setEnabled(bool enabled);
Q_INVOKABLE void requestOverlayUpdate();
Q_INVOKABLE QString generateUniqueName(const QString &nameRoot);
public slots:
void handleUpdateTimer();
signals:
void updateInputs();
void enabledChanged(bool enabled);
void overlayUpdateNeeded();
private:
bool m_enabled = false;
QTimer m_inputUpdateTimer;
QTimer m_overlayUpdateTimer;
};
}
}

View File

@@ -1,9 +1,11 @@
HEADERS += $$PWD/cameracontrolhelper.h \
HEADERS += $$PWD/generalhelper.h \
$$PWD/mousearea3d.h \
$$PWD/camerageometry.h \
$$PWD/gridgeometry.h
$$PWD/gridgeometry.h \
$$PWD/selectionboxgeometry.h
SOURCES += $$PWD/cameracontrolhelper.cpp \
SOURCES += $$PWD/generalhelper.cpp \
$$PWD/mousearea3d.cpp \
$$PWD/camerageometry.cpp \
$$PWD/gridgeometry.cpp
$$PWD/gridgeometry.cpp \
$$PWD/selectionboxgeometry.cpp

View File

@@ -0,0 +1,210 @@
/****************************************************************************
**
** Copyright (C) 2019 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.
**
****************************************************************************/
#include "generalhelper.h"
#ifdef QUICK3D_MODULE
#include "selectionboxgeometry.h"
#include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
#include <QtQuick3D/private/qquick3dperspectivecamera_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/qquick3dviewport_p.h>
#include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
#include <QtQuick3DUtils/private/qssgbounds3_p.h>
#include <QtQuick/qquickwindow.h>
#include <QtCore/qmath.h>
namespace QmlDesigner {
namespace Internal {
GeneralHelper::GeneralHelper()
: QObject()
{
m_overlayUpdateTimer.setInterval(16);
m_overlayUpdateTimer.setSingleShot(true);
QObject::connect(&m_overlayUpdateTimer, &QTimer::timeout,
this, &GeneralHelper::overlayUpdateNeeded);
}
void GeneralHelper::requestOverlayUpdate()
{
if (!m_overlayUpdateTimer.isActive())
m_overlayUpdateTimer.start();
}
QString GeneralHelper::generateUniqueName(const QString &nameRoot)
{
static QHash<QString, int> counters;
int count = counters[nameRoot]++;
return QStringLiteral("%1_%2").arg(nameRoot).arg(count);
}
void GeneralHelper::orbitCamera(QQuick3DCamera *camera, const QVector3D &startRotation,
const QVector3D &lookAtPoint, const QVector3D &pressPos,
const QVector3D &currentPos)
{
QVector3D dragVector = currentPos - pressPos;
if (dragVector.length() < 0.001f)
return;
camera->setRotation(startRotation);
QVector3D newRotation(dragVector.y(), dragVector.x(), 0.f);
newRotation *= 0.5f; // Emprically determined multiplier for nice drag
newRotation += startRotation;
camera->setRotation(newRotation);
const QVector3D oldLookVector = camera->position() - lookAtPoint;
QMatrix4x4 m = camera->sceneTransform();
const float *dataPtr(m.data());
QVector3D newLookVector(-dataPtr[8], -dataPtr[9], -dataPtr[10]);
newLookVector.normalize();
newLookVector *= oldLookVector.length();
camera->setPosition(lookAtPoint + newLookVector);
}
// Pans camera and returns the new look-at point
QVector3D GeneralHelper::panCamera(QQuick3DCamera *camera, const QMatrix4x4 startTransform,
const QVector3D &startPosition, const QVector3D &startLookAt,
const QVector3D &pressPos, const QVector3D &currentPos,
float zoomFactor)
{
QVector3D dragVector = currentPos - pressPos;
if (dragVector.length() < 0.001f)
return startLookAt;
const float *dataPtr(startTransform.data());
const QVector3D xAxis = QVector3D(dataPtr[0], dataPtr[1], dataPtr[2]).normalized();
const QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized();
const QVector3D xDelta = -1.f * xAxis * dragVector.x();
const QVector3D yDelta = yAxis * dragVector.y();
const QVector3D delta = (xDelta + yDelta) * zoomFactor;
camera->setPosition(startPosition + delta);
return startLookAt + delta;
}
float GeneralHelper::zoomCamera(QQuick3DCamera *camera, float distance, float defaultLookAtDistance,
const QVector3D &lookAt, float zoomFactor, bool relative)
{
// Emprically determined divisor for nice zoom
float multiplier = 1.f + (distance / 40.f);
float newZoomFactor = relative ? qBound(.0001f, zoomFactor * multiplier, 10000.f)
: zoomFactor;
if (qobject_cast<QQuick3DOrthographicCamera *>(camera)) {
// Ortho camera we can simply scale
camera->setScale(QVector3D(newZoomFactor, newZoomFactor, newZoomFactor));
} else if (qobject_cast<QQuick3DPerspectiveCamera *>(camera)) {
// Perspective camera is zoomed by moving camera forward or backward while keeping the
// look-at point the same
const QVector3D lookAtVec = (camera->position() - lookAt).normalized();
const float newDistance = defaultLookAtDistance * newZoomFactor;
camera->setPosition(lookAt + (lookAtVec * newDistance));
}
return newZoomFactor;
}
// Return value contains new lookAt point (xyz) and zoom factor (w)
QVector4D GeneralHelper::fitObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort)
{
if (!camera)
return QVector4D(0.f, 0.f, 0.f, 1.f);
QVector3D lookAt = targetObject ? targetObject->scenePosition() : QVector3D();
// Get object bounds
qreal maxExtent = 200.;
if (auto modelNode = qobject_cast<QQuick3DModel *>(targetObject)) {
auto targetPriv = QQuick3DObjectPrivate::get(targetObject);
if (auto renderModel = static_cast<QSSGRenderModel *>(targetPriv->spatialNode)) {
QWindow *window = static_cast<QWindow *>(viewPort->window());
if (window) {
auto context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window));
if (!context.isNull()) {
QSSGBounds3 bounds;
auto geometry = qobject_cast<SelectionBoxGeometry *>(modelNode->geometry());
if (geometry) {
bounds = geometry->bounds();
} else {
auto bufferManager = context->bufferManager();
bounds = renderModel->getModelBounds(bufferManager);
}
QVector3D center = bounds.center();
const QVector3D e = bounds.extents();
const QVector3D s = targetObject->sceneScale();
qreal maxScale = qSqrt(qreal(s.x() * s.x() + s.y() * s.y() + s.z() * s.z()));
maxExtent = qSqrt(qreal(e.x() * e.x() + e.y() * e.y() + e.z() * e.z()));
maxExtent *= maxScale;
// Adjust lookAt to look directly at the center of the object bounds
lookAt = renderModel->globalTransform.map(center);
lookAt.setZ(-lookAt.z()); // Render node transforms have inverted z
}
}
}
}
// Reset camera position to default zoom
QMatrix4x4 m = camera->sceneTransform();
const float *dataPtr(m.data());
QVector3D newLookVector(-dataPtr[8], -dataPtr[9], -dataPtr[10]);
newLookVector.normalize();
newLookVector *= defaultLookAtDistance;
camera->setPosition(lookAt + newLookVector);
// Emprically determined algorithm for nice zoom
float newZoomFactor = qBound(.0001f, float(maxExtent / 700.), 10000.f);
return QVector4D(lookAt,
zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false));
}
void GeneralHelper::delayedPropertySet(QObject *obj, int delay, const QString &property,
const QVariant &value)
{
QTimer::singleShot(delay, [obj, property, value]() {
obj->setProperty(property.toLatin1().constData(), value);
});
}
}
}
#endif // QUICK3D_MODULE

View File

@@ -0,0 +1,80 @@
/****************************************************************************
**
** Copyright (C) 2019 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.
**
****************************************************************************/
#pragma once
#ifdef QUICK3D_MODULE
#include <QtCore/qobject.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 Internal {
class GeneralHelper : public QObject
{
Q_OBJECT
public:
GeneralHelper();
Q_INVOKABLE void requestOverlayUpdate();
Q_INVOKABLE QString generateUniqueName(const QString &nameRoot);
Q_INVOKABLE void orbitCamera(QQuick3DCamera *camera, const QVector3D &startRotation,
const QVector3D &lookAtPoint, const QVector3D &pressPos,
const QVector3D &currentPos);
Q_INVOKABLE QVector3D panCamera(QQuick3DCamera *camera, const QMatrix4x4 startTransform,
const QVector3D &startPosition, const QVector3D &startLookAt,
const QVector3D &pressPos, const QVector3D &currentPos,
float zoomFactor);
Q_INVOKABLE float zoomCamera(QQuick3DCamera *camera, float distance,
float defaultLookAtDistance, const QVector3D &lookAt,
float zoomFactor, bool relative);
Q_INVOKABLE QVector4D fitObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort);
Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property,
const QVariant& value);
signals:
void overlayUpdateNeeded();
private:
QTimer m_overlayUpdateTimer;
};
}
}
#endif // QUICK3D_MODULE

View File

@@ -37,7 +37,6 @@ namespace QmlDesigner {
namespace Internal {
MouseArea3D *MouseArea3D::s_mouseGrab = nullptr;
static const qreal s_mouseDragMultiplier = .02;
MouseArea3D::MouseArea3D(QQuick3DNode *parent)
: QQuick3DNode(parent)
@@ -124,6 +123,13 @@ void MouseArea3D::setGrabsMouse(bool grabsMouse)
return;
m_grabsMouse = grabsMouse;
if (!m_grabsMouse && s_mouseGrab == this) {
setDragging(false);
setHovering(false);
s_mouseGrab = nullptr;
}
emit grabsMouseChanged();
}
@@ -133,6 +139,13 @@ void MouseArea3D::setActive(bool active)
return;
m_active = active;
if (!m_active && s_mouseGrab == this) {
setDragging(false);
setHovering(false);
s_mouseGrab = nullptr;
}
emit activeChanged();
}
@@ -340,7 +353,7 @@ qreal QmlDesigner::Internal::MouseArea3D::getNewRotationAngle(
dragDir = (screenDragDir - nodePos).normalized();
const QVector3D pressToCurrent = (currentPos - pressPos);
float magnitude = QVector3D::dotProduct(pressToCurrent, dragDir);
qreal angle = -s_mouseDragMultiplier * qreal(magnitude);
qreal angle = -mouseDragMultiplier() * qreal(magnitude);
return angle;
} else {
const QVector3D nodeToPress = (pressPos - nodePos).normalized();
@@ -397,7 +410,7 @@ void MouseArea3D::applyFreeRotation(QQuick3DNode *node, const QVector3D &startRo
QVector3D finalAxis = (dragVector.x() * yAxis + dragVector.y() * xAxis);
qreal degrees = qRadiansToDegrees(qreal(finalAxis.length()) * s_mouseDragMultiplier);
qreal degrees = qRadiansToDegrees(qreal(finalAxis.length()) * mouseDragMultiplier());
finalAxis.normalize();

View File

@@ -51,7 +51,7 @@ class MouseArea3D : public QQuick3DNode
Q_PROPERTY(bool hovering READ hovering NOTIFY hoveringChanged)
Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
Q_PROPERTY(int priority READ priority WRITE setPriority NOTIFY priorityChanged)
Q_PROPERTY(int active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(QPointF circlePickArea READ circlePickArea WRITE setCirclePickArea NOTIFY circlePickAreaChanged)
Q_PROPERTY(qreal minAngle READ minAngle WRITE setMinAngle NOTIFY minAngleChanged)
Q_PROPERTY(QQuick3DNode *pickNode READ pickNode WRITE setPickNode NOTIFY pickNodeChanged)
@@ -77,6 +77,8 @@ public:
qreal minAngle() const;
QQuick3DNode *pickNode() const;
static qreal mouseDragMultiplier() { return .02; }
public slots:
void setView3D(QQuick3DViewport *view3D);
void setGrabsMouse(bool grabsMouse);
@@ -157,7 +159,7 @@ private:
QVector3D getMousePosInPlane(const QPointF &mousePosInView) const;
static MouseArea3D *s_mouseGrab;
bool m_grabsMouse;
bool m_grabsMouse = false;
QVector3D m_mousePosInPlane;
QPointF m_circlePickArea;
qreal m_minAngle = 0.;

View File

@@ -0,0 +1,336 @@
/****************************************************************************
**
** Copyright (C) 2019 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.
**
****************************************************************************/
#ifdef QUICK3D_MODULE
#include "selectionboxgeometry.h"
#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
#include <QtQuick3D/private/qquick3dmodel_p.h>
#include <QtQuick3D/private/qquick3dobject_p_p.h>
#include <QtQuick/qquickwindow.h>
#include <QtCore/qvector.h>
#include <limits>
namespace QmlDesigner {
namespace Internal {
SelectionBoxGeometry::SelectionBoxGeometry()
: QQuick3DGeometry()
{
}
SelectionBoxGeometry::~SelectionBoxGeometry()
{
for (auto &connection : qAsConst(m_connections))
QObject::disconnect(connection);
m_connections.clear();
}
QQuick3DNode *SelectionBoxGeometry::targetNode() const
{
return m_targetNode;
}
QQuick3DNode *SelectionBoxGeometry::rootNode() const
{
return m_rootNode;
}
QQuick3DViewport *SelectionBoxGeometry::view3D() const
{
return m_view3D;
}
bool QmlDesigner::Internal::SelectionBoxGeometry::isEmpty() const
{
return m_isEmpty;
}
QSSGBounds3 SelectionBoxGeometry::bounds() const
{
return m_bounds;
}
void SelectionBoxGeometry::setTargetNode(QQuick3DNode *targetNode)
{
if (m_targetNode == targetNode)
return;
if (m_targetNode)
m_targetNode->disconnect(this);
m_targetNode = targetNode;
if (auto model = qobject_cast<QQuick3DModel *>(m_targetNode)) {
QObject::connect(model, &QQuick3DModel::sourceChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
QObject::connect(model, &QQuick3DModel::geometryChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
}
emit targetNodeChanged();
update();
}
void SelectionBoxGeometry::setRootNode(QQuick3DNode *rootNode)
{
if (m_rootNode == rootNode)
return;
m_rootNode = rootNode;
emit rootNodeChanged();
update();
}
void SelectionBoxGeometry::setView3D(QQuick3DViewport *view)
{
if (m_view3D == view)
return;
m_view3D = view;
emit view3DChanged();
update();
}
QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphObject *node)
{
node = QQuick3DGeometry::updateSpatialNode(node);
QSSGRenderGeometry *geometry = static_cast<QSSGRenderGeometry *>(node);
geometry->clear();
for (auto &connection : qAsConst(m_connections))
QObject::disconnect(connection);
m_connections.clear();
QByteArray vertexData;
QByteArray indexData;
static const float floatMin = std::numeric_limits<float>::lowest();
static const float floatMax = std::numeric_limits<float>::max();
QVector3D minBounds = QVector3D(floatMax, floatMax, floatMax);
QVector3D maxBounds = QVector3D(floatMin, floatMin, floatMin);
if (m_targetNode) {
auto rootPriv = QQuick3DObjectPrivate::get(m_rootNode);
auto targetPriv = QQuick3DObjectPrivate::get(m_targetNode);
auto rootRN = static_cast<QSSGRenderNode *>(rootPriv->spatialNode);
auto targetRN = static_cast<QSSGRenderNode *>(targetPriv->spatialNode);
if (rootRN && targetRN) {
// 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.
targetRN->parent->calculateGlobalVariables();
QMatrix4x4 m = targetRN->parent->globalTransform;
rootRN->localTransform = m;
rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty);
rootRN->calculateGlobalVariables();
}
getBounds(m_targetNode, vertexData, indexData, minBounds, maxBounds, QMatrix4x4());
} else {
// Fill some dummy data so geometry won't get rejected
appendVertexData(vertexData, indexData, minBounds, maxBounds);
}
geometry->addAttribute(QSSGRenderGeometry::Attribute::PositionSemantic, 0,
QSSGRenderGeometry::Attribute::ComponentType::F32Type);
geometry->addAttribute(QSSGRenderGeometry::Attribute::IndexSemantic, 0,
QSSGRenderGeometry::Attribute::ComponentType::U16Type);
geometry->setStride(12);
geometry->setVertexData(vertexData);
geometry->setIndexData(indexData);
geometry->setPrimitiveType(QSSGRenderGeometry::Lines);
geometry->setBounds(minBounds, maxBounds);
m_bounds = QSSGBounds3(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;
}
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)
{
int initialVertexSize = vertexData.size();
int initialIndexSize = indexData.size();
const int vertexSize = int(sizeof(float)) * 8 * 3; // 8 vertices, 3 floats/vert
quint16 indexAdd = quint16(initialVertexSize / 12);
vertexData.resize(initialVertexSize + vertexSize);
const int indexSize = int(sizeof(quint16)) * 12 * 2; // 12 lines, 2 vert/line
indexData.resize(initialIndexSize + indexSize);
auto dataPtr = reinterpret_cast<float *>(vertexData.data() + initialVertexSize);
auto indexPtr = reinterpret_cast<quint16 *>(indexData.data() + initialIndexSize);
*dataPtr++ = maxBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = maxBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = maxBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = minBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = minBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z();
*dataPtr++ = maxBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z();
*indexPtr++ = 0 + indexAdd; *indexPtr++ = 1 + indexAdd;
*indexPtr++ = 1 + indexAdd; *indexPtr++ = 2 + indexAdd;
*indexPtr++ = 2 + indexAdd; *indexPtr++ = 3 + indexAdd;
*indexPtr++ = 3 + indexAdd; *indexPtr++ = 0 + indexAdd;
*indexPtr++ = 0 + indexAdd; *indexPtr++ = 4 + indexAdd;
*indexPtr++ = 1 + indexAdd; *indexPtr++ = 5 + indexAdd;
*indexPtr++ = 2 + indexAdd; *indexPtr++ = 6 + indexAdd;
*indexPtr++ = 3 + indexAdd; *indexPtr++ = 7 + indexAdd;
*indexPtr++ = 4 + indexAdd; *indexPtr++ = 5 + indexAdd;
*indexPtr++ = 5 + indexAdd; *indexPtr++ = 6 + indexAdd;
*indexPtr++ = 6 + indexAdd; *indexPtr++ = 7 + indexAdd;
*indexPtr++ = 7 + indexAdd; *indexPtr++ = 4 + indexAdd;
}
}
}
#endif // QUICK3D_MODULE

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** Copyright (C) 2019 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.
**
****************************************************************************/
#pragma once
#ifdef QUICK3D_MODULE
#include <QtQuick3D/private/qquick3dnode_p.h>
#include <QtQuick3D/private/qquick3dgeometry_p.h>
#include <QtQuick3D/private/qquick3dviewport_p.h>
#include <QtQuick3DUtils/private/qssgbounds3_p.h>
namespace QmlDesigner {
namespace Internal {
class SelectionBoxGeometry : public QQuick3DGeometry
{
Q_OBJECT
Q_PROPERTY(QQuick3DNode *targetNode READ targetNode WRITE setTargetNode NOTIFY targetNodeChanged)
Q_PROPERTY(QQuick3DNode *rootNode READ rootNode WRITE setRootNode NOTIFY rootNodeChanged)
Q_PROPERTY(QQuick3DViewport *view3D READ view3D WRITE setView3D NOTIFY view3DChanged)
Q_PROPERTY(bool isEmpty READ isEmpty NOTIFY isEmptyChanged)
public:
SelectionBoxGeometry();
~SelectionBoxGeometry() override;
QQuick3DNode *targetNode() const;
QQuick3DNode *rootNode() const;
QQuick3DViewport *view3D() const;
bool isEmpty() const;
QSSGBounds3 bounds() const;
public Q_SLOTS:
void setTargetNode(QQuick3DNode *targetNode);
void setRootNode(QQuick3DNode *rootNode);
void setView3D(QQuick3DViewport *view);
Q_SIGNALS:
void targetNodeChanged();
void rootNodeChanged();
void view3DChanged();
void isEmptyChanged();
protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override;
private:
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);
QQuick3DNode *m_targetNode = nullptr;
QQuick3DViewport *m_view3D = nullptr;
QQuick3DNode *m_rootNode = nullptr;
bool m_isEmpty = true;
QVector<QMetaObject::Connection> m_connections;
QSSGBounds3 m_bounds;
};
}
}
QML_DECLARE_TYPE(QmlDesigner::Internal::SelectionBoxGeometry)
#endif // QUICK3D_MODULE

View File

@@ -62,10 +62,11 @@
#include <drop3dlibraryitemcommand.h>
#include "dummycontextobject.h"
#include "../editor3d/cameracontrolhelper.h"
#include "../editor3d/generalhelper.h"
#include "../editor3d/mousearea3d.h"
#include "../editor3d/camerageometry.h"
#include "../editor3d/gridgeometry.h"
#include "../editor3d/selectionboxgeometry.h"
#include <designersupportdelegate.h>
@@ -104,13 +105,13 @@ bool Qt5InformationNodeInstanceServer::eventFilter(QObject *, QEvent *event)
QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine)
{
auto helper = new QmlDesigner::Internal::CameraControlHelper();
engine->rootContext()->setContextProperty("designStudioNativeCameraControlHelper", helper);
#ifdef QUICK3D_MODULE
auto helper = new QmlDesigner::Internal::GeneralHelper();
engine->rootContext()->setContextProperty("_generalHelper", helper);
qmlRegisterType<QmlDesigner::Internal::MouseArea3D>("MouseArea3D", 1, 0, "MouseArea3D");
qmlRegisterType<QmlDesigner::Internal::CameraGeometry>("CameraGeometry", 1, 0, "CameraGeometry");
qmlRegisterType<QmlDesigner::Internal::GridGeometry>("GridGeometry", 1, 0, "GridGeometry");
qmlRegisterType<QmlDesigner::Internal::SelectionBoxGeometry>("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry");
#endif
QQmlComponent component(engine, QUrl("qrc:/qtquickplugin/mockfiles/EditView3D.qml"));
@@ -136,7 +137,10 @@ QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine)
surfaceFormat.setVersion(4, 1);
surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
window->setFormat(surfaceFormat);
#ifdef QUICK3D_MODULE
helper->setParent(window);
#endif
return window;
}
@@ -336,12 +340,24 @@ QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport(
{
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)
return child.internalObject()->property("parent").value<QObject *>();
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;
@@ -599,10 +615,8 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
if (hasInstanceForId(id)) {
ServerNodeInstance instance = instanceForId(id);
QObject *object = nullptr;
if (instance.isSubclassOf("QQuick3DModel") || instance.isSubclassOf("QQuick3DCamera")
|| instance.isSubclassOf("QQuick3DAbstractLight")) {
if (instance.isSubclassOf("QQuick3DNode"))
object = instance.internalObject();
}
QMetaObject::invokeMethod(m_editView3D, "selectObject", Q_ARG(QVariant,
objectToVariant(object)));
return; // TODO: support multi-selection

View File

@@ -56,17 +56,7 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo
InstanceContainer::NodeFlags flags)
{
ObjectNodeInstance::initialize(objectNodeInstance, flags);
#ifdef QUICK3D_MODULE
if (quick3DNode()) {
QQuick3DObject::Type nodeType = quick3DNode()->type();
if (nodeType == QQuick3DObject::Camera || nodeType == QQuick3DObject::Light
|| nodeType == QQuick3DObject::Model || nodeType == QQuick3DObject::Image
|| nodeType == QQuick3DObject::Text) {
setPropertyVariant("pickable", true); // allow 3D objects to receive mouse clicks
}
}
#endif
setPickable(true, true, false);
}
Qt5NodeInstanceServer *Quick3DNodeInstance::qt5NodeInstanceServer() const
@@ -83,6 +73,48 @@ QQuick3DNode *Quick3DNodeInstance::quick3DNode() const
#endif
}
void Quick3DNodeInstance::setPickable(bool enable, bool checkParent, bool applyToChildren)
{
#ifdef QUICK3D_MODULE
auto node = quick3DNode();
if (node) {
QQuick3DObject::Type nodeType = node->type();
bool parentHidden = false;
if (checkParent) {
// First check if any parent node is already hidden. Never set pickable on that case.
auto parentNode = node->parentNode();
while (parentNode && !parentHidden) {
parentHidden = QQuick3DNodePrivate::get(parentNode)->m_isHiddenInEditor;
parentNode = parentNode->parentNode();
}
}
if (!parentHidden) {
if (applyToChildren) {
auto getQuick3DInstance = [this](QQuick3DObject *obj) -> Quick3DNodeInstance * {
if (nodeInstanceServer()->hasInstanceForObject(obj)) {
ServerNodeInstance instance = nodeInstanceServer()->instanceForObject(obj);
if (instance.isValid() && qobject_cast<QQuick3DNode *>(instance.internalObject()))
return static_cast<Quick3DNodeInstance *>(instance.internalInstance().data());
}
return nullptr;
};
const auto childItems = node->childItems();
for (auto childItem : childItems) {
if (auto quick3dInstance = getQuick3DInstance(childItem)) {
// Don't override explicit block in children
if (!QQuick3DNodePrivate::get(quick3dInstance->quick3DNode())->m_isHiddenInEditor)
quick3dInstance->setPickable(enable, false, true);
}
}
}
if (nodeType == QQuick3DObject::Model)
setPropertyVariant("pickable", enable); // allow 3D objects to receive mouse clicks
}
}
#endif
}
Quick3DNodeInstance::Pointer Quick3DNodeInstance::create(QObject *object)
{
Pointer instance(new Quick3DNodeInstance(object));
@@ -94,8 +126,12 @@ void Quick3DNodeInstance::setHideInEditor(bool b)
{
#ifdef QUICK3D_MODULE
QQuick3DNodePrivate *privateNode = QQuick3DNodePrivate::get(quick3DNode());
if (privateNode)
if (privateNode) {
privateNode->setIsHiddenInEditor(b);
// Hidden objects should not be pickable
setPickable(!b, true, true);
}
#else
Q_UNUSED(b)
#endif

View File

@@ -53,6 +53,7 @@ protected:
private:
Qt5NodeInstanceServer *qt5NodeInstanceServer() const;
QQuick3DNode *quick3DNode() const;
void setPickable(bool enable, bool checkParent, bool applyToChildren);
};
} // namespace Internal

View File

@@ -60,6 +60,7 @@ namespace Internal {
class GraphicsObjectNodeInstance;
class QmlStateNodeInstance;
class QuickItemNodeInstance;
class Quick3DNodeInstance;
}
class ServerNodeInstance
@@ -82,6 +83,7 @@ class ServerNodeInstance
friend class QmlDesigner::Internal::ObjectNodeInstance;
friend class QmlDesigner::Internal::QmlPropertyChangesNodeInstance;
friend class QmlDesigner::Internal::QmlStateNodeInstance;
friend class QmlDesigner::Internal::Quick3DNodeInstance;
public:
enum ComponentWrap {