Qt Quick 3D - Custom Geometry Example

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick3D
import QtQuick3D.Helpers
import CustomGeometryExample

ApplicationWindow {
    id: window
    width: 1280
    height: 720
    visible: true
    title: "Custom Geometry Example"

    property bool isLandscape: width > height

    View3D {
        id: v3d
        anchors.left: window.isLandscape ? controlsPane.right : parent.left
        anchors.top: window.isLandscape ? parent.top : controlsPane.bottom
        anchors.right: parent.right
        anchors.bottom: parent.bottom

        camera: camera

        environment: SceneEnvironment {
            id: env
            backgroundMode: SceneEnvironment.Color
            clearColor: "#002b36"
        }

        Node {
            id: originNode
            PerspectiveCamera {
                id: cameraNode
                z: 600
            }
        }

        DirectionalLight {
            id: directionalLight
            color: Qt.rgba(0.4, 0.2, 0.6, 1.0)
            ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
        }

        PointLight {
            id: pointLight
            position: Qt.vector3d(0, 0, 100)
            color: Qt.rgba(0.1, 1.0, 0.1, 1.0)
            ambientColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
        }

        Model {
            id: gridModel
            visible: false
            scale: Qt.vector3d(100, 100, 100)
            geometry: GridGeometry {
                id: grid
                horizontalLines: 20
                verticalLines: 20
            }
            materials: [
                DefaultMaterial {
                    lineWidth: sliderLineWidth.value
                }
            ]
        }

        Model {
            id: triangleModel
            visible: false
            scale: Qt.vector3d(100, 100, 100)
            geometry: ExampleTriangleGeometry {
                normals: cbNorm.checked
                normalXY: sliderNorm.value
                uv: cbUV.checked
                uvAdjust: sliderUV.value
            }
            materials: [
                DefaultMaterial {
                    Texture {
                        id: baseColorMap
                        source: "qt_logo_rect.png"
                    }
                    cullMode: DefaultMaterial.NoCulling
                    diffuseMap: cbTexture.checked ? baseColorMap : null
                    specularAmount: 0.5
                }
            ]
        }

        Model {
            id: pointModel
            visible: false
            scale: Qt.vector3d(100, 100, 100)
            geometry: ExamplePointGeometry { }
            materials: [
                DefaultMaterial {
                    lighting: DefaultMaterial.NoLighting
                    cullMode: DefaultMaterial.NoCulling
                    diffuseColor: "yellow"
                    pointSize: sliderPointSize.value
                }
            ]
        }

        Model {
            id: torusModel
            visible: false
            geometry: TorusMesh {
                radius: radiusSlider.value
                tubeRadius: tubeRadiusSlider.value
                segments: segmentsSlider.value
                rings: ringsSlider.value
            }
            materials: [
                PrincipledMaterial {
                    id: torusMaterial
                    baseColor: "#dc322f"
                    metalness: 0.0
                    roughness: 0.1
                }
            ]
        }

        OrbitCameraController {
            origin: originNode
            camera: cameraNode
        }
    }

    Pane {
        id: controlsPane
        width: window.isLandscape ? implicitWidth : window.width
        height: window.isLandscape ? window.height : implicitHeight
        ColumnLayout {
            GroupBox {
                title: "Mode"
                ButtonGroup {
                    id: modeGroup
                    buttons: [ radioGridGeom, radioCustGeom, radioPointGeom, radioQMLGeom ]
                }
                ColumnLayout {
                    RadioButton {
                        id: radioGridGeom
                        text: "GridGeometry"
                        checked: true
                    }
                    RadioButton {
                        id: radioCustGeom
                        text: "Custom geometry from application (triangle)"
                        checked: false
                    }
                    RadioButton {
                        id: radioPointGeom
                        text: "Custom geometry from application (points)"
                        checked: false
                    }
                    RadioButton {
                        id: radioQMLGeom
                        text: "Custom geometry from QML"
                        checked: false
                    }
                }
            }

            Pane {
                id: gridSettings
                visible: false
                ColumnLayout {
                    Button {
                        text: "+ Y Cells"
                        onClicked: grid.horizontalLines += 1
                        Layout.alignment: Qt.AlignHCenter

                    }
                    RowLayout {
                        Layout.alignment: Qt.AlignHCenter
                        Button  {
                            text: "- X Cells"
                            onClicked: grid.verticalLines -= 1
                        }
                        Button {
                            text: "+ X Cells"
                            onClicked: grid.verticalLines += 1
                        }
                    }
                    Button  {
                        text: "- Y Cells"
                        onClicked: grid.horizontalLines -= 1
                        Layout.alignment: Qt.AlignHCenter
                    }

                    Label {
                        text: "Line width (if supported)"
                    }
                    Slider {
                        Layout.fillWidth: true
                        id: sliderLineWidth
                        from: 1.0
                        to: 10.0
                        stepSize: 0.5
                        value: 1.0
                    }
                }
            }
            Pane {
                id: triangleSettings
                visible: false
                ColumnLayout {
                    CheckBox {
                        id: cbNorm
                        text: "provide normals in geometry"
                        checked: false
                    }
                    RowLayout {
                        enabled: cbNorm.checked
                        Label {
                            Layout.fillWidth: true
                            text: "Normal adjust: "
                        }
                        Slider {
                            id: sliderNorm

                            from: 0.0
                            to: 1.0
                            stepSize: 0.01
                            value: 0.0
                        }
                    }
                    CheckBox {
                        id: cbTexture
                        text: "enable base color map"
                        checked: false
                    }
                    CheckBox {
                        id: cbUV
                        text: "provide UV in geometry"
                        checked: false
                    }
                    RowLayout {
                        enabled: cbUV.checked
                        Label {
                            Layout.fillWidth: true
                            text: "UV adjust:"
                        }
                        Slider {
                            id: sliderUV
                            from: 0.0
                            to: 1.0
                            stepSize: 0.01
                            value: 0.0
                        }
                    }
                }

            }
            Pane {
                id: pointSettings
                visible: false
                RowLayout {
                    ColumnLayout {
                        RowLayout {
                            Label {
                                text: "Point size (if supported)"
                            }
                            Slider {
                                id: sliderPointSize
                                from: 1.0
                                to: 16.0
                                stepSize: 1.0
                                value: 1.0
                            }
                        }
                    }
                }
            }
            Pane {
                id: torusSettings
                visible: false
                ColumnLayout {
                    Label {
                        text: "Radius: (" + radiusSlider.value + ")"
                    }
                    Slider {
                        id: radiusSlider
                        from: 1.0
                        to: 1000.0
                        stepSize: 1.0
                        value: 200
                    }
                    Label {
                        text: "Tube Radius: (" + tubeRadiusSlider.value + ")"
                    }
                    Slider {
                        id: tubeRadiusSlider
                        from: 1.0
                        to: 500.0
                        stepSize: 1.0
                        value: 50
                    }
                    Label {
                        text: "Rings: (" + ringsSlider.value + ")"
                    }
                    Slider {
                        id: ringsSlider
                        from: 3
                        to: 35
                        stepSize: 1.0
                        value: 20
                    }
                    Label {
                        text: "Segments: (" + segmentsSlider.value + ")"
                    }
                    Slider {
                        id: segmentsSlider
                        from: 3
                        to: 35
                        stepSize: 1.0
                        value: 20
                    }
                    CheckBox {
                        id: wireFrameCheckbox
                        text: "Wireframe Mode"
                        checked: false
                        onCheckedChanged: {
                            env.debugSettings.wireframeEnabled = checked
                            torusMaterial.cullMode = checked ? Material.NoCulling : Material.BackFaceCulling

                        }
                    }
                }

            }
        }
        states: [
            State {
                name: "gridMode"
                when: radioGridGeom.checked
                PropertyChanges {
                    gridModel.visible: true
                    gridSettings.visible: true
                    env.debugSettings.wireframeEnabled: false
                    originNode.position: Qt.vector3d(0, 0, 0)
                    originNode.rotation: Qt.quaternion(1, 0, 0, 0)
                    cameraNode.z: 600

                }
            },
            State {
                name: "triangleMode"
                when: radioCustGeom.checked
                PropertyChanges {
                    triangleModel.visible: true
                    triangleSettings.visible: true
                    env.debugSettings.wireframeEnabled: false
                    originNode.position: Qt.vector3d(0, 0, 0)
                    originNode.rotation: Qt.quaternion(1, 0, 0, 0)
                    cameraNode.z: 600
                }
            },
            State {
                name: "pointMode"
                when: radioPointGeom.checked
                PropertyChanges {
                    pointModel.visible: true
                    pointSettings.visible: true
                    env.debugSettings.wireframeEnabled: false
                    originNode.position: Qt.vector3d(0, 0, 0)
                    originNode.rotation: Qt.quaternion(1, 0, 0, 0)
                    cameraNode.z: 600
                }
            },
            State {
                name: "qmlMode"
                when: radioQMLGeom.checked
                PropertyChanges {
                    torusModel.visible: true
                    torusSettings.visible: true
                    directionalLight.eulerRotation: Qt.vector3d(-40, 0, 0)
                    directionalLight.color: "white"
                    pointLight.color: "white"
                    pointLight.position: Qt.vector3d(0, 0, 0)
                    originNode.position: Qt.vector3d(0, 0, 0)
                    originNode.eulerRotation: Qt.vector3d(-40, 0, 0)
                    cameraNode.z: 600
                }
            }
        ]
    }
}