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")

    PhysicsWorld {
        scene: viewport.scene
    }

    View3D {
        id: viewport
        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)
            }
        }
    }

}