QmlDesigner: Delay camera frustum geometry update in edit 3D view

Calculating camera frustum geometry requires linked camera spatial
node to be up-to-date. However, we can't control the order in which
the spatial nodes of cameras and geometries are updated, so geometry
update will often happen first. Therefore we need to defer geometry
update to the next frame when we detect camera change.

Change-Id: I056eb1bf95db357a73511a6bb7ef0c55c6f4befa
Fixes: QDS-1649
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2020-03-12 14:32:13 +02:00
parent be8cdeafd6
commit 644493fa59
2 changed files with 28 additions and 45 deletions

View File

@@ -35,6 +35,7 @@
#include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
#include <QtQuick3D/private/qquick3dutils_p.h>
#include <QtCore/qmath.h>
#include <QtCore/qtimer.h>
#include <limits>
@@ -70,31 +71,31 @@ void CameraGeometry::setCamera(QQuick3DCamera *camera)
m_camera = camera;
if (auto perspectiveCamera = qobject_cast<QQuick3DPerspectiveCamera *>(m_camera)) {
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::clipNearChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::clipFarChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::fieldOfViewChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
QObject::connect(perspectiveCamera, &QQuick3DPerspectiveCamera::fieldOfViewOrientationChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
if (auto frustumCamera = qobject_cast<QQuick3DFrustumCamera *>(m_camera)) {
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::topChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::bottomChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::rightChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
QObject::connect(frustumCamera, &QQuick3DFrustumCamera::leftChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
}
} else if (auto orthoCamera = qobject_cast<QQuick3DOrthographicCamera *>(m_camera)) {
QObject::connect(orthoCamera, &QQuick3DOrthographicCamera::clipNearChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
QObject::connect(orthoCamera, &QQuick3DOrthographicCamera::clipFarChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
} else if (auto customCamera = qobject_cast<QQuick3DCustomCamera *>(m_camera)) {
QObject::connect(customCamera, &QQuick3DCustomCamera::projectionChanged,
this, &CameraGeometry::update);
this, &CameraGeometry::handleCameraPropertyChange);
}
emit cameraChanged();
update();
@@ -110,48 +111,28 @@ void CameraGeometry::setViewPortRect(const QRectF &rect)
update();
}
void CameraGeometry::handleCameraPropertyChange()
{
m_cameraUpdatePending = true;
update();
}
QSSGRenderGraphObject *CameraGeometry::updateSpatialNode(QSSGRenderGraphObject *node)
{
if (!m_camera)
return node;
// If camera properties have been updated, we need to defer updating the frustum geometry
// to the next frame to ensure camera's spatial node has been properly updated.
if (m_cameraUpdatePending) {
QTimer::singleShot(0, this, &CameraGeometry::update);
m_cameraUpdatePending = false;
return node;
}
if (!m_camera->cameraNode()) {
#if QT_VERSION <= QT_VERSION_CHECK(5, 14, 1)
// 5.14.1 doesn't yet have function to force camera node creation, so we must do it
// the hard way
auto camera = new QSSGRenderCamera();
bool changed = false;
if (auto perspCamera = qobject_cast<QQuick3DPerspectiveCamera *>(m_camera)) {
changed |= qUpdateIfNeeded(camera->clipNear, perspCamera->clipNear());
changed |= qUpdateIfNeeded(camera->clipFar, perspCamera->clipFar());
changed |= qUpdateIfNeeded(camera->fov, qDegreesToRadians(perspCamera->fieldOfView()));
changed |= qUpdateIfNeeded(camera->fovHorizontal, perspCamera->fieldOfViewOrientation()
== QQuick3DCamera::FieldOfViewOrientation::Horizontal);
changed |= qUpdateIfNeeded(camera->enableFrustumClipping, perspCamera->frustumCullingEnabled());
if (auto frustCamera = qobject_cast<QQuick3DFrustumCamera *>(m_camera)) {
camera->flags.setFlag(QSSGRenderNode::Flag::CameraFrustumProjection, true);
changed |= qUpdateIfNeeded(camera->top, frustCamera->top());
changed |= qUpdateIfNeeded(camera->bottom, frustCamera->bottom());
changed |= qUpdateIfNeeded(camera->right, frustCamera->right());
changed |= qUpdateIfNeeded(camera->left, frustCamera->left());
}
} else if (auto orthoCamera = qobject_cast<QQuick3DOrthographicCamera *>(m_camera)) {
camera->flags.setFlag(QSSGRenderNode::Flag::Orthographic, true);
changed |= qUpdateIfNeeded(camera->clipNear, orthoCamera->clipNear());
changed |= qUpdateIfNeeded(camera->clipFar, orthoCamera->clipFar());
changed |= qUpdateIfNeeded(camera->enableFrustumClipping, orthoCamera->frustumCullingEnabled());
} else if (auto customCamera = qobject_cast<QQuick3DCustomCamera *>(m_camera)) {
camera->flags.setFlag(QSSGRenderNode::Flag::CameraCustomProjection, true);
changed |= qUpdateIfNeeded(camera->projection, customCamera->projection());
changed |= qUpdateIfNeeded(camera->enableFrustumClipping, customCamera->frustumCullingEnabled());
}
if (changed)
camera->flags.setFlag(QSSGRenderNode::Flag::CameraDirty);
m_camera->setCameraNode(camera);
#else
// Doing explicit viewport mapping forces cameraNode creation
m_camera->mapToViewport({}, m_viewPortRect.width(), m_viewPortRect.height());
#endif
}
node = QQuick3DGeometry::updateSpatialNode(node);

View File

@@ -49,6 +49,7 @@ public:
public Q_SLOTS:
void setCamera(QQuick3DCamera *camera);
void setViewPortRect(const QRectF &rect);
void handleCameraPropertyChange();
Q_SIGNALS:
void cameraChanged();
@@ -62,6 +63,7 @@ private:
QVector3D &minBounds, QVector3D &maxBounds);
QQuick3DCamera *m_camera = nullptr;
QRectF m_viewPortRect;
bool m_cameraUpdatePending = false;
};
}