Commit 25481f26 authored by Richard Gustavsen's avatar Richard Gustavsen

QQuick3DSceneManager: handle global transform nodes in a separate pass

Whenever someone creates a connection to any of the global transform releated
properties for a QQuick3DNode, we inform the scene manager about it. What it
means is that we need to calulate the global transform and emit changes to it
whenever it changes. But knowing when it changes is costly, since it will change
whenever the transform of an ancestor change (and we don't want to listen for
that all the way to the root). So the scene manager will instead delay all global
transform calculations to a be done in a single pass before we update instead.
parent 97594df8
Pipeline #4986 passed with stage
in 5 minutes and 17 seconds
......@@ -32,6 +32,8 @@
#include <QtQuick3DRuntimeRender/private/qssgrendernode_p.h>
#include <QtQuick3DUtils/private/qssgutils_p.h>
#include <QtQuick3DUtils/private/qssgrendereulerangles_p.h>
#include <QtQuick3D/private/qquick3dobject_p_p.h>
#include <QtQuick3D/private/qquick3dscenemanager_p.h>
#include <QtMath>
......@@ -369,6 +371,89 @@ QMatrix4x4 QQuick3DNode::calculateLocalTransformRightHanded()
return localTransform;
}
void QQuick3DNode::calculateAndNotifyGlobalTransformChanges()
{
const QMatrix4x4 oldGlobalTransform = m_globalTransformRightHanded;
const QMatrix4x4 newGlobalTransform = globalTransformRightHanded();
if (oldGlobalTransform == newGlobalTransform)
return;
const QVector3D oldGlobalPosition = mat44::getPosition(oldGlobalTransform);
const QVector3D newGlobalPosition = mat44::getPosition(newGlobalTransform);
const QVector3D oldGlobalRotation = mat44::getRotation(oldGlobalTransform, m_rotationorder);
const QVector3D newGlobalRotation = mat44::getRotation(newGlobalTransform, m_rotationorder);
const QVector3D oldGlobalScale = mat44::getScale(oldGlobalTransform);
const QVector3D newGlobalScale = mat44::getScale(newGlobalTransform);
emit globalTransformChanged();
if (oldGlobalPosition != newGlobalPosition)
emit globalPositionChanged();
if (oldGlobalRotation != newGlobalRotation)
emit globalRotationChanged();
if (oldGlobalScale != newGlobalScale)
emit globalScaleChanged();
}
bool QQuick3DNode::isGlobalTransformRelatedSignal(const QMetaMethod &signal)
{
static const QMetaMethod globalTransformSignal = QMetaMethod::fromSignal(&QQuick3DNode::globalTransformChanged);
static const QMetaMethod globalPositionSignal = QMetaMethod::fromSignal(&QQuick3DNode::globalPositionChanged);
static const QMetaMethod globalRotationSignal = QMetaMethod::fromSignal(&QQuick3DNode::globalRotationChanged);
static const QMetaMethod globalScaleSignal = QMetaMethod::fromSignal(&QQuick3DNode::globalScaleChanged);
return (signal == globalTransformSignal
|| signal == globalPositionSignal
|| signal == globalRotationSignal
|| signal == globalScaleSignal);
}
void QQuick3DNode::updateGlobalTransformRegistration()
{
// Whenever someone creates a connection to any of the global transform releated
// properties, we inform the scene manager about it. What it means is that we need
// to calulate the global transform and emit changes to it whenever it changes. But
// knowing when it changes is costly, since it will change whenever the transform of
// an ancestor change (and we don't want to listen for that all the way to the root).
// So the scene manager will instead delay all global transform calculations to a be
// done in a single pass before we update instead.
if (!QQuick3DObjectPrivate::get(this)->componentComplete) {
// We need to wait until completed so that we have a scene manager assigned
// todo: We might need to listen for e.g parent changes, to catch if the scene manager changes.
return;
}
auto sceneManager = QQuick3DObjectPrivate::get(this)->sceneManager;
if (!sceneManager)
return;
if (m_globalTransformConnectionCount != 0)
sceneManager->globalTransformNodes.append(this);
else
sceneManager->globalTransformNodes.removeOne(this);
}
void QQuick3DNode::connectNotify(const QMetaMethod &signal)
{
if (isGlobalTransformRelatedSignal(signal))
m_globalTransformConnectionCount++;
if (m_globalTransformConnectionCount == 1)
updateGlobalTransformRegistration();
}
void QQuick3DNode::disconnectNotify(const QMetaMethod &signal)
{
if (isGlobalTransformRelatedSignal(signal))
m_globalTransformConnectionCount--;
if (m_globalTransformConnectionCount == 0)
updateGlobalTransformRegistration();
}
void QQuick3DNode::componentComplete()
{
QQuick3DObject::componentComplete();
if (m_globalTransformConnectionCount != 0)
updateGlobalTransformRegistration();
}
QQuick3DObject::Type QQuick3DNode::type() const
{
return QQuick3DObject::Node;
......@@ -414,6 +499,7 @@ void QQuick3DNode::setRotation(QVector3D rotation)
m_rotation = rotation;
emit rotationChanged(m_rotation);
update();
}
......
......@@ -68,9 +68,9 @@ class Q_QUICK3D_EXPORT QQuick3DNode : public QQuick3DObject
Q_PROPERTY(QVector3D forward READ forward)
Q_PROPERTY(QVector3D up READ up)
Q_PROPERTY(QVector3D right READ right)
Q_PROPERTY(QVector3D globalPosition READ globalPosition)
Q_PROPERTY(QVector3D globalRotation READ globalRotation)
Q_PROPERTY(QVector3D globalScale READ globalScale)
Q_PROPERTY(QVector3D globalPosition READ globalPosition NOTIFY globalPositionChanged)
Q_PROPERTY(QVector3D globalRotation READ globalRotation NOTIFY globalRotationChanged)
Q_PROPERTY(QVector3D globalScale READ globalScale NOTIFY globalScaleChanged)
Q_PROPERTY(QMatrix4x4 globalTransform READ globalTransform NOTIFY globalTransformChanged)
public:
......@@ -123,6 +123,11 @@ public:
QQuick3DObject::Type type() const override;
protected:
void connectNotify(const QMetaMethod &signal) override;
void disconnectNotify(const QMetaMethod &signal) override;
void componentComplete() override;
public Q_SLOTS:
void setX(float x);
void setY(float y);
......@@ -150,7 +155,10 @@ Q_SIGNALS:
void rotationOrderChanged(RotationOrder rotationorder);
void orientationChanged(Orientation orientation);
void visibleChanged(bool visible);
void globalTransformChanged(QMatrix4x4 transform);
void globalTransformChanged();
void globalPositionChanged();
void globalRotationChanged();
void globalScaleChanged();
protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override;
......@@ -166,10 +174,15 @@ private:
Orientation m_orientation = LeftHanded;
bool m_visible = true;
QMatrix4x4 m_globalTransformRightHanded;
int m_globalTransformConnectionCount = 0;
void calculateAndNotifyGlobalTransformChanges();
QMatrix4x4 calculateLocalTransformRightHanded();
void calculateGlobalVariables();
bool isGlobalTransformRelatedSignal(const QMetaMethod &signal);
void updateGlobalTransformRegistration();
friend QQuick3DSceneManager;
};
......
......@@ -74,6 +74,9 @@ void QQuick3DSceneManager::updateDirtyNodes()
{
cleanupNodes();
if (dirtySpatialNodeList)
updateGlobalTransformNodes();
auto updateNodes = [this](QQuick3DObject *updateList) {
if (updateList)
QQuick3DObjectPrivate::get(updateList)->prevDirtyItem = &updateList;
......@@ -178,6 +181,21 @@ void QQuick3DSceneManager::updateDirtySpatialNode(QQuick3DNode *spatialNode)
}
}
void QQuick3DSceneManager::updateGlobalTransformNodes()
{
// dirtyGlobalTransformList contains the nodes that have signal/slot connections to their
// global transform. In other words, other nodes/components are depending/have bindings to
// their global position/rotation/scale. For such nodes, we need to notify when their global
// transform has changed. But knowing when the global transform of a node has changed is a
// relatively costly operation, since it involves detecting whenever any of its anchestor has
// changed transform as well. So instead of trying to listes for all such changes all the way
// to the root, we instead optimize this by checking it once in a separate pass. This needs
// to be done whenever another node in the scene changes transform, since we don't know upfront
// if that change might affect any of the globalTransformNodes as well.
for (const auto globalTransformNode : globalTransformNodes)
globalTransformNode->calculateAndNotifyGlobalTransformChanges();
}
QQuick3DObject *QQuick3DSceneManager::lookUpNode(QSSGRenderGraphObject *node) const
{
return m_nodeMap[node];
......
......@@ -70,6 +70,7 @@ public:
void updateDirtyNode(QQuick3DObject *object);
void updateDirtyResource(QQuick3DObject *resourceObject);
void updateDirtySpatialNode(QQuick3DNode *spatialNode);
void updateGlobalTransformNodes();
QQuick3DObject *lookUpNode(QSSGRenderGraphObject *node) const;
......@@ -79,6 +80,7 @@ public:
QQuick3DObject *dirtyResourceList;
QQuick3DObject *dirtyImageList;
QList<QQuick3DObject *> dirtyLightList;
QList<QQuick3DNode *> globalTransformNodes;
QList<QSSGRenderGraphObject *> cleanupNodeList;
QSet<QQuick3DObject *> parentlessItems;
QVector<QSGDynamicTexture *> qsgDynamicTextures;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment