From 8787bdd8f6ca5a6ca1a210ec79b3b95573c59731 Mon Sep 17 00:00:00 2001 From: "Marek S. Lukasiewicz" Date: Tue, 11 Feb 2025 15:22:30 +0100 Subject: [PATCH 1/6] Add initial Mi-2 3D model --- .gitattributes | 1 + project/assets/mi2/Mi-2.glb | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 project/assets/mi2/Mi-2.glb diff --git a/.gitattributes b/.gitattributes index 8ad74f7..4bbc33a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ # Normalize EOL for all files that Git considers text files. * text=auto eol=lf +*.glb filter=lfs diff=lfs merge=lfs -text diff --git a/project/assets/mi2/Mi-2.glb b/project/assets/mi2/Mi-2.glb new file mode 100644 index 0000000..7d7de03 --- /dev/null +++ b/project/assets/mi2/Mi-2.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f50f5845353f88ca781a5605fa3aa57cbdbf688280e3b8cbd6fdcf094318145 +size 339648 From 046388a3eb7493bc231a7f82153cbe1df3257134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20S=2E=20=C5=81ukasiewicz?= Date: Wed, 12 Feb 2025 15:46:23 +0100 Subject: [PATCH 2/6] Start project in Godot 4.4 beta3 --- .godot-version | 1 + README.md | 4 ++-- project/assets/mi2/Mi-2.glb.import | 37 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 .godot-version create mode 100644 project/assets/mi2/Mi-2.glb.import diff --git a/.godot-version b/.godot-version new file mode 100644 index 0000000..80ef135 --- /dev/null +++ b/.godot-version @@ -0,0 +1 @@ +4.4-beta3 diff --git a/README.md b/README.md index cfed8ed..c9831c4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ git clone --recurse-submodules
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`. 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 godot --dump-extension-api # after updating Godot -scons compile_commands # after modifying SConstruct python update_mavlink.py # after updating MAVLink dialect python update_addons.py # after changing any addon submodules +scons compile_commands # after modifying SConstruct ``` ### Windows setup diff --git a/project/assets/mi2/Mi-2.glb.import b/project/assets/mi2/Mi-2.glb.import new file mode 100644 index 0000000..1503c35 --- /dev/null +++ b/project/assets/mi2/Mi-2.glb.import @@ -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 From b868c2bdbbce676cb7c2b49d54fab2d6c1388d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20S=2E=20=C5=81ukasiewicz?= Date: Wed, 12 Feb 2025 15:57:45 +0100 Subject: [PATCH 3/6] Cleanup MarshConnector Receive ATTITUDE and LOCAL_POSITION_NED Remove unnecessary prints --- project/main-xr.tscn | 8 ++------ project/main.tscn | 1 - src/marshconnector.cpp | 27 +++++++++++++++++---------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/project/main-xr.tscn b/project/main-xr.tscn index 38d8ce1..18c3f25 100644 --- a/project/main-xr.tscn +++ b/project/main-xr.tscn @@ -22,7 +22,6 @@ sky = SubResource("Sky_0xm2m") 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") @@ -31,12 +30,9 @@ 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"] +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." index="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="." index="5"] +[node name="WorldEnvironment" type="WorldEnvironment" parent="." index="4"] environment = SubResource("Environment_jdgxj") camera_attributes = SubResource("CameraAttributesPractical_fpvso") diff --git a/project/main.tscn b/project/main.tscn index 6163934..2cb87ad 100644 --- a/project/main.tscn +++ b/project/main.tscn @@ -18,7 +18,6 @@ sky = SubResource("Sky_0xm2m") [node name="Main" type="Node3D"] [node name="MarshConnector" type="MarshConnector" parent="."] -process_mode = 0 [node name="Aircraft" type="Node3D" parent="." node_paths=PackedStringArray("connector")] script = ExtResource("1_ig7tw") diff --git a/src/marshconnector.cpp b/src/marshconnector.cpp index f11e62e..3a1990c 100644 --- a/src/marshconnector.cpp +++ b/src/marshconnector.cpp @@ -38,11 +38,6 @@ MarshConnector::MarshConnector() { heartbeat_timer->connect("timeout", Callable(this, "send_heartbeat")); socket = memnew(PacketPeerUDP); - - if (Engine::get_singleton()->is_editor_hint()) { - // Don't run _process() in the editor - set_process_mode(Node::ProcessMode::PROCESS_MODE_DISABLED); - } } MarshConnector::~MarshConnector() { @@ -65,7 +60,7 @@ void MarshConnector::_process(double delta) { } Error MarshConnector::send_heartbeat() { - print_line("Sending HEARTBEAT at ", time_passed, " seconds"); + // print_line("Sending HEARTBEAT at ", time_passed, " seconds"); mavlink_heartbeat_t heartbeat; heartbeat.type = MAV_TYPE_HELICOPTER; @@ -84,10 +79,10 @@ Error MarshConnector::send_heartbeat() { for (int i = 0; i < sizeof(send_buffer); i++) { array.append(send_buffer[i]); } - print_line("Data array ", array); + // print_line("Data array ", array); const auto result = socket->put_packet(array); - print_line("Send result ", result); + // print_line("Send result ", result); return result; } @@ -195,11 +190,23 @@ Quaternion MarshConnector::godot_rotation_from_mavlink(float roll_rad, } 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) { - 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) { From 9ebb025f8a1a2288d8d13f8fb4d7a0ec56b7018a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20S=2E=20=C5=81ukasiewicz?= Date: Thu, 13 Feb 2025 09:48:52 +0100 Subject: [PATCH 4/6] Add cone model --- .gitattributes | 3 ++ project/CREDITS.txt | 6 +++ project/assets/cone/Cone80cm.fbx | 3 ++ project/assets/cone/Cone80cm.fbx.import | 38 ++++++++++++++++++ .../cone/Cone_DefaultMaterial_BaseColor.webp | 3 ++ ...Cone_DefaultMaterial_BaseColor.webp.import | 34 ++++++++++++++++ .../cone/Cone_DefaultMaterial_Normal.webp | 3 ++ .../Cone_DefaultMaterial_Normal.webp.import | 34 ++++++++++++++++ ...ltMaterial_OcclusionRoughnessMetallic.webp | 3 ++ ...ial_OcclusionRoughnessMetallic.webp.import | 34 ++++++++++++++++ project/assets/cone/cone.tscn | 9 +++++ project/assets/cone/cone_material.tres | 28 +++++++++++++ project/assets/splash/splash.png | Bin 15311 -> 130 bytes 13 files changed, 198 insertions(+) create mode 100644 project/CREDITS.txt create mode 100644 project/assets/cone/Cone80cm.fbx create mode 100644 project/assets/cone/Cone80cm.fbx.import create mode 100644 project/assets/cone/Cone_DefaultMaterial_BaseColor.webp create mode 100644 project/assets/cone/Cone_DefaultMaterial_BaseColor.webp.import create mode 100644 project/assets/cone/Cone_DefaultMaterial_Normal.webp create mode 100644 project/assets/cone/Cone_DefaultMaterial_Normal.webp.import create mode 100644 project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp create mode 100644 project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp.import create mode 100644 project/assets/cone/cone.tscn create mode 100644 project/assets/cone/cone_material.tres diff --git a/.gitattributes b/.gitattributes index 4bbc33a..aa8aed0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ # Normalize EOL for all files that Git considers text files. * 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 diff --git a/project/CREDITS.txt b/project/CREDITS.txt new file mode 100644 index 0000000..5e84675 --- /dev/null +++ b/project/CREDITS.txt @@ -0,0 +1,6 @@ +Programming: + Marek S. Łukasiewicz + +3D models: + Igor Samek + Marek S. Łukasiewicz diff --git a/project/assets/cone/Cone80cm.fbx b/project/assets/cone/Cone80cm.fbx new file mode 100644 index 0000000..cce66e4 --- /dev/null +++ b/project/assets/cone/Cone80cm.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53604872f79dd1e52a8144811eef77179d66313b7d1cba8b2ed6bee691203b1f +size 32252 diff --git a/project/assets/cone/Cone80cm.fbx.import b/project/assets/cone/Cone80cm.fbx.import new file mode 100644 index 0000000..d252069 --- /dev/null +++ b/project/assets/cone/Cone80cm.fbx.import @@ -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 diff --git a/project/assets/cone/Cone_DefaultMaterial_BaseColor.webp b/project/assets/cone/Cone_DefaultMaterial_BaseColor.webp new file mode 100644 index 0000000..f1d1788 --- /dev/null +++ b/project/assets/cone/Cone_DefaultMaterial_BaseColor.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fda4c784b19821b8de716bfebfe58c5a138baca15269328fec68389b5b5a6910 +size 74366 diff --git a/project/assets/cone/Cone_DefaultMaterial_BaseColor.webp.import b/project/assets/cone/Cone_DefaultMaterial_BaseColor.webp.import new file mode 100644 index 0000000..25e24c5 --- /dev/null +++ b/project/assets/cone/Cone_DefaultMaterial_BaseColor.webp.import @@ -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 diff --git a/project/assets/cone/Cone_DefaultMaterial_Normal.webp b/project/assets/cone/Cone_DefaultMaterial_Normal.webp new file mode 100644 index 0000000..b60f821 --- /dev/null +++ b/project/assets/cone/Cone_DefaultMaterial_Normal.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac629f1490250708b020f94be6dcd9b6d8562ca4a1d62af55e0ec596e374857b +size 126732 diff --git a/project/assets/cone/Cone_DefaultMaterial_Normal.webp.import b/project/assets/cone/Cone_DefaultMaterial_Normal.webp.import new file mode 100644 index 0000000..5bea403 --- /dev/null +++ b/project/assets/cone/Cone_DefaultMaterial_Normal.webp.import @@ -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 diff --git a/project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp b/project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp new file mode 100644 index 0000000..17e7a65 --- /dev/null +++ b/project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:812432b6750ab8f6391534efa2036aa5c192052672fcf7c4f63c292d63f944a7 +size 148532 diff --git a/project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp.import b/project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp.import new file mode 100644 index 0000000..9c84306 --- /dev/null +++ b/project/assets/cone/Cone_DefaultMaterial_OcclusionRoughnessMetallic.webp.import @@ -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 diff --git a/project/assets/cone/cone.tscn b/project/assets/cone/cone.tscn new file mode 100644 index 0000000..d17839b --- /dev/null +++ b/project/assets/cone/cone.tscn @@ -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") diff --git a/project/assets/cone/cone_material.tres b/project/assets/cone/cone_material.tres new file mode 100644 index 0000000..2a52f25 --- /dev/null +++ b/project/assets/cone/cone_material.tres @@ -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") diff --git a/project/assets/splash/splash.png b/project/assets/splash/splash.png index 32960db65fbdcd6a36f6d48b14385b00b216e791..a6d93760fb90cc91ac7a359dfbefefab58bc1723 100644 GIT binary patch literal 130 zcmWN{xe>!45J1tKDrmssW`(-p*fq@9Mq`iyS8t;Kf`8+8wEp3B&T||>JllM{Xl$$X zN?Y!4F;2|lR;;(eM~&L~g8(6-^mgA7!4wZhmZEbiAvqwuB*|Nd2COCsq~p4DQaQ>H N2-#>qF|Mm-N|y zc)xS+@BZSGAgyUpiJXQCYMow^{fF9$cryn{T=YCC~B>M3P1o9`zdk6waNC2x*P(UCe zqQ3)l^#3rpT5>bd~_CF17*l{K7 zo54SJ{4a0)|BA^2Ifvm$s&lci{d#V_R&Mp;d~R;NuePpa!#=+b{KKuJshI;^n<9>k z!M@sNc21OMKonXltFfR)BEuwV%&?HXwu{R+;?tj;D@4dMcjf~xO2JB@Najg#VBYdr zaOu|bP<{R=_^0!b2OTHuDPm2b6$N=(B6DAZQ6MAYr{d(``!Ak{0 zsGLQAcX6bjyiO?1@;B2rN!}-O#26+WlMabyuk-u4CtnH9k+ z)=pr-r@F~_&1n|bTw?C`H(?}>>=rZj*f|pw0%sk{c2N2(zkC-D6L9D`Vcjg$JD7G# ziH&S)HwBk*MSVa?N^*o#Ux^^I=bewW@r3JaasD75lEiuF)Yf&6ptI+lQ!@M=Zf2Q- zay(mS!q$;~oq*v%m*Qqqj0vOYYz-zzSpoz)A?-Z=`ZnAFDcV!|cIBIE@`TQ5vy}$J zL85VLMx>{{N=-cy&%N-PPI?UIj_!hVdt_hDcb(EYGtUIED2Vr~fXTUne)l4(1JCKn z1|;s^)e?oMo<@C9gZu~Pl8WK=qa7ZUi&bEe*rg7|22`E*`X6E-PSj!ppFKW$l6<+ZHg5mztAt72d$iPlT9#&=frc~&DuQpMVsdBS5~B^Xs4!Sf zwc*Sy8&*72lH-nA{ibw?n0K#2bXV<|4&VwxG(*lBN-pgD%>69H!QwVhFf?fpqL-?F zkCQX_=1jGRv_vxzO0tM5&CZ_tKl`6tnM1W=fUTG^zZqU>=aK=)Ld%p= z3{xeuy!unf&2wM10QHJJ`6*P@x#|0yP^yhr*Q#y!h^S6sL!F(W!5(Sg+jac8`%2mi zZoap7Y|aBo00EEgeAg61uC5t;?OzBLnSeSr_6cELnm%J)nlN56L5C_#r7lKqQB{$TDDAy+E4j+ONL_? z<188x<@@-*L(GEJ3jC|NC_3B4UcO9*MlOxyS}6vbWOp91AZRWPF-E=-?ww9z5e2Q(_Iwd3)R^NWx2sRZ(3$Jye1HQA%dtft((4 z^c|5S=q|T+ENx)p1gzR+|Eov=JDb*p(TfbmUu;_(nimx#4>?sj&f6}KQwgMm(S6~4 zyARVsc!e(3=45TtNp=;eXy5+cog1q)cWNXUFK3l9Fls=%bbE?fh?Q?y71HtSy>wFy zGv?!s+^+RXFRYX0Qn0{w5r}E8edS}t?)L2|+NoEOntL1W91v@%lh&m?pNi5$>*al< zX!#+xR3Uio8eIs~@t9Pm&fU1$?=UOBO z%@Y_xK~MEd+=j2`s_S{gzIeiw=r6`DCS1G(=I8P%QygPOe9kyDDqIjAB}F9P%qUoc zRk`e06m>OAYzd+%ya?&_s}Mm&?uWuNri4Cws_UomUD`I+{=SoCr)bDyb>s{@&zQ!; ztd_U)^2uY;_i2TVPgz+$hU1h9B3&S>9k5xLugJtmJ#v1A(lu+)t5NRRm8UD#HqeB} zw^hhC`dS54##dM}Dx~<#PXsWbR~uF#t``WF#*l3LOXl_72Npw=`}l;fejYQ(FLVHKEHuKB`Q zmrTYCQlbwX|7l{ z(B0FlAUzqQq~c&NmP_gRDB>?t2ca|E%$&?{y=6sX7RC7?DbH2N`M`^D>c2)-N+tu* zf^z6IwV-4R>7lXPvwL*8H_~mszNUoF({nr4#{|vZ$mXt;eYiyd%2lMilfrkAr}g93 zL!zZT?Sg(k+MD%c7{kJkGi@l~?-Z({;SrM~h=zLZ`TZ*bC9+U;%2z09>|4t%dsIBT zZh(veQGuvOmt@ykzABMYZ%cWDOw-fzAfo}-UL=5dVWEJs#na*<3f7Uzs9B_O zB*xp(C`nNC_GtnkxgPoU@cZv;(ZA=(kq}$Zw@XrL8h=xdFXTr$eSAfcBY%DlkIz{l zOXE>{i;erV?$++`i-~I{iaQ>##w3o@rz9mlk>p<}KAa!-kAL1z*rE-&##+PEs#Rjd zhb(*_AGSMTTqtpgocgi;sejnJ{a!STL~{bw>no2gFnd#GzC<7Zh2l#&sJ7z3BEtIy3fK zSIHq3LlJvW~CVjiqm8uAY}my^FiM%}7>XA{RJ|KIy!g-p8fZz-!&rZnF5Pot@p=Ph&@B zvW60x6dzeMb@lZ2kKi=65qz37Ws&+x{0=2GgWIZO!(hlulyHn|#7CCWpQL&Q2V=(b zWhVqJ((?TNocKkstV~!~ZhviCLm<>1zqjQ5&M6NSEEt5so;Nf!=RBo4PM_bx8@fcoo7Pn0DJ^*%UMjceq)xVozTKb!@Hs#1%xXzcrx>d zjP<=tYonVf&AL;~YJ1YU8af7~rL?(vV4(26r*L*_VF9**MLOBk1CrYI)_#`Hsj2jH z9xVz>^hIR6Cm$b5&CY59nb~D>Od9iLy3(Mnd%6N)8%P&l1A()Aevr`dOAP8NxkAlu zgV(dRv+=B=F`2ccQ^v*gy%Zr8dV@Ni!U=oi?-57jy%U^hejXmRlAVu9VB9dY zYhBCqb=5B6ws2bu;$L<rpu2VSC1r@({2$;X=eqWU>_Z*-!_Q$o%MNO$mG_ z@A;LIUg~sx#Y&FJN_aa+!hZJG!#;Z@f7|TFR#%+iG5usNSJ^E!@ot8PA^rR*N5Z zW>CxOsA&z8#W=ewwv<3pwmd8bQZpz^1ha0I!%j!p_6eny;YkjS30ooF`*nFwK5!n4 zGU}sDRaZ&f_k^doGmFgkB~^7si!N@T*67@jV){Zw7ShZ7CCwE!<>b(Qv3lo1M?*t{ zI%>|2brrXh=g!8M%p=x3WbzVSQJ+RH^VgZ!aZdv5%dp~)O1yO(g7)^)Cx@a4K$`6c z2TMjfQ8YJ{*TZB)!py=|@9Phy5l+$WW4Z36tVKS)R~mF_=;$e2JkP&H>hJIf2;`h> zH2BRXZQB$!YSPisQFj5Z2`M0}CDGg5C;fz0*us5dBEqZQea^EA6BBc}4K@mkyW#7f zR3P#Gx^usxvU06oc!PS^{B+u;P|ek~LP~XNwxG=1g2@i01d?aHI_`+H0c(C~7nv8{ ztnY!z_FSS9Cf7MNacLw2M^(xO*t<= zKO!SqY?wz$+~#7ZXvV%lUrB8HBcQSx3JL{pN`!4hj~d;7dVU}$0>jr9ZuPc*Y-E~i zw**&k%}nlZOW&D!Wm#@oEm&TYd4rlc=2w0 z77*&0K15e)rB-|2*e%fod4(79_*NwIkfr(pvS{R!!%DCleHFApgSp#o!=hOzHRU7= z&6STll9|lDF!k-GX3LF^v?dF(J}+w&$X5UZD~+RZb7To z{~i;3F>{XOrRTZX1m*h6V!HpOrd^g#80a@d+39j&6whMqSK(X?{FJFbz zyIzSfe<~gDo-X#*A`Hm%M}vX&oRhLcN|nhz9f>LcSBJ{oqF;bZW+ zymiBoPsd}%qGvzo=PIm|3O#tVSa_!PltKV+Z9V&*z!1`BH_kuIOj-xe3{1+$Pkzlmqi)g|b}Ynt zBI-AaI0TT{PwP(S54UqvxE!+PhG#c6@ZU{31o{GN60UV2_f#G{I+#Amn?Y*hPzCn& zy&!Q#Hk2Or&I@}Hj^K5#i&h-+)!fLXlr8U1b<*-Xm<0m1BAM9e8*i}S*yL+)$G+$? zKd6(@H&f7GTfH;h5HGM@sswNc4Gr@38Hsmvx zI)*t8d2t)f_MD=$IQ`^TH(Zk4#gj?q(22>=d!62WaSMRcfV z1dw#>?2MWg@n7vougZXU+dQ|&(IvnnKHy!=JPNj*)r%c0l9{qODglQGVkVJ@^SK+- zFP+(a+NoQ-fwNa*Fht_GgGC`rPmdC|+kQNbHg^B^^0(x#5)S7Dy(#iFN?XO#-f54A zqu@z)&xTo%slT~v8ENN1%pKA{v(jEG7DI6v>GT;Db>cqIeH@Cbe>aZJthD(u@1gFj_CGqR@!KbW%1jDQ*Gvn} zcurEp#&aNFMwjJ#ZuX=~>6M~p8a|dVAP8yo#I;ocKUQ>8M4D^>7@(DBN zPl_K?hV&oe@yA6$$@zfy@Q?~vrnkbkTT0@Fyc2qj#hc&>^udfNhz=9$o+MapQVmk^ z@H`W~sLzPp$qsh~kmmu&&y@r4CBGhgN*UyXa{AR8o(0}k&@436C3=mL05gD;nn3nH zB$Yi*D;1*zS{R#U8z_sW)UIRV2;m5v0r(%}W(98?|`oD#wvJ5E7TqdV+OTDH{sh#sD1iP~8Jt?!dWYffJd)`WMMi#rD7RuRM=>y=pyn znx$u0$?liMx;9*wt!ft0_4%C~#(`Wnq6|{G7U%SAhPA5Ja=ohYs(6 ztR~uBoki+V%7yQv{Odx`cf=|#-tJr9e9Mj^Q*v73w>Q6-+GJvPX|jj^7G<0%e4Z>e zYr}M`RjMGx(mG8$wHhx@lgzQBT{Wk^>qF=%ey;~qL03=PmpFAVvdrAfQb)4{GxXlu z_r!31rInlWxZslB?^qLdU07Qu4V2w!N~C*a|9kXd7v#KUwHWr7S7q`Ura&*n-b+}; z5a<9{1$PV0e#w#QQ>Ev{zGRgEUCN3&tJJ-r9KMZ$p-8LXk0C_wI^w--?ChVs%uVJ| zg#X>HDEX6+;(&|Syr~IBexUG$|GM<5eU&Q z6EJ+o6SWGIeeE$m{Wba5-Ys)eAYQPB`Z>_uMs}?oJSkw=H)?^Xl_3!tZg^n2?+9ge z@XIL3>K3inF2R*QJa5ujcCo=Pf$Jc@+m6+Kb#B<&7CTX2EI$rvc{zUk6?eVBwyeZD zVihtj=*Lwlh;`NNxahCx>vFtDEv;L7qMCNO<;RMSlLv~*iqH?fbcc9Ir|$Fqj)j}M#g!*oQ>HX$m9DI7YrA&3;`KS71rJq zyN&BnQjL&sYjzpXZW=KdF_?)m?kKh4Lr%I8xN*2CCQ4wY*uv@I;-TW3*RL+(`!V`a z;l;!;=Rd*{>y9<%XYD3*DZRA64pk1Dvl6zSA9kiipC+G0ofS5O5Py8$?+y2cQ^X1% zUV}r0;{|z=<+IhYm4re&_Cg@vBcIg3=&#Zd?~WrnxHVkTqM>YNRc$A0VrnHHV&GWl z?3*O4b)|}qYsZzl&=e>g2Q#7Y_x=;bRLJCa_hT}H<50uhtu3y3h-zrG&!^q07Wkgs z{2kEeT0jB(?+9Ov9kIisL(X=K{aiVy4vGTAFEWe|gb(V(&FQ!Ori#EQAP~s^>G^oZ zJlz|;Upcts@y-0@KT+GbeTu&C>C^|Ezp`gXDBYE7TTUwbz9}0F$Ua7aj_obVL9@T*5VxmAao*-B@=5mN`gGB-*Xq{^Kj%r?@+|=-NvQw@>zOb%HgP5 zY6z;k&Sek4@DkXK$sx+Z2K%mG*f;{R?;FRLl#)!htY(^UIegvJr4`SZm@KE)+#qL)Bdeh$B?pR*h59j4A z5DGeoeB5+d1*a$TZel%FNbU-#LbS29!aKv4pQmsoh+9dWhr3&!D)tLL%w5a8TlsLy zq&dF-f&&9(SsUji&LskZx1NO=%P!^o`05Uy%E%Ap-;D9xvqo?zG38UyRawcWltxS) zkC%Nn%lFJ}F9xG5**gsT?JSIVm5!l;G;g6u$SH_zY$2;kMDf?A)$J7aGP5|@3#oHb zuo(9`i>N20h#7N&8`G*hcvYY(;M^XIA0T%FT#=*#a!|1)I%wMJ*LFyd7?;Pm(VZ5o z-U9i-YRo_668NUPm0;pi-^NAbJ|#=r0_Xg6_m`-hmiz`F27eG)=+<4xyH~Pn4^Kkj zeexrJLOduW@TAc}hs z{YekIFj61H^<{|^2Y>W|-%1Pe=U%SuiC-q8|0%H^8i+^&L@CniozMZ5h1N9HhO|3~ z!BaSS=PTzih|q*8w_i``o@^qlbk?ZiKIn=?w&Pu@E3ERs8?{b0fc2ISr}0~+!U?`M z(7mN6fTQa@ighQBq39 zQvA$*T9nVPxZC3c@XW=xv$yQ)uW0X@!~L**`nIX`Xk24FboJKn$#ND{qC%x`i566yW?_q|kRZYH=*arPR$HS!IKRnJ6S)qzxQ-iX`#sHGlMC&uUC{8!&J> zA-kkGZZ3sfzd_(tWt=g$Hec-X(L>SWqi7wbMP>?RE$I|tp7g}_7d?3!_w>5bk1vGh zp6}o&fH+xHKvpphrf-5CY=NT&s%hUVU+~NwDy`pP((B@IF0EVpxTuj0oCYJd4LZy%Q-aFapnDG~~4IddqH z>-0t22FXojyy7d=5)u4Kh`ThoS&+E_#_}xi5$zha4IR4r$PwfO_WS6YH?GbvW7?0Df!Egf=;U4@GoqxN~UCx!#ZYwx1WdK=hX5U#nvLqzrmRp-t=AwFKw+EKwc=?OI9!6K3pgP971TYZ zEwu*DT5oq~Te3E$$XddW)6K9MobtX!a{f$ZsH0jyt;j?7%o(yGq>3`GoVI;_g+;k5 z&(Mb>{r&sYSx&Jg5BNqQh9cl7)t>%OZu!-D2S`_a(zj5b7{IKGrL(H0c+RSX8s-%}z>^$vXl)u0Bq+5oFoIu5 z?rY2&r^m|be`(hKXFFN9tZi;b+p~JjS$_2>`Qh=p_OQd{>&8>i`==o&rm=tE3cbhL z%?y*N*1W0ifS(2~4^V@EgXh7ko@lAqaWSbS&nZihq}QYpLewFFKH%fY&Yb9>RJ+L| z2nGhxrI8jJ=8j`fMX%-Z>H1j5P3JzreN927FnYNA(7*cJm*@CAnR=w7QEJ`LY;P`C z)k`g+kWy3Y)fH)=Ji0VjVJ9dgb$hKLZw~VZFpH4TO8#ZBHLZQS)eSl0Aacn&G6baZh zBQffIlZ?&D++YJXLlq@PjE%_}mHnPh<*wbTHJy#d4Y%gpULHC40TK9NbzxW_< zbS*M!L%ql)Hu!NF;@ivpq9sq-h4<1dd9%iFuB7>h%ir6YH$k@qAH~#BUfd^=sSQrm zIPs1$a@B1h6~(hRrw|fb-~1J9++rcoUuIYxaK-2^&ZwP~8C|2wfgqEL(ji=_*k5_K z77%0rw;KR%V`L2MRDa&=8IxKsGNP0y;aX|0h9m~60leXEdE5Dz!=5{*{`pH73jX?-p^=#$*T@;_U~P;Im;w=3~L6l^b0bU3LS0$0l*dJl`}cd zc%;ijDgQ-i8mPe}As@S$sZwMX^5OslTvG^lK%qyl9yE%?Y zmb7w%4QnfFlouVk#LLE2AF*P3{fc}6mA%Z2gLK|@~W4JK# z(KRx5#gv?4>Q63g?p+0nrVSQch6=&)(QYGgg8P_Bd*8(Us|g))&@m>5PmX_Sap}DQ z?Oa@ieVy_TYQO75_=R8T!K?4fzb|^S!Kc9PZ@J^`@Se3YQrkO`%0{ zrDCAhR>M1yL8+7u%4&ED%l2De;=-wWG%@#qP3Ffx$9?#MuBqy0=%KtTN8NME_vD%g zK-@V zgXpkG?gc{!QBUh4*|~2IUg@kt_G`YI-J(YxpEn$f%TLQFOP%q?@yqRdzl-vkg-CWy z{8b3!#7+^-uc7|goh@q+POBs%1ySA_VI`kPrJSgV(T+GA1G?Ypzw{TNLnp4mCmb>r z==3`JOJ3aRtoKIL51*`oyPQX}8jJX`8Khr=KvsZ59aJC)b5l;cmmUCJuCc^g{h_M% zD{;;AdDWv&?c2|}dN6wOtPzwt6`LdVRQJu$A@%X@mC``(<^@NXx0Ib5*zo zUCt-p()g3)Wsbv|t~_p`oqQ4U^$zD`+xvK4=kE5ts}G)=vQJ5j$5xnr$-lSneNJ>4 zZh#&GaF+l`1{^5xxxNDy27JH1U&eLd^Iz|PQUjms`~QUmcmy~K&_?6QMpR2E;pT&$ z9beF8a_Ikp#lu$Eq%}-g?6DQeC0c)#3@P^9aM;uP(oywNBz-@fJU&Pw%2pOibRYpgW* z+}pn}EWgli^L{j9047;f6G`-@9_5mzt=}2Zm8xI>%;Nmu%wb@VHH+#J|?X$Cv zcSVWg{!;7j-1dRvO~omLqM7o8ljsR8_$bhDxy&{3h&W_k1ehGY>aRIJM%e66FMQ2o zKy&^JOC+Hg)!c#a&Z=1mU)?{H`&}5a>_Tu2iP}1^0`k0{w!3$%Z(1GYSL{wuT*|0I z**^{RL$0sH2Hnfirr1oXyVphR=_hrjxiQfqxcSyOqv3|j7>kSO?52IA>iAef1eiKM-O7d0_>pVNrz4R+k~tqY}pZE5{c zKdg2@P?K=WQd+vhq#S2>c$oV>k`r~osnMrNxKh!xrLUTP_bZg>I6s5F0#$b-8XAdavs*(Ffp(kfZLQgqIrSDwj~p%M zz2}aJ6S?LT#G=IrSOpbkyT`z063}p>*Guw1y-kIj5B9^PbAX;5EOV`oyDagTZrq+| zPrFm|@T{Dt@%}dKF*Rfo3)u^=W39nLM+g%l&VMcxhys5&y)Li&7x@03U|c`UZZ_a^E3IWQ3)CmNi7-y>1Ea*B^+UBq z2%=2+p?n9O|GM({93+IfB!%EHr7M;{%PMJ6z`%kdJ+xi$Pq`j8pO2vV?MgWhL2hAB zu&8hVq9lsBaiches0tidLhvM@wHASfx-3jj`MA~1wD;$sn~ZLXL<_uv81^`_YU5&( z8oDod;FL6?NcZ$(7{^-kmuGk7_?SBEZ(+xjJ9Ha5gLOV@Hf`7+XoOMO_~d0k#HGJP zBP1o{}O_n_!IYmiVLqe z9(GK?-E2Bni_dg|j~|cw%0}_D$qL{{{(M!A(EDjWsOLSPrW424K}dow)Ag(k^_MY6 z6*<1w&WZL9!Lv@+1RgW`!%xNADb95o9~@TBzlu^9b#;;ukT`-&`%<{6PJ(t)#TEY9-l&zoRH- zhl5oJF3f_?+LFTYt@=bqKDp%d1mE*1Kb(wVKeQ&HfH#OMCDl65L^Ekq(e-$FRV6(=6}x0oM|?*HOHbW^u$ zcTb*)w82?msl*$ujD_UO_&fUg{E5>0_{p$WEHxJk1vQD&(o3c}9h=|WV?y}PAzb&r zY-B7jMq#$s`$$5b@-t)rYVS6TOQI<8)i9ZRqz(%`Doo2b9!Bi z88tIyw>ozk(c^yf;p~ns56ZI=tk-Im+fSFc#MkLy|H+>{qBI&Z@LpdU+e^X9x8u9F$F?2D#EDw{*BEOElAR@FwZv z5RIp_xc{juD6l(F{=+bRr`O`fHwL70%C}=WBthUoVVg$7PhEd44N3gkr;Oi=;Nz(w z$$n@sk9mLlh!L(mq3U0pRxwL}QvC_S0hK-bh9jCDUdNX51--x2;kYNL4|5SBv~uDW z4Ej0Ld0(*ooyf)ng)F~w6m(49UzsRT#(C+>f<_PQF{@;^%$9@pW4yaT=ztp>Wp-&! zL6bX9x{obp^wlkVWvQtq-SHkXhYa&?7*70CCXZS6pO|##)7V7KNzBV2VF#nIi5kA=Wc4$1X zP8Zjc_a#pvC#C)x&`%>uBHF=>z9F}M0TwR)MJDgqM$`62H!Q=~{dVGx6trW(^(W5^ z0{alYX|{DypEAyW3{s9qxea9Xnh})UAIS{Lm{aT!x!3X|&*Q@9VXgBVUNddNIm5lc z$Jxw3$Z;TsZ+AJ&i3Hip^?4XnHJ6{F+!r|4-V*C}H@Z{R3dqH-cfXO}JhjYR2Ydjz zzg{U<`jd`bIzZ`%z{Td>>Q@v*xYw4BQ<7L0^lz{17+Ws?aCVw7v z4pcq2DeZrc!lD}V4PMS~0g>}MIFK4tP-`=%+na3?H}yawAN;kQAV(Y+5zWGGc~Myt z#-i1x$Bfdf=z|+HVIru@MD9aI_zxTn#}vrfE~XR zS0fY<{tFf4CUlRr>UjXe^GxdN7l1$}-gGDLGSNl{vey6l$GpY=_$NO9GF%{y2l8nU z@YAX*0D{YZ3O41U0of@7ko)@sQ~&-um Date: Tue, 18 Feb 2025 11:34:59 +0100 Subject: [PATCH 5/6] Redo main scene Much simpler XR setup based on Bastiaan Olij's Dev stream 58 Correctly applied bone deformation in Mi-2 model --- project/addons/godot-xr-tools/plugin.gd.uid | 1 - project/addons/godot-xr-tools/xr_tools.gd.uid | 1 - project/aircraft.gd | 6 - project/aircraft/aircraft.gd | 19 +++ project/{ => aircraft}/aircraft.gd.uid | 0 project/aircraft/aircraft.tscn | 26 ++++ project/assets/mi2/Mi-2.glb | 4 +- project/main-xr.tscn | 38 ------ project/main.tscn | 42 +++---- project/openxr_action_map.tres | 2 +- project/project.godot | 11 +- project/start-xr.tscn | 11 -- project/start_vr.gd | 112 ++++++++++++++++++ project/start_vr.gd.uid | 1 + update_addons.py | 11 -- 15 files changed, 181 insertions(+), 104 deletions(-) delete mode 100644 project/addons/godot-xr-tools/plugin.gd.uid delete mode 100644 project/addons/godot-xr-tools/xr_tools.gd.uid delete mode 100644 project/aircraft.gd create mode 100644 project/aircraft/aircraft.gd rename project/{ => aircraft}/aircraft.gd.uid (100%) create mode 100644 project/aircraft/aircraft.tscn delete mode 100644 project/main-xr.tscn delete mode 100644 project/start-xr.tscn create mode 100644 project/start_vr.gd create mode 100644 project/start_vr.gd.uid delete mode 100644 update_addons.py diff --git a/project/addons/godot-xr-tools/plugin.gd.uid b/project/addons/godot-xr-tools/plugin.gd.uid deleted file mode 100644 index 6116563..0000000 --- a/project/addons/godot-xr-tools/plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://46i64f2dmonl diff --git a/project/addons/godot-xr-tools/xr_tools.gd.uid b/project/addons/godot-xr-tools/xr_tools.gd.uid deleted file mode 100644 index ee86026..0000000 --- a/project/addons/godot-xr-tools/xr_tools.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c086s0jrgaiwi diff --git a/project/aircraft.gd b/project/aircraft.gd deleted file mode 100644 index ce15b7a..0000000 --- a/project/aircraft.gd +++ /dev/null @@ -1,6 +0,0 @@ -extends Node3D - -@export var connector: MarshConnector - -func _process(_delta: float) -> void: - transform = connector.get_aircraft() diff --git a/project/aircraft/aircraft.gd b/project/aircraft/aircraft.gd new file mode 100644 index 0000000..f8c9918 --- /dev/null +++ b/project/aircraft/aircraft.gd @@ -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() diff --git a/project/aircraft.gd.uid b/project/aircraft/aircraft.gd.uid similarity index 100% rename from project/aircraft.gd.uid rename to project/aircraft/aircraft.gd.uid diff --git a/project/aircraft/aircraft.tscn b/project/aircraft/aircraft.tscn new file mode 100644 index 0000000..867edc0 --- /dev/null +++ b/project/aircraft/aircraft.tscn @@ -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"] diff --git a/project/assets/mi2/Mi-2.glb b/project/assets/mi2/Mi-2.glb index 7d7de03..9c72d04 100644 --- a/project/assets/mi2/Mi-2.glb +++ b/project/assets/mi2/Mi-2.glb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f50f5845353f88ca781a5605fa3aa57cbdbf688280e3b8cbd6fdcf094318145 -size 339648 +oid sha256:12565394be73ddc6c5150deaa400bbfc29ca3911dd36315fec59cd96eb3b838b +size 524076 diff --git a/project/main-xr.tscn b/project/main-xr.tscn deleted file mode 100644 index 18c3f25..0000000 --- a/project/main-xr.tscn +++ /dev/null @@ -1,38 +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"] - -[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="DirectionalLight3D" type="DirectionalLight3D" parent="." index="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="." index="4"] -environment = SubResource("Environment_jdgxj") -camera_attributes = SubResource("CameraAttributesPractical_fpvso") diff --git a/project/main.tscn b/project/main.tscn index 2cb87ad..9b40194 100644 --- a/project/main.tscn +++ b/project/main.tscn @@ -1,37 +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"] -sky_material = SubResource("ProceduralSkyMaterial_ig7tw") - -[sub_resource type="Environment" id="Environment_ig7tw"] +[sub_resource type="Environment" id="Environment_0xm2m"] background_mode = 2 -sky = SubResource("Sky_0xm2m") - -[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_0xm2m"] +sky = SubResource("Sky_ig7tw") [node name="Main" type="Node3D"] -[node name="MarshConnector" type="MarshConnector" parent="."] - -[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="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_0xm2m") [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="."] -environment = SubResource("Environment_ig7tw") -camera_attributes = SubResource("CameraAttributesPractical_0xm2m") +[node name="StartVR" type="Node3D" parent="."] +script = ExtResource("1_ig7tw") +maximum_refresh_rate = 144 + +[node name="Aircraft" parent="." instance=ExtResource("2_0xm2m")] diff --git a/project/openxr_action_map.tres b/project/openxr_action_map.tres index 035ca38..1adcca3 100644 --- a/project/openxr_action_map.tres +++ b/project/openxr_action_map.tres @@ -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"] resource_name = "trigger" diff --git a/project/project.godot b/project/project.godot index 7d875dc..561ce7d 100644 --- a/project/project.godot +++ b/project/project.godot @@ -11,18 +11,13 @@ config_version=5 [application] config/name="Visualisation for MARSH" -run/main_scene="uid://ba764fx7hx8ei" +run/main_scene="uid://crq3o0eu4y8ya" config/features=PackedStringArray("4.4", "GL Compatibility") 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] -enabled=PackedStringArray("res://addons/godot-xr-tools/plugin.cfg") +enabled=PackedStringArray() [physics] @@ -36,4 +31,6 @@ renderer/rendering_method.mobile="gl_compatibility" [xr] openxr/enabled=true +openxr/reference_space=0 +openxr/foveation_level=3 shaders/enabled=true diff --git a/project/start-xr.tscn b/project/start-xr.tscn deleted file mode 100644 index adc9b83..0000000 --- a/project/start-xr.tscn +++ /dev/null @@ -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") diff --git a/project/start_vr.gd b/project/start_vr.gd new file mode 100644 index 0000000..c610b4c --- /dev/null +++ b/project/start_vr.gd @@ -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() diff --git a/project/start_vr.gd.uid b/project/start_vr.gd.uid new file mode 100644 index 0000000..036f8dc --- /dev/null +++ b/project/start_vr.gd.uid @@ -0,0 +1 @@ +uid://dfcjdxesyn3l0 diff --git a/update_addons.py b/update_addons.py deleted file mode 100644 index 151aea3..0000000 --- a/update_addons.py +++ /dev/null @@ -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, -) From c83135a67b33b25b2ab550df89ee1e52ec7f3339 Mon Sep 17 00:00:00 2001 From: "Marek S. Lukasiewicz" Date: Tue, 18 Feb 2025 12:47:35 +0100 Subject: [PATCH 6/6] Track connection to manager and receive MANUAL_CONTROL --- src/marshconnector.cpp | 111 ++++++++++++++++++++++++++++++++++++++--- src/marshconnector.h | 25 ++++++++++ 2 files changed, 129 insertions(+), 7 deletions(-) diff --git a/src/marshconnector.cpp b/src/marshconnector.cpp index 3a1990c..0d171a6 100644 --- a/src/marshconnector.cpp +++ b/src/marshconnector.cpp @@ -2,6 +2,7 @@ #include "godot_cpp/classes/engine.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/core/math.hpp" #include "godot_cpp/core/memory.hpp" @@ -12,18 +13,29 @@ #include "godot_cpp/variant/transform3d.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/common/mavlink_msg_command_long.h" +#include "mavlink/common/mavlink_msg_manual_control.h" #include "mavlink/common/mavlink_msg_sim_state.h" #include "mavlink/mavlink_helpers.h" - -// How much extra bytes are needed on top of message payload length -#define MAVLINK_OVERHEAD 12 +#include "mavlink/mavlink_types.h" using namespace godot; 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"), &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() { @@ -38,6 +50,15 @@ MarshConnector::MarshConnector() { heartbeat_timer->connect("timeout", Callable(this, "send_heartbeat")); socket = memnew(PacketPeerUDP); + + manager_connected = false; + + 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() { @@ -72,17 +93,19 @@ Error MarshConnector::send_heartbeat() { mavlink_message_t message; mavlink_msg_heartbeat_encode_chan(1, MARSH_COMP_ID_VISUALISATION, 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); PackedByteArray array; for (int i = 0; i < sizeof(send_buffer); i++) { array.append(send_buffer[i]); } - // print_line("Data array ", array); const auto result = socket->put_packet(array); - // print_line("Send result ", result); return result; } @@ -109,6 +132,12 @@ void MarshConnector::receive_data(const PackedByteArray &data) { case MAVLINK_MSG_ID_PARAM_SET: handle_param(message); break; + case MAVLINK_MSG_ID_MANUAL_CONTROL: + handle_manual_control(message); + break; + case MAVLINK_MSG_ID_HEARTBEAT: + handle_heartbeat(message); + break; default: break; } @@ -130,6 +159,13 @@ Transform3D MarshConnector::get_aircraft() { 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, double longitude) { @@ -212,3 +248,64 @@ void MarshConnector::handle_attitude(mavlink_message_t message) { void MarshConnector::handle_param(mavlink_message_t message) { 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(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; +} diff --git a/src/marshconnector.h b/src/marshconnector.h index fff1971..cb7eb49 100644 --- a/src/marshconnector.h +++ b/src/marshconnector.h @@ -33,8 +33,25 @@ public: Error set_parameter(const String &id, float value); // Get current parameter value float get_parameter(const String &id); + // Get current state of the 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 // taking into account current configuration. @@ -55,6 +72,10 @@ private: void handle_local_position(mavlink_message_t message); void handle_attitude(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 // Not an enum class on purpose, to make use more convenient, it's scoped @@ -78,10 +99,14 @@ private: // TODO: Interpolate with some delay Vector3 last_location; Quaternion last_rotation; + Vector4 last_controls; double time_passed; Timer *heartbeat_timer; PacketPeerUDP *socket; + + bool manager_connected; + Timer *manager_timer; }; } // namespace godot