File: FreeformTeleporter.qml

package info (click to toggle)
qt6-quick3d 6.8.2-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 140,860 kB
  • sloc: cpp: 380,464; ansic: 36,078; xml: 252; sh: 241; makefile: 29
file content (124 lines) | stat: -rw-r--r-- 4,119 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick3D

Node {
    id: teleporter
    required property var rayPicker //any object that has implemented rayPick(pos, dir)
    required property Node cameraOrigin
    required property Node camera
    required property Node beamHandle
    property real cameraSnapRotation: 30
    property real xStickValue: 0
    property real yStickValue: 0
    property alias screenVisibility: screenValueFader.value
    property bool targetValid: false
    property color rayHitColor: "green"
    property color rayMissColor: "red"
    property bool teleporting: false
    property int blinkSpeed: 150

    function teleportTo(position) {
        teleporter.teleporting = true
        let offset = camera.scenePosition.minus(cameraOrigin.scenePosition)
        let cameraOriginPosition = position.minus(offset)
        cameraOriginPosition.y = position.y

        screenValueFader.blink(()=>{
                                   teleporter.doTeleportation(cameraOriginPosition)
                               },()=>{teleporter.teleporting = false}, teleporter.blinkSpeed)
    }

    function rotateBy(degrees) {
        let r = Quaternion.fromEulerAngles(0, degrees, 0)
        let origin = Qt.vector3d(camera.position.x, 0, camera.position.z)
        let mappedOrigin = cameraOrigin.rotation.times(origin).plus(cameraOrigin.position)
        let rotatedOrigin = r.times(origin)
        let mappedRO = cameraOrigin.rotation.times(rotatedOrigin).plus(cameraOrigin.position)
        let delta = mappedRO.minus(mappedOrigin)

        doRotation(cameraOrigin.rotation.times(r), cameraOrigin.position.minus(delta))
    }

    signal doTeleportation(var cameraOriginPosition)

    signal doRotation(var cameraOriginRotation, var cameraOriginPosition)

    readonly property bool xPlusRotation: xStickValue > 0.5
    onXPlusRotationChanged: {
        if (xPlusRotation)
            rotateBy(-cameraSnapRotation)
    }

    readonly property bool xMinusRotation: xStickValue < -0.5
    onXMinusRotationChanged: {
        if (xMinusRotation)
            rotateBy(cameraSnapRotation)
    }

    ValueFader {
        id: screenValueFader
    }

    TargetIndicator {
        id: targetIndicator
    }

    BeamModel {
        id: beamModel
        color: teleporter.targetValid ? teleporter.rayHitColor : teleporter.rayMissColor
    }

    FrameAnimation {
        running: teleporter.yStickValue > 0.7
        onTriggered: {
            teleporter.updateTarget()
        }
        onRunningChanged: {
            if (running) {
                beamModel.show()
            }else {
                beamModel.hide()
                targetIndicator.hide()
                if (teleporter.targetValid)
                    teleporter.teleportTo(targetIndicator.scenePosition)
            }
        }
    }

    function updateTarget() : bool {
        // Not a pure gravity parabola: We want a flatter curve

        let beamPositions = [];
        let pos = beamHandle.scenePosition
        const dx = beamHandle.forward.x
        const dz = beamHandle.forward.z
        const a = Qt.vector3d(dx * 2, -4, dz * 2)
        let d = beamHandle.forward.times(50)
        let index = 0
        let hit = false
        let pickResult = null
        beamPositions.push(Qt.vector3d(pos.x, pos.y, pos.z))
        for (let i = 0; !hit && i < 50; ++i) {
            pickResult = teleporter.rayPicker.rayPick(pos, d)
            pos = pos.plus(d)
            d = d.plus(a)
            hit = pickResult.objectHit && pickResult.distance < d.length()
            beamPositions.push(Qt.vector3d(pos.x, pos.y, pos.z))
        }
        beamModel.generate(beamPositions)

        if (pickResult.objectHit && pickResult.sceneNormal.y > 0.9) {
            teleporter.targetValid = true
            targetIndicator.moveTo(pickResult.scenePosition)
            targetIndicator.show()
        } else {
            teleporter.targetValid = false
            targetIndicator.hide()
        }

        return teleporter.targetValid
    }
}