Revision control

Copy as Markdown

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick3D
import QtQuick3D.Physics
import QtQuick3D.Helpers
import QtQuick.Controls
import QtQuick.Layouts
Window {
id: appWindow
width: 1280
height: 720
visible: true
title: qsTr("PhysX Physics Demo")
PhysicsWorld {
id: physicsWorld
gravity: Qt.vector3d(0, -9.81, 0)
running: false
forceDebugDraw: true
minimumTimestep: minTimestepSlider.value
maximumTimestep: maxTimestepSlider.value
typicalLength: 1
typicalSpeed: 10
scene: viewport.scene
}
View3D {
id: viewport
anchors.fill: parent
environment: SceneEnvironment {
antialiasingMode: SceneEnvironment.MSAA
}
focus: true
Keys.onSpacePressed: {
console.log("toggle physics")
physicsWorld.running = !physicsWorld.running
}
Node {
id: scene1
PerspectiveCamera {
id: camera1
position: Qt.vector3d(-2, 1, 5)
eulerRotation : Qt.vector3d(-20, -20, 0)
clipFar: 50
clipNear: 0.01
}
DirectionalLight {
eulerRotation: Qt.vector3d(-45, 45, 0)
castsShadow: true
brightness: 1
shadowMapQuality: Light.ShadowMapQualityVeryHigh
shadowBias: 0.05
pcfFactor: 0.005
}
TriggerBody {
id: movingArea
position: Qt.vector3d(4, -0.25, 1)
onBodyEntered: (body) => {
if (body.hasOwnProperty('inArea'))
body.inArea = true;
}
onBodyExited: (body) => {
if (body.hasOwnProperty('inArea'))
body.inArea = false;
}
Model {
source: "#Cube"
scale: Qt.vector3d(0.02, 0.015, 0.02)
materials: PrincipledMaterial {
baseColor: Qt.rgba(0, 0, 1, 0.2)
alphaMode: PrincipledMaterial.Blend
}
}
SequentialAnimation {
running: true
paused: !physicsWorld.running
loops: -1
NumberAnimation {
target: movingArea
property: "position.x"
duration: 5000
from: 4
to: -4
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: movingArea
property: "position.x"
duration: 5000
from: -4
to: 4
easing.type: Easing.InOutQuad
}
}
collisionShapes: BoxShape {
extents: Qt.vector3d(2, 1.5, 2)
}
}
DynamicRigidBody {
id: impeller
position: Qt.vector3d(-3, -0.5, 0)
isKinematic: true
receiveContactReports: true
SequentialAnimation {
running: physicsWorld.running
loops: Animation.Infinite;
NumberAnimation {target: impeller; property: "kinematicPosition.x"; from: -3; to: 3; duration: 2000 }
NumberAnimation {target: impeller; property: "kinematicPosition.x"; from: 3; to: -3; duration: 2000 }
}
Model {
source: "#Sphere"
materials: PrincipledMaterial {
baseColor: "yellow"
}
scale: Qt.vector3d(0.01, 0.01, 0.01)
}
collisionShapes: SphereShape {
diameter: 1
}
onBodyContact: (body, positions, impulses, normals) => {
for (var normal of normals) {
let force = normal.times(-0.001);
body.applyCentralImpulse(force);
}
}
}
StaticRigidBody {
id: floor
position: Qt.vector3d(0, -1, 0)
eulerRotation: Qt.vector3d(-90, 0, 0)
collisionShapes: PlaneShape {}
Model {
source: "#Rectangle"
scale: Qt.vector3d(5, 5, 1)
materials: PrincipledMaterial {
baseColor: "green"
}
castsShadows: false
receivesShadows: true
}
}
StaticRigidBody {
id: leftWall
eulerRotation: Qt.vector3d(0, 90, 0)
position: Qt.vector3d(-15, 3, 0)
collisionShapes: PlaneShape {
scale: Qt.vector3d(0.2, 0.2, 0.2)
}
}
StaticRigidBody {
id: rightWall
position: Qt.vector3d(15, 3, 0)
eulerRotation: Qt.vector3d(0, 270, 0)
collisionShapes: PlaneShape {
scale: Qt.vector3d(0.2, 0.2, 0.2)
}
}
StaticRigidBody {
id: backWall
position: Qt.vector3d(0, 3, -15)
collisionShapes: PlaneShape {
scale: Qt.vector3d(0.2, 0.2, 0.2)
}
}
StaticRigidBody {
id: frontWall
position: Qt.vector3d(0, 3, 10)
eulerRotation: Qt.vector3d(0, 180, 0)
collisionShapes: PlaneShape {
scale: Qt.vector3d(0.2, 0.2, 0.2)
}
}
}
Node {
id: shapeSpawner
property var instancesBoxes: []
property var instancesSpheres: []
function createBox() {
var component = Qt.createComponent("box.qml");
let box = component.createObject(scene1, {y: 3});
instancesBoxes.push(box);
if (box === null) {
// Error Handling
console.log("Error creating object");
}
}
function deleteBox() {
if (instancesBoxes.length > 0) {
let box = instancesBoxes.pop();
box.destroy();
}
}
function updateBoxes() {
while (numberBoxes.value > instancesBoxes.length) {
createBox();
}
while (numberBoxes.value < instancesBoxes.length) {
deleteBox();
}
updateLocks();
}
function doImpulse() {
for (var i = 0; i < instancesBoxes.length; i++) {
let box = instancesBoxes[i];
box.applyCentralImpulse(Qt.vector3d(0,0.01,0));
}
for (i = 0; i < instancesSpheres.length; i++) {
let sphere = instancesSpheres[i];
sphere.applyCentralImpulse(Qt.vector3d(0,0.01,0));
}
}
//// Sphere
function createSphere() {
var component = Qt.createComponent("sphere.qml");
let sphere = component.createObject(scene1, {y: 3});
instancesSpheres.push(sphere);
if (sphere === null) {
// Error Handling
console.log("Error creating object");
}
}
function deleteSphere() {
if (instancesSpheres.length > 0) {
let sphere = instancesSpheres.pop();
sphere.destroy();
}
}
function updateSpheres() {
while (numberSpheres.value > instancesSpheres.length) {
createSphere();
}
while (numberSpheres.value < instancesSpheres.length) {
deleteSphere();
}
updateLocks();
}
function updateLocks() {
var i;
for (i = 0; i < instancesSpheres.length; i++) {
let sphere = instancesSpheres[i];
sphere.angularAxisLock =
(lockAngularX.checked ? DynamicRigidBody.LockX : 0) |
(lockAngularY.checked ? DynamicRigidBody.LockY : 0) |
(lockAngularZ.checked ? DynamicRigidBody.LockZ : 0);
sphere.linearAxisLock =
(lockLinearX.checked ? DynamicRigidBody.LockX : 0) |
(lockLinearY.checked ? DynamicRigidBody.LockY : 0) |
(lockLinearZ.checked ? DynamicRigidBody.LockZ : 0);
}
for (i = 0; i < instancesBoxes.length; i++) {
let box = instancesBoxes[i];
box.angularAxisLock =
(lockAngularX.checked ? DynamicRigidBody.LockX : 0) |
(lockAngularY.checked ? DynamicRigidBody.LockY : 0) |
(lockAngularZ.checked ? DynamicRigidBody.LockZ : 0);
box.linearAxisLock =
(lockLinearX.checked ? DynamicRigidBody.LockX : 0) |
(lockLinearY.checked ? DynamicRigidBody.LockY : 0) |
(lockLinearZ.checked ? DynamicRigidBody.LockZ : 0);
}
}
function setKinematic() {
for (var i = 0; i < instancesSpheres.length; i++) {
let sphere = instancesSpheres[i];
sphere.isKinematic = isKinematicCheck.checked;
}
for (i = 0; i < instancesBoxes.length; i++) {
let box = instancesBoxes[i];
box.isKinematic = isKinematicCheck.checked;
}
}
}
}
WasdController {
keysEnabled: true
property bool controllingUnit: false
controlledObject: camera1
speed: 0.02
shiftSpeed: 0.2
Keys.onPressed: (event)=> {
if (keysEnabled) handleKeyPress(event);
if (event.key === Qt.Key_Space) {
physicsWorld.running = !physicsWorld.running
} else if (event.key === Qt.Key_H) {
physicsWorld.forceDebugDraw = !physicsWorld.forceDebugDraw
}
else if (event.key === Qt.Key_J) {
floor.eulerRotation.x = (360 + floor.eulerRotation.x - 1) % 360
}
else if (event.key === Qt.Key_K) {
floor.eulerRotation.x = floor.eulerRotation.x + 1 % 360
}
}
Keys.onReleased: (event)=> { if (keysEnabled) handleKeyRelease(event) }
}
ColumnLayout {
Label {
text: "Number of boxes: " + numberBoxes.value
}
Slider {
id: numberBoxes
focusPolicy: Qt.NoFocus
from: 0
to: 20
value: 0
stepSize: 1
onMoved: { shapeSpawner.updateBoxes() }
}
Label {
text: "Number of spheres: " + numberSpheres.value
}
Slider {
id: numberSpheres
focusPolicy: Qt.NoFocus
from: 0
to: 20
value: 0
stepSize: 1
onMoved: { shapeSpawner.updateSpheres() }
}
CheckBox {
id: lockAngularX
text: qsTr("Lock angular X")
onCheckedChanged: shapeSpawner.updateLocks()
}
CheckBox {
id: lockAngularY
text: qsTr("Lock angular Y")
onCheckedChanged: shapeSpawner.updateLocks()
}
CheckBox {
id: lockAngularZ
text: qsTr("Lock angular Z")
onCheckedChanged: shapeSpawner.updateLocks()
}
CheckBox {
id: lockLinearX
text: qsTr("Lock linear X")
onCheckedChanged: shapeSpawner.updateLocks()
}
CheckBox {
id: lockLinearY
text: qsTr("Lock linear Y")
onCheckedChanged: shapeSpawner.updateLocks()
}
CheckBox {
id: lockLinearZ
text: qsTr("Lock linear Z")
onCheckedChanged: shapeSpawner.updateLocks()
}
CheckBox {
id: isKinematicCheck
text: qsTr("Is Kinematic")
onCheckedChanged: shapeSpawner.setKinematic()
}
Button {
text: "Add impulse"
onClicked: shapeSpawner.doImpulse()
}
Label {
text: "Min timestep: " + minTimestepSlider.value
}
Slider {
id: minTimestepSlider
focusPolicy: Qt.NoFocus
from: 1
to: 100
value: 5
}
Label {
text: "Max timestep: " + maxTimestepSlider.value
}
Slider {
id: maxTimestepSlider
focusPolicy: Qt.NoFocus
from: 1
to: 100
value: 50
}
Label {
text: "Debug view (forced): " + physicsWorld.forceDebugDraw
}
}
}