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
}
}
|