Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Takes the scene away from the world from a handler on the velocity a sync
// writes. Like the position in tst_scene_changed_mid_sync.qml, the velocities
// are written while the walk over the bodies is on the stack, so moving the
// world from there deregisters every body, the one being synced included, and
// that body has nothing left to finish its sync with.
//
// Reaching the end of both of the frame's walks is the assertion, asked for as
// two more finished frames after the move.
import QtQuick
import QtTest
import QtQuick3D
import QtQuick3D.Physics
import QtQuick3D.Physics.TestUtils
Item {
id: root
width: 64
height: 64
// The frame the world was moved on, set by whichever body the walk reached
// first.
property int frameAtMove: -1
PhysicsWorld {
id: world
gravity: Qt.vector3d(0, -981, 0)
running: true
// On, so the frame that loses its scene mid-walk goes on to the second
// walk it makes over the same backends, the one debug drawing does.
forceDebugDraw: true
typicalLength: 100
typicalSpeed: 1000
minimumTimestep: 15
maximumTimestep: 15
scene: fallingScene
property int frameCount: 0
onFrameDone: world.frameCount++
}
Node {
id: fallingScene
// Falling freely, so every sync has a new velocity to write.
Repeater3D {
model: 3
DynamicRigidBody {
required property int index
position: Qt.vector3d(index * 120 - 120, 200, 0)
collisionShapes: BoxShape {}
onLinearVelocityChanged: {
if (world.frameCount > 5 && world.scene === fallingScene) {
root.frameAtMove = world.frameCount;
world.scene = elsewhere;
}
}
}
}
}
// What the world is moved to. It holds no bodies, so the move leaves every
// body without a world rather than handing it to a new one.
Node {
id: elsewhere
}
PhysicsTestCase {
name: "moving the world while it updates a body's velocity"
goalReached: root.frameAtMove >= 0 && world.frameCount > root.frameAtMove + 1
}
}