Compare commits
No commits in common. "master" and "2025.11.28" have entirely different histories.
master
...
2025.11.28
49 changed files with 181 additions and 1415 deletions
|
|
@ -1,395 +0,0 @@
|
|||
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.
|
||||
113
README.md
113
README.md
|
|
@ -1,9 +1,114 @@
|
|||
# Visualisation for MARSH
|
||||
|
||||
This is a visualisation module for [Modular Architecture for Reconfigurable Simulation of Helicopters](https://marsh-sim.github.io/).
|
||||
For documentation, see [MARSH Documentation - Visualisation](https://marsh-sim.github.io/nodes/visualisation/)
|
||||
The project is configured to display cockpit instruments with [lidia](https://pypi.org/project/lidia) Python package.
|
||||
|
||||
## License
|
||||
Named in this order so not everything starts with the same word.
|
||||
|
||||
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/).
|
||||
## 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 <address of this repository>
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
[tools]
|
||||
clangd = "latest"
|
||||
"pipx:scons" = "latest"
|
||||
|
|
@ -1264,11 +1264,9 @@ 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)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
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"
|
||||
|
|
@ -28,8 +25,6 @@ var fog_index: int = 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
|
||||
|
|
@ -49,11 +44,6 @@ func _process(delta: float) -> void:
|
|||
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():
|
||||
_rotor_azimuth += rotor_rpm * 2 * PI / 60 * delta
|
||||
|
|
@ -68,14 +58,6 @@ 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
|
||||
|
|
@ -83,14 +65,3 @@ 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
|
||||
|
|
|
|||
|
|
@ -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/instruments.tscn" id="3_5w717"]
|
||||
[ext_resource type="PackedScene" uid="uid://cis4s43ubuynp" path="res://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"]
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@ 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
|
||||
|
|
@ -22,7 +20,6 @@ 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:
|
||||
|
|
@ -55,14 +52,6 @@ func _input(event):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://6g3b2ohwbkyv
|
||||
|
|
@ -1,12 +1,33 @@
|
|||
extends Node
|
||||
|
||||
@onready var controls: Node = $SubViewport2/Controls
|
||||
@onready var pfd: Node = $SubViewport/PFD
|
||||
const PFD_PATH: String = "pfd#adi,vsi,alt,ias,rht,rpm,hsi"
|
||||
const CONTROLS_PATH: String = "controls#collective,cyclic,rudder"
|
||||
|
||||
# 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)
|
||||
@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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
[gd_scene load_steps=9 format=3 uid="uid://cis4s43ubuynp"]
|
||||
[gd_scene load_steps=7 format=3 uid="uid://cis4s43ubuynp"]
|
||||
|
||||
[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"]
|
||||
[ext_resource type="Script" uid="uid://01bmfj4wthwg" path="res://instruments.gd" id="1_h5at3"]
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_nowl7"]
|
||||
|
||||
|
|
@ -21,12 +19,15 @@ resource_local_to_scene = true
|
|||
albedo_texture = SubResource("ViewportTexture_h5at3")
|
||||
|
||||
[node name="Instruments" type="Node3D"]
|
||||
script = ExtResource("1_wlkep")
|
||||
script = ExtResource("1_h5at3")
|
||||
lidia_hostname = "192.168.1.2"
|
||||
|
||||
[node name="SubViewport" type="SubViewport" parent="."]
|
||||
size = Vector2i(1024, 1024)
|
||||
|
||||
[node name="PFD" parent="SubViewport" instance=ExtResource("2_uhiu3")]
|
||||
[node name="TextureRect" type="TextureRect" parent="SubViewport"]
|
||||
offset_right = 512.0
|
||||
offset_bottom = 512.0
|
||||
expand_mode = 5
|
||||
|
||||
[node name="Quad" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(0.8, 0, 0, 0, 0.8, 0, 0, 0, 0.8, -0.35, 0, 0)
|
||||
|
|
@ -35,7 +36,10 @@ surface_material_override/0 = SubResource("StandardMaterial3D_h5at3")
|
|||
|
||||
[node name="SubViewport2" type="SubViewport" parent="."]
|
||||
|
||||
[node name="Controls" parent="SubViewport2" instance=ExtResource("2_372d7")]
|
||||
[node name="TextureRect" type="TextureRect" parent="SubViewport2"]
|
||||
offset_right = 512.0
|
||||
offset_bottom = 512.0
|
||||
expand_mode = 5
|
||||
|
||||
[node name="Quad2" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(0.6, 0, 0, 0, 0.6, 0, 0, 0, 0.6, 0.425, 0, 0)
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
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.
|
||||
Binary file not shown.
|
|
@ -1,35 +0,0 @@
|
|||
[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={}
|
||||
BIN
project/instruments/controls-background.png
(Stored with Git LFS)
BIN
project/instruments/controls-background.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/controls-ref.png
(Stored with Git LFS)
BIN
project/instruments/controls-ref.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
@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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://ds6f8cur05ka0
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
[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")
|
||||
BIN
project/instruments/current-arrow.png
(Stored with Git LFS)
BIN
project/instruments/current-arrow.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/current-dot.png
(Stored with Git LFS)
BIN
project/instruments/current-dot.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/heading-ticks.png
(Stored with Git LFS)
BIN
project/instruments/heading-ticks.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/horizon-mask.png
(Stored with Git LFS)
BIN
project/instruments/horizon-mask.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/horizon-overlay.png
(Stored with Git LFS)
BIN
project/instruments/horizon-overlay.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/horizon.png
(Stored with Git LFS)
BIN
project/instruments/horizon.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/pfd-background.png
(Stored with Git LFS)
BIN
project/instruments/pfd-background.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/pfd-ref.png
(Stored with Git LFS)
BIN
project/instruments/pfd-ref.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
@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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://368tfiyen071
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
[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")
|
||||
BIN
project/instruments/trim-long.png
(Stored with Git LFS)
BIN
project/instruments/trim-long.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
BIN
project/instruments/trim-short.png
(Stored with Git LFS)
BIN
project/instruments/trim-short.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,34 +0,0 @@
|
|||
[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
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://crq3o0eu4y8ya"]
|
||||
[gd_scene load_steps=7 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"]
|
||||
|
|
@ -13,10 +12,6 @@ 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"]
|
||||
|
||||
|
|
@ -26,7 +21,6 @@ fog_depth_end = 80.0
|
|||
|
||||
[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)
|
||||
|
|
@ -35,5 +29,4 @@ 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"]
|
||||
|
|
|
|||
|
|
@ -25,68 +25,19 @@ 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
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#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"
|
||||
|
|
@ -27,20 +26,16 @@
|
|||
#include "mavlink/mavlink_types.h"
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string.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_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"),
|
||||
|
|
@ -53,15 +48,6 @@ 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);
|
||||
|
|
@ -195,9 +181,6 @@ 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;
|
||||
|
|
@ -252,13 +235,6 @@ 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};
|
||||
}
|
||||
|
|
@ -267,21 +243,6 @@ 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; }
|
||||
|
|
@ -302,10 +263,6 @@ 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) {
|
||||
|
|
@ -325,8 +282,6 @@ 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,
|
||||
|
|
@ -437,11 +392,7 @@ void MarshConnector::handle_param(mavlink_message_t message) {
|
|||
|
||||
if (param_value.has_value()) {
|
||||
// TODO: Verify the value before updating
|
||||
if (i == FOG_DENSITY) {
|
||||
set_fog_density(*param_value);
|
||||
} else {
|
||||
parameters[i] = *param_value;
|
||||
}
|
||||
parameters[i] = *param_value;
|
||||
}
|
||||
send_param(Parameter{i});
|
||||
}
|
||||
|
|
@ -452,11 +403,10 @@ 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();
|
||||
|
||||
// strncpy will write nulls after data until reaching target count
|
||||
strncpy(param_value.param_id, name, sizeof(param_value.param_id));
|
||||
strcpy_s(param_value.param_id, name);
|
||||
param_value.param_value = parameters[index];
|
||||
param_value.param_type = MAV_PARAM_TYPE_REAL32;
|
||||
param_value.param_count = PARAM_COUNT;
|
||||
|
|
@ -485,22 +435,6 @@ 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;
|
||||
|
|
@ -511,16 +445,27 @@ void MarshConnector::handle_heartbeat(mavlink_message_t message) {
|
|||
if (!manager_connected) {
|
||||
print_line("Connected to MARSH Manager");
|
||||
|
||||
Error result = subscribe_message(message.sysid, message.compid,
|
||||
MAVLINK_MSG_ID_MANUAL_CONTROL);
|
||||
if (result != OK) {
|
||||
print_line("Subscribe control send result ", result);
|
||||
}
|
||||
// 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<float>(MAVLINK_MSG_ID_MANUAL_CONTROL);
|
||||
command.param2 = 0; // Default rate
|
||||
command.param3 = 0; // Not used
|
||||
command.param4 = 0;
|
||||
command.param5 = 0;
|
||||
command.param6 = 0;
|
||||
command.param7 = 1; // Address of requestor
|
||||
|
||||
result = subscribe_message(message.sysid, message.compid,
|
||||
MAVLINK_MSG_ID_MANUAL_SETPOINT);
|
||||
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);
|
||||
if (result != OK) {
|
||||
print_line("Subscribe setpoint send result ", result);
|
||||
print_line("Subscribe send result ", result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -529,29 +474,6 @@ 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<float>(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;
|
||||
|
|
|
|||
|
|
@ -39,21 +39,12 @@ 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();
|
||||
|
|
@ -85,7 +76,6 @@ 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();
|
||||
|
|
@ -98,7 +88,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,
|
||||
|
|
@ -106,25 +96,21 @@ private:
|
|||
LOCAL_FRAME_LON,
|
||||
PARAM_COUNT,
|
||||
};
|
||||
float parameters[PARAM_COUNT] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
float parameters[PARAM_COUNT] = {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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue