diff --git a/LICENSE-CC-BY.txt b/LICENSE-CC-BY.txt new file mode 100644 index 0000000..4ea99c2 --- /dev/null +++ b/LICENSE-CC-BY.txt @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSE.txt b/LICENSE-MIT.txt similarity index 100% rename from LICENSE.txt rename to LICENSE-MIT.txt diff --git a/README.md b/README.md index e7525a6..23d4d29 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,9 @@ # Visualisation for MARSH This is a visualisation module for [Modular Architecture for Reconfigurable Simulation of Helicopters](https://marsh-sim.github.io/). -The project is configured to display cockpit instruments with [lidia](https://pypi.org/project/lidia) Python package. +For documentation, see [MARSH Documentation - Visualisation](https://marsh-sim.github.io/nodes/visualisation/) -Named in this order so not everything starts with the same word. +## License -## Usage - -The application will attempt to start in VR, but if it fails it will display the default pilot viewpoint looking forward. - -### Parameters - -These can be modified over MAVLink, for example using MARSH Manager. - -Example changes to `NAV_OFS_` parameters to start in given positions: - -- Sidestep, vertical repositioning: all default -- Accelerate/decelerate: `HDG` = -90 -- Hover maneuver: `X` = 50, `Y` = 50 -- Start of slalom: `HDG` = -90, `X` = -100, `Y` = 300 - -#### NAV_OFS_HDG - -Displayed heading relative to default position, in degrees. - -#### NAV_OFS_X - -Starting position offset, forward in default orientation, in meters. - -#### NAV_OFS_X - -Starting position offset, right in default orientation, in meters. - -#### LOCAL_FRAME_LAT - -Latitude of local coordinate frame origin when using messages with global position, in degrees. - -#### LOCAL_FRAME_LON - -Longitude of local coordinate frame origin when using messages with global position, in degrees. - -## Development - -The asset files like textures and models are very big, so the project also uses [Git Large File Storage](https://git-lfs.com/). -Once installed, it should work correctly with usual `git` commands but you may need to run this if you install it after cloning the repository: - -```sh -git lfs pull -``` - -This repository uses [Git Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules), to get all the code you need to run either: - -```sh -git clone --recurse-submodules
-# Or at any later point -git submodule update --init --recursive -``` - -Using Godot v4.4. -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). - -For updating MAVLink generator, you additionally `pip install future`. -It is recommended to install any packages in a [virtual environment](https://docs.python.org/3/library/venv.html), for example: - -```sh -python3 -m venv venv -source venv/bin/activate -pip install future -``` - -Some files are generated, run the following commands on first setup and when dependencies change: - -```sh -godot --dump-extension-api # after updating Godot -python update_mavlink.py # after updating MAVLink dialect -python update_addons.py # after changing any addon submodules -scons compile_commands # after modifying SConstruct -cd project/addons/gdcef; python build.py -``` - -### Windows setup - -The most convenient way of getting a C++ compiler is via the LLVM-MinGW project (by the way, it optimizes the binary more). -Download the `llvm-mingw-...-msvcrt-x86_64.zip` from the [Releases page](https://github.com/mstorsjo/llvm-mingw/releases). -Unzip the archive to some location you like, and add its `bin` folder to `Path` using the "Edit environment variables for your account" program. - -### Build - -To build the GDExtension binary run SCons in the repository root, the default arguments have been added to the file. - -```sh -scons -``` - -When building for Windows with MinGW-LLVM, you need to [select this compiler](https://docs.godotengine.org/en/stable/contributing/development/compiling/compiling_for_windows.html#selecting-a-compiler): - -```powershell -scons platform=windows use_mingw=yes use_llvm=yes -``` - -## Roadmap - -- Try to have some acceptable setup for Varjo even without the tracker -- Rework the fly area - - Make it bigger - - Keep it square to avoid assymetric geometry - - Don't use the flat plane, just flatten the terrain - - Make the terrain mostly green, but add some small patches of different color -- Fix z-figthing when looking at the runway from afar -- Reduce pixel artifacts - - Adjust antialiasing - - Ensure there are mipmaps and they're used +Source code available under the terms of [The MIT License](https://opensource.org/license/mit). +Other assets available under [Creative Commons Attribution 4.0 (CC-BY)](https://creativecommons.org/licenses/by/4.0/). diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..8f8d3d7 --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +clangd = "latest" +"pipx:scons" = "latest" diff --git a/project/ads33-terrain/Cones_scene_terrain.tscn b/project/ads33-terrain/Cones_scene_terrain.tscn index 7d7ce4b..d55a8ce 100644 --- a/project/ads33-terrain/Cones_scene_terrain.tscn +++ b/project/ads33-terrain/Cones_scene_terrain.tscn @@ -1264,9 +1264,11 @@ surface_material_override/0 = SubResource("StandardMaterial3D_073i7") [node name="Cone80cm" parent="Sidestep/Marques_0ft" instance=ExtResource("2_hj5f7")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -2.438) +visible = false [node name="Cone80cm3" parent="Sidestep/Marques_0ft" instance=ExtResource("2_hj5f7")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2.438) +visible = false [node name="Carre_ext_400ft" type="Node3D" parent="Sidestep"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 121.92, 0, 0) diff --git a/project/aircraft/aircraft.gd b/project/aircraft/aircraft.gd index 9ecfbde..5e01092 100644 --- a/project/aircraft/aircraft.gd +++ b/project/aircraft/aircraft.gd @@ -1,15 +1,18 @@ extends Node3D signal use_fallback(active: bool) +signal fog_density_changed(new_density: float) ## Rotation of cyclic stick model for full control deflection, in degrees @export var range_cyclic: float = 30 ## Main rotor speed, in revolutions per minute @export var rotor_rpm: float = 500 @export var track_platform: bool = false +@export var fog_values: Array[float] = [0.0, -1.0, -2.0, -3.0, -4.0] ## Current angle of rotor rotation var _rotor_azimuth: float = 0 +var fog_index: int = 0 @onready var connector: MarshConnector = $MarshConnector @onready var skeleton: Skeleton3D = $"Mi-2/Armature/Skeleton3D" @@ -25,6 +28,8 @@ var _rotor_azimuth: float = 0 @onready var bone_cyclic: int = skeleton.find_bone("Cyclic") @onready var bone_rotor: int = skeleton.find_bone("Rotor") +@onready var instruments: Node = $AttitudeRoot/Instruments + func _process(delta: float) -> void: var target: Transform3D = connector.get_aircraft() position = target.origin @@ -43,6 +48,11 @@ func _process(delta: float) -> void: var cyc_angles_rad: Vector2 = cyclic * deg_to_rad(range_cyclic) var cyc_att := Quaternion.from_euler(Vector3(cyc_angles_rad.y, cyc_angles_rad.x, 0)) skeleton.set_bone_pose_rotation(bone_cyclic, cyc_att * cyc_rest) + + # Update the instruments + instruments.call("set_controls", connector.get_controls()) + instruments.call("set_trim", connector.get_trim()) + instruments.call("update_pfd", connector.get_aircraft(), connector.get_velocity()) # Spin the rotor only when receiving flight data if connector.get_model_connected(): @@ -58,6 +68,14 @@ func _process(delta: float) -> void: xr_origin.transform = tracker_mount.transform * tracker.transform.affine_inverse() recent_tracking.start() +func _input(event): + if event.is_action_pressed("fog_cycle"): + var step = 1 + if event is InputEventWithModifiers and event.shift_pressed: + step = -1 + # The index is updated in the signal callback to handle parameter changes + connector.fog_density = fog_values[(fog_index + step) % len(fog_values)] + # Only reset the position when tracking is lost for some time func _on_recent_tracking_timeout() -> void: xr_origin.transform = Transform3D.IDENTITY @@ -65,3 +83,14 @@ func _on_recent_tracking_timeout() -> void: func _on_vr_setup_failed() -> void: xr_camera.current = false use_fallback.emit(true) + +func _on_marsh_connector_fog_density_changed(new_density: float) -> void: + # Forward to parent + fog_density_changed.emit(new_density) + + # Round down to nearest defined value or assign the last one (so it will disable on next cycle) + # Example: -1.5 should select -2 and on next cycle it will be -3 + var new_index = fog_values.find_custom(func (d): return d <= new_density) + if new_index == -1: + new_index = len(fog_values) - 1 + fog_index = new_index diff --git a/project/aircraft/aircraft.tscn b/project/aircraft/aircraft.tscn index d572319..edd497e 100644 --- a/project/aircraft/aircraft.tscn +++ b/project/aircraft/aircraft.tscn @@ -3,7 +3,7 @@ [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"] [ext_resource type="PackedScene" uid="uid://fmygcraoturj" path="res://reference_axes.tscn" id="3_2bi7g"] -[ext_resource type="PackedScene" uid="uid://cis4s43ubuynp" path="res://instruments.tscn" id="3_5w717"] +[ext_resource type="PackedScene" uid="uid://cis4s43ubuynp" path="res://instruments/instruments.tscn" id="3_5w717"] [ext_resource type="Script" uid="uid://bt32fse84itrp" path="res://aircraft/fallback_input.gd" id="3_gf6ud"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mrxe8"] @@ -66,11 +66,11 @@ fov = 60.0 [node name="Instruments" parent="AttitudeRoot" instance=ExtResource("3_5w717")] transform = Transform3D(-0.5, -1.27582e-08, 7.43353e-08, 4.33325e-10, 0.4923, 0.0874084, -7.5421e-08, 0.0874084, -0.4923, 0.00724897, -1.1581, 2.40391) -lidia_hostname = "127.0.0.1" [node name="MarshConnector" type="MarshConnector" parent="."] [connection signal="use_fallback" from="." to="AttitudeRoot/PilotEyes" method="_on_aircraft_use_fallback"] [connection signal="timeout" from="AttitudeRoot/PilotEyes/PilotFloor/XROrigin3D/RecentTracking" to="." method="_on_recent_tracking_timeout"] +[connection signal="fog_density_changed" from="MarshConnector" to="." method="_on_marsh_connector_fog_density_changed"] [editable path="Mi-2"] diff --git a/project/aircraft/fallback_input.gd b/project/aircraft/fallback_input.gd index a0cf4de..ecbaade 100644 --- a/project/aircraft/fallback_input.gd +++ b/project/aircraft/fallback_input.gd @@ -9,9 +9,11 @@ const CAMERA_X_ROT_MIN := deg_to_rad(-85) const CAMERA_X_ROT_MAX := deg_to_rad(70) @export var use_mouse: bool = true +@export var pov_move_speed = 0.05 var active: bool = false var initial_rotation: Vector3 +var initial_pov_position: Vector3 @onready var camera_base = $"." @onready var camera = $FallbackCamera @@ -20,6 +22,7 @@ func _ready() -> void: set_process(false) set_process_input(false) initial_rotation = camera.rotation + initial_pov_position = position func _on_aircraft_use_fallback(active_in: bool) -> void: if active_in: @@ -51,7 +54,15 @@ func _input(event): if use_mouse and event is InputEventMouseMotion: var camera_speed_this_frame = CAMERA_MOUSE_ROTATION_SPEED rotate_camera(event.relative * camera_speed_this_frame * scale_factor) + + if event.is_action_pressed("pov_reset"): + position = initial_pov_position +func _process(delta: float): + var horizontal = Input.get_vector("pov_left", "pov_right", "pov_forward", "pov_back") + var vertical = Input.get_axis("pov_down", "pov_up") + # Currently PilotEyes has Yaw 180, so horizontal axes need to be inverted + position += delta * pov_move_speed * Vector3(-horizontal.x, vertical, -horizontal.y) func rotate_camera(move): camera_base.rotate_y(-move.x) diff --git a/project/fog_controller.gd b/project/fog_controller.gd new file mode 100644 index 0000000..248abf7 --- /dev/null +++ b/project/fog_controller.gd @@ -0,0 +1,8 @@ +extends WorldEnvironment + +func _on_fog_density_changed(new_density: float) -> void: + if new_density == 0.0 or not is_finite(new_density): + environment.fog_enabled = false + else: + environment.fog_enabled = true + environment.fog_density = 10 ** new_density diff --git a/project/fog_controller.gd.uid b/project/fog_controller.gd.uid new file mode 100644 index 0000000..285eeb8 --- /dev/null +++ b/project/fog_controller.gd.uid @@ -0,0 +1 @@ +uid://6g3b2ohwbkyv diff --git a/project/instruments.gd b/project/instruments.gd index 28a25e6..de11eba 100644 --- a/project/instruments.gd +++ b/project/instruments.gd @@ -1,33 +1,12 @@ extends Node -const PFD_PATH: String = "pfd#adi,vsi,alt,ias,rht,rpm,hsi" -const CONTROLS_PATH: String = "controls#collective,cyclic,rudder" +@onready var controls: Node = $SubViewport2/Controls +@onready var pfd: Node = $SubViewport/PFD -@export var lidia_hostname: String = "localhost" -@export var lidia_port: int = 5555 - -func _ready(): - if !$CEF.initialize({"incognito":true, "locale":"en-US"}): - push_error($CEF.get_error()) - get_tree().quit() - return - print("CEF version: " + $CEF.get_full_version()) - - # Wait one frame for the texture rect to get its size - await get_tree().process_frame - - var browser_pfd = $CEF.create_browser( - "http://{}:{}/{}".format([lidia_hostname, lidia_port, PFD_PATH], "{}"), - $SubViewport/TextureRect, - { "frame_rate": 90, "javascript": true }, - ) - browser_pfd.name = "pfd" - browser_pfd.enable_ad_block(false) # Required for lidia static assets - - var browser_ctrl = $CEF.create_browser( - "http://{}:{}/{}".format([lidia_hostname, lidia_port, CONTROLS_PATH], "{}"), - $SubViewport2/TextureRect, - { "frame_rate": 90, "javascript": true }, - ) - browser_ctrl.name = "controls" - browser_ctrl.enable_ad_block(false) # Required for lidia static assets +# Propagate the calls to child +func set_controls(current: Vector4): + controls.set("controls", current) +func set_trim(trim: Vector4): + controls.set("trim", trim) +func update_pfd(aircraft: Transform3D, velocity: Vector3): + pfd.call("update", aircraft, velocity) diff --git a/project/instruments/RobotoMono-LICENSE.txt b/project/instruments/RobotoMono-LICENSE.txt new file mode 100644 index 0000000..f08da9b --- /dev/null +++ b/project/instruments/RobotoMono-LICENSE.txt @@ -0,0 +1,41 @@ +Roboto Mono + +Copyright 2015 Google + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +===================================================================== + +Socket.IO + +MIT License + +Copyright (c) 2014-2022 Guillermo Rauch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/project/instruments/RobotoMono-Regular.ttf b/project/instruments/RobotoMono-Regular.ttf new file mode 100644 index 0000000..d9371a1 Binary files /dev/null and b/project/instruments/RobotoMono-Regular.ttf differ diff --git a/project/instruments/RobotoMono-Regular.ttf.import b/project/instruments/RobotoMono-Regular.ttf.import new file mode 100644 index 0000000..5f58fa5 --- /dev/null +++ b/project/instruments/RobotoMono-Regular.ttf.import @@ -0,0 +1,35 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://chqgmpsthpbgt" +path="res://.godot/imported/RobotoMono-Regular.ttf-48a8b9dfd581c05f54f90e11c9fc9d5f.fontdata" + +[deps] + +source_file="res://instruments/RobotoMono-Regular.ttf" +dest_files=["res://.godot/imported/RobotoMono-Regular.ttf-48a8b9dfd581c05f54f90e11c9fc9d5f.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/project/instruments/controls-background.png b/project/instruments/controls-background.png new file mode 100644 index 0000000..01648dd --- /dev/null +++ b/project/instruments/controls-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94045c4fc08b5d93d3a2436de2c8399341246ec157f8ac3dbf0a89806a9e0e91 +size 2842 diff --git a/project/instruments/controls-background.png.import b/project/instruments/controls-background.png.import new file mode 100644 index 0000000..b6fb2d4 --- /dev/null +++ b/project/instruments/controls-background.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b0oeokas7mq4q" +path="res://.godot/imported/controls-background.png-626db610a70b7b467da1123d55529d5c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/controls-background.png" +dest_files=["res://.godot/imported/controls-background.png-626db610a70b7b467da1123d55529d5c.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/instruments/controls-ref.png b/project/instruments/controls-ref.png new file mode 100644 index 0000000..f4f3726 --- /dev/null +++ b/project/instruments/controls-ref.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4eef65cc3a825494e9d405ea68c480de3da6fe2e62f202e5d387771331d2904f +size 15488 diff --git a/project/instruments/controls-ref.png.import b/project/instruments/controls-ref.png.import new file mode 100644 index 0000000..f623b6a --- /dev/null +++ b/project/instruments/controls-ref.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5bcvbphgg1nk" +path="res://.godot/imported/controls-ref.png-ed1b9d803223424fa895ad19151b7e9f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/controls-ref.png" +dest_files=["res://.godot/imported/controls-ref.png-ed1b9d803223424fa895ad19151b7e9f.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/instruments/controls.gd b/project/instruments/controls.gd new file mode 100644 index 0000000..5211b83 --- /dev/null +++ b/project/instruments/controls.gd @@ -0,0 +1,30 @@ +@tool +class_name Controls +extends Node2D +## Recreate the controls page from lidia Python package + +const AXIS_SIZE: float = 326 + +@export var controls: Vector4: + set(value): + controls = value + current_cyclic.position.x = AXIS_SIZE / 2.0 * controls.x + current_cyclic.position.y = AXIS_SIZE / 2.0 * controls.y + current_pedals.position.x = AXIS_SIZE / 2.0 * controls.z + current_collective.position.y = -AXIS_SIZE * controls.w + +@export var trim: Vector4: + set(value): + trim = value + trim_lateral.position.x = AXIS_SIZE / 2.0 * trim.x + trim_longitudinal.position.y = AXIS_SIZE / 2.0 * trim.y + trim_pedals.position.x = AXIS_SIZE / 2.0 * trim.z + trim_collective.position.y = -AXIS_SIZE * trim.w + +@onready var trim_longitudinal: Node2D = $CyclicOrigin/TrimLongitudinal +@onready var trim_lateral: Node2D = $CyclicOrigin/TrimLateral +@onready var current_cyclic: Node2D = $CyclicOrigin/CurrentCyclic +@onready var trim_collective: Node2D = $CollectiveOrigin/TrimCollective +@onready var current_collective: Node2D = $CollectiveOrigin/CurrentCollective +@onready var trim_pedals: Node2D = $PedalsOrigin/TrimPedals +@onready var current_pedals: Node2D = $PedalsOrigin/CurrentPedals diff --git a/project/instruments/controls.gd.uid b/project/instruments/controls.gd.uid new file mode 100644 index 0000000..0a62356 --- /dev/null +++ b/project/instruments/controls.gd.uid @@ -0,0 +1 @@ +uid://ds6f8cur05ka0 diff --git a/project/instruments/controls.tscn b/project/instruments/controls.tscn new file mode 100644 index 0000000..be77aeb --- /dev/null +++ b/project/instruments/controls.tscn @@ -0,0 +1,54 @@ +[gd_scene load_steps=7 format=3 uid="uid://bgkpwebqksth5"] + +[ext_resource type="Script" uid="uid://ds6f8cur05ka0" path="res://instruments/controls.gd" id="1_g7kvs"] +[ext_resource type="Texture2D" uid="uid://b0oeokas7mq4q" path="res://instruments/controls-background.png" id="2_wbvwt"] +[ext_resource type="Texture2D" uid="uid://c561m3pvc0kop" path="res://instruments/trim-long.png" id="3_37ekc"] +[ext_resource type="Texture2D" uid="uid://wdqnlguxmi2a" path="res://instruments/current-dot.png" id="4_4updv"] +[ext_resource type="Texture2D" uid="uid://dytpn8rcdimr" path="res://instruments/trim-short.png" id="5_ni3tk"] +[ext_resource type="Texture2D" uid="uid://dkda6ju5daocf" path="res://instruments/current-arrow.png" id="6_mfntf"] + +[node name="Controls" type="Node2D"] +script = ExtResource("1_g7kvs") + +[node name="Background" type="Sprite2D" parent="."] +position = Vector2(256, 256) +texture = ExtResource("2_wbvwt") + +[node name="CyclicOrigin" type="Node2D" parent="."] +position = Vector2(309, 189) + +[node name="TrimLongitudinal" type="Sprite2D" parent="CyclicOrigin"] +position = Vector2(10, 0) +texture = ExtResource("3_37ekc") + +[node name="TrimLateral" type="Sprite2D" parent="CyclicOrigin"] +position = Vector2(0, 10) +rotation = 1.5708 +texture = ExtResource("3_37ekc") + +[node name="CurrentCyclic" type="Sprite2D" parent="CyclicOrigin"] +texture = ExtResource("4_4updv") + +[node name="CollectiveOrigin" type="Node2D" parent="."] +position = Vector2(72.5, 352) + +[node name="TrimCollective" type="Sprite2D" parent="CollectiveOrigin"] +position = Vector2(-10.5, 0) +texture = ExtResource("5_ni3tk") + +[node name="CurrentCollective" type="Sprite2D" parent="CollectiveOrigin"] +position = Vector2(20.5, 0) +texture = ExtResource("6_mfntf") + +[node name="PedalsOrigin" type="Node2D" parent="."] +position = Vector2(309, 425.5) + +[node name="TrimPedals" type="Sprite2D" parent="PedalsOrigin"] +position = Vector2(0, 10.5) +rotation = -1.5708 +texture = ExtResource("5_ni3tk") + +[node name="CurrentPedals" type="Sprite2D" parent="PedalsOrigin"] +position = Vector2(0, -21.5) +rotation = -1.5708 +texture = ExtResource("6_mfntf") diff --git a/project/instruments/current-arrow.png b/project/instruments/current-arrow.png new file mode 100644 index 0000000..e4aa8e4 --- /dev/null +++ b/project/instruments/current-arrow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9f32544518014aa8fbb852c82b5a9a32e78cc96724a095e261f3bb870602e4a +size 1228 diff --git a/project/instruments/current-arrow.png.import b/project/instruments/current-arrow.png.import new file mode 100644 index 0000000..430c1a0 --- /dev/null +++ b/project/instruments/current-arrow.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dkda6ju5daocf" +path="res://.godot/imported/current-arrow.png-8e092288abc9ec8d31cbc8f708a9c800.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/current-arrow.png" +dest_files=["res://.godot/imported/current-arrow.png-8e092288abc9ec8d31cbc8f708a9c800.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/instruments/current-dot.png b/project/instruments/current-dot.png new file mode 100644 index 0000000..c7eea25 --- /dev/null +++ b/project/instruments/current-dot.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac4ea7768fdae5a5f8e6f2ab7d48d332a6ab09f78d520c4fe0404c414aaf1b69 +size 1047 diff --git a/project/instruments/current-dot.png.import b/project/instruments/current-dot.png.import new file mode 100644 index 0000000..ce845b9 --- /dev/null +++ b/project/instruments/current-dot.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wdqnlguxmi2a" +path="res://.godot/imported/current-dot.png-789da8759af9526ce5f8705121a1077d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/current-dot.png" +dest_files=["res://.godot/imported/current-dot.png-789da8759af9526ce5f8705121a1077d.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/instruments/heading-ticks.png b/project/instruments/heading-ticks.png new file mode 100644 index 0000000..ab34048 --- /dev/null +++ b/project/instruments/heading-ticks.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb45a774c6a9381a172996207b5b2b3c23e902990b22f8e4888d282dadc3ac0c +size 21042 diff --git a/project/instruments/heading-ticks.png.import b/project/instruments/heading-ticks.png.import new file mode 100644 index 0000000..19e6c58 --- /dev/null +++ b/project/instruments/heading-ticks.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c36kaqyfalb2v" +path="res://.godot/imported/heading-ticks.png-8d0b08e5e3af463192c04d5a2068a1e0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/heading-ticks.png" +dest_files=["res://.godot/imported/heading-ticks.png-8d0b08e5e3af463192c04d5a2068a1e0.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/instruments/horizon-mask.png b/project/instruments/horizon-mask.png new file mode 100644 index 0000000..24cd07a --- /dev/null +++ b/project/instruments/horizon-mask.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b5aa4314b575de2f8a680150f96f3f6510984356293977c28201e637e4e7229 +size 5964 diff --git a/project/instruments/horizon-mask.png.import b/project/instruments/horizon-mask.png.import new file mode 100644 index 0000000..d6e70d0 --- /dev/null +++ b/project/instruments/horizon-mask.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5nbrnus8pcyd" +path="res://.godot/imported/horizon-mask.png-03d2bafdd84b9816ae1fe68fc4baf43d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/horizon-mask.png" +dest_files=["res://.godot/imported/horizon-mask.png-03d2bafdd84b9816ae1fe68fc4baf43d.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/instruments/horizon-overlay.png b/project/instruments/horizon-overlay.png new file mode 100644 index 0000000..100f1e8 --- /dev/null +++ b/project/instruments/horizon-overlay.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:496d869e45085d3547edeab73e93711320bd7f2dc7f314f28db47750348f6dce +size 4334 diff --git a/project/instruments/horizon-overlay.png.import b/project/instruments/horizon-overlay.png.import new file mode 100644 index 0000000..cae3ce4 --- /dev/null +++ b/project/instruments/horizon-overlay.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b703lu8yhaoav" +path="res://.godot/imported/horizon-overlay.png-7e128e1a0dd8183251d6119879899864.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/horizon-overlay.png" +dest_files=["res://.godot/imported/horizon-overlay.png-7e128e1a0dd8183251d6119879899864.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/instruments/horizon.png b/project/instruments/horizon.png new file mode 100644 index 0000000..19ecd6f --- /dev/null +++ b/project/instruments/horizon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20b2cd5de91fbc589e3a0c33b67b8837b535a2c41a0f256940c779bfb4339f98 +size 16585 diff --git a/project/instruments/horizon.png.import b/project/instruments/horizon.png.import new file mode 100644 index 0000000..9196986 --- /dev/null +++ b/project/instruments/horizon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bxdfrv8bovdai" +path="res://.godot/imported/horizon.png-9384af1095d9251c4f8861113a545ccf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/horizon.png" +dest_files=["res://.godot/imported/horizon.png-9384af1095d9251c4f8861113a545ccf.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/instruments.tscn b/project/instruments/instruments.tscn similarity index 71% rename from project/instruments.tscn rename to project/instruments/instruments.tscn index 4ececc9..dcd9dc1 100644 --- a/project/instruments.tscn +++ b/project/instruments/instruments.tscn @@ -1,6 +1,8 @@ -[gd_scene load_steps=7 format=3 uid="uid://cis4s43ubuynp"] +[gd_scene load_steps=9 format=3 uid="uid://cis4s43ubuynp"] -[ext_resource type="Script" uid="uid://01bmfj4wthwg" path="res://instruments.gd" id="1_h5at3"] +[ext_resource type="Script" uid="uid://01bmfj4wthwg" path="res://instruments.gd" id="1_wlkep"] +[ext_resource type="PackedScene" uid="uid://bgkpwebqksth5" path="res://instruments/controls.tscn" id="2_372d7"] +[ext_resource type="PackedScene" uid="uid://b26tu6wb2h3ce" path="res://instruments/pfd.tscn" id="2_uhiu3"] [sub_resource type="QuadMesh" id="QuadMesh_nowl7"] @@ -19,15 +21,12 @@ resource_local_to_scene = true albedo_texture = SubResource("ViewportTexture_h5at3") [node name="Instruments" type="Node3D"] -script = ExtResource("1_h5at3") -lidia_hostname = "192.168.1.2" +script = ExtResource("1_wlkep") [node name="SubViewport" type="SubViewport" parent="."] +size = Vector2i(1024, 1024) -[node name="TextureRect" type="TextureRect" parent="SubViewport"] -offset_right = 512.0 -offset_bottom = 512.0 -expand_mode = 5 +[node name="PFD" parent="SubViewport" instance=ExtResource("2_uhiu3")] [node name="Quad" type="MeshInstance3D" parent="."] transform = Transform3D(0.8, 0, 0, 0, 0.8, 0, 0, 0, 0.8, -0.35, 0, 0) @@ -36,10 +35,7 @@ surface_material_override/0 = SubResource("StandardMaterial3D_h5at3") [node name="SubViewport2" type="SubViewport" parent="."] -[node name="TextureRect" type="TextureRect" parent="SubViewport2"] -offset_right = 512.0 -offset_bottom = 512.0 -expand_mode = 5 +[node name="Controls" parent="SubViewport2" instance=ExtResource("2_372d7")] [node name="Quad2" type="MeshInstance3D" parent="."] transform = Transform3D(0.6, 0, 0, 0, 0.6, 0, 0, 0, 0.6, 0.425, 0, 0) diff --git a/project/instruments/pfd-background.png b/project/instruments/pfd-background.png new file mode 100644 index 0000000..d160558 --- /dev/null +++ b/project/instruments/pfd-background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24e12f54cb40f58eb2e51247e6f90bc9daf9d46017ac7f531364d099b435889d +size 60304 diff --git a/project/instruments/pfd-background.png.import b/project/instruments/pfd-background.png.import new file mode 100644 index 0000000..4efb713 --- /dev/null +++ b/project/instruments/pfd-background.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://colp4xsovikmv" +path="res://.godot/imported/pfd-background.png-99626101f4d3a30d7dd189ac71adf1c0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/pfd-background.png" +dest_files=["res://.godot/imported/pfd-background.png-99626101f4d3a30d7dd189ac71adf1c0.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/instruments/pfd-ref.png b/project/instruments/pfd-ref.png new file mode 100644 index 0000000..b765e28 --- /dev/null +++ b/project/instruments/pfd-ref.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44f6cebabe3ae23e9993b7485ee2fb3904a3312dd1ba388d6e76306e856ceccc +size 133172 diff --git a/project/instruments/pfd-ref.png.import b/project/instruments/pfd-ref.png.import new file mode 100644 index 0000000..3f31bfa --- /dev/null +++ b/project/instruments/pfd-ref.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://toojp8r3424r" +path="res://.godot/imported/pfd-ref.png-18e0fc5274406321f68279cb4defcf25.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/pfd-ref.png" +dest_files=["res://.godot/imported/pfd-ref.png-18e0fc5274406321f68279cb4defcf25.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/instruments/pfd.gd b/project/instruments/pfd.gd new file mode 100644 index 0000000..43790a0 --- /dev/null +++ b/project/instruments/pfd.gd @@ -0,0 +1,58 @@ +@tool +class_name PFD +extends Node2D + +@export var euler_attitude_deg: Vector3: + set(value): + euler_attitude_deg = value + horizon_pivot.rotation = deg_to_rad(-value.x) + const px_per_deg: float = 6.56 + horizon_background.position.y = px_per_deg * clamp(value.y, -30, 30) + heading_ticks.rotation = deg_to_rad(-value.z) + heading_label.text = "%03d" % (int(round(value.z) + 360) % 360) + +@export var airspeed: float: + set(value): + airspeed = value + airspeed_label.text = str(int(round(value))) + +@export var groundspeed: float: + set(value): + groundspeed = value + groundspeed_label.text = str(int(round(value))) + +@export var altitude: float: + set(value): + altitude = value + var step = 1 + altitude_label.text = str(int(step * round(value / step))) + +@export var climbrate: float = 1000: + set(value): + climbrate = value + climbrate_arrow.scale.y = clamp(value / 1000.0, -1, 1) + +@onready var horizon_pivot: Node2D = $HorizonMask/HorizonOrigin/Pivot +@onready var horizon_background: Node2D = $HorizonMask/HorizonOrigin/Pivot/Background +@onready var heading_ticks: Node2D = $HeadingOrigin/Ticks +@onready var climbrate_arrow: Node2D = $ClimbrateOrigin/Polygon2D +@onready var airspeed_label: Label = $Airspeed +@onready var groundspeed_label: Label = $Groundspeed +@onready var altitude_label: Label = $Altitude +@onready var heading_label: Label = $Heading + +func update(aircraft: Transform3D, velocity: Vector3): + var godot_euler = aircraft.basis.get_euler() + euler_attitude_deg = Vector3(godot_euler.z, -godot_euler.x, -godot_euler.y) * rad_to_deg(1) + + const mps_to_kt = 1.94384 + var v_local = aircraft.basis.inverse() * velocity + airspeed = max(0, v_local.z) * mps_to_kt + var v_horizontal = Vector3(velocity.x, 0, velocity.z) + groundspeed = v_horizontal.length() * mps_to_kt + + const mps_to_fpm = 196.848 + climbrate = velocity.y * mps_to_fpm + + const m_to_ft = 3.2808 + altitude = aircraft.origin.y * m_to_ft diff --git a/project/instruments/pfd.gd.uid b/project/instruments/pfd.gd.uid new file mode 100644 index 0000000..fbc3cd3 --- /dev/null +++ b/project/instruments/pfd.gd.uid @@ -0,0 +1 @@ +uid://368tfiyen071 diff --git a/project/instruments/pfd.tscn b/project/instruments/pfd.tscn new file mode 100644 index 0000000..306ed2a --- /dev/null +++ b/project/instruments/pfd.tscn @@ -0,0 +1,103 @@ +[gd_scene load_steps=9 format=3 uid="uid://b26tu6wb2h3ce"] + +[ext_resource type="Texture2D" uid="uid://colp4xsovikmv" path="res://instruments/pfd-background.png" id="1_inkqg"] +[ext_resource type="Script" uid="uid://368tfiyen071" path="res://instruments/pfd.gd" id="1_t02it"] +[ext_resource type="Texture2D" uid="uid://c5nbrnus8pcyd" path="res://instruments/horizon-mask.png" id="2_t02it"] +[ext_resource type="Texture2D" uid="uid://bxdfrv8bovdai" path="res://instruments/horizon.png" id="3_kwqn1"] +[ext_resource type="Texture2D" uid="uid://b703lu8yhaoav" path="res://instruments/horizon-overlay.png" id="4_1vq1p"] +[ext_resource type="Texture2D" uid="uid://c36kaqyfalb2v" path="res://instruments/heading-ticks.png" id="5_3w2cf"] +[ext_resource type="FontFile" uid="uid://chqgmpsthpbgt" path="res://instruments/RobotoMono-Regular.ttf" id="6_wtx4e"] +[ext_resource type="Texture2D" uid="uid://toojp8r3424r" path="res://instruments/pfd-ref.png" id="7_86xjv"] + +[node name="PFD" type="Node2D"] +script = ExtResource("1_t02it") + +[node name="Background" type="Sprite2D" parent="."] +position = Vector2(512, 512) +texture = ExtResource("1_inkqg") + +[node name="HorizonMask" type="Sprite2D" parent="."] +clip_children = 1 +position = Vector2(512, 512) +texture = ExtResource("2_t02it") + +[node name="HorizonOrigin" type="Node2D" parent="HorizonMask"] +position = Vector2(-82, -244) + +[node name="Pivot" type="Node2D" parent="HorizonMask/HorizonOrigin"] + +[node name="Background" type="Sprite2D" parent="HorizonMask/HorizonOrigin/Pivot"] +texture = ExtResource("3_kwqn1") + +[node name="RollArrowContour" type="Polygon2D" parent="HorizonMask/HorizonOrigin/Pivot"] +position = Vector2(-430, -267) +color = Color(0, 0, 0, 1) +polygon = PackedVector2Array(430, 70, 416, 92, 444, 92) + +[node name="RollArrowFill" type="Polygon2D" parent="HorizonMask/HorizonOrigin/Pivot"] +position = Vector2(-430, -267) +polygon = PackedVector2Array(430, 74, 420, 90, 440, 90) + +[node name="Overlay" type="Sprite2D" parent="HorizonMask/HorizonOrigin"] +texture = ExtResource("4_1vq1p") + +[node name="HeadingOrigin" type="Node2D" parent="."] +position = Vector2(430, 794) + +[node name="Ticks" type="Sprite2D" parent="HeadingOrigin"] +texture = ExtResource("5_3w2cf") + +[node name="ClimbrateOrigin" type="Node2D" parent="."] +position = Vector2(711, 268) + +[node name="Polygon2D" type="Polygon2D" parent="ClimbrateOrigin"] +polygon = PackedVector2Array(12, 0, -12, 0, -12, -121, 0, -133, 12, -121) + +[node name="Airspeed" type="Label" parent="."] +offset_left = 35.0 +offset_top = 233.0 +offset_right = 154.0 +offset_bottom = 298.0 +theme_override_fonts/font = ExtResource("6_wtx4e") +theme_override_font_sizes/font_size = 48 +text = "0" +horizontal_alignment = 2 +vertical_alignment = 1 + +[node name="Groundspeed" type="Label" parent="."] +offset_left = 59.0 +offset_top = 477.0 +offset_right = 178.0 +offset_bottom = 542.0 +theme_override_fonts/font = ExtResource("6_wtx4e") +theme_override_font_sizes/font_size = 33 +text = "0" +horizontal_alignment = 2 +vertical_alignment = 1 + +[node name="Altitude" type="Label" parent="."] +offset_left = 859.0 +offset_top = 233.0 +offset_right = 978.0 +offset_bottom = 298.0 +theme_override_fonts/font = ExtResource("6_wtx4e") +theme_override_font_sizes/font_size = 48 +text = "0" +horizontal_alignment = 2 +vertical_alignment = 1 + +[node name="Heading" type="Label" parent="."] +offset_left = 370.0 +offset_top = 498.0 +offset_right = 489.0 +offset_bottom = 563.0 +theme_override_fonts/font = ExtResource("6_wtx4e") +theme_override_font_sizes/font_size = 33 +text = "000" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Reference" type="Sprite2D" parent="."] +visible = false +position = Vector2(512, 512) +texture = ExtResource("7_86xjv") diff --git a/project/instruments/trim-long.png b/project/instruments/trim-long.png new file mode 100644 index 0000000..0a52de6 --- /dev/null +++ b/project/instruments/trim-long.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f57cf6e93c3c68e48afdc3ee078c28c9f3292d4303a7fcc0ec51e1651d33e3a +size 2108 diff --git a/project/instruments/trim-long.png.import b/project/instruments/trim-long.png.import new file mode 100644 index 0000000..fbfafb1 --- /dev/null +++ b/project/instruments/trim-long.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c561m3pvc0kop" +path="res://.godot/imported/trim-long.png-0b9b67642209f6a2ae4b2165f3b33d5d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/trim-long.png" +dest_files=["res://.godot/imported/trim-long.png-0b9b67642209f6a2ae4b2165f3b33d5d.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/instruments/trim-short.png b/project/instruments/trim-short.png new file mode 100644 index 0000000..f54ccfb --- /dev/null +++ b/project/instruments/trim-short.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa70ce3b0fa0c5716ba0e771877eb8499cdd33d8b0c7478bea1c91b79b32e6b1 +size 1265 diff --git a/project/instruments/trim-short.png.import b/project/instruments/trim-short.png.import new file mode 100644 index 0000000..9777f63 --- /dev/null +++ b/project/instruments/trim-short.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dytpn8rcdimr" +path="res://.godot/imported/trim-short.png-707c75166ad2d77e18c02dc4882968b1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://instruments/trim-short.png" +dest_files=["res://.godot/imported/trim-short.png-707c75166ad2d77e18c02dc4882968b1.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/main.tscn b/project/main.tscn index 1f92149..364a3b0 100644 --- a/project/main.tscn +++ b/project/main.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=7 format=3 uid="uid://crq3o0eu4y8ya"] +[gd_scene load_steps=8 format=3 uid="uid://crq3o0eu4y8ya"] [ext_resource type="Script" uid="uid://dfcjdxesyn3l0" path="res://start_vr.gd" id="1_ig7tw"] [ext_resource type="PackedScene" uid="uid://bj1s0g7ixjw71" path="res://aircraft/aircraft.tscn" id="2_0xm2m"] +[ext_resource type="Script" uid="uid://6g3b2ohwbkyv" path="res://fog_controller.gd" id="3_1bvp3"] [ext_resource type="PackedScene" uid="uid://bchcr54i3piaw" path="res://ads33-terrain/Cones_scene_terrain.tscn" id="3_h2yge"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_7dm0k"] @@ -12,6 +13,10 @@ sky_material = SubResource("ProceduralSkyMaterial_7dm0k") [sub_resource type="Environment" id="Environment_0xm2m"] background_mode = 2 sky = SubResource("Sky_ig7tw") +fog_enabled = true +fog_sun_scatter = 0.5 +fog_density = 0.0 +fog_depth_end = 80.0 [node name="Main" type="Node3D"] @@ -21,6 +26,7 @@ sky = SubResource("Sky_ig7tw") [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_0xm2m") +script = ExtResource("3_1bvp3") [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform3D(0.866025, 0.433013, 0.25, 0, -0.5, 0.866025, 0.5, -0.75, -0.433013, -5, 5, -5) @@ -29,4 +35,5 @@ shadow_enabled = true [node name="StartVR" type="Node3D" parent="."] script = ExtResource("1_ig7tw") +[connection signal="fog_density_changed" from="Aircraft" to="WorldEnvironment" method="_on_fog_density_changed"] [connection signal="setup_failed" from="StartVR" to="Aircraft" method="_on_vr_setup_failed"] diff --git a/project/project.godot b/project/project.godot index a75ba2f..5f7adb8 100644 --- a/project/project.godot +++ b/project/project.godot @@ -25,19 +25,68 @@ window/stretch/mode="viewport" enabled=PackedStringArray() +[input] + +pov_up={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +] +} +pov_down={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":99,"location":0,"echo":false,"script":null) +] +} +pov_forward={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +pov_back={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +pov_right={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +pov_left={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +pov_reset={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +] +} +fog_cycle={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null) +] +} + [physics] common/enable_object_picking=false [rendering] +textures/canvas_textures/default_texture_filter=2 renderer/rendering_method="gl_compatibility" renderer/rendering_method.mobile="gl_compatibility" +lights_and_shadows/directional_shadow/size=8192 +lights_and_shadows/directional_shadow/soft_shadow_filter_quality=3 +lights_and_shadows/positional_shadow/soft_shadow_filter_quality=3 anti_aliasing/quality/msaa_2d=1 anti_aliasing/quality/msaa_3d=1 +lights_and_shadows/positional_shadow/atlas_size=8192 [xr] +openxr/enabled=true openxr/reference_space=2 openxr/foveation_level=3 shaders/enabled=true diff --git a/src/marshconnector.cpp b/src/marshconnector.cpp index 3f66679..00edf56 100644 --- a/src/marshconnector.cpp +++ b/src/marshconnector.cpp @@ -18,6 +18,7 @@ #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_manual_setpoint.h" #include "mavlink/common/mavlink_msg_param_request_list.h" #include "mavlink/common/mavlink_msg_param_set.h" #include "mavlink/common/mavlink_msg_param_value.h" @@ -26,16 +27,20 @@ #include "mavlink/mavlink_types.h" #include #include +#include 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_velocity"), &MarshConnector::get_velocity); 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); + ClassDB::bind_method(D_METHOD("get_controls"), &MarshConnector::get_controls); + ClassDB::bind_method(D_METHOD("get_trim"), &MarshConnector::get_trim); ClassDB::bind_method(D_METHOD("get_model_connected"), &MarshConnector::get_model_connected); ClassDB::bind_method(D_METHOD("get_manager_connected"), @@ -48,6 +53,15 @@ void MarshConnector::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "hostname"), "set_hostname", "get_hostname"); + ClassDB::bind_method(D_METHOD("get_fog_density"), + &MarshConnector::get_fog_density); + ClassDB::bind_method(D_METHOD("set_fog_density", "p_fog_density"), + &MarshConnector::set_fog_density); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density"), "set_fog_density", + "get_fog_density"); + ADD_SIGNAL(MethodInfo("fog_density_changed", + PropertyInfo(Variant::FLOAT, "new_density"))); + // Timer callbacks ClassDB::bind_method(D_METHOD("send_heartbeat"), &MarshConnector::send_heartbeat); @@ -181,6 +195,9 @@ void MarshConnector::receive_data(const PackedByteArray &data) { case MAVLINK_MSG_ID_MANUAL_CONTROL: handle_manual_control(message); break; + case MAVLINK_MSG_ID_MANUAL_SETPOINT: + handle_manual_setpoint(message); + break; case MAVLINK_MSG_ID_HEARTBEAT: handle_heartbeat(message); break; @@ -235,6 +252,13 @@ Transform3D MarshConnector::get_aircraft() { return offset * marsh; } +Vector3 MarshConnector::get_velocity() { + float heading_rad = parameters[NAV_OFS_HDG] * Math_PI / 180.0; + // To rotate clockwise looking from above, that's negative Y in Godot + Quaternion heading_offset = Quaternion(Vector3(0, 1, 0), -heading_rad); + return heading_offset.xform(last_velocity); +} + Vector2 MarshConnector::get_cyclic() { return Vector2{last_controls.x, last_controls.y}; } @@ -243,6 +267,21 @@ float MarshConnector::get_collective() { return last_controls.w; } float MarshConnector::get_pedals() { return last_controls.z; } +Vector4 MarshConnector::get_controls() { return last_controls; } + +Vector4 MarshConnector::get_trim() { return last_trim; } + +float MarshConnector::get_fog_density() { return parameters[FOG_DENSITY]; } + +void MarshConnector::set_fog_density(float density) { + if (density == parameters[FOG_DENSITY]) + return; + + parameters[FOG_DENSITY] = density; + emit_signal("fog_density_changed", density); + send_param(FOG_DENSITY); +} + bool MarshConnector::get_manager_connected() { return manager_connected; } bool MarshConnector::get_model_connected() { return model_connected; } @@ -263,6 +302,10 @@ Vector2 MarshConnector::local_meters_from_global_degrees(double latitude, // Y East is just length along the circle of latitude double y = (lon - lon0) * (EARTH_RADIUS * cos(lat0)); + const double map_size = 3000.0; + x = (Math::fract(x / map_size + 0.5) - 0.5) * map_size; + y = (Math::fract(y / map_size + 0.5) - 0.5) * map_size; + return Vector2(x, y); } void MarshConnector::handle_sim_state(mavlink_message_t message) { @@ -282,6 +325,8 @@ void MarshConnector::handle_sim_state(mavlink_message_t message) { local_position.x, local_position.y, sim_state.alt)); receive_rotation(godot_rotation_from_mavlink(sim_state.roll, sim_state.pitch, sim_state.yaw)); + + last_velocity = Vector3{-sim_state.ve, -sim_state.vd, sim_state.vn}; } Vector3 MarshConnector::godot_location_from_mavlink(float north_meters, float east_meters, @@ -392,7 +437,11 @@ void MarshConnector::handle_param(mavlink_message_t message) { if (param_value.has_value()) { // TODO: Verify the value before updating - parameters[i] = *param_value; + if (i == FOG_DENSITY) { + set_fog_density(*param_value); + } else { + parameters[i] = *param_value; + } } send_param(Parameter{i}); } @@ -403,10 +452,11 @@ Error MarshConnector::send_param(Parameter index) { } mavlink_param_value_t param_value; - memset(param_value.param_id, 0, sizeof(param_value.param_id)); CharString nameBuffer = parameter_names[index].ascii(); const char *name = nameBuffer.get_data(); - strcpy_s(param_value.param_id, name); + + // strncpy will write nulls after data until reaching target count + strncpy(param_value.param_id, name, sizeof(param_value.param_id)); param_value.param_value = parameters[index]; param_value.param_type = MAV_PARAM_TYPE_REAL32; param_value.param_count = PARAM_COUNT; @@ -435,6 +485,22 @@ void MarshConnector::handle_manual_control(mavlink_message_t message) { last_controls.w = manual_control.z / 1000.0f; } +void MarshConnector::handle_manual_setpoint(mavlink_message_t message) { + if (message.msgid != MAVLINK_MSG_ID_MANUAL_SETPOINT) + return; + + mavlink_manual_setpoint_t manual_setpoint; + mavlink_msg_manual_setpoint_decode(&message, &manual_setpoint); + if (manual_setpoint.mode_switch == MARSH_MANUAL_SETPOINT_MODE_TRIM) { + last_trim = Vector4{ + manual_setpoint.roll, + manual_setpoint.pitch, + manual_setpoint.yaw, + manual_setpoint.thrust, + }; + } +} + void MarshConnector::handle_heartbeat(mavlink_message_t message) { if (message.msgid != MAVLINK_MSG_ID_HEARTBEAT) return; @@ -445,27 +511,16 @@ void MarshConnector::handle_heartbeat(mavlink_message_t message) { 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 = - MAV_COMP_ID_USER1; // HACK: Should get it from received HEARTBEAT - 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( - system_id, component_id, MAVLINK_COMM_0, &message_sent, &command); - Error result = send_message(message_sent); + Error result = subscribe_message(message.sysid, message.compid, + MAVLINK_MSG_ID_MANUAL_CONTROL); if (result != OK) { - print_line("Subscribe send result ", result); + print_line("Subscribe control send result ", result); + } + + result = subscribe_message(message.sysid, message.compid, + MAVLINK_MSG_ID_MANUAL_SETPOINT); + if (result != OK) { + print_line("Subscribe setpoint send result ", result); } } @@ -474,6 +529,29 @@ void MarshConnector::handle_heartbeat(mavlink_message_t message) { } } +Error MarshConnector::subscribe_message(uint8_t manager_system, + uint8_t manager_component, + uint32_t msgid) { + // subscribe to messages not sent to visualisation node by default + mavlink_command_long_t command; + command.target_system = manager_system; + command.target_component = manager_component; + command.command = MAV_CMD_SET_MESSAGE_INTERVAL; + command.confirmation = 0; + command.param1 = static_cast(msgid); + command.param2 = 0; // Default rate + command.param3 = 0; // Not used + command.param4 = 0; + command.param5 = 0; + command.param6 = 0; + command.param7 = 1; // Address of requester + + mavlink_message_t message_sent; + mavlink_msg_command_long_encode_chan(system_id, component_id, MAVLINK_COMM_0, + &message_sent, &command); + return send_message(message_sent); +} + 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 897300a..2de3a7d 100644 --- a/src/marshconnector.h +++ b/src/marshconnector.h @@ -39,12 +39,21 @@ public: // Get current state of the aircraft Transform3D get_aircraft(); + Vector3 get_velocity(); // 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(); + // Get normalized position for all controls + Vector4 get_controls(); + // Get normalized trim position + Vector4 get_trim(); + + // Get current fog density as set in the parameter + float get_fog_density(); + void set_fog_density(float value); // Is there a connection to MARSH Manager bool get_manager_connected(); @@ -76,6 +85,7 @@ private: void handle_attitude(mavlink_message_t message); void handle_param(mavlink_message_t message); void handle_manual_control(mavlink_message_t message); + void handle_manual_setpoint(mavlink_message_t message); void handle_heartbeat(mavlink_message_t message); void receive_model_data(); @@ -88,7 +98,7 @@ private: // Not an enum class on purpose, to make use more convenient, it's scoped // inside the class namespace anyway enum Parameter : uint16_t { - // FOG_DENSITY, + FOG_DENSITY, NAV_OFS_HDG, NAV_OFS_X, NAV_OFS_Y, @@ -96,21 +106,25 @@ private: LOCAL_FRAME_LON, PARAM_COUNT, }; - float parameters[PARAM_COUNT] = {0.0, 0.0, 0.0, 0.0, 0.0}; + float parameters[PARAM_COUNT] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; const String parameter_names[PARAM_COUNT] = { - // "FOG_DENSITY", - "NAV_OFS_HDG", "NAV_OFS_X", "NAV_OFS_Y", - "LOCAL_FRAME_LAT", "LOCAL_FRAME_LON", + "FOG_DENSITY", "NAV_OFS_HDG", "NAV_OFS_X", + "NAV_OFS_Y", "LOCAL_FRAME_LAT", "LOCAL_FRAME_LON", }; Error send_param(Parameter index); + Error subscribe_message(uint8_t manager_system, uint8_t manager_component, + uint32_t msgid); uint8_t system_id; uint8_t component_id; // TODO: Interpolate with some delay + // Or not, in practice the simple solution works nicely Vector3 last_location; Quaternion last_rotation; + Vector3 last_velocity; Vector4 last_controls; + Vector4 last_trim; double time_passed; Timer *heartbeat_timer;