QmlDesigner: Implement camera frustum visualization for edit 3D

Task-number: QDS-1208
Change-Id: I3241fcdc68d01666a2536df32f170cd898cbd7af
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-07 10:46:14 +02:00
parent f4d6609d91
commit db7087225d
13 changed files with 351 additions and 9 deletions

View File

@@ -25,17 +25,27 @@
import QtQuick 2.0
import QtQuick3D 1.0
import CameraGeometry 1.0
IconGizmo {
id: cameraGizmo
iconSource: "qrc:///qtquickplugin/mockfiles/images/camera-pick-icon.png"
gizmoModel.source: "#Cube"
gizmoModel.geometry: cameraGeometry
property alias geometryName: cameraGeometry.name // Name must be unique for each geometry
property alias viewPortRect: cameraGeometry.viewPortRect
CameraGeometry {
id: cameraGeometry
camera: cameraGizmo.targetNode
}
gizmoModel.materials: [
DefaultMaterial {
id: defaultMaterial
emissiveColor: "blue"
lighting: DefaultMaterial.NoLighting
cullingMode: Material.DisableCulling
}
]
}

View File

@@ -46,6 +46,7 @@ Window {
property var lightGizmos: []
property var cameraGizmos: []
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
signal objectClicked(var object)
signal commitObjectProperty(var object, var propName)
@@ -75,10 +76,14 @@ Window {
{
var component = Qt.createComponent("CameraGizmo.qml");
if (component.status === Component.Ready) {
var gizmo = component.createObject(overlayScene,
{"view3D": overlayView, "targetNode": obj});
var geometryName = designStudioNativeCameraControlHelper.generateUniqueName("CameraGeometry");
var gizmo = component.createObject(
overlayScene,
{"view3D": overlayView, "targetNode": obj, "geometryName": geometryName,
"viewPortRect": viewPortRect});
cameraGizmos[cameraGizmos.length] = gizmo;
gizmo.selected.connect(emitObjectClicked);
gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;});
}
}

View File

@@ -45,7 +45,6 @@ Node {
Model {
id: gizmoModel
scale: Qt.vector3d(0.05, 0.05, 0.05)
visible: iconGizmo.visible
}
Overlay2D {

View File

@@ -31,6 +31,7 @@ IconGizmo {
iconSource: "qrc:///qtquickplugin/mockfiles/images/light-pick-icon.png"
gizmoModel.source: "#Sphere"
gizmoModel.scale: Qt.vector3d(0.10, 0.10, 0.10)
gizmoModel.materials: [
DefaultMaterial {
id: defaultMaterial

View File

@@ -65,5 +65,12 @@ void CameraControlHelper::requestOverlayUpdate()
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

@@ -42,6 +42,7 @@ public:
void setEnabled(bool enabled);
Q_INVOKABLE void requestOverlayUpdate();
Q_INVOKABLE QString generateUniqueName(const QString &nameRoot);
public slots:
void handleUpdateTimer();

View File

@@ -0,0 +1,211 @@
/****************************************************************************
**
** 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 "camerageometry.h"
#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
#include <QtQuick3D/private/qquick3dcustomcamera_p.h>
#include <QtQuick3D/private/qquick3dfrustumcamera_p.h>
#include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
#include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
#include <limits>
namespace QmlDesigner {
namespace Internal {
CameraGeometry::CameraGeometry()
: QQuick3DGeometry()
{
}
CameraGeometry::~CameraGeometry()
{
}
QQuick3DCamera *CameraGeometry::camera() const
{
return m_camera;
}
QRectF CameraGeometry::viewPortRect() const
{
return m_viewPortRect;
}
void CameraGeometry::setCamera(QQuick3DCamera *camera)
{
if (m_camera == camera)
return;
if (m_camera)
m_camera->disconnect(this);
m_camera = camera;
if (auto perspectiveCamera = qobject_cast<QQuick3DPerspectiveCamera *>(m_camera)) {
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::clipNearChanged,
this, &CameraGeometry::update);
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::clipFarChanged,
this, &CameraGeometry::update);
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::fieldOfViewChanged,
this, &CameraGeometry::update);
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::fieldOfViewOrientationChanged,
this, &CameraGeometry::update);
if (auto frustumCamera = qobject_cast<QQuick3DFrustumCamera *>(m_camera)) {
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::topChanged,
this, &CameraGeometry::update);
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::bottomChanged,
this, &CameraGeometry::update);
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::rightChanged,
this, &CameraGeometry::update);
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::leftChanged,
this, &CameraGeometry::update);
}
} else if (auto orthoCamera = qobject_cast<QQuick3DOrthographicCamera *>(m_camera)) {
QObject::connect(orthoCamera, &QQuick3DOrthographicCamera::clipNearChanged,
this, &CameraGeometry::update);
QObject::connect(orthoCamera, &QQuick3DOrthographicCamera::clipFarChanged,
this, &CameraGeometry::update);
} else if (auto customCamera = qobject_cast<QQuick3DCustomCamera *>(m_camera)) {
QObject::connect(customCamera, &QQuick3DCustomCamera::projectionChanged,
this, &CameraGeometry::update);
}
emit cameraChanged();
update();
}
void CameraGeometry::setViewPortRect(const QRectF &rect)
{
if (m_viewPortRect == rect)
return;
m_viewPortRect = rect;
emit viewPortRectChanged();
update();
}
QSSGRenderGraphObject *CameraGeometry::updateSpatialNode(QSSGRenderGraphObject *node)
{
if (!m_camera)
return node;
node = QQuick3DGeometry::updateSpatialNode(node);
QSSGRenderGeometry *geometry = static_cast<QSSGRenderGeometry *>(node);
geometry->clear();
QByteArray vertexData;
QByteArray indexData;
QVector3D minBounds;
QVector3D maxBounds;
fillVertexData(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);
return node;
}
void CameraGeometry::fillVertexData(QByteArray &vertexData, QByteArray &indexData,
QVector3D &minBounds, QVector3D &maxBounds)
{
const int vertexSize = int(sizeof(float)) * 8 * 3; // 8 vertices, 3 floats/vert
vertexData.resize(vertexSize);
const int indexSize = int(sizeof(quint16)) * 12 * 2; // 16 lines, 2 vert/line
indexData.resize(indexSize);
auto dataPtr = reinterpret_cast<float *>(vertexData.data());
auto indexPtr = reinterpret_cast<quint16 *>(indexData.data());
QSSGRenderCamera *camera = m_camera->cameraNode();
if (qobject_cast<QQuick3DOrthographicCamera *>(m_camera)) {
// For some reason ortho cameras show double what projection suggests,
// so give them doubled viewport to match visualization to actual camera view
camera->calculateProjection(QRectF(0, 0, m_viewPortRect.width() * 2.0,
m_viewPortRect.height() * 2.0));
} else {
camera->calculateProjection(m_viewPortRect);
}
const QMatrix4x4 m = camera->projection.inverted();
const QVector3D farTopLeft = m * QVector3D(1.f, -1.f, 1.f);
const QVector3D farBottomRight = m * QVector3D(-1.f, 1.f, 1.f);
const QVector3D nearTopLeft = m * QVector3D(1.f, -1.f, -1.f);
const QVector3D nearBottomRight = m * QVector3D(-1.f, 1.f, -1.f);
*dataPtr++ = nearTopLeft.x(); *dataPtr++ = nearBottomRight.y(); *dataPtr++ = nearTopLeft.z();
*dataPtr++ = nearTopLeft.x(); *dataPtr++ = nearTopLeft.y(); *dataPtr++ = nearTopLeft.z();
*dataPtr++ = nearBottomRight.x(); *dataPtr++ = nearTopLeft.y(); *dataPtr++ = nearTopLeft.z();
*dataPtr++ = nearBottomRight.x(); *dataPtr++ = nearBottomRight.y(); *dataPtr++ = nearTopLeft.z();
*dataPtr++ = farTopLeft.x(); *dataPtr++ = farBottomRight.y(); *dataPtr++ = farTopLeft.z();
*dataPtr++ = farTopLeft.x(); *dataPtr++ = farTopLeft.y(); *dataPtr++ = farTopLeft.z();
*dataPtr++ = farBottomRight.x(); *dataPtr++ = farTopLeft.y(); *dataPtr++ = farTopLeft.z();
*dataPtr++ = farBottomRight.x(); *dataPtr++ = farBottomRight.y(); *dataPtr++ = farTopLeft.z();
// near rect
*indexPtr++ = 0; *indexPtr++ = 1;
*indexPtr++ = 1; *indexPtr++ = 2;
*indexPtr++ = 2; *indexPtr++ = 3;
*indexPtr++ = 3; *indexPtr++ = 0;
// near to far
*indexPtr++ = 0; *indexPtr++ = 4;
*indexPtr++ = 1; *indexPtr++ = 5;
*indexPtr++ = 2; *indexPtr++ = 6;
*indexPtr++ = 3; *indexPtr++ = 7;
// far rect
*indexPtr++ = 4; *indexPtr++ = 5;
*indexPtr++ = 5; *indexPtr++ = 6;
*indexPtr++ = 6; *indexPtr++ = 7;
*indexPtr++ = 7; *indexPtr++ = 4;
static const float floatMin = std::numeric_limits<float>::lowest();
static const float floatMax = std::numeric_limits<float>::max();
auto vertexPtr = reinterpret_cast<QVector3D *>(vertexData.data());
minBounds = QVector3D(floatMax, floatMax, floatMax);
maxBounds = QVector3D(floatMin, floatMin, floatMin);
for (int i = 0; i < vertexSize / 12; ++i) {
minBounds[0] = qMin((*vertexPtr)[0], minBounds[0]);
minBounds[1] = qMin((*vertexPtr)[1], minBounds[1]);
minBounds[2] = qMin((*vertexPtr)[2], minBounds[2]);
maxBounds[0] = qMax((*vertexPtr)[0], maxBounds[0]);
maxBounds[1] = qMax((*vertexPtr)[1], maxBounds[1]);
maxBounds[2] = qMax((*vertexPtr)[2], maxBounds[2]);
++vertexPtr;
}
}
}
}
#endif // QUICK3D_MODULE

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** 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/qquick3dgeometry_p.h>
#include <QtQuick3D/private/qquick3dcamera_p.h>
namespace QmlDesigner {
namespace Internal {
class CameraGeometry : public QQuick3DGeometry
{
Q_OBJECT
Q_PROPERTY(QQuick3DCamera *camera READ camera WRITE setCamera NOTIFY cameraChanged)
Q_PROPERTY(QRectF viewPortRect READ viewPortRect WRITE setViewPortRect NOTIFY viewPortRectChanged)
public:
CameraGeometry();
~CameraGeometry() override;
QQuick3DCamera *camera() const;
QRectF viewPortRect() const;
public Q_SLOTS:
void setCamera(QQuick3DCamera *camera);
void setViewPortRect(const QRectF &rect);
Q_SIGNALS:
void cameraChanged();
void viewPortRectChanged();
protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override;
private:
void fillVertexData(QByteArray &vertexData, QByteArray &indexData,
QVector3D &minBounds, QVector3D &maxBounds);
QQuick3DCamera *m_camera = nullptr;
QRectF m_viewPortRect;
};
}
}
QML_DECLARE_TYPE(QmlDesigner::Internal::CameraGeometry)
#endif // QUICK3D_MODULE

View File

@@ -1,5 +1,7 @@
HEADERS += $$PWD/cameracontrolhelper.h \
$$PWD/mousearea3d.h
$$PWD/mousearea3d.h \
$$PWD/camerageometry.h
SOURCES += $$PWD/cameracontrolhelper.cpp \
$$PWD/mousearea3d.cpp
$$PWD/mousearea3d.cpp \
$$PWD/camerageometry.cpp

View File

@@ -61,6 +61,7 @@
#include "dummycontextobject.h"
#include "../editor3d/cameracontrolhelper.h"
#include "../editor3d/mousearea3d.h"
#include "../editor3d/camerageometry.h"
#include <designersupportdelegate.h>
@@ -85,6 +86,7 @@ QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine)
#ifdef QUICK3D_MODULE
qmlRegisterType<QmlDesigner::Internal::MouseArea3D>("MouseArea3D", 1, 0, "MouseArea3D");
qmlRegisterType<QmlDesigner::Internal::CameraGeometry>("CameraGeometry", 1, 0, "CameraGeometry");
#endif
QQmlComponent component(engine, QUrl("qrc:/qtquickplugin/mockfiles/EditView3D.qml"));
@@ -214,6 +216,14 @@ void Qt5InformationNodeInstanceServer::handleObjectPropertyChange(const QVariant
m_changedProperty = propertyName;
}
void Qt5InformationNodeInstanceServer::updateViewPortRect()
{
QRectF viewPortrect(0, 0, m_viewPortInstance.internalObject()->property("width").toDouble(),
m_viewPortInstance.internalObject()->property("height").toDouble());
QQmlProperty viewPortProperty(m_editView3D, "viewPortRect", context());
viewPortProperty.write(viewPortrect);
}
Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) :
Qt5NodeInstanceServer(nodeInstanceClient)
{
@@ -316,7 +326,6 @@ void Qt5InformationNodeInstanceServer::findCamerasAndLights(
const QList<ServerNodeInstance> &instanceList,
QObjectList &cameras, QObjectList &lights) const
{
QObjectList objList;
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DCamera"))
cameras << instance.internalObject();
@@ -325,6 +334,16 @@ void Qt5InformationNodeInstanceServer::findCamerasAndLights(
}
}
ServerNodeInstance Qt5InformationNodeInstanceServer::findViewPort(
const QList<ServerNodeInstance> &instanceList)
{
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DViewport"))
return instance;
}
return ServerNodeInstance();
}
void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeInstance> &instanceList)
{
ServerNodeInstance root = rootNodeInstance();
@@ -350,8 +369,17 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
sceneProperty.write(objectToVariant(node));
QQmlProperty parentProperty(node, "parent", context());
parentProperty.write(objectToVariant(m_editView3D));
QQmlProperty completeSceneProperty(m_editView3D, "showLight", context());
completeSceneProperty.write(showCustomLight);
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();
}
// Create camera and light gizmos
QObjectList cameras;

View File

@@ -53,6 +53,7 @@ private slots:
void objectClicked(const QVariant &object);
void handleObjectPropertyCommit(const QVariant &object, const QVariant &propName);
void handleObjectPropertyChange(const QVariant &object, const QVariant &propName);
void updateViewPortRect();
protected:
void collectItemChangesAndSendChangeCommands() override;
@@ -70,6 +71,7 @@ private:
QObject *findRootNodeOf3DViewport(const QList<ServerNodeInstance> &instanceList) const;
void findCamerasAndLights( const QList<ServerNodeInstance> &instanceList,
QObjectList &cameras, QObjectList &lights) const;
ServerNodeInstance findViewPort(const QList<ServerNodeInstance> &instanceList);
QVector<InstancePropertyValueTriple> vectorToPropertyValue(const ServerNodeInstance &instance,
const PropertyName &propertyName,
const QVariant &variant);
@@ -84,6 +86,7 @@ private:
QTimer m_propertyChangeTimer;
QVariant m_changedNode;
PropertyName m_changedProperty;
ServerNodeInstance m_viewPortInstance;
};
} // namespace QmlDesigner

View File

@@ -109,6 +109,7 @@ extend_qtc_executable(qml2puppet
SOURCES
cameracontrolhelper.cpp cameracontrolhelper.h
mousearea3d.cpp mousearea3d.h
camerageometry.cpp camerageometry.h
)
extend_qtc_executable(qml2puppet

View File

@@ -199,6 +199,8 @@ QtcTool {
"editor3d/cameracontrolhelper.h",
"editor3d/mousearea3d.cpp",
"editor3d/mousearea3d.h",
"editor3d/camerageometry.cpp",
"editor3d/camerageometry.h",
"qml2puppetmain.cpp",
]
}