Compare commits
6 commits
e3b6c3f501
...
c83135a67b
| Author | SHA1 | Date | |
|---|---|---|---|
| c83135a67b | |||
|
|
4ddccdaf62 | ||
| 9ebb025f8a | |||
| b868c2bdbb | |||
| 046388a3eb | |||
| 8787bdd8f6 |
33 changed files with 564 additions and 123 deletions
4
.gitattributes
vendored
4
.gitattributes
vendored
|
|
@ -1,2 +1,6 @@
|
||||||
# Normalize EOL for all files that Git considers text files.
|
# Normalize EOL for all files that Git considers text files.
|
||||||
* text=auto eol=lf
|
* text=auto eol=lf
|
||||||
|
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.fbx filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.webp filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
|
||||||
1
.godot-version
Normal file
1
.godot-version
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
4.4-beta3
|
||||||
|
|
@ -12,7 +12,7 @@ git clone --recurse-submodules <address of this repository>
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
```
|
```
|
||||||
|
|
||||||
Using Godot v4.4.beta1.official [d33da79d3](https://github.com/godotengine/godot/commit/d33da79d3f8fe84be2521d25b9ba8e440cf25a88).
|
Using Godot v4.4 beta3 (expect it to change often until 4.4 is released)
|
||||||
Install SCons with `pipx install scons`.
|
Install SCons with `pipx install scons`.
|
||||||
You will need a C++ compiler, you might have one already on Linux, see below for Windows, otherwise in [Godot documentation](https://docs.godotengine.org/en/stable/contributing/development/compiling/index.html).
|
You will need a C++ compiler, you might have one already on Linux, see below for Windows, otherwise in [Godot documentation](https://docs.godotengine.org/en/stable/contributing/development/compiling/index.html).
|
||||||
|
|
||||||
|
|
@ -29,9 +29,9 @@ Some files are generated, run the following commands on first setup and when dep
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
godot --dump-extension-api # after updating Godot
|
godot --dump-extension-api # after updating Godot
|
||||||
scons compile_commands # after modifying SConstruct
|
|
||||||
python update_mavlink.py # after updating MAVLink dialect
|
python update_mavlink.py # after updating MAVLink dialect
|
||||||
python update_addons.py # after changing any addon submodules
|
python update_addons.py # after changing any addon submodules
|
||||||
|
scons compile_commands # after modifying SConstruct
|
||||||
```
|
```
|
||||||
|
|
||||||
### Windows setup
|
### Windows setup
|
||||||
|
|
|
||||||
6
project/CREDITS.txt
Normal file
6
project/CREDITS.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Programming:
|
||||||
|
Marek S. Łukasiewicz
|
||||||
|
|
||||||
|
3D models:
|
||||||
|
Igor Samek
|
||||||
|
Marek S. Łukasiewicz
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://46i64f2dmonl
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://c086s0jrgaiwi
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
@export var connector: MarshConnector
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
|
||||||
transform = connector.get_aircraft()
|
|
||||||
19
project/aircraft/aircraft.gd
Normal file
19
project/aircraft/aircraft.gd
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
@onready var connector = $MarshConnector
|
||||||
|
@onready var skeleton = $"Mi-2/Armature/Skeleton3D"
|
||||||
|
@onready var attitude_root = $AttitudeRoot
|
||||||
|
|
||||||
|
@onready var bone_cg: int = skeleton.find_bone("BodyCG")
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
var target: Transform3D = connector.get_aircraft()
|
||||||
|
position = target.origin
|
||||||
|
|
||||||
|
# Add the rotation to the bone
|
||||||
|
var rest = skeleton.get_bone_rest(bone_cg).basis.get_rotation_quaternion()
|
||||||
|
var attitude = target.basis.get_rotation_quaternion()
|
||||||
|
skeleton.set_bone_pose_rotation(bone_cg, attitude * rest)
|
||||||
|
|
||||||
|
# Rotate other children (not using BoneAttachment3D due to jitter)
|
||||||
|
attitude_root.rotation = target.basis.get_euler()
|
||||||
26
project/aircraft/aircraft.tscn
Normal file
26
project/aircraft/aircraft.tscn
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
[gd_scene load_steps=4 format=3 uid="uid://bj1s0g7ixjw71"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cx30pr7kn4c74" path="res://aircraft/aircraft.gd" id="1_l4uib"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cux4tju0ovvly" path="res://assets/mi2/Mi-2.glb" id="1_mrxe8"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mrxe8"]
|
||||||
|
|
||||||
|
[node name="Aircraft" type="Node3D"]
|
||||||
|
script = ExtResource("1_l4uib")
|
||||||
|
|
||||||
|
[node name="Mi-2" parent="." instance=ExtResource("1_mrxe8")]
|
||||||
|
|
||||||
|
[node name="Łopata" parent="Mi-2/Armature/Skeleton3D" index="16"]
|
||||||
|
material_override = SubResource("StandardMaterial3D_mrxe8")
|
||||||
|
|
||||||
|
[node name="AttitudeRoot" type="Node3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.4, 0)
|
||||||
|
|
||||||
|
[node name="XROrigin3D" type="XROrigin3D" parent="AttitudeRoot"]
|
||||||
|
transform = Transform3D(-1, 0, -8.74227e-08, 0, 0.999999, 0, 8.74228e-08, 0, -0.999999, 0.214, -0.721897, 1.53478)
|
||||||
|
|
||||||
|
[node name="XRCamera3D" type="XRCamera3D" parent="AttitudeRoot/XROrigin3D"]
|
||||||
|
|
||||||
|
[node name="MarshConnector" type="MarshConnector" parent="."]
|
||||||
|
|
||||||
|
[editable path="Mi-2"]
|
||||||
BIN
project/assets/cone/Cone80cm.fbx
(Stored with Git LFS)
Normal file
BIN
project/assets/cone/Cone80cm.fbx
(Stored with Git LFS)
Normal file
Binary file not shown.
38
project/assets/cone/Cone80cm.fbx.import
Normal file
38
project/assets/cone/Cone80cm.fbx.import
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://b6gpwum12u52w"
|
||||||
|
path="res://.godot/imported/Cone80cm.fbx-d949dbbc55b52604489414c5f9df328e.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/cone/Cone80cm.fbx"
|
||||||
|
dest_files=["res://.godot/imported/Cone80cm.fbx-d949dbbc55b52604489414c5f9df328e.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
nodes/import_as_skeleton_bones=false
|
||||||
|
nodes/use_node_type_suffixes=true
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=1
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=true
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
animation/import_rest_as_RESET=false
|
||||||
|
import_script/path=""
|
||||||
|
_subresources={}
|
||||||
|
fbx/importer=0
|
||||||
|
fbx/allow_geometry_helper_nodes=false
|
||||||
|
fbx/embedded_image_handling=1
|
||||||
BIN
project/assets/cone/Cone_DefaultMaterial_BaseColor.webp
(Stored with Git LFS)
Normal file
BIN
project/assets/cone/Cone_DefaultMaterial_BaseColor.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
|
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://8d1ovbs1uls5"
|
||||||
|
path="res://.godot/imported/Cone_DefaultMaterial_BaseColor.webp-bad00f3f7b1863582144eaca57549c7d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/cone/Cone_DefaultMaterial_BaseColor.webp"
|
||||||
|
dest_files=["res://.godot/imported/Cone_DefaultMaterial_BaseColor.webp-bad00f3f7b1863582144eaca57549c7d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
project/assets/cone/Cone_DefaultMaterial_Normal.webp
(Stored with Git LFS)
Normal file
BIN
project/assets/cone/Cone_DefaultMaterial_Normal.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
34
project/assets/cone/Cone_DefaultMaterial_Normal.webp.import
Normal file
34
project/assets/cone/Cone_DefaultMaterial_Normal.webp.import
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cq5sci0t8q0hy"
|
||||||
|
path="res://.godot/imported/Cone_DefaultMaterial_Normal.webp-6a67b945c81f799997551ce26f4f31c8.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/cone/Cone_DefaultMaterial_Normal.webp"
|
||||||
|
dest_files=["res://.godot/imported/Cone_DefaultMaterial_Normal.webp-6a67b945c81f799997551ce26f4f31c8.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp
(Stored with Git LFS)
Normal file
BIN
project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
|
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://0x0t6mks64wj"
|
||||||
|
path="res://.godot/imported/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp-1a5b8b1d7a0eb8a9d7d6e168dd7479db.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp"
|
||||||
|
dest_files=["res://.godot/imported/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp-1a5b8b1d7a0eb8a9d7d6e168dd7479db.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
9
project/assets/cone/cone.tscn
Normal file
9
project/assets/cone/cone.tscn
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[gd_scene load_steps=3 format=3 uid="uid://7tddcx4u26w4"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b6gpwum12u52w" path="res://assets/cone/Cone80cm.fbx" id="1_caoip"]
|
||||||
|
[ext_resource type="Material" uid="uid://8my138hapqp8" path="res://assets/cone/cone_material.tres" id="2_pk7h2"]
|
||||||
|
|
||||||
|
[node name="Cone80cm" instance=ExtResource("1_caoip")]
|
||||||
|
|
||||||
|
[node name="Cylinder" parent="." index="0"]
|
||||||
|
surface_material_override/0 = ExtResource("2_pk7h2")
|
||||||
28
project/assets/cone/cone_material.tres
Normal file
28
project/assets/cone/cone_material.tres
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
[gd_resource type="StandardMaterial3D" load_steps=6 format=3 uid="uid://8my138hapqp8"]
|
||||||
|
|
||||||
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_2wwbo"]
|
||||||
|
load_path = "res://.godot/imported/Cone_DefaultMaterial_BaseColor.webp-bad00f3f7b1863582144eaca57549c7d.ctex"
|
||||||
|
|
||||||
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_86lm8"]
|
||||||
|
load_path = "res://.godot/imported/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp-1a5b8b1d7a0eb8a9d7d6e168dd7479db.ctex"
|
||||||
|
|
||||||
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_c2qq7"]
|
||||||
|
load_path = "res://.godot/imported/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp-1a5b8b1d7a0eb8a9d7d6e168dd7479db.ctex"
|
||||||
|
|
||||||
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_f28vr"]
|
||||||
|
load_path = "res://.godot/imported/Cone_DefaultMaterial_Normal.webp-6a67b945c81f799997551ce26f4f31c8.ctex"
|
||||||
|
|
||||||
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_tpcsw"]
|
||||||
|
load_path = "res://.godot/imported/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp-1a5b8b1d7a0eb8a9d7d6e168dd7479db.ctex"
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
albedo_texture = SubResource("CompressedTexture2D_2wwbo")
|
||||||
|
metallic = 1.0
|
||||||
|
metallic_texture = SubResource("CompressedTexture2D_c2qq7")
|
||||||
|
metallic_texture_channel = 2
|
||||||
|
roughness_texture = SubResource("CompressedTexture2D_tpcsw")
|
||||||
|
roughness_texture_channel = 1
|
||||||
|
normal_enabled = true
|
||||||
|
normal_texture = SubResource("CompressedTexture2D_f28vr")
|
||||||
|
ao_enabled = true
|
||||||
|
ao_texture = SubResource("CompressedTexture2D_86lm8")
|
||||||
BIN
project/assets/mi2/Mi-2.glb
(Stored with Git LFS)
Normal file
BIN
project/assets/mi2/Mi-2.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
37
project/assets/mi2/Mi-2.glb.import
Normal file
37
project/assets/mi2/Mi-2.glb.import
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://cux4tju0ovvly"
|
||||||
|
path="res://.godot/imported/Mi-2.glb-12197697bde557481ecf457851b9e618.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/mi2/Mi-2.glb"
|
||||||
|
dest_files=["res://.godot/imported/Mi-2.glb-12197697bde557481ecf457851b9e618.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
nodes/import_as_skeleton_bones=false
|
||||||
|
nodes/use_node_type_suffixes=true
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=1
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=false
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
animation/import_rest_as_RESET=false
|
||||||
|
import_script/path=""
|
||||||
|
_subresources={}
|
||||||
|
gltf/naming_version=1
|
||||||
|
gltf/embedded_image_handling=1
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 130 B |
|
|
@ -1,42 +0,0 @@
|
||||||
[gd_scene load_steps=8 format=3 uid="uid://cx82op4eecbwr"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cx30pr7kn4c74" path="res://aircraft.gd" id="1_hips3"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://qbmx03iibuuu" path="res://addons/godot-xr-tools/staging/scene_base.tscn" id="1_k0lu6"]
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_2w36v"]
|
|
||||||
|
|
||||||
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_4tcqf"]
|
|
||||||
|
|
||||||
[sub_resource type="Sky" id="Sky_0xm2m"]
|
|
||||||
sky_material = SubResource("ProceduralSkyMaterial_4tcqf")
|
|
||||||
|
|
||||||
[sub_resource type="Environment" id="Environment_jdgxj"]
|
|
||||||
background_mode = 2
|
|
||||||
sky = SubResource("Sky_0xm2m")
|
|
||||||
|
|
||||||
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_fpvso"]
|
|
||||||
|
|
||||||
[node name="SceneBase" instance=ExtResource("1_k0lu6")]
|
|
||||||
|
|
||||||
[node name="XROrigin3D" parent="." index="0"]
|
|
||||||
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, -3)
|
|
||||||
|
|
||||||
[node name="MarshConnector" type="MarshConnector" parent="." index="1"]
|
|
||||||
process_mode = 0
|
|
||||||
|
|
||||||
[node name="Aircraft" type="Node3D" parent="." index="2" node_paths=PackedStringArray("connector")]
|
|
||||||
script = ExtResource("1_hips3")
|
|
||||||
connector = NodePath("../MarshConnector")
|
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Aircraft" index="0"]
|
|
||||||
mesh = SubResource("BoxMesh_2w36v")
|
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="." index="3"]
|
|
||||||
transform = Transform3D(-1, -2.26267e-08, 8.44439e-08, 0, 0.965926, 0.258819, -8.74228e-08, 0.258819, -0.965926, 0, 1, -3)
|
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." index="4"]
|
|
||||||
transform = Transform3D(-0.707107, -0.683013, 0.183013, 0, 0.258819, 0.965926, -0.707107, 0.683013, -0.183013, 3, 3, 3)
|
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." index="5"]
|
|
||||||
environment = SubResource("Environment_jdgxj")
|
|
||||||
camera_attributes = SubResource("CameraAttributesPractical_fpvso")
|
|
||||||
|
|
@ -1,38 +1,27 @@
|
||||||
[gd_scene load_steps=7 format=3 uid="uid://cjrkxv8ix1h8s"]
|
[gd_scene load_steps=6 format=3 uid="uid://crq3o0eu4y8ya"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cx30pr7kn4c74" path="res://aircraft.gd" id="1_ig7tw"]
|
[ext_resource type="Script" path="res://start_vr.gd" id="1_ig7tw"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bj1s0g7ixjw71" path="res://aircraft/aircraft.tscn" id="2_0xm2m"]
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_0xm2m"]
|
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_7dm0k"]
|
||||||
|
|
||||||
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_ig7tw"]
|
[sub_resource type="Sky" id="Sky_ig7tw"]
|
||||||
|
sky_material = SubResource("ProceduralSkyMaterial_7dm0k")
|
||||||
|
|
||||||
[sub_resource type="Sky" id="Sky_0xm2m"]
|
[sub_resource type="Environment" id="Environment_0xm2m"]
|
||||||
sky_material = SubResource("ProceduralSkyMaterial_ig7tw")
|
|
||||||
|
|
||||||
[sub_resource type="Environment" id="Environment_ig7tw"]
|
|
||||||
background_mode = 2
|
background_mode = 2
|
||||||
sky = SubResource("Sky_0xm2m")
|
sky = SubResource("Sky_ig7tw")
|
||||||
|
|
||||||
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_0xm2m"]
|
|
||||||
|
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D"]
|
||||||
|
|
||||||
[node name="MarshConnector" type="MarshConnector" parent="."]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
process_mode = 0
|
environment = SubResource("Environment_0xm2m")
|
||||||
|
|
||||||
[node name="Aircraft" type="Node3D" parent="." node_paths=PackedStringArray("connector")]
|
|
||||||
script = ExtResource("1_ig7tw")
|
|
||||||
connector = NodePath("../MarshConnector")
|
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Aircraft"]
|
|
||||||
mesh = SubResource("BoxMesh_0xm2m")
|
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="."]
|
|
||||||
transform = Transform3D(-1, -2.26267e-08, 8.44439e-08, 0, 0.965926, 0.258819, -8.74228e-08, 0.258819, -0.965926, 0, 1, -3)
|
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
transform = Transform3D(-0.707107, -0.683013, 0.183013, 0, 0.258819, 0.965926, -0.707107, 0.683013, -0.183013, 3, 3, 3)
|
transform = Transform3D(0.707107, 0.683013, -0.183013, 0, 0.258819, 0.965926, 0.707107, -0.683013, 0.183013, -3, 3, -3)
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
[node name="StartVR" type="Node3D" parent="."]
|
||||||
environment = SubResource("Environment_ig7tw")
|
script = ExtResource("1_ig7tw")
|
||||||
camera_attributes = SubResource("CameraAttributesPractical_0xm2m")
|
maximum_refresh_rate = 144
|
||||||
|
|
||||||
|
[node name="Aircraft" parent="." instance=ExtResource("2_0xm2m")]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_resource type="OpenXRActionMap" load_steps=417 format=3 uid="uid://c52lwapbt6wt2"]
|
[gd_resource type="OpenXRActionMap" load_steps=417 format=3 uid="uid://5a62ftnefy7o"]
|
||||||
|
|
||||||
[sub_resource type="OpenXRAction" id="OpenXRAction_6ivru"]
|
[sub_resource type="OpenXRAction" id="OpenXRAction_6ivru"]
|
||||||
resource_name = "trigger"
|
resource_name = "trigger"
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,13 @@ config_version=5
|
||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="Visualisation for MARSH"
|
config/name="Visualisation for MARSH"
|
||||||
run/main_scene="uid://ba764fx7hx8ei"
|
run/main_scene="uid://crq3o0eu4y8ya"
|
||||||
config/features=PackedStringArray("4.4", "GL Compatibility")
|
config/features=PackedStringArray("4.4", "GL Compatibility")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
[autoload]
|
|
||||||
|
|
||||||
XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd"
|
|
||||||
XRToolsRumbleManager="*res://addons/godot-xr-tools/rumble/rumble_manager.gd"
|
|
||||||
|
|
||||||
[editor_plugins]
|
[editor_plugins]
|
||||||
|
|
||||||
enabled=PackedStringArray("res://addons/godot-xr-tools/plugin.cfg")
|
enabled=PackedStringArray()
|
||||||
|
|
||||||
[physics]
|
[physics]
|
||||||
|
|
||||||
|
|
@ -36,4 +31,6 @@ renderer/rendering_method.mobile="gl_compatibility"
|
||||||
[xr]
|
[xr]
|
||||||
|
|
||||||
openxr/enabled=true
|
openxr/enabled=true
|
||||||
|
openxr/reference_space=0
|
||||||
|
openxr/foveation_level=3
|
||||||
shaders/enabled=true
|
shaders/enabled=true
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
[gd_scene load_steps=3 format=3 uid="uid://ba764fx7hx8ei"]
|
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bnqnnnet4dw12" path="res://addons/godot-xr-tools/staging/staging.tscn" id="1_hips3"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://ftrrxm7sxndi" path="res://assets/splash/splash.png" id="2_k0lu6"]
|
|
||||||
|
|
||||||
[node name="Staging" instance=ExtResource("1_hips3")]
|
|
||||||
main_scene = "uid://cx82op4eecbwr"
|
|
||||||
prompt_for_continue = false
|
|
||||||
|
|
||||||
[node name="LoadingScreen" parent="." index="2"]
|
|
||||||
splash_screen = ExtResource("2_k0lu6")
|
|
||||||
112
project/start_vr.gd
Normal file
112
project/start_vr.gd
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
signal focus_lost
|
||||||
|
signal focus_gained
|
||||||
|
signal pose_recentered
|
||||||
|
|
||||||
|
@export var maximum_refresh_rate : int = 90
|
||||||
|
|
||||||
|
var xr_interface : OpenXRInterface
|
||||||
|
var xr_is_focused := false
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
xr_interface = XRServer.find_interface("OpenXR")
|
||||||
|
if xr_interface and xr_interface.is_initialized():
|
||||||
|
print("OpenXR instantiated successfully.")
|
||||||
|
var vp : Viewport = get_viewport()
|
||||||
|
|
||||||
|
# Enable XR on our viewport.
|
||||||
|
vp.use_xr = true
|
||||||
|
|
||||||
|
# Make sure V-Sync is off, as V-Sync is handled by OpenXR.
|
||||||
|
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
|
||||||
|
|
||||||
|
# Enable variable rate shading.
|
||||||
|
if RenderingServer.get_rendering_device():
|
||||||
|
vp.vrs_mode = Viewport.VRS_XR
|
||||||
|
elif int(ProjectSettings.get_setting("xr/openxr/foveation_level")) == 0:
|
||||||
|
push_warning("OpenXR: Recommend setting Foveation level to High in Project Settings")
|
||||||
|
|
||||||
|
# Connect the OpenXR events.
|
||||||
|
xr_interface.session_begun.connect(_on_openxr_session_begun)
|
||||||
|
xr_interface.session_visible.connect(_on_openxr_visible_state)
|
||||||
|
xr_interface.session_focussed.connect(_on_openxr_focused_state)
|
||||||
|
xr_interface.session_stopping.connect(_on_openxr_stopping)
|
||||||
|
xr_interface.pose_recentered.connect(_on_openxr_pose_recentered)
|
||||||
|
else:
|
||||||
|
# We couldn't start OpenXR.
|
||||||
|
print("OpenXR not instantiated!")
|
||||||
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
|
# Handle OpenXR session ready.
|
||||||
|
func _on_openxr_session_begun() -> void:
|
||||||
|
# Get the reported refresh rate.
|
||||||
|
var current_refresh_rate := xr_interface.get_display_refresh_rate()
|
||||||
|
if current_refresh_rate > 0:
|
||||||
|
print("OpenXR: Refresh rate reported as ", str(current_refresh_rate))
|
||||||
|
else:
|
||||||
|
print("OpenXR: No refresh rate given by XR runtime")
|
||||||
|
|
||||||
|
# See if we have a better refresh rate available.
|
||||||
|
var new_rate := current_refresh_rate
|
||||||
|
var available_rates: Array = xr_interface.get_available_display_refresh_rates()
|
||||||
|
if available_rates.is_empty():
|
||||||
|
print("OpenXR: Target does not support refresh rate extension")
|
||||||
|
elif available_rates.size() == 1:
|
||||||
|
# Only one available, so use it.
|
||||||
|
new_rate = available_rates[0]
|
||||||
|
else:
|
||||||
|
for rate in available_rates:
|
||||||
|
if rate > new_rate and rate <= maximum_refresh_rate:
|
||||||
|
new_rate = rate
|
||||||
|
|
||||||
|
# Did we find a better rate?
|
||||||
|
if current_refresh_rate != new_rate:
|
||||||
|
print("OpenXR: Setting refresh rate to ", str(new_rate))
|
||||||
|
xr_interface.set_display_refresh_rate(new_rate)
|
||||||
|
current_refresh_rate = new_rate
|
||||||
|
|
||||||
|
# Now match our physics rate. This is currently needed to avoid jittering,
|
||||||
|
# due to physics interpolation not being used.
|
||||||
|
Engine.physics_ticks_per_second = roundi(current_refresh_rate)
|
||||||
|
|
||||||
|
|
||||||
|
# Handle OpenXR visible state.
|
||||||
|
func _on_openxr_visible_state() -> void:
|
||||||
|
# We always pass this state at startup,
|
||||||
|
# but the second time we get this, it means our player took off their headset.
|
||||||
|
if xr_is_focused:
|
||||||
|
print("OpenXR lost focus")
|
||||||
|
|
||||||
|
xr_is_focused = false
|
||||||
|
|
||||||
|
# Pause our game.
|
||||||
|
process_mode = Node.PROCESS_MODE_DISABLED
|
||||||
|
|
||||||
|
focus_lost.emit()
|
||||||
|
|
||||||
|
|
||||||
|
# Handle OpenXR focused state
|
||||||
|
func _on_openxr_focused_state() -> void:
|
||||||
|
print("OpenXR gained focus")
|
||||||
|
xr_is_focused = true
|
||||||
|
|
||||||
|
# Unpause our game.
|
||||||
|
process_mode = Node.PROCESS_MODE_INHERIT
|
||||||
|
|
||||||
|
focus_gained.emit()
|
||||||
|
|
||||||
|
|
||||||
|
# Handle OpenXR stopping state.
|
||||||
|
func _on_openxr_stopping() -> void:
|
||||||
|
# Our session is being stopped.
|
||||||
|
print("OpenXR is stopping")
|
||||||
|
|
||||||
|
|
||||||
|
# Handle OpenXR pose recentered signal.
|
||||||
|
func _on_openxr_pose_recentered() -> void:
|
||||||
|
# User recentered view, we have to react to this by recentering the view.
|
||||||
|
# This is game implementation dependent.
|
||||||
|
pose_recentered.emit()
|
||||||
1
project/start_vr.gd.uid
Normal file
1
project/start_vr.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dfcjdxesyn3l0
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "godot_cpp/classes/engine.hpp"
|
#include "godot_cpp/classes/engine.hpp"
|
||||||
#include "godot_cpp/classes/font_file.hpp"
|
#include "godot_cpp/classes/font_file.hpp"
|
||||||
|
#include "godot_cpp/classes/global_constants.hpp"
|
||||||
#include "godot_cpp/classes/packet_peer_udp.hpp"
|
#include "godot_cpp/classes/packet_peer_udp.hpp"
|
||||||
#include "godot_cpp/core/math.hpp"
|
#include "godot_cpp/core/math.hpp"
|
||||||
#include "godot_cpp/core/memory.hpp"
|
#include "godot_cpp/core/memory.hpp"
|
||||||
|
|
@ -12,18 +13,29 @@
|
||||||
#include "godot_cpp/variant/transform3d.hpp"
|
#include "godot_cpp/variant/transform3d.hpp"
|
||||||
#include "godot_cpp/variant/vector3.hpp"
|
#include "godot_cpp/variant/vector3.hpp"
|
||||||
#include "mavlink/all/mavlink.h" // IWYU pragma: keep; always include the mavlink.h file for selected dialect
|
#include "mavlink/all/mavlink.h" // IWYU pragma: keep; always include the mavlink.h file for selected dialect
|
||||||
|
#include "mavlink/common/mavlink_msg_command_long.h"
|
||||||
|
#include "mavlink/common/mavlink_msg_manual_control.h"
|
||||||
#include "mavlink/common/mavlink_msg_sim_state.h"
|
#include "mavlink/common/mavlink_msg_sim_state.h"
|
||||||
#include "mavlink/mavlink_helpers.h"
|
#include "mavlink/mavlink_helpers.h"
|
||||||
|
#include "mavlink/mavlink_types.h"
|
||||||
// How much extra bytes are needed on top of message payload length
|
|
||||||
#define MAVLINK_OVERHEAD 12
|
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
void MarshConnector::_bind_methods() {
|
void MarshConnector::_bind_methods() {
|
||||||
|
// Data access
|
||||||
|
ClassDB::bind_method(D_METHOD("get_aircraft"), &MarshConnector::get_aircraft);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_cyclic"), &MarshConnector::get_cyclic);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_collective"),
|
||||||
|
&MarshConnector::get_collective);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_pedals"), &MarshConnector::get_pedals);
|
||||||
|
|
||||||
|
// Timer callbacks
|
||||||
ClassDB::bind_method(D_METHOD("send_heartbeat"),
|
ClassDB::bind_method(D_METHOD("send_heartbeat"),
|
||||||
&MarshConnector::send_heartbeat);
|
&MarshConnector::send_heartbeat);
|
||||||
ClassDB::bind_method(D_METHOD("get_aircraft"), &MarshConnector::get_aircraft);
|
ClassDB::bind_method(D_METHOD("manager_timeout"),
|
||||||
|
&MarshConnector::manager_timeout);
|
||||||
|
// ClassDB::bind_method(D_METHOD("model_timeout"),
|
||||||
|
// &MarshConnector::model_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
MarshConnector::MarshConnector() {
|
MarshConnector::MarshConnector() {
|
||||||
|
|
@ -39,10 +51,14 @@ MarshConnector::MarshConnector() {
|
||||||
|
|
||||||
socket = memnew(PacketPeerUDP);
|
socket = memnew(PacketPeerUDP);
|
||||||
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
manager_connected = false;
|
||||||
// Don't run _process() in the editor
|
|
||||||
set_process_mode(Node::ProcessMode::PROCESS_MODE_DISABLED);
|
manager_timer = memnew(Timer);
|
||||||
}
|
add_child(manager_timer);
|
||||||
|
manager_timer->set_wait_time(5.0);
|
||||||
|
manager_timer->set_one_shot(true);
|
||||||
|
manager_timer->set_autostart(false);
|
||||||
|
manager_timer->connect("timeout", Callable(this, "manager_timeout"));
|
||||||
}
|
}
|
||||||
|
|
||||||
MarshConnector::~MarshConnector() {
|
MarshConnector::~MarshConnector() {
|
||||||
|
|
@ -65,7 +81,7 @@ void MarshConnector::_process(double delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Error MarshConnector::send_heartbeat() {
|
Error MarshConnector::send_heartbeat() {
|
||||||
print_line("Sending HEARTBEAT at ", time_passed, " seconds");
|
// print_line("Sending HEARTBEAT at ", time_passed, " seconds");
|
||||||
|
|
||||||
mavlink_heartbeat_t heartbeat;
|
mavlink_heartbeat_t heartbeat;
|
||||||
heartbeat.type = MAV_TYPE_HELICOPTER;
|
heartbeat.type = MAV_TYPE_HELICOPTER;
|
||||||
|
|
@ -77,17 +93,19 @@ Error MarshConnector::send_heartbeat() {
|
||||||
mavlink_message_t message;
|
mavlink_message_t message;
|
||||||
mavlink_msg_heartbeat_encode_chan(1, MARSH_COMP_ID_VISUALISATION,
|
mavlink_msg_heartbeat_encode_chan(1, MARSH_COMP_ID_VISUALISATION,
|
||||||
MAVLINK_COMM_0, &message, &heartbeat);
|
MAVLINK_COMM_0, &message, &heartbeat);
|
||||||
uint8_t send_buffer[MAVLINK_OVERHEAD + MAVLINK_MSG_ID_HEARTBEAT_LEN];
|
return send_message(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Error MarshConnector::send_message(mavlink_message_t message) {
|
||||||
|
uint8_t send_buffer[MAVLINK_MAX_PACKET_LEN];
|
||||||
mavlink_msg_to_send_buffer(send_buffer, &message);
|
mavlink_msg_to_send_buffer(send_buffer, &message);
|
||||||
|
|
||||||
PackedByteArray array;
|
PackedByteArray array;
|
||||||
for (int i = 0; i < sizeof(send_buffer); i++) {
|
for (int i = 0; i < sizeof(send_buffer); i++) {
|
||||||
array.append(send_buffer[i]);
|
array.append(send_buffer[i]);
|
||||||
}
|
}
|
||||||
print_line("Data array ", array);
|
|
||||||
|
|
||||||
const auto result = socket->put_packet(array);
|
const auto result = socket->put_packet(array);
|
||||||
print_line("Send result ", result);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,6 +132,12 @@ void MarshConnector::receive_data(const PackedByteArray &data) {
|
||||||
case MAVLINK_MSG_ID_PARAM_SET:
|
case MAVLINK_MSG_ID_PARAM_SET:
|
||||||
handle_param(message);
|
handle_param(message);
|
||||||
break;
|
break;
|
||||||
|
case MAVLINK_MSG_ID_MANUAL_CONTROL:
|
||||||
|
handle_manual_control(message);
|
||||||
|
break;
|
||||||
|
case MAVLINK_MSG_ID_HEARTBEAT:
|
||||||
|
handle_heartbeat(message);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +159,13 @@ Transform3D MarshConnector::get_aircraft() {
|
||||||
return Transform3D{Basis{last_rotation}, last_location};
|
return Transform3D{Basis{last_rotation}, last_location};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector2 MarshConnector::get_cyclic() {
|
||||||
|
return Vector2{last_controls.x, last_controls.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
float MarshConnector::get_collective() { return last_controls.w; }
|
||||||
|
float MarshConnector::get_pedals() { return last_controls.z; }
|
||||||
|
|
||||||
Vector2 MarshConnector::local_meters_from_global_degrees(double latitude,
|
Vector2 MarshConnector::local_meters_from_global_degrees(double latitude,
|
||||||
double longitude) {
|
double longitude) {
|
||||||
|
|
||||||
|
|
@ -195,13 +226,86 @@ Quaternion MarshConnector::godot_rotation_from_mavlink(float roll_rad,
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarshConnector::handle_local_position(mavlink_message_t message) {
|
void MarshConnector::handle_local_position(mavlink_message_t message) {
|
||||||
print_line("Implement handle_local_position");
|
if (message.msgid != MAVLINK_MSG_ID_LOCAL_POSITION_NED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mavlink_local_position_ned_t local_pos;
|
||||||
|
mavlink_msg_local_position_ned_decode(&message, &local_pos);
|
||||||
|
last_location =
|
||||||
|
godot_location_from_mavlink(local_pos.x, local_pos.y, -local_pos.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarshConnector::handle_attitude(mavlink_message_t message) {
|
void MarshConnector::handle_attitude(mavlink_message_t message) {
|
||||||
print_line("Implement handle_attitude");
|
if (message.msgid != MAVLINK_MSG_ID_ATTITUDE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mavlink_attitude_t attitude;
|
||||||
|
mavlink_msg_attitude_decode(&message, &attitude);
|
||||||
|
last_rotation =
|
||||||
|
godot_rotation_from_mavlink(attitude.roll, attitude.pitch, attitude.yaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarshConnector::handle_param(mavlink_message_t message) {
|
void MarshConnector::handle_param(mavlink_message_t message) {
|
||||||
print_line("Implement handle_param");
|
print_line("Implement handle_param");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MarshConnector::handle_manual_control(mavlink_message_t message) {
|
||||||
|
if (message.msgid != MAVLINK_MSG_ID_MANUAL_CONTROL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mavlink_manual_control_t manual_control;
|
||||||
|
mavlink_msg_manual_control_decode(&message, &manual_control);
|
||||||
|
const int16_t invalid = 0x7FFF;
|
||||||
|
if (manual_control.y != invalid)
|
||||||
|
last_controls.x = manual_control.y / 1000.0f;
|
||||||
|
if (manual_control.x != invalid)
|
||||||
|
last_controls.y = manual_control.x / -1000.0f;
|
||||||
|
if (manual_control.r != invalid)
|
||||||
|
last_controls.z = manual_control.r / 1000.0f;
|
||||||
|
if (manual_control.z != invalid)
|
||||||
|
last_controls.w = manual_control.z / 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarshConnector::handle_heartbeat(mavlink_message_t message) {
|
||||||
|
if (message.msgid != MAVLINK_MSG_ID_HEARTBEAT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// mavlink_heartbeat_t heartbeat;
|
||||||
|
// mavlink_msg_heartbeat_decode(&message, &heartbeat);
|
||||||
|
if (message.compid == MARSH_COMP_ID_MANAGER) {
|
||||||
|
if (!manager_connected) {
|
||||||
|
print_line("Connected to MARSH Manager");
|
||||||
|
|
||||||
|
// subscribe to messages not sent to visualisation node by default
|
||||||
|
mavlink_command_long_t command;
|
||||||
|
command.target_system = 1;
|
||||||
|
command.target_component = MARSH_COMP_ID_MANAGER;
|
||||||
|
command.command = MAV_CMD_SET_MESSAGE_INTERVAL;
|
||||||
|
command.confirmation = 0;
|
||||||
|
command.param1 = static_cast<float>(MAVLINK_MSG_ID_MANUAL_CONTROL);
|
||||||
|
command.param2 = 0; // Default rate
|
||||||
|
command.param3 = 0; // Not used
|
||||||
|
command.param4 = 0;
|
||||||
|
command.param5 = 0;
|
||||||
|
command.param6 = 0;
|
||||||
|
command.param7 = 1; // Address of requestor
|
||||||
|
|
||||||
|
mavlink_message_t message_sent;
|
||||||
|
mavlink_msg_command_long_encode_chan(1, MARSH_COMP_ID_VISUALISATION,
|
||||||
|
MAVLINK_COMM_0, &message_sent,
|
||||||
|
&command);
|
||||||
|
Error result = send_message(message_sent);
|
||||||
|
if (result != OK) {
|
||||||
|
print_line("Subscribe send result ", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manager_connected = true;
|
||||||
|
manager_timer->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarshConnector::manager_timeout() {
|
||||||
|
print_line("Lost connection to MARSH Manager");
|
||||||
|
manager_connected = false;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,25 @@ public:
|
||||||
Error set_parameter(const String &id, float value);
|
Error set_parameter(const String &id, float value);
|
||||||
// Get current parameter value
|
// Get current parameter value
|
||||||
float get_parameter(const String &id);
|
float get_parameter(const String &id);
|
||||||
|
|
||||||
// Get current state of the aircraft
|
// Get current state of the aircraft
|
||||||
Transform3D get_aircraft();
|
Transform3D get_aircraft();
|
||||||
|
// Get normalized (-1 to 1) cyclic position, where X is right, Y is pitch up
|
||||||
|
Vector2 get_cyclic();
|
||||||
|
// Get normalized (0 to 1) collective position, positive to climb
|
||||||
|
float get_collective();
|
||||||
|
// Get normalized (-1 to 1) pedals position, positive turn right
|
||||||
|
float get_pedals();
|
||||||
|
|
||||||
|
// Is there a connection to MARSH Manager
|
||||||
|
bool get_manager_connected();
|
||||||
|
// Is receiving data from flight model
|
||||||
|
bool get_model_connected();
|
||||||
|
|
||||||
|
// Called by timer
|
||||||
|
void manager_timeout();
|
||||||
|
// // Called by timer
|
||||||
|
// void model_timeout();
|
||||||
|
|
||||||
// Convert from global coordinates to local position x=north, y=east
|
// Convert from global coordinates to local position x=north, y=east
|
||||||
// taking into account current configuration.
|
// taking into account current configuration.
|
||||||
|
|
@ -55,6 +72,10 @@ private:
|
||||||
void handle_local_position(mavlink_message_t message);
|
void handle_local_position(mavlink_message_t message);
|
||||||
void handle_attitude(mavlink_message_t message);
|
void handle_attitude(mavlink_message_t message);
|
||||||
void handle_param(mavlink_message_t message);
|
void handle_param(mavlink_message_t message);
|
||||||
|
void handle_manual_control(mavlink_message_t message);
|
||||||
|
void handle_heartbeat(mavlink_message_t message);
|
||||||
|
|
||||||
|
Error send_message(mavlink_message_t message);
|
||||||
|
|
||||||
// Do not specify specific values, to ensure PARAM_COUNT is correct
|
// Do not specify specific values, to ensure PARAM_COUNT is correct
|
||||||
// Not an enum class on purpose, to make use more convenient, it's scoped
|
// Not an enum class on purpose, to make use more convenient, it's scoped
|
||||||
|
|
@ -78,10 +99,14 @@ private:
|
||||||
// TODO: Interpolate with some delay
|
// TODO: Interpolate with some delay
|
||||||
Vector3 last_location;
|
Vector3 last_location;
|
||||||
Quaternion last_rotation;
|
Quaternion last_rotation;
|
||||||
|
Vector4 last_controls;
|
||||||
|
|
||||||
double time_passed;
|
double time_passed;
|
||||||
Timer *heartbeat_timer;
|
Timer *heartbeat_timer;
|
||||||
PacketPeerUDP *socket;
|
PacketPeerUDP *socket;
|
||||||
|
|
||||||
|
bool manager_connected;
|
||||||
|
Timer *manager_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
from os import path
|
|
||||||
from shutil import copytree
|
|
||||||
|
|
||||||
# add repository root path
|
|
||||||
root_path = path.abspath(path.dirname(__file__))
|
|
||||||
|
|
||||||
copytree(
|
|
||||||
path.join(root_path, 'modules', 'godot-xr-tools', 'addons'),
|
|
||||||
path.join(root_path, 'project', 'addons'),
|
|
||||||
dirs_exist_ok=True,
|
|
||||||
)
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue