Revision control
++ // 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.Controls.Universal
++ import QtQuick.Layouts
++
++ ApplicationWindow {
++ width: 1280
++ height: 720
++ visible: true
++ title: qsTr("PhysX Physics Demo")
++
++ Universal.theme: Universal.Dark
++ Universal.accent: Universal.Orange
++
++ PhysicsWorld {
++ id: physicsWorld
++ gravity: Qt.vector3d(0, -9.81, 0)
++ running: false
++ forceDebugDraw: true
++ typicalLength: 0.2
++ typicalSpeed: 10
++ scene: scene
++ viewport: viewport1.scene
++ }
++
++ Node {
++ id: scene
++ PerspectiveCamera {
++ id: camera1
++ position: Qt.vector3d(1, 3, 7)
++ eulerRotation.x: -30
++ eulerRotation.y: 10
++ clipFar: 100
++ clipNear: 0.01
++ }
++
++ DirectionalLight {
++ eulerRotation.x: -45
++ eulerRotation.y: 45
++ castsShadow: true
++ brightness: 1
++ shadowMapQuality: Light.ShadowMapQualityMedium
++ shadowBias: 0.05
++ pcfFactor: 0.005
++ }
++
++ StaticRigidBody {
++ id: floor
++ position: Qt.vector3d(0, -1, 0)
++ eulerRotation: Qt.vector3d(-90, 0, 0)
++ collisionShapes: PlaneShape {
++ enableDebugDraw: viewport.debugView
++ }
++
++ Model {
++ source: "#Rectangle"
++ scale: Qt.vector3d(0.3, 0.3, 1)
++ materials: PrincipledMaterial {
++ baseColor: "green"
++ }
++ castsShadows: false
++ receivesShadows: true
++ }
++ }
++
++ StaticRigidBody {
++ id: wall
++ position: Qt.vector3d(-4, 0, 0)
++ eulerRotation: Qt.vector3d(0, 90, 0)
++
++ collisionShapes: PlaneShape {
++ id: wallShape
++ enableDebugDraw: viewport.debugView
++ }
++ Model {
++ source: "#Rectangle"
++ scale: Qt.vector3d(0.3, 0.3, 1)
++ materials: PrincipledMaterial {
++ baseColor: "gray"
++ cullMode: Material.NoCulling
++
++ }
++ opacity: 0.3
++ }
++ }
++
++ DynamicRigidBody {
++ id: dynamicTorus
++ position: Qt.vector3d(0.5, 3.5, 1.0)
++ property bool inArea: false
++
++ mass: 10 // kg
++ scale: Qt.vector3d(0.01, 0.01, 0.01).times(scaleSlider.value)
++ Model {
++ source: "meshes/newTorus.mesh"
++ materials: PrincipledMaterial {
++ baseColor: "orange"
++ }
++ }
++ collisionShapes: ConvexMeshShape {
++ id: torusShape
++ source: "meshes/newConvexTorus.mesh"
++ enableDebugDraw: viewport.debugView
++ }
++ }
++
++ DynamicRigidBody {
++ id: kinematicTorus
++ kinematicPosition: Qt.vector3d(-0.5, 2, 0.5)
++ position: Qt.vector3d(-0.5, 2, 0.5)
++ property bool inArea: false
++
++ scale: Qt.vector3d(0.015, 0.015, 0.015).times(scaleSlider.value)
++ mass: 10 // kg
++ Model {
++ source: "meshes/newTorus.mesh"
++ materials: PrincipledMaterial {
++ baseColor: "lightGray"
++ metalness: 1.0
++ roughness: 0.5
++ }
++ }
++ collisionShapes: TriangleMeshShape {
++ id: torusShape2
++ source: "meshes/newTorus.mesh"
++ enableDebugDraw: viewport.debugView
++ }
++ isKinematic: true
++ }
++
++ DynamicRigidBody {
++ id: dynamicTorusConvexHullOfNonConvexMesh
++ position: Qt.vector3d(2, 3, 1.5)
++ property bool inArea: false
++ scale: Qt.vector3d(0.01, 0.01, 0.01)
++
++ mass: 10 // kg
++ Model {
++ source: "meshes/newTorus.mesh"
++ materials: PrincipledMaterial {
++ baseColor: "#c07030"
++ metalness: 1.0
++ roughness: 0.5
++ }
++ }
++ collisionShapes: ConvexMeshShape {
++ id: torusShape3
++ source: "meshes/newTorus.mesh"
++ enableDebugDraw: viewport.debugView
++ }
++ }
++
++ // x-axis reference
++ Model {
++ materials: PrincipledMaterial {
++ baseColor: "red"
++ roughness: 1
++ }
++ source: "#Cube"
++ scale: "0.002, 0.002, 0.002"
++ position: "-0.5, 2, 4"
++ }
++
++ Model {
++ materials: PrincipledMaterial {
++ baseColor: "green"
++ roughness: 1
++ }
++ source: "#Cube"
++ scale: "0.002, 0.002, 0.002"
++ position: "2, 2, 4"
++ }
++
++ Model {
++ materials: PrincipledMaterial {
++ baseColor: "blue"
++ roughness: 1
++ }
++ source: "#Cube"
++ scale: "0.002, 0.002, 0.002"
++ position: "4.5, 2, 4"
++ }
++
++ // z-axis
++ Model {
++ materials: PrincipledMaterial {
++ baseColor: "cyan"
++ roughness: 1
++ }
++ source: "#Cube"
++ scale: "0.002, 0.002, 0.002"
++ position: "-0.5, 3, -1"
++ }
++
++ DynamicRigidBody {
++ id: hfTest
++ position: Qt.vector3d(0, 2, 1.5)
++ kinematicPosition: Qt.vector3d(0, 2, 1.5)
++ isKinematic: true
++
++ Model {
++ id: hfModel
++ geometry: HeightFieldGeometry {
++ extents: Qt.vector3d(5, 2, 5).times(scaleSlider.value)
++ source: "maps/heightfield.png"
++ smoothShading: false
++ }
++ materials: PrincipledMaterial {
++ baseColor: "#ff77ff"
++ roughness: 0.3
++ }
++ opacity: 0.6
++ }
++
++ collisionShapes: HeightFieldShape {
++ id: hfShape
++ extents: Qt.vector3d(5, 2, 5).times(scaleSlider.value)
++ source: "maps/heightfield.png"
++ enableDebugDraw: viewport.debugView
++ }
++ }
++
++ SequentialAnimation {
++ running: animateCheckBox.checked && physicsWorld.running
++ loops: -1
++ NumberAnimation {
++ target: hfTest
++ property: "kinematicPosition.x"
++ duration: 2000
++ from: 0
++ to: -2
++ easing.type: Easing.InOutQuad
++ }
++ NumberAnimation {
++ target: hfTest
++ property: "kinematicPosition.x"
++ duration: 2000
++ from: -2
++ to: 0
++ easing.type: Easing.InOutQuad
++ }
++ }
++
++ DynamicRigidBody {
++ id: triangleMeshTest
++ position: Qt.vector3d(0, 0.6, 0)
++ eulerRotation: Qt.vector3d(0, 180, 0)
++ kinematicPosition: Qt.vector3d(0, 0.6, 0)
++ kinematicEulerRotation: Qt.vector3d(0, 180, 0)
++ isKinematic: true
++ property bool inArea: false
++
++ scale: Qt.vector3d(0.015, 0.01, 0.015).times(scaleSlider.value)
++ Model {
++ id: testModel
++ source: "meshes/field.mesh"
++ materials: PrincipledMaterial {
++ baseColor: "#5070a0"
++ roughness: 0.3
++ }
++ opacity: 1
++ }
++ collisionShapes: TriangleMeshShape {
++ id: triShape
++ source: "meshes/field.mesh"
++ enableDebugDraw: viewport.debugView
++ }
++ }
++
++ SequentialAnimation {
++ running: animateCheckBox.checked && physicsWorld.running
++ loops: -1
++ NumberAnimation {
++ target: triangleMeshTest
++ property: "kinematicEulerRotation.y"
++ duration: 5000
++ from: 180
++ to: -180
++ easing.type: Easing.InOutQuad
++ }
++ NumberAnimation {
++ target: triangleMeshTest
++ property: "kinematicEulerRotation.y"
++ duration: 5000
++ from: -180
++ to: 180
++ easing.type: Easing.InOutQuad
++ }
++ }
++
++
++ // DynamicRigidBody {
++ // id: table
++ // y: -50
++
++ // isKinematic: true
++
++ // Model {
++ // id: tableModel
++ // source: "#Cube"
++ // materials: PrincipledMaterial {
++ // baseColor: "gray"
++ // }
++ // scale: Qt.vector3d(4, 1, 4)
++ // }
++ // collisionShapes: BoxShape {
++ // id: tableShape
++ // extents: Qt.vector3d(200, 50, 200)
++ // }
++ // }
++
++ Texture {
++ id: qtLogoTexture
++ source: "qt_logo_rect.png"
++ scaleV: 0.5
++ positionV: 0.2
++ }
++ Component {
++ id: doughnutComponent
++
++ Node {
++ id: thisNode
++ function randomInRange(min, max) {
++ return Math.random() * (max - min) + min;
++ }
++ eulerRotation: Qt.vector3d(randomInRange(0, 360),
++ randomInRange(0, 360),
++ randomInRange(0, 360))
++
++ position: Qt.vector3d(randomInRange(-1.5, 1.5),
++ randomInRange( 4, 30),
++ randomInRange(-1.5, 1.5))
++
++ property real sf: randomInRange(0.2, 0.45)
++ property color baseCol: Qt.hsla(randomInRange(0, 1), randomInRange(0.3, 0.7), randomInRange(0.4, 0.7), 1.0)
++ property bool grabbed: false
++ property bool isSphere: randomInRange(0, 1) < 0.5
++ property SphereShape sphereShape : SphereShape {
++ diameter: sf
++ enableDebugDraw: viewport.debugView
++ }
++ property ConvexMeshShape torusShape : ConvexMeshShape {
++ source: "meshes/newConvexTorus.mesh"
++ enableDebugDraw: viewport.debugView
++ scale: Qt.vector3d(sf, sf, sf).times(0.01)
++ }
++ DynamicRigidBody {
++ id: thisBody
++ mass: 1 //heavy donut
++ isKinematic: thisNode.grabbed
++ Model {
++ id: thisModel
++ source: isSphere ? "#Sphere" : "meshes/newTorus.mesh"
++ materials: PrincipledMaterial {
++ metalness: 1.0
++ roughness: randomInRange(0.3, 0.6)
++ baseColor: thisNode.grabbed ? "white" : baseCol
++ emissiveMap: qtLogoTexture
++ emissiveFactor: "0.2, 0.2, 0.2"
++ heightAmount: 0.5
++ }
++
++ scale: Qt.vector3d(sf, sf, sf).times(0.01)
++ pickable: true
++ property Node proxyNode: thisNode
++ }
++ collisionShapes: isSphere ? sphereShape : torusShape
++ }
++ }
++ }
++
++ Repeater3D {
++ id: rep
++ model: 400
++ delegate: doughnutComponent
++ }
++ } // scene
++
++ View3D {
++ id: viewport
++ width: radioSideBySide.checked ? parent.width/2 : parent.width
++ height: parent.height
++ visible: !radioDebug.checked
++ importScene: scene
++
++ environment: SceneEnvironment {
++ clearColor: "#d6dbdf"
++ backgroundMode: SceneEnvironment.Color
++ antialiasingMode: SceneEnvironment.MSAA
++ antialiasingQuality: SceneEnvironment.High
++ }
++
++ focus: true
++ property bool debugView: false
++ }
++
++ View3D {
++ id: viewport1
++ width: radioSideBySide.checked ? parent.width/2 : parent.width
++ height: parent.height
++ x: radioSideBySide.checked ? parent.width/2 : 0
++ visible: !radioScene.checked
++
++ environment: SceneEnvironment {
++ clearColor: "black"
++ backgroundMode: SceneEnvironment.Color
++ antialiasingMode: SceneEnvironment.MSAA
++ antialiasingQuality: SceneEnvironment.High
++ }
++
++ PerspectiveCamera {
++ id: camera2
++ position: camera1.position
++ eulerRotation: camera1.eulerRotation
++ clipFar: camera1.clipFar
++ clipNear: camera1.clipNear
++ }
++
++ }
++
++ WasdController {
++ keysEnabled: true
++ 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_G) {
++ viewport.debugView = !viewport.debugView
++ } else if (event.key === Qt.Key_H) {
++ physicsWorld.forceDebugDraw = !physicsWorld.forceDebugDraw
++ } else if (event.key === Qt.Key_T) {
++ tableShape.extents = "150, 100, 150"
++ tableModel.scale = "3, 2, 3"
++ }
++ }
++ Keys.onReleased: (event)=> { if (keysEnabled) handleKeyRelease(event) }
++ }
++
++ MouseArea {
++ anchors.fill: viewport
++ acceptedButtons: Qt.RightButton
++ property Node grabbedNode: null
++ onPressed: (mouse) =>
++ {
++ var result = viewport.pick(mouse.x, mouse.y);
++ if (result.objectHit) {
++ var pickedNode = result.objectHit.proxyNode;
++ console.log("PRESSED:", pickedNode)
++
++ pickedNode.grabbed = true
++ //pickedNode.y += 100
++ grabbedNode = pickedNode
++ }
++ }
++ onReleased:
++ {
++ if (grabbedNode) {
++ grabbedNode.grabbed = false
++ grabbedNode = null
++ }
++ }
++ }
++
++ ColumnLayout {
++ ColumnLayout {
++ RadioButton {
++ id: radioScene
++ checked: true
++ text: qsTr("Scene view")
++ }
++ RadioButton {
++ id: radioSideBySide
++ text: qsTr("Side by side")
++ }
++ RadioButton {
++ id: radioDebug
++ text: qsTr("Debug view")
++ }
++ }
++ Label {
++ text: "Scale: " + scaleSlider.value
++ }
++ Slider {
++ id: scaleSlider
++ focusPolicy: Qt.NoFocus
++ from: 0.5
++ to: 1.5
++ value: 1
++ stepSize: 0.1
++ }
++ CheckBox {
++ id: animateCheckBox
++ checked: false
++ text: qsTr("Animate")
++ }
++ }
++ }