Qt Quick 3D Physics - Cannon Example

 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 import QtQuick
 import QtQuick3D
 import QtQuick3D.Physics
 import QtQuick3D.Helpers
 import QtQuick.Controls
 import QtQuick.Layouts

 Window {
     id: appWindow
     width: 800
     height: 600
     visible: true
     title: qsTr("Qt Quick 3D Physics - Cannon")

     DynamicsWorld {}

     View3D {
         width: parent.width
         height: parent.height
         focus: true

         environment: SceneEnvironment {
             antialiasingMode: SceneEnvironment.MSAA
             backgroundMode: SceneEnvironment.Color
             clearColor: "#f0f0f0"
         }

         PerspectiveCamera {
             id: camera
             position: Qt.vector3d(-4000, 5000, 10000)
             eulerRotation : Qt.vector3d(-20, -20, 0)
             clipFar: 500000
             clipNear: 100
         }

         DirectionalLight {
             eulerRotation: Qt.vector3d(-45, 45, 0)
             castsShadow: true
             brightness: 1
             shadowMapQuality: Light.ShadowMapQualityVeryHigh
             shadowFactor: 100
         }

         StaticRigidBody {
             eulerRotation: Qt.vector3d(-90, 0, 0)
             collisionShapes: PlaneShape {}
             Model {
                 source: "#Rectangle"
                 scale: Qt.vector3d(2000, 2000, 0)
                 materials: PrincipledMaterial {
                     baseColor: "green"
                 }
                 castsShadows: false
                 receivesShadows: true
             }
         }

         Node {
             id: shapeSpawner
             property var instancesBoxes: []
             property var instancesSpheres: []
             property int stackCount: 0

             function createStack(stackZ) {
                 var size = 10;
                 var extents = 400;

                 for (var i = 0; i < size; i++) {
                     for (var j = 0; j < size-i; j++) {
                         var component = Qt.createComponent("box.qml");
                         let x = j*2 - size + i;
                         let y = i*2 + 1;
                         let z = -5*stackZ;
                         let center = Qt.vector3d(x, y, z).times(0.5*extents);
                         let box = component.createObject(shapeSpawner, {position: center, xyzExtents: extents});
                         instancesBoxes.push(box);

                         if (box === null) {
                             console.log("Error creating object");
                         }
                     }
                 }
             }

             function createBall(position, forward) {
                 var diameter = 600;
                 var speed = 20000;
                 var component = Qt.createComponent("sphere.qml");
                 let sphere = component.createObject(shapeSpawner, {position: position, linearVelocity: forward.times(speed), sphereDiameter: diameter});
                 instancesSpheres.push(sphere);

                 if (sphere === null) {
                     console.log("Error creating object");
                 }
             }

             function reset() {
                 instancesSpheres.forEach(sphere => { sphere.destroy(); });
                 instancesBoxes.forEach(boxes => { boxes.destroy(); });
                 instancesSpheres = [];
                 instancesBoxes = [];

                 for (var stackI = 0; stackI < stackSlider.value; stackI++) {
                     shapeSpawner.createStack(stackI);
                 }
             }
         }
         Crosshair {
             id: crossHair
             anchors.centerIn: parent
         }
     }

     Component.onCompleted: {
         shapeSpawner.reset()
     }

     WasdController {
         speed: 100
         controlledObject: camera
         Keys.onPressed: (event)=> {
                             handleKeyPress(event);
                             if (event.key === Qt.Key_Space) {
                                 shapeSpawner.createBall(camera.position, camera.forward);
                             }
                         }
         Keys.onReleased: (event)=> { handleKeyRelease(event) }
     }

     Frame {
         background: Rectangle {
             color: "#c0c0c0"
             border.color: "#202020"
         }
         anchors.top: parent.top
         anchors.left: parent.left
         anchors.margins: 10

         ColumnLayout {
             Label {
                 text: "No. Stacks: " + stackSlider.value.toFixed(0)
             }
             Slider {
                 id: stackSlider
                 focusPolicy: Qt.NoFocus
                 from: 1
                 to: 9
                 value: 4
                 stepSize: 1
                 snapMode: Slider.SnapOnRelease
             }
             Button {
                 id: resetButton
                 Layout.alignment: Qt.AlignHCenter
                 text: "Reset scene"
                 onClicked: shapeSpawner.reset()
             }
             Button {
                 id: fireButton
                 Layout.alignment: Qt.AlignHCenter
                 text: "Fire!"
                 onClicked: shapeSpawner.createBall(camera.position, camera.forward)
             }
         }
     }

 }