Initial commit.
This commit is contained in:
commit
907695442f
28 changed files with 717 additions and 0 deletions
8
scripts/aircraft/nodes/aircraft_controller.gd
Normal file
8
scripts/aircraft/nodes/aircraft_controller.gd
Normal file
|
@ -0,0 +1,8 @@
|
|||
class_name AircraftController
|
||||
extends Node
|
||||
|
||||
@export var pitch: float # Negative: down, positive: up
|
||||
@export var yaw: float # Negative: left, positive: right
|
||||
@export var roll: float # Negative: left, positive: right
|
||||
@export var throttle: float # 0: minimum, 1: maximum
|
||||
@export var brake: float # 0: fully released, 1: fully applied
|
1
scripts/aircraft/nodes/aircraft_controller.gd.uid
Normal file
1
scripts/aircraft/nodes/aircraft_controller.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b2k35mmv3g82l
|
55
scripts/aircraft/nodes/atmosphere.gd
Normal file
55
scripts/aircraft/nodes/atmosphere.gd
Normal file
|
@ -0,0 +1,55 @@
|
|||
extends Node
|
||||
|
||||
@export var pressure_ASL := 101.325 # 1 atm. in kPa
|
||||
@export var density_ASL := 1.225 # at 288.15k in kg/m^3
|
||||
@export var temperature_ASL := 288.15 # surface temperature in Kelvin (288.15K = 15C)
|
||||
@export var tropopause_altitude := 11000.0 # meters above ASL
|
||||
@export var tropopause_temp := 216.65 # (216.65K = -56.5C)
|
||||
@export var gravity := 9.8 # here until I figure out the best way to get the project's default gravity.
|
||||
|
||||
const AIR_MOLAR_MASS := 0.029 # kg/mol
|
||||
const GAS_CONSTANT := 8.31447
|
||||
const MACH_COEFF := 20.02948
|
||||
|
||||
var pressure_constant: float
|
||||
var density_constant: float
|
||||
var temp_lapse_at_tropo: float # temperature lapse at troposphere, in K or C
|
||||
|
||||
# Use this for initialization
|
||||
func _ready() -> void:
|
||||
_recalculate()
|
||||
|
||||
func _recalculate() -> void:
|
||||
pressure_constant = (gravity * AIR_MOLAR_MASS) / (GAS_CONSTANT * temperature_ASL)
|
||||
density_constant = 1 / density_ASL
|
||||
temp_lapse_at_tropo = (temperature_ASL - tropopause_temp) / tropopause_altitude
|
||||
|
||||
func pressure_by_alt(altitude: float, atm := false) -> float: # returns the pressure at altitude in kPa (default) or atm.
|
||||
var ret := exp(pressure_constant * altitude)
|
||||
if !atm:
|
||||
ret *= pressure_ASL
|
||||
return ret
|
||||
|
||||
func density_by_alt(altitude: float, relative := false) -> float: # returns the density at altitude in kg/m^3 (default), or relative.
|
||||
var ret := pressure_by_alt(altitude) / (0.287058 * temperature_by_alt(altitude))
|
||||
if relative:
|
||||
ret *= density_constant
|
||||
return ret
|
||||
|
||||
func temperature_by_alt(altitude: float) -> float:
|
||||
if altitude < tropopause_altitude:
|
||||
return temperature_ASL - altitude * temp_lapse_at_tropo
|
||||
else: # mesopause coming soon!
|
||||
return tropopause_temp
|
||||
|
||||
func speed_of_sound_by_alt(altitude: float) -> float:
|
||||
return sqrt(temperature_by_alt(altitude)) * MACH_COEFF
|
||||
|
||||
func mach_by_alt(altitude: float, speed: float) -> float:# a little bit expensive - use with caution
|
||||
return speed / speed_of_sound_by_alt(altitude)
|
||||
|
||||
func atm_to_kPa(pressure: float) -> float:
|
||||
return pressure * pressure_ASL
|
||||
|
||||
func kPa_to_atm(pressure: float) -> float:
|
||||
return pressure / pressure_ASL
|
1
scripts/aircraft/nodes/atmosphere.gd.uid
Normal file
1
scripts/aircraft/nodes/atmosphere.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://0nphoxplw624
|
201
scripts/aircraft/nodes/fixed_wing_aircraft.gd
Normal file
201
scripts/aircraft/nodes/fixed_wing_aircraft.gd
Normal file
|
@ -0,0 +1,201 @@
|
|||
class_name FixedWingAircraft
|
||||
extends CharacterBody3D
|
||||
|
||||
@export var performance: FixedWingAircraftPerformance
|
||||
@export var controller: AircraftController
|
||||
|
||||
@export var initial_speed: float
|
||||
|
||||
#var acceleration := Vector3
|
||||
#var angular_velocity := Vector3
|
||||
#var angular_acceleration := Vector3
|
||||
var m_aoa: float
|
||||
var m_sideslip: float
|
||||
var m_tas: float
|
||||
var m_ias: float
|
||||
var m_relative_velocity: Vector3
|
||||
|
||||
var m_is_landed := false
|
||||
|
||||
func _ready() -> void:
|
||||
velocity = -global_basis.z * initial_speed
|
||||
m_relative_velocity = transform.basis.inverse() * velocity
|
||||
m_tas = _tas()
|
||||
m_ias = _ias()
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
|
||||
m_relative_velocity = transform.basis.inverse() * velocity
|
||||
m_aoa = -atan2(m_relative_velocity.y, -m_relative_velocity.z)
|
||||
if m_aoa > PI:
|
||||
m_aoa -= TAU
|
||||
m_sideslip = -atan2(m_relative_velocity.x, -m_relative_velocity.z)
|
||||
if m_sideslip > PI:
|
||||
m_sideslip -= TAU
|
||||
m_tas = _tas()
|
||||
m_ias = _ias()
|
||||
|
||||
# aero
|
||||
var vel_forward := m_relative_velocity.normalized()
|
||||
var vel_right := vel_forward.cross(Vector3.UP).normalized()
|
||||
var vel_up := vel_right.cross(vel_forward).normalized()
|
||||
#var vel_basis := Basis(vel_right, vel_up, vel_forward)
|
||||
var linear_forces := Vector3.ZERO
|
||||
|
||||
var horiz_lift := _lift(performance.horizontal_surface,
|
||||
performance.horizontal_area,
|
||||
performance.horizontal_aspect_ratio,
|
||||
performance.horizontal_sweep,
|
||||
m_aoa, m_tas)
|
||||
var horiz_drag := _drag(performance.horizontal_surface,
|
||||
performance.horizontal_area,
|
||||
performance.horizontal_aspect_ratio,
|
||||
performance.horizontal_sweep,
|
||||
m_aoa, m_tas)
|
||||
var vert_lift := _lift(performance.vertical_surface,
|
||||
performance.vertical_area,
|
||||
performance.vertical_aspect_ratio,
|
||||
performance.vertical_sweep,
|
||||
m_sideslip, m_tas)
|
||||
var vert_drag := _drag(performance.vertical_surface,
|
||||
performance.vertical_area,
|
||||
performance.vertical_aspect_ratio,
|
||||
performance.vertical_sweep,
|
||||
m_sideslip, m_tas)
|
||||
|
||||
linear_forces += vel_up * horiz_lift
|
||||
linear_forces += vel_right * vert_lift
|
||||
linear_forces -= vel_forward * horiz_drag
|
||||
linear_forces -= vel_forward * vert_drag
|
||||
linear_forces += Vector3.FORWARD * _thrust()
|
||||
m_relative_velocity += linear_forces * delta / performance.empty_mass
|
||||
velocity = (transform.basis * m_relative_velocity) + (get_gravity() * delta)
|
||||
|
||||
# steering
|
||||
var steering_axis := _get_steering_axis() * delta
|
||||
if (steering_axis.length_squared() > 0.0):
|
||||
rotate_object_local(steering_axis.normalized(), steering_axis.length())
|
||||
|
||||
move_and_slide()
|
||||
#if !is_landed:
|
||||
# _fly()
|
||||
#else:
|
||||
# taxi()
|
||||
#ResetControls()
|
||||
|
||||
#func _sync()
|
||||
|
||||
func _get_steering_axis() -> Vector3:
|
||||
var pitch_effect := Vector3.RIGHT * performance.pitch_power.sample(absf(rad_to_deg(m_aoa))) * controller.pitch
|
||||
var yaw_effect := performance.yaw_axis.normalized() * performance.yaw_power.sample(absf(rad_to_deg(m_sideslip))) * controller.yaw
|
||||
var roll_effect := performance.roll_axis.normalized() * performance.roll_power.sample(absf(rad_to_deg(m_aoa))) * controller.roll
|
||||
return (pitch_effect + yaw_effect + roll_effect) * m_ias / performance.reference_ias_mps
|
||||
|
||||
func _lift(foil: Airfoil,
|
||||
area: float,
|
||||
aspect_ratio: float,
|
||||
_sweep: float,
|
||||
aoa: float,
|
||||
tas: float) -> float:
|
||||
# https://www1.grc.nasa.gov/beginners-guide-to-aeronautics/downwash-effects-on-lift/
|
||||
var Cl0: float
|
||||
if absf(aoa) < PI / 9:
|
||||
Cl0 = foil.lift_curve.sample(rad_to_deg(aoa))
|
||||
else:
|
||||
Cl0 = sin(aoa * 2.0) * 2.0
|
||||
var Cl := Cl0 / (1.0 + Cl0 / (PI * aspect_ratio))
|
||||
return Cl * area * tas * tas * Atmosphere.density_by_alt(position.y) * 0.5
|
||||
|
||||
func _drag(foil: Airfoil,
|
||||
area: float,
|
||||
_aspect_ratio: float,
|
||||
_sweep: float,
|
||||
aoa: float,
|
||||
tas: float) -> float:
|
||||
var Dl0: float
|
||||
if absf(aoa) < PI / 9:
|
||||
Dl0 = foil.drag_curve.sample(rad_to_deg(aoa))
|
||||
else:
|
||||
Dl0 = sin(aoa) * sin(aoa) * 10.0
|
||||
#var Dl := Dl0 / (1.0 + Dl0 / (PI * aspect_ratio))
|
||||
return Dl0 * area * tas * tas * Atmosphere.density_by_alt(position.y) * 0.5
|
||||
|
||||
func _moment(foil: Airfoil,
|
||||
area: float,
|
||||
aspect_ratio: float,
|
||||
_sweep: float,
|
||||
aoa: float,
|
||||
tas: float) -> float:
|
||||
var Ml0 := foil.lift_curve.sample(rad_to_deg(aoa))
|
||||
#var Ml := Ml0 / (1.0 + Ml0 / (PI * aspect_ratio))
|
||||
var mean_chord := sqrt(area / aspect_ratio)
|
||||
return Ml0 * area * tas * tas * Atmosphere.density_by_alt(position.y) * 0.5 * mean_chord
|
||||
|
||||
func _thrust() -> float:
|
||||
var speed_contrib := performance.propultion.propultion_speed_curve.sample(Atmosphere.mach_by_alt(position.y, m_tas))
|
||||
var density_contrib := performance.propultion.propultion_density_curve.sample(Atmosphere.density_by_alt(position.y, true))
|
||||
var temp_contrib := performance.propultion.propultion_temperature_curve.sample(Atmosphere.temperature_by_alt(position.y))
|
||||
return speed_contrib * density_contrib * temp_contrib * performance.base_thrust * controller.throttle
|
||||
|
||||
#func _fly() -> void:
|
||||
#acceleration = Vector3.DOWN * global_basis * gravity
|
||||
#if controller.brake > 0.0:
|
||||
# mThrust -= performance.braking_power * _ias()
|
||||
#acceleration += Vector3.FORWARD * mThrust / mass;
|
||||
#lift();
|
||||
#control();
|
||||
#velocity += mAcceleration * Time.fixedDeltaTime;
|
||||
#transform.Translate(velocity * Time.fixedDeltaTime * iScale);
|
||||
#Vector3 prevVel = transform.TransformDirection(velocity);
|
||||
#transform.Rotate(mAngularVelocity * Time.fixedDeltaTime);
|
||||
#velocity = transform.InverseTransformDirection(prevVel);
|
||||
# Check to see if we've landed
|
||||
#if (isLandingGearDeployed)
|
||||
#{
|
||||
# RaycastHit ground;
|
||||
# Physics.Raycast(transform.position, -transform.up, out ground, rideHeight, 0xFF, QueryTriggerInteraction.Ignore);
|
||||
# if (ground.collider != null && ground.distance < rideHeight)
|
||||
# {
|
||||
# AttemptLanding(ref ground);
|
||||
# }
|
||||
#}
|
||||
|
||||
#func lift() -> void:
|
||||
# var speed_sqr_YZ := velocity.y * velocity.y + velocity.z * velocity.z
|
||||
# var speed_sqr_XZ := velocity.x * velocity.x + velocity.z * velocity.z
|
||||
# var altitude := Atmosphere.altitude(position.y)
|
||||
# float horizLiftPerCoeff = speed_sqr_YZ * horizWingArea * Atmosphere.Density(altitude, true) * 0.5f / mass;
|
||||
# float vertLiftPerCoeff = speed_sqr_XZ * vertWingArea * Atmosphere.Density(altitude, true) * 0.5f / mass;
|
||||
# float degAoA = AoA * Mathf.Rad2Deg;
|
||||
# float degSideslip = sideslip * Mathf.Rad2Deg;
|
||||
# float wingDrag = horizAirfoil.getDrag(degAoA) * horizWingArea + vertAirfoil.getDrag(degSideslip) * vertWingArea;
|
||||
# float mach = Atmosphere.Mach(altitude, velocity.magnitude);
|
||||
# if (mach > 0.7f) # No need to do expensive calculations unless we're going fast enough for them to matter.
|
||||
# {
|
||||
# wingDrag += 3 * wingDrag * Mathf.Min(Mathf.Exp(16 * (mach - 1.0f + Mathf.Log10(1 - wingSweep / 90.0f))), Mathf.Exp(-mach + 1.0f));
|
||||
# }
|
||||
# float totalDrag = indicatedVelocity.sqrMagnitude * Atmosphere.Density(altitude, true) * 0.5f * (wingDrag + bodyDragCoeff * frontalArea) / mass;
|
||||
# Vector3 relativeAccel = new Vector3(-vertAirfoil.getLift(degSideslip) * vertLiftPerCoeff, horizAirfoil.getLift(degAoA) * horizLiftPerCoeff, -totalDrag);
|
||||
# Vector3 fixedAcceleration = TransformR(relativeAccel);
|
||||
# if ((velocity.x + mAcceleration.x * Time.fixedDeltaTime) * velocity.x < 0.0f)
|
||||
# {
|
||||
# fixedAcceleration.x = -velocity.x / Time.fixedDeltaTime;
|
||||
# }
|
||||
# if ((velocity.y + mAcceleration.y * Time.fixedDeltaTime) * velocity.y < 0.0f)
|
||||
# {
|
||||
# fixedAcceleration.y = -velocity.y / Time.fixedDeltaTime;
|
||||
# }
|
||||
# acceleration += fixedAcceleration;
|
||||
# mAngularVelocity = new Vector3(horizAirfoil.getMoment(degAoA) * horizLiftPerCoeff, vertAirfoil.getMoment(degSideslip) * vertLiftPerCoeff, 0f);
|
||||
# mAngularVelocity += new Vector3(-moment * Mathf.Cos(Vector3.Angle(Physics.gravity, transform.up) * Mathf.Deg2Rad) * horizLiftPerCoeff, 0.0f, 0.0f)
|
||||
|
||||
func _tas() -> float:
|
||||
return sqrt(m_relative_velocity.z * m_relative_velocity.z + m_relative_velocity.y * m_relative_velocity.y) * -sign(m_relative_velocity.z)
|
||||
|
||||
func _ias() -> float:
|
||||
return _tas() * sqrt(Atmosphere.density_by_alt(position.y, true))
|
||||
|
||||
#func _
|
||||
|
||||
#func _g_force() -> Vector3:
|
||||
# return (acceleration - transform.inverse(get_gravity())) / get_gravity().length()
|
1
scripts/aircraft/nodes/fixed_wing_aircraft.gd.uid
Normal file
1
scripts/aircraft/nodes/fixed_wing_aircraft.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://bddnsq0h2tpf
|
37
scripts/aircraft/nodes/flight_sim_controller.gd
Normal file
37
scripts/aircraft/nodes/flight_sim_controller.gd
Normal file
|
@ -0,0 +1,37 @@
|
|||
class_name FlightSimController
|
||||
extends AircraftController
|
||||
|
||||
@export var pitch_down := &"pitch_down"
|
||||
@export var pitch_up := &"pitch_up"
|
||||
@export var yaw_left := &"yaw_left"
|
||||
@export var yaw_right := &"yaw_right"
|
||||
@export var roll_left := &"roll_left"
|
||||
@export var roll_right := &"roll_right"
|
||||
@export var throttle_down := &"throttle_down"
|
||||
@export var throttle_up := &"throttle_up"
|
||||
|
||||
@export var pitch_speed := 1.0
|
||||
@export var yaw_speed := 1.0
|
||||
@export var roll_speed := 1.0
|
||||
@export var throttle_speed := 1.0
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if get_multiplayer_authority() == multiplayer.get_unique_id():
|
||||
var pitch_target := Input.get_axis(pitch_down, pitch_up)
|
||||
var pitch_diff := pitch_target - pitch
|
||||
pitch += signf(pitch_diff) * minf(absf(pitch_diff), pitch_speed * delta)
|
||||
|
||||
var yaw_target := Input.get_axis(yaw_left, yaw_right)
|
||||
var yaw_diff := yaw_target - yaw
|
||||
yaw += signf(yaw_diff) * minf(absf(yaw_diff), yaw_speed * delta)
|
||||
|
||||
var roll_target := Input.get_axis(roll_left, roll_right)
|
||||
var roll_diff := roll_target - roll
|
||||
roll += signf(roll_diff) * minf(absf(roll_diff), roll_speed * delta)
|
||||
|
||||
var throttle_target := Input.get_axis(throttle_down, throttle_up) * 0.5 + 0.5
|
||||
if throttle_target > throttle:
|
||||
throttle = minf(throttle_target, throttle + throttle_speed * delta)
|
||||
elif throttle_target < throttle:
|
||||
throttle = maxf(throttle_target, throttle - throttle_speed * delta)
|
||||
brake = 1.0 if throttle < 0.05 else 0.0
|
1
scripts/aircraft/nodes/flight_sim_controller.gd.uid
Normal file
1
scripts/aircraft/nodes/flight_sim_controller.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://pc3j1c6e1ra7
|
Loading…
Add table
Add a link
Reference in a new issue