changelog
arcology updated ship models in particle fx is broken on kestrel
This commit is contained in:
+46
-17
@@ -66,20 +66,40 @@ const EARTH_SOI: float = 20000000.0
|
|||||||
const EVA_SNAP_PEAK: float = 6.0
|
const EVA_SNAP_PEAK: float = 6.0
|
||||||
const STEP_HEIGHT: float = 0.22
|
const STEP_HEIGHT: float = 0.22
|
||||||
const WALK_FLOORS: Array[String] = [
|
const WALK_FLOORS: Array[String] = [
|
||||||
"Deck_", "Stair_A_Treads", "Stair_B_Treads",
|
"Deck_",
|
||||||
"Stair_A_Landings", "Stair_B_Landings",
|
"Stair_A_Treads", "Stair_B_Treads",
|
||||||
"Stair_TopLandings", "Stair_HeadLanding", "Stair_LevelLandings",
|
"Stair_A_Landings", "Stair_B_Landings", "Stair_LevelLandings",
|
||||||
"Lecture_Steps", "Lecture_Tiers",
|
"Stair_TopLandings", "Stair_HeadLanding",
|
||||||
"Yard_Floor", "Yard_Pads", "Yard_Apron", "Yard_Ramps", "Yard_Bridges", "Yard_Spoke",
|
"Lecture_Steps", "Lecture_Tiers", "Lecture_Seats", "School_Stage",
|
||||||
|
"Yard_Floor", "Yard_Apron", "Yard_Spoke", "Yard_Ramps",
|
||||||
|
"Yard_Bridges", "Yard_Stairs", "Yard_CollarWalk", "Yard_Piers",
|
||||||
"Dock_Gallery", "Dock_Threshold",
|
"Dock_Gallery", "Dock_Threshold",
|
||||||
"Elev_Landings", "Fusion_Basemat", "School_Stage",
|
"Elev_Landings", "Fusion_Basemat", "Fusion_AmbulatoryWide",
|
||||||
|
"Live_Terraces",
|
||||||
]
|
]
|
||||||
const WALK_WALLS: Array[String] = [
|
const WALK_WALLS: Array[String] = [
|
||||||
"Hull_Below", "Hull_Band", "Hull_Lintel", "Hull_Gates",
|
"Hull_Below", "Hull_Band", "Hull_Lintel",
|
||||||
"Yard_Wall", "NAF_Blocks", "Fusion_Wall",
|
"Yard_Wall", "Yard_StairRail", "Yard_Rails", "Yard_RampCurbs",
|
||||||
|
"Stair_A_Rails", "Stair_B_Rails",
|
||||||
|
"NAF_Blocks",
|
||||||
|
"Fusion_Wall", "Fusion_Pane", "Fusion_Frame", "Fusion_DoorFrames",
|
||||||
|
"Fusion_Control", "Fusion_Cryoplant", "Fusion_Turbine", "Fusion_Exchanger",
|
||||||
|
"Fusion_Columns", "Fusion_ShieldHigh", "Fusion_ShieldLow",
|
||||||
|
"Fusion_CryostatHigh", "Fusion_CryostatLow",
|
||||||
"School_Rooms", "School_Library", "School_Bulkhead",
|
"School_Rooms", "School_Library", "School_Bulkhead",
|
||||||
"Live_Wall", "Core_", "Dock_Hall", "Dock_Vestibule",
|
"School_Colonnade", "Lecture_Pillars", "Lecture_Backs", "Lecture_Curb",
|
||||||
"Well_Curb", "Lecture_Backs",
|
"Live_Wall",
|
||||||
|
"Market_Colonnade", "Market_Stalls",
|
||||||
|
"Core_",
|
||||||
|
"Dock_Hall", "Dock_Vestibule", "Dock_Frame", "Dock_Roof", "Dock_Rail",
|
||||||
|
"Well_Curb", "Well_Beam",
|
||||||
|
"Cap_Shell",
|
||||||
|
]
|
||||||
|
const WALK_SKIP: Array[String] = [
|
||||||
|
"Light", "LightRig_", "Elev_Cab_", "Exchange_Neon_",
|
||||||
|
"Stair_Lamps", "School_RoomDoors", "School_LibraryDoor",
|
||||||
|
"Well_Seal", "Well_Planet_", "Well_Beacon_",
|
||||||
|
"Marker_",
|
||||||
]
|
]
|
||||||
|
|
||||||
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||||
@@ -1180,24 +1200,32 @@ func _add_walk_collision(n: Node) -> int:
|
|||||||
for c in n.get_children():
|
for c in n.get_children():
|
||||||
made += _add_walk_collision(c)
|
made += _add_walk_collision(c)
|
||||||
if n is MeshInstance3D and _walkable_mesh(n.name) and (n as MeshInstance3D).mesh:
|
if n is MeshInstance3D and _walkable_mesh(n.name) and (n as MeshInstance3D).mesh:
|
||||||
for c in n.get_children():
|
|
||||||
if c is StaticBody3D:
|
|
||||||
return made
|
|
||||||
var mesh := n as MeshInstance3D
|
var mesh := n as MeshInstance3D
|
||||||
|
var body: StaticBody3D = null
|
||||||
|
for c in mesh.get_children():
|
||||||
|
if c is StaticBody3D:
|
||||||
|
body = c as StaticBody3D
|
||||||
|
break
|
||||||
|
if body == null:
|
||||||
mesh.create_trimesh_collision()
|
mesh.create_trimesh_collision()
|
||||||
for c in mesh.get_children():
|
for c in mesh.get_children():
|
||||||
if c is StaticBody3D:
|
if c is StaticBody3D:
|
||||||
var body := c as StaticBody3D
|
body = c as StaticBody3D
|
||||||
|
break
|
||||||
|
if body == null:
|
||||||
|
return made
|
||||||
body.collision_layer = 1
|
body.collision_layer = 1
|
||||||
body.collision_mask = 0
|
body.collision_mask = 0
|
||||||
|
for s in body.get_children():
|
||||||
|
if s is CollisionShape3D and (s as CollisionShape3D).shape is ConcavePolygonShape3D:
|
||||||
|
((s as CollisionShape3D).shape as ConcavePolygonShape3D).backface_collision = true
|
||||||
made += 1
|
made += 1
|
||||||
return made
|
return made
|
||||||
|
|
||||||
|
|
||||||
func _walkable_mesh(mesh_name: String) -> bool:
|
func _walkable_mesh(mesh_name: String) -> bool:
|
||||||
if mesh_name.begins_with("Light") or mesh_name.begins_with("Stair_A_Rails") or mesh_name.begins_with("Stair_B_Rails"):
|
for s in WALK_SKIP:
|
||||||
return false
|
if mesh_name.begins_with(s):
|
||||||
if mesh_name.begins_with("Stair_Lamps") or mesh_name.begins_with("Elev_Cab_"):
|
|
||||||
return false
|
return false
|
||||||
for prefix in WALK_FLOORS:
|
for prefix in WALK_FLOORS:
|
||||||
if mesh_name.begins_with(prefix):
|
if mesh_name.begins_with(prefix):
|
||||||
@@ -1254,3 +1282,4 @@ func _try_step(up: Vector3) -> void:
|
|||||||
if lift < 0.04 or lift > STEP_HEIGHT:
|
if lift < 0.04 or lift > STEP_HEIGHT:
|
||||||
return
|
return
|
||||||
global_position += up * (lift + 0.02)
|
global_position += up * (lift + 0.02)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cqa6tjsx6i2e3"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bs88v1pkp2opr" path="res://Young_2077.glb" id="1_gctqc"]
|
||||||
|
|
||||||
|
[node name="Node3D" type="Node3D" unique_id=1355800869]
|
||||||
|
|
||||||
|
[node name="Young" parent="." unique_id=1459793303 instance=ExtResource("1_gctqc")]
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
[gd_scene format=3 uid="uid://tqhk0a248g5k"]
|
[gd_scene format=3 uid="uid://tqhk0a248g5k"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://1udl42sp42sp" path="res://Scenes/Scripts/escape_capsule.gd" id="1_mfps1"]
|
[ext_resource type="Script" uid="uid://1udl42sp42sp" path="res://Scenes/Scripts/escape_capsule.gd" id="1_mfps1"]
|
||||||
[ext_resource type="PackedScene" uid="uid://dg0yewov2bymm" path="res://kestrel_ec01_godot/kestrel.tscn" id="2_85cng"]
|
|
||||||
[ext_resource type="Script" uid="uid://ond7pwt03hgd" path="res://kestrel_driver.gd" id="3_81abi"]
|
|
||||||
|
|
||||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_j65qs"]
|
[sub_resource type="CylinderShape3D" id="CylinderShape3D_j65qs"]
|
||||||
height = 3.795288
|
height = 3.795288
|
||||||
@@ -12,158 +10,12 @@ radius = 2.2
|
|||||||
height = 2.0190659
|
height = 2.0190659
|
||||||
radius = 0.10839844
|
radius = 0.10839844
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_kq58d"]
|
|
||||||
albedo_color = Color(0.3529412, 0.36862746, 0.4, 1)
|
|
||||||
|
|
||||||
[sub_resource type="CylinderMesh" id="CylinderMesh_18y2q"]
|
|
||||||
material = SubResource("StandardMaterial3D_kq58d")
|
|
||||||
top_radius = 1.8
|
|
||||||
bottom_radius = 2.2
|
|
||||||
height = 3.5
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ryguw"]
|
|
||||||
albedo_color = Color(0.017732391, 0.017732391, 0.01773239, 1)
|
|
||||||
metallic = 0.56
|
|
||||||
|
|
||||||
[sub_resource type="CylinderMesh" id="CylinderMesh_3jrso"]
|
|
||||||
material = SubResource("StandardMaterial3D_ryguw")
|
|
||||||
top_radius = 2.3
|
|
||||||
bottom_radius = 2.3
|
|
||||||
height = 0.25
|
|
||||||
|
|
||||||
[sub_resource type="SphereShape3D" id="SphereShape3D_crvdt"]
|
[sub_resource type="SphereShape3D" id="SphereShape3D_crvdt"]
|
||||||
radius = 3.5
|
radius = 3.5
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_58fjt"]
|
|
||||||
transparency = 1
|
|
||||||
cull_mode = 2
|
|
||||||
shading_mode = 0
|
|
||||||
albedo_color = Color(0.2347951, 0.2347951, 0.2347951, 0.060000002)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_ijs7n"]
|
|
||||||
material = SubResource("StandardMaterial3D_58fjt")
|
|
||||||
size = Vector3(1.4949585, 0.8827637, 0.16409448)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_a6jrf"]
|
|
||||||
albedo_color = Color(0.101960786, 0.13725491, 0.19607843, 1)
|
|
||||||
emission = Color(0, 0.9704087, 1, 1)
|
|
||||||
emission_energy_multiplier = 0.2
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_xuqvo"]
|
|
||||||
material = SubResource("StandardMaterial3D_a6jrf")
|
|
||||||
size = Vector3(1.2497559, 0.6052246, 0.04)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1u8w0"]
|
|
||||||
albedo_color = Color(0.37187055, 0.11207495, 0.050708726, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_d13ii"]
|
|
||||||
material = SubResource("StandardMaterial3D_1u8w0")
|
|
||||||
size = Vector3(0.65, 0.12, 0.65)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_l6148"]
|
|
||||||
albedo_color = Color(0.88857853, 0.025840001, 0, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_ts8u1"]
|
|
||||||
material = SubResource("StandardMaterial3D_l6148")
|
|
||||||
size = Vector3(0.65, 0.85, 0.1)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wqh1g"]
|
|
||||||
albedo_color = Color(0.05882353, 0.05882353, 0.05882353, 1)
|
|
||||||
metallic = 0.51
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_l6148"]
|
|
||||||
material = SubResource("StandardMaterial3D_wqh1g")
|
|
||||||
size = Vector3(1, 0.7, 0.5)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_kx67c"]
|
|
||||||
albedo_color = Color(0.101960786, 0.13725491, 0.19607843, 1)
|
|
||||||
emission_enabled = true
|
|
||||||
emission = Color(0.101960786, 0.22745098, 0.36078432, 1)
|
|
||||||
emission_energy_multiplier = 2.0
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_wqh1g"]
|
|
||||||
material = SubResource("StandardMaterial3D_kx67c")
|
|
||||||
size = Vector3(0.35, 0.25, 0.02)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_r6cyv"]
|
|
||||||
albedo_color = Color(0.16470589, 0.16470589, 0.16470589, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_kx67c"]
|
|
||||||
material = SubResource("StandardMaterial3D_r6cyv")
|
|
||||||
size = Vector3(0.15, 0.08, 0.02)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_omqyl"]
|
|
||||||
albedo_color = Color(0.039215688, 0.0627451, 0.09411765, 1)
|
|
||||||
emission_enabled = true
|
|
||||||
emission = Color(0.101960786, 0.22745098, 0.36078432, 1)
|
|
||||||
emission_energy_multiplier = 2.0
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_52c6f"]
|
|
||||||
material = SubResource("StandardMaterial3D_omqyl")
|
|
||||||
size = Vector3(0.24501038, 0.20367628, 0.01890625)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0odxb"]
|
|
||||||
albedo_color = Color(0.07058824, 0.07058824, 0.07058824, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_r6cyv"]
|
|
||||||
material = SubResource("StandardMaterial3D_0odxb")
|
|
||||||
size = Vector3(0.08, 2.1133926, 3.2745347)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_d13ii"]
|
|
||||||
albedo_color = Color(0.07058824, 0.07058824, 0.07058824, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_4yprg"]
|
|
||||||
material = SubResource("StandardMaterial3D_d13ii")
|
|
||||||
size = Vector3(2.7717957, 0.04, 3.2841797)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_52c6f"]
|
|
||||||
albedo_color = Color(0.07058824, 0.07058824, 0.07058824, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_o0dcp"]
|
|
||||||
material = SubResource("StandardMaterial3D_52c6f")
|
|
||||||
size = Vector3(1.187207, 0.06, 0.35)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p2ucw"]
|
|
||||||
albedo_color = Color(0.05882353, 0.05882353, 0.05882353, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_omqyl"]
|
|
||||||
material = SubResource("StandardMaterial3D_p2ucw")
|
|
||||||
size = Vector3(2.9555702, 0.75, 0.08)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ucmdv"]
|
|
||||||
albedo_color = Color(0.05882353, 0.05882353, 0.05882353, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_p2ucw"]
|
|
||||||
material = SubResource("StandardMaterial3D_ucmdv")
|
|
||||||
size = Vector3(2.9560547, 0.7133789, 0.08)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_seu75"]
|
|
||||||
albedo_color = Color(0.05882353, 0.05882353, 0.05882353, 1)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_81jjf"]
|
|
||||||
albedo_color = Color(0.05882353, 0.05882353, 0.05882353, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_ucmdv"]
|
|
||||||
material = SubResource("StandardMaterial3D_81jjf")
|
|
||||||
size = Vector3(0.65, 0.05, 3.2439454)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ijs7n"]
|
|
||||||
albedo_color = Color(0.05882353, 0.05882353, 0.05882353, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_81jjf"]
|
|
||||||
material = SubResource("StandardMaterial3D_ijs7n")
|
|
||||||
size = Vector3(0.64851075, 0.05, 3.2384765)
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nei3f"]
|
|
||||||
albedo_color = Color(0.05882353, 0.05882353, 0.05882353, 1)
|
|
||||||
|
|
||||||
[node name="EscapeCapsule" type="CharacterBody3D" unique_id=900802887 groups=["interactable"]]
|
[node name="EscapeCapsule" type="CharacterBody3D" unique_id=900802887 groups=["interactable"]]
|
||||||
script = ExtResource("1_mfps1")
|
script = ExtResource("1_mfps1")
|
||||||
|
|
||||||
[node name="Kestrel" parent="." unique_id=193910237 instance=ExtResource("2_85cng")]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.0820568, 0)
|
|
||||||
script = ExtResource("3_81abi")
|
|
||||||
|
|
||||||
[node name="CameraPivot" type="Node3D" parent="." unique_id=1263006067]
|
[node name="CameraPivot" type="Node3D" parent="." unique_id=1263006067]
|
||||||
transform = Transform3D(1, 8.742278e-08, -8.7422784e-08, -8.742278e-08, -4.371139e-08, -1, -8.7422784e-08, 1, -4.371138e-08, -0.6827204, -1.8933923, 1.2853639)
|
transform = Transform3D(1, 8.742278e-08, -8.7422784e-08, -8.742278e-08, -4.371139e-08, -1, -8.7422784e-08, 1, -4.371138e-08, -0.6827204, -1.8933923, 1.2853639)
|
||||||
|
|
||||||
@@ -190,26 +42,6 @@ shape = SubResource("CylinderShape3D_81abi")
|
|||||||
transform = Transform3D(-0.792779, -0.34827715, -0.5002045, -0.45405236, 0.8849464, 0.10347074, 0.40661794, 0.30914834, -0.85970277, 1.929804, -2.7048826, -1.8303667)
|
transform = Transform3D(-0.792779, -0.34827715, -0.5002045, -0.45405236, 0.8849464, 0.10347074, 0.40661794, 0.30914834, -0.85970277, 1.929804, -2.7048826, -1.8303667)
|
||||||
shape = SubResource("CylinderShape3D_81abi")
|
shape = SubResource("CylinderShape3D_81abi")
|
||||||
|
|
||||||
[node name="Body" type="MeshInstance3D" parent="." unique_id=683390934]
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource("CylinderMesh_18y2q")
|
|
||||||
|
|
||||||
[node name="ExternalView" type="SubViewport" parent="Body" unique_id=1265488610]
|
|
||||||
handle_input_locally = false
|
|
||||||
size = Vector2i(3840, 2160)
|
|
||||||
render_target_update_mode = 4
|
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="Body/ExternalView" unique_id=698867349]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 18.911898, 5.1233277, -0.039017588)
|
|
||||||
visible = false
|
|
||||||
fov = 72.0
|
|
||||||
far = 1000000.0
|
|
||||||
|
|
||||||
[node name="HeatShield" type="MeshInstance3D" parent="." unique_id=2080285390]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1.9, 0)
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource("CylinderMesh_3jrso")
|
|
||||||
|
|
||||||
[node name="InteractionArea" type="Area3D" parent="." unique_id=1761123778]
|
[node name="InteractionArea" type="Area3D" parent="." unique_id=1761123778]
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractionArea" unique_id=1229487990]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractionArea" unique_id=1229487990]
|
||||||
@@ -218,126 +50,3 @@ shape = SubResource("SphereShape3D_crvdt")
|
|||||||
[node name="ThirdPersonCamera" type="Camera3D" parent="." unique_id=284479039]
|
[node name="ThirdPersonCamera" type="Camera3D" parent="." unique_id=284479039]
|
||||||
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, -9.5, 3.5)
|
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, -9.5, 3.5)
|
||||||
far = 1000000.0
|
far = 1000000.0
|
||||||
|
|
||||||
[node name="Cockpit" type="Node3D" parent="." unique_id=1933795690]
|
|
||||||
transform = Transform3D(-1, 8.742278e-08, 3.821371e-15, 0, -4.371139e-08, 1, 8.742278e-08, 1, 4.371139e-08, 0, -0.94330907, -0.62695575)
|
|
||||||
visible = false
|
|
||||||
|
|
||||||
[node name="WindowGlass" type="MeshInstance3D" parent="Cockpit" unique_id=99141125]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0015087128, 1.5509896, 2.2775838)
|
|
||||||
mesh = SubResource("BoxMesh_ijs7n")
|
|
||||||
|
|
||||||
[node name="HudShape" type="MeshInstance3D" parent="Cockpit" unique_id=1572026781]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0777357, 1.8649361)
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource("BoxMesh_xuqvo")
|
|
||||||
|
|
||||||
[node name="CockpitLight" type="OmniLight3D" parent="Cockpit" unique_id=1492843635]
|
|
||||||
transform = Transform3D(-1, 0, 8.742278e-08, 8.742278e-08, -4.371139e-08, 1, 3.821371e-15, 1, 4.371139e-08, 0, 2.1579213, 0.00050091743)
|
|
||||||
light_color = Color(1, 0.89411765, 0.76862746, 1)
|
|
||||||
omni_range = 3.5
|
|
||||||
|
|
||||||
[node name="Seat" type="MeshInstance3D" parent="Cockpit" unique_id=123094182]
|
|
||||||
mesh = SubResource("BoxMesh_d13ii")
|
|
||||||
|
|
||||||
[node name="SeatBack" type="MeshInstance3D" parent="Cockpit" unique_id=1553533167]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.38205957, -0.36825216)
|
|
||||||
mesh = SubResource("BoxMesh_ts8u1")
|
|
||||||
|
|
||||||
[node name="MainPanel" type="MeshInstance3D" parent="Cockpit" unique_id=2127997099]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.42596802, 1.6161394)
|
|
||||||
mesh = SubResource("BoxMesh_l6148")
|
|
||||||
|
|
||||||
[node name="GlassCockpitPanel" type="MeshInstance3D" parent="Cockpit/MainPanel" unique_id=56681667]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.16014825, -0.25493002)
|
|
||||||
mesh = SubResource("BoxMesh_wqh1g")
|
|
||||||
|
|
||||||
[node name="LeftInstrument" type="MeshInstance3D" parent="Cockpit/MainPanel" unique_id=944149313]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.294096, 0.21341178, -0.25256205)
|
|
||||||
mesh = SubResource("BoxMesh_kx67c")
|
|
||||||
|
|
||||||
[node name="RightInstrument" type="MeshInstance3D" parent="Cockpit/MainPanel" unique_id=1061352713]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.294, 0.213, -0.253)
|
|
||||||
mesh = SubResource("BoxMesh_kx67c")
|
|
||||||
|
|
||||||
[node name="ForwardDisplay" type="MeshInstance3D" parent="Cockpit/MainPanel" unique_id=1977279423]
|
|
||||||
transform = Transform3D(0.9999994, -5.728489e-05, -0.0011173013, 5.5921435e-05, 0.99999917, -0.0012259485, 0.0011173704, 0.0012257855, 0.9999986, 0.32665062, 0.049695313, -0.25658345)
|
|
||||||
mesh = SubResource("BoxMesh_52c6f")
|
|
||||||
|
|
||||||
[node name="LeftWall" type="MeshInstance3D" parent="Cockpit" unique_id=1514749488]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4201202, 0.6709095, 0.801975)
|
|
||||||
mesh = SubResource("BoxMesh_r6cyv")
|
|
||||||
|
|
||||||
[node name="RightWall" type="MeshInstance3D" parent="Cockpit" unique_id=1764263004]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.4199867, 0.67109835, 0.79347587)
|
|
||||||
mesh = SubResource("BoxMesh_r6cyv")
|
|
||||||
|
|
||||||
[node name="Floor" type="MeshInstance3D" parent="Cockpit" unique_id=1998677292]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0061683655, -0.25959706, 0.80126953)
|
|
||||||
mesh = SubResource("BoxMesh_4yprg")
|
|
||||||
|
|
||||||
[node name="OverheadConsole" type="MeshInstance3D" parent="Cockpit" unique_id=976403028]
|
|
||||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 2.2589908, 0.95627284)
|
|
||||||
mesh = SubResource("BoxMesh_o0dcp")
|
|
||||||
|
|
||||||
[node name="ForwardBulkhead" type="MeshInstance3D" parent="Cockpit" unique_id=1582033665]
|
|
||||||
transform = Transform3D(1, -6.905431e-09, -3.4056352e-08, -6.905438e-09, 0.92101103, -0.38953644, 3.4054356e-08, 0.38953644, 0.92101103, -0.0020980835, 0.10092795, 2.3168466)
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource("BoxMesh_omqyl")
|
|
||||||
|
|
||||||
[node name="ForwardBulkhead2" type="MeshInstance3D" parent="Cockpit/ForwardBulkhead" unique_id=1004286348]
|
|
||||||
transform = Transform3D(1, -6.9062125e-09, 3.4056193e-08, -6.905424e-09, 0.9210111, 0.38953647, -3.405436e-08, -0.38953647, 0.9210111, 0.018800735, 0.70290273, -0.13796377)
|
|
||||||
mesh = SubResource("BoxMesh_omqyl")
|
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Cockpit/ForwardBulkhead" unique_id=411181651]
|
|
||||||
transform = Transform3D(1, -7.2910282e-09, 3.4950116e-08, -7.2894153e-09, 0.91661024, 0.39978403, -3.4950364e-08, -0.39978403, 0.91661024, 0.021972656, 1.129911, -0.3276949)
|
|
||||||
mesh = SubResource("BoxMesh_p2ucw")
|
|
||||||
|
|
||||||
[node name="FrontBulkhead" type="CSGCombiner3D" parent="Cockpit/ForwardBulkhead" unique_id=1462779310]
|
|
||||||
transform = Transform3D(1, -6.9062196e-09, 3.40562e-08, -6.905424e-09, 0.92101115, 0.3895365, -3.405436e-08, -0.3895365, 0.92101115, 0.0020980835, -0.0909484, 0.044061184)
|
|
||||||
visible = false
|
|
||||||
|
|
||||||
[node name="BulkheadShape" type="CSGPolygon3D" parent="Cockpit/ForwardBulkhead/FrontBulkhead" unique_id=991532318]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.9277502, 0.22244406)
|
|
||||||
polygon = PackedVector2Array(-1.55, -0.345, 1.55, -0.345, 0.72924423, 0.34169006, -0.75127983, 0.3621254)
|
|
||||||
depth = 0.12
|
|
||||||
material = SubResource("StandardMaterial3D_seu75")
|
|
||||||
|
|
||||||
[node name="RoofLeft" type="MeshInstance3D" parent="Cockpit" unique_id=1535690315]
|
|
||||||
transform = Transform3D(0.7071067, 0.7071067, 0, -0.7071067, 0.7071067, 0, 0, 0, 1, 1.1796818, 1.8760827, 0.79144645)
|
|
||||||
mesh = SubResource("BoxMesh_ucmdv")
|
|
||||||
|
|
||||||
[node name="RoofLeft1" type="MeshInstance3D" parent="Cockpit/RoofLeft" unique_id=1785270765]
|
|
||||||
transform = Transform3D(0.8763269, -0.4817174, 9.8520925e-09, 0.4817174, 0.8763269, 7.3968387e-09, -1.2196834e-08, -1.7361046e-09, 1, -0.6168623, -0.15812302, 0.005322695)
|
|
||||||
mesh = SubResource("BoxMesh_ucmdv")
|
|
||||||
|
|
||||||
[node name="RoofRight" type="MeshInstance3D" parent="Cockpit" unique_id=10541876]
|
|
||||||
transform = Transform3D(0.7071067, -0.7071067, 0, 0.7071067, 0.7071067, 0, 0, 0, 1, -1.18, 1.876, 0.791)
|
|
||||||
mesh = SubResource("BoxMesh_ucmdv")
|
|
||||||
|
|
||||||
[node name="RoofRight1" type="MeshInstance3D" parent="Cockpit/RoofRight" unique_id=597720853]
|
|
||||||
transform = Transform3D(0.87630665, 0.4817537, 0, -0.4817537, 0.87630665, 0, 3.543832e-13, 0, 1, 0.6105108, -0.15750027, -0.0017383099)
|
|
||||||
mesh = SubResource("BoxMesh_81jjf")
|
|
||||||
|
|
||||||
[node name="RoofTop" type="MeshInstance3D" parent="Cockpit" unique_id=974546659]
|
|
||||||
transform = Transform3D(0.9999999, 0, 0, 0, 0.9999999, 0, 0, 0, 1, -0.00787735, 2.2835252, 0.79144645)
|
|
||||||
mesh = SubResource("BoxMesh_ucmdv")
|
|
||||||
|
|
||||||
[node name="ForwardBulkhead2" type="CSGCombiner3D" parent="Cockpit" unique_id=1803926178]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.14711916, 2.8341548)
|
|
||||||
|
|
||||||
[node name="BulkTop" type="CSGPolygon3D" parent="Cockpit/ForwardBulkhead2" unique_id=1704103664]
|
|
||||||
transform = Transform3D(1, -1.4087291e-09, 1.562843e-08, -1.4074928e-09, 0.9838952, 0.17874739, -1.5626552e-08, -0.17874739, 0.9838952, 0.0020980835, 1.7711809, -0.50199556)
|
|
||||||
polygon = PackedVector2Array(-1.55, -0.345, 1.55, -0.345, 0.72924423, 0.34169006, -0.75127983, 0.3621254)
|
|
||||||
depth = 0.12
|
|
||||||
material = SubResource("StandardMaterial3D_seu75")
|
|
||||||
|
|
||||||
[node name="BulkBottom" type="CSGBox3D" parent="Cockpit/ForwardBulkhead2" unique_id=1217332907]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0018692017, 0.49510977, -0.49700785)
|
|
||||||
size = Vector3(2.788086, 1.8628662, 0.08208008)
|
|
||||||
material = SubResource("StandardMaterial3D_nei3f")
|
|
||||||
|
|
||||||
[node name="WindowCutout" type="CSGBox3D" parent="Cockpit/ForwardBulkhead2" unique_id=1392835612]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.4177693, -0.5765586)
|
|
||||||
operation = 2
|
|
||||||
size = Vector3(1.5, 0.89470214, 0.2836914)
|
|
||||||
|
|||||||
+9185
-9083
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
|
|||||||
|
# Brachistochrone — NAF ship assets
|
||||||
|
|
||||||
|
Rebuilt 2026-09-20 in App Builder from the Expert-mode handoff. Procedural Three.js is the source of truth (`src/ship/*`, `src/capsule/buildCapsule.ts`). Do not hand-edit GLBs.
|
||||||
|
|
||||||
|
## Frame
|
||||||
|
|
||||||
|
- 1 glTF unit = 1 meter, Y-up
|
||||||
|
- **Kestrel EC-01:** heat shield at Y≈0, nose +Y, crew backs on the shield, gaze +Y, visors +Z
|
||||||
|
- **Resolute CVT-07:** +Y = nose / thrust up, decks = XZ stacked on Y, +X starboard, +Z dorsal
|
||||||
|
- Faction: North American Federation. Charcoal hull `#2a3038`, liner `#6a727c`
|
||||||
|
- No keel gun. No planet in the hangar preview. Default view is solid exterior.
|
||||||
|
|
||||||
|
## Resolute
|
||||||
|
|
||||||
|
- Length ~46 m, beam ~13 m, height ~11.4 m
|
||||||
|
- Superellipse loft, `SEGS=8`, `SUPER_EXP=0.22`
|
||||||
|
- `WALL=0.58 m`, floor extra inset `0.30 m`
|
||||||
|
- 11 decks, pitch 2.65 m, room height 2.42 m
|
||||||
|
- Companionway: well 0.95 × 3.35 m, rise 0.221 m, run 0.255 m, 12 treads, trunk walls, switchback per deck
|
||||||
|
- Cabins on decks 03/04 against port/starboard inner hull; corridor + stair in the center
|
||||||
|
- Dorsal airlock on storage deck 07, walk-through vestibule + round hatches
|
||||||
|
|
||||||
|
## Kestrel
|
||||||
|
|
||||||
|
- ~4.55 m capsule, four Gemini L-couches
|
||||||
|
- Cabin lights ~0.28 intensity (dim)
|
||||||
|
- Glass opacity 0.20, roughness 0.07, metalness 0, hull holes behind panes
|
||||||
|
- Sitting bulkhead at z≈−0.36, stow lids in a line
|
||||||
|
- Docking probe stays in the tunnel
|
||||||
|
|
||||||
|
## Clips (seek 0..1)
|
||||||
|
|
||||||
|
Kestrel: `gear_deploy` `hatch_crew_open` `hatch_ladder_deploy` `hatch_dock_open` `instruments_demo`
|
||||||
|
|
||||||
|
Resolute: `gear_deploy` `radiators_deploy` `hatch_airlock_open` `hatch_dock_open` `hatch_cargo_open` `torpedo_doors_open`
|
||||||
|
|
||||||
|
Named cameras: `SeatCamera` (Kestrel), `HelmCamera` + `PlayerSpawn` (Resolute)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
Godot 4.7 project. Units: 1 = 1 meter. Open this folder (it has project.godot). Hongqi is the main scene. The other ships are resolute.tscn, kestrel.tscn, soyuz.tscn, and shrike.tscn. Do not re-import these GLBs into the hangar. These .tscn files are ordinary Godot 4.7 scenes; replace the whole folder if an older copy still says a scene is corrupt.
|
||||||
|
|
||||||
|
Each scene is a glb (mesh, embedded PNG textures, 0..1 animation clips) plus a script that spawns particles on the Vfx sockets when you press Play. Textures are inside the glb. Ribbons are not. Drive, RCS, and abort flames are crossed additive ribbons from naf_fx.gd and ribbon.gdshader. Chaff and abort sparks are the only GPUParticles. PDC is a stream of short tracer bolts, not one beam. The tscn adds a near-black world so the additive jets show. Godot's default grey sky washes them out.
|
||||||
|
|
||||||
|
Press Play, then use the inspector. Drive On plus Throttle runs the torch. The 0..1 sliders seek the imported clips. Gear starts down. Hatches start shut. Hongqi radiators start open.
|
||||||
|
|
||||||
|
Keep these files together. The scripts preload naf_fx.gd from this folder.
|
||||||
|
|
||||||
|
| Scene | Open | Axes |
|
||||||
|
|---|---|---|
|
||||||
|
| resolute.tscn | NAF Resolute | nose +Y, starboard +X, dorsal +Z |
|
||||||
|
| kestrel.tscn | Kestrel EC-01 | nose +Y, starboard +X, dorsal +Z |
|
||||||
|
| hongqi.tscn | 红旗-07 | nose +Y, starboard +X, dorsal +Z. Root scale 1.14, same as the hangar. |
|
||||||
|
| soyuz.tscn | Soyuz-MS | dock +Z, dorsal +Y, starboard +X. Nose thrust is +Z. |
|
||||||
|
| shrike.tscn | Phantom II | nose +Z, belly -Y, starboard +X. Nose thrust is +Z. |
|
||||||
|
|
||||||
|
Rest pose: gear down where the ship has gear, hatches shut, torpedo doors shut, canopy shut, effects off. Hongqi radiators start open. Sliders seek the imported AnimationPlayer. If Godot nests that player, the scripts look it up with find_child.
|
||||||
|
|
||||||
|
Hongqi clips: gear_deploy, radiators_deploy, hatch_boarding_port, hatch_boarding_stbd, hatch_airlock_open, hatch_deck_open, hatch_ramp_open, torpedo_doors_open.
|
||||||
|
Hongqi sockets: Vfx_DrivePlume (main bell, exhaust -Y), Vfx_Drive_Port, Vfx_Drive_Stbd, Vfx_Chaff, RCS *_Vfx on the grafted clusters, PDT_*_Muzzle.
|
||||||
|
Hongqi drive_on plus throttle is a gold core inside a cinnabar sheath. Outboard bells use a shorter copy of the same plume. battle_lights goes dim cinnabar, not the Resolute red.
|
||||||
|
|
||||||
|
Soyuz clips: hatch_crew_open, hatch_transfer_open, hatch_dock_open.
|
||||||
|
Soyuz sockets: Vfx_DrivePlume (exhaust -Z, short chemical plume), Vfx_RCS_*, Vfx_Abort_0..5 (the six soft-landing motors). abort_burn is that landing burn.
|
||||||
|
|
||||||
|
Shrike clips: gear_deploy, canopy_open.
|
||||||
|
Shrike sockets: Vfx_DrivePlume (exhaust -Z), Vfx_RCS_*, PDT_L_Muzzle, PDT_R_Muzzle, Vfx_Chaff.
|
||||||
|
|
||||||
|
Resolute and Kestrel are unchanged in behavior. See PARTICLE_FX.md for ribbon sizes, which bell fires, and what not to replace with a particle spray.
|
||||||
|
|
||||||
|
Exhaust direction is the socket's local position, rotated by the inverse of its quaternion. Do not aim the ribbons by the ship's nose axis. A bell fires only when that exhaust dots the negated thrust by more than 0.7.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
NAF Kestrel EC-01 — Godot GLB
|
||||||
|
Units: 1 = 1 m. Y-up. Docking collar is +Y. Visors are +Z. Heat shield is -Y.
|
||||||
|
This file is the original mesh with hangar fixes baked into node transforms:
|
||||||
|
- Nav lights (port, starboard, white) sit outside the hull, above the heat shield.
|
||||||
|
- RCS bells seated on their housings and aimed outward.
|
||||||
|
- Abort motors yawed off the RCS line, fairings on the skin.
|
||||||
|
- Blade and VHF antennas pulled onto the hull. S-band seated on the shoulder.
|
||||||
|
- Docking hatch lid and probe seated. Probe stays a child of DockHatch, so hatch_dock_open still carries it.
|
||||||
|
Animations: gear_deploy, hatch_crew_open, hatch_ladder_deploy, hatch_dock_open, instruments_demo.
|
||||||
|
kestrel_ec01.gd drives those clips 0..1.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# Particle FX handoff — Resolute and Kestrel
|
||||||
|
|
||||||
|
The hangar look is **not a GPUParticles spray**. Drive, RCS, and the abort flame are additive ribbon meshes parented to the Vfx sockets. A spray of quads (what the old Godot scripts built) reads as smoke. The hangar reads as a solid jet.
|
||||||
|
|
||||||
|
Source of truth: `public/hangar-fx.js`, function `updateFx`. Several helpers in that file are **dead** and must not be ported:
|
||||||
|
|
||||||
|
- `makeDrivePlume` / `stepDrivePlume` — unused. The live drive is `makeSpear`.
|
||||||
|
- `makeRcsJet` / `stepRcsJet` — unused. The live RCS jet is `makeSpear`.
|
||||||
|
- `makePdcStream` / `stepPdcStream` — unused. The live PDC is `spawnTracer`.
|
||||||
|
- `burstChaff` — unused. The live chaff is `makeChaffCloud`.
|
||||||
|
|
||||||
|
The scripts in this folder already implement the live path:
|
||||||
|
|
||||||
|
- `naf_fx.gd` builds ribbons, abort sparks, chaff, tracer bolts, and the exhaust light.
|
||||||
|
- `ribbon.gdshader` is the additive gradient. UV.y 0 is the socket, 1 is the tip. Edges fade so the ribbon is a soft streak, not a rectangle.
|
||||||
|
- `naf_resolute.gd` arms drive, RCS, PDC, chaff.
|
||||||
|
- `kestrel_ec01.gd` arms RCS and the four abort motors.
|
||||||
|
|
||||||
|
Do not replace the ribbons with `GPUParticles3D`. Particles are only used where the hangar uses points: abort sparks and chaff.
|
||||||
|
|
||||||
|
## Axes and which bell fires
|
||||||
|
|
||||||
|
Ship space, same as the GLB: nose +Y, starboard +X, dorsal +Z. Port is -X. Ventral is -Z.
|
||||||
|
|
||||||
|
`rcs_direction` is a thrust, not an exhaust. A bell fires only when its exhaust direction (ribbon local +Y, in ship space) dots the **negated** thrust by more than 0.7. Off fires nothing. They do not all fire together.
|
||||||
|
|
||||||
|
| Direction | Thrust | Bells that fire |
|
||||||
|
|---|---|---|
|
||||||
|
| Nose | +Y | exhaust mostly -Y |
|
||||||
|
| Tail | -Y | exhaust mostly +Y |
|
||||||
|
| Starboard | +X | exhaust mostly -X |
|
||||||
|
| Port | -X | exhaust mostly +X |
|
||||||
|
| Dorsal | +Z | exhaust mostly -Z |
|
||||||
|
| Ventral | -Z | exhaust mostly +Z |
|
||||||
|
|
||||||
|
Exhaust direction comes from the socket, not from a guess. Take the socket's local `position`, rotate it by the inverse of the socket quaternion, and normalize. That is the nozzle axis. Aim the ribbon so its +Y matches. The socket stays put, so a gimballed drive bell carries the plume with it.
|
||||||
|
|
||||||
|
Burn does not pop. Ease toward the target with `1 - exp(-dt / tau)`. Tau is 0.08 s turning on, 0.14 s turning off.
|
||||||
|
|
||||||
|
Opacity flickers. For drive and RCS: `0.82 + 0.18 * sin(time * 37 + seed * 2.1)`. Hide the ribbon when the flickered opacity drops under 0.02.
|
||||||
|
|
||||||
|
## Resolute drive
|
||||||
|
|
||||||
|
Socket `Vfx_DrivePlume`. Two ribbons, each three double-sided planes spun by `i / 3 * PI` (0°, 60°, 120°). Additive, unshaded, no depth write, depth test on, no fog.
|
||||||
|
|
||||||
|
| Piece | Length | Width | Planes | Base opacity | Color along the jet |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Core | 38 m | 1.35 m | 3 | 0.95 | white `f4f8ff` at the throat, ice blue at mid, clear at the tip |
|
||||||
|
| Sheath | 26 m | 3.2 m | 3 | 0.50 | saturated blue `5aa8ff`, wider and shorter than the core |
|
||||||
|
|
||||||
|
`drive_on` with `throttle` is the power. Spool eases toward 1 over 0.26 s when power > 0.04, and back to 0 over 0.07 s. Visible amount is `power * spool`. The sheath uses 0.82 of that amount.
|
||||||
|
|
||||||
|
If the world exhaust points down (`world_dir.y < -0.35`) and the bell is above the hangar pad, shorten both ribbons by the same factor so they stop at the deck instead of spearing it. Deck is world y = -2.45, with 0.22 m of clearance. Minimum reach 0.9 m. If the bell is already below that plane (the ship sitting at the origin in these scenes), do not clip: the torch stays full length. The sheath mesh is already the shorter one. Do **not** multiply its scale by 26/38 a second time. In the hangar the ship stands nose-up on the pad, so the torch points at the deck and this clip is why the plume is a short bright jet under the bell instead of a 38 m lance. In space, or with the ship at the scene origin, exhaust is not aimed at a pad under the bell and the full length shows.
|
||||||
|
|
||||||
|
Spot light on the socket, 0.35 m out along the exhaust, color `c5dcff`, cone half-angle 0.3 rad, range follows the clipped length (at least 2.2 m, at most 38 m), decay 2. Energy is `5 * plume amount`. Godot's tonemapper is harsher than the hangar. If it blooms white, lower the light energy. Do not dim the ribbon to compensate.
|
||||||
|
|
||||||
|
`DriveGlow` and `DrivePlasma` are materials, not particles. Emission color `(0.55, 0.72, 1.0) * power`, energy `power * 7`. Radiator fins stay cold unless throttle has been above 0.72 with the panels open. That heat path is already in `naf_resolute.gd`. Leave it.
|
||||||
|
|
||||||
|
## Resolute RCS
|
||||||
|
|
||||||
|
One ribbon per socket whose name ends in `_Vfx` or starts with `Vfx_RCS_`. Skip `Vfx_DrivePlume`, `Vfx_Chaff`, and anything named `Vfx_Abort_`.
|
||||||
|
|
||||||
|
Length 1.2 m, width 0.09 m, **two** planes (0° and 90°). Base opacity 0.82. Color `dcefff` at the throat, fading to pale blue, then clear. Same flicker and burn ease as the drive. No gravity, no spread. It should look like a pencil of light out of the bell, not a cone of sparks.
|
||||||
|
|
||||||
|
## Resolute PDC
|
||||||
|
|
||||||
|
Not a beam and not a particle system. Each `PDT_*_Muzzle` fires discrete bolts at 42 Hz, at most 2 per frame, 90 bolts alive across the whole ship.
|
||||||
|
|
||||||
|
Each bolt is a 5-sided additive cone, color `ffe6b0`, radius 0.016 at the tail and 0.008 at the nose, then scaled in length to a random 1.5–2.3 m. Life 0.12–0.2 s. Speed 90–140 m/s. Direction is the muzzle exhaust, jittered in a 1.15° cone. Spawn 0.18 m ahead of the socket. When `pdc_fire` turns off, stop spawning and let the live bolts finish.
|
||||||
|
|
||||||
|
Turret slew is not part of this handoff.
|
||||||
|
|
||||||
|
## Resolute chaff
|
||||||
|
|
||||||
|
Socket `Vfx_Chaff`. This one **is** particles, because the hangar cloud is points.
|
||||||
|
|
||||||
|
28 soft discs. Life about 1.55 s (the hangar rolls 1.35–1.75). Speed 3–8 m/s. Spread 22°. No gravity, no damping. Size 0.07–0.16 m. Color `(0.90, 0.93, 0.97)`, additive. Hold alpha until 55% of life, then fade to nothing. `chaff_fire` is the continuous cloud.
|
||||||
|
|
||||||
|
## Kestrel RCS
|
||||||
|
|
||||||
|
Same ribbon as Resolute RCS, on every `Vfx_RCS_*` socket. Kestrel has no drive, no PDC, and no chaff.
|
||||||
|
|
||||||
|
## Kestrel abort
|
||||||
|
|
||||||
|
Socket per motor: `Vfx_Abort_FR`, `Vfx_Abort_FL`, `Vfx_Abort_RR`, `Vfx_Abort_RL`. `abort_burn` ramps a hold value up over 0.22 s and down over 0.28 s. All four motors use that one value.
|
||||||
|
|
||||||
|
Each motor has:
|
||||||
|
|
||||||
|
| Piece | What it is |
|
||||||
|
|---|---|
|
||||||
|
| Core ribbon | Tapered, 2.1 m. Width 0.06 m at the throat, 0.20 m at the tip. Width along the length is `w0 + (w1 - w0) * t^0.7`. Three planes. White-hot `fff4e6` at the root, orange, then clear. Opacity `hold * 0.95 * flick`. |
|
||||||
|
| Sheath ribbon | Tapered, 5.2 m. Width 0.10 → 0.78 m, same exponent. Orange `ff6418`. Opacity `hold * 0.82 * flick`. Its length scale breathes: `0.9 + 0.1 * sin(time * 19)`. |
|
||||||
|
| Core sparks | 10 round additive points. Life 0.12–0.18 s (use 0.15). Speed 18–28 m/s. Spread 1.6°. Size about 0.05 m. |
|
||||||
|
| Sheath sparks | 18 points. Life 0.20–0.32 s (use 0.26). Speed 14–22 m/s. Spread 4.5°. Size about 0.09 m. |
|
||||||
|
| Color of sparks | White `(1, 0.97, 0.92)` at birth, orange `(1, 0.46, 0.12)` by mid-life, dark red `(0.55, 0.16, 0.05)` at the end. Opacity `hold * 0.55`. |
|
||||||
|
| Flash | Soft additive disc on the throat, color `fff1dd`, opacity `hold * 0.45`, scale `0.06 + hold * 0.04` m. |
|
||||||
|
| Light | Spot, color `ff6a28`, energy `hold * 8`, half-angle 0.5 rad, range 6.5 m, decay 1.8, sitting 0.08 m out from the socket. |
|
||||||
|
|
||||||
|
Abort flicker is **not** the RCS flicker. Use `0.86 + 0.14 * sin(time * 41)`.
|
||||||
|
|
||||||
|
The ribbons are the flame. The sparks are a thin hot core inside them. If you only keep the sparks, it will look like the old spray.
|
||||||
|
|
||||||
|
## Hongqi, Soyuz, Phantom II
|
||||||
|
|
||||||
|
Same ribbon code as above (`naf_fx.gd`). Do not invent a second particle style. Sizes match `hangar-fx.js`.
|
||||||
|
|
||||||
|
### Hongqi
|
||||||
|
|
||||||
|
Axes: nose +Y, starboard +X, dorsal +Z. Torch exhaust -Y.
|
||||||
|
|
||||||
|
| Socket | Piece | Length | Width | Planes | Color |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Vfx_DrivePlume | Core | 30 m | 1.15 m | 3 | gold-white `fff6d4` |
|
||||||
|
| Vfx_DrivePlume | Sheath | 22 m | 2.7 m | 3 | cinnabar `e23b2f` |
|
||||||
|
| Vfx_Drive_Port / Vfx_Drive_Stbd | Core | 12 m | 0.55 m | 3 | same gold |
|
||||||
|
| Vfx_Drive_Port / Vfx_Drive_Stbd | Sheath | 8 m | 1.2 m | 3 | same cinnabar |
|
||||||
|
|
||||||
|
RCS is the Resolute pencil (1.2 m, two planes) on every `*_Vfx` / `Vfx_RCS_*` socket. PDC is the same 42 Hz tracer stream on `PDT_*_Muzzle`. Chaff is the Resolute cloud on `Vfx_Chaff`. `battle_lights` uses cinnabar `c4342c`, not Resolute red. Radiator fins glow only after a hard burn, same rule as Resolute.
|
||||||
|
|
||||||
|
### Soyuz-MS
|
||||||
|
|
||||||
|
Axes: dock +Z, dorsal +Y, starboard +X. Service-engine exhaust -Z. `rcs_direction` Nose is +Z thrust.
|
||||||
|
|
||||||
|
| Socket | Piece | Length | Width | Planes | Color |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Vfx_DrivePlume | Core | 7 m | 0.5 m | 4 | warm `fff3d4` |
|
||||||
|
| Vfx_DrivePlume | Sheath | 5 m | 1.25 m | 3 | chemical orange `e85a12` |
|
||||||
|
|
||||||
|
`Vfx_Abort_0` … `Vfx_Abort_5` are the six soft-landing motors. They use the Kestrel abort recipe (tapered core, orange sheath, sparks). `abort_burn` holds all six.
|
||||||
|
|
||||||
|
### Phantom II (shrike)
|
||||||
|
|
||||||
|
Axes: nose +Z, belly -Y, starboard +X. Torch exhaust -Z. `rcs_direction` Nose is +Z thrust. Dorsal is +Y.
|
||||||
|
|
||||||
|
| Socket | Piece | Length | Width | Planes | Color |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Vfx_DrivePlume | Core | 22 m | 0.68 m | 3 | ice `f4f8ff` |
|
||||||
|
| Vfx_DrivePlume | Sheath | 14 m | 1.7 m | 3 | blue `5aa8ff` |
|
||||||
|
|
||||||
|
RCS, the two `PDT_*_Muzzle` guns, and `Vfx_Chaff` match Resolute. `cabin_dim` only lowers the cabin lights. There is no battle-red swap.
|
||||||
|
|
||||||
|
## What to give Godot 4.7
|
||||||
|
|
||||||
|
One folder, with `project.godot`. Open it in Godot 4.7. Hongqi is the main scene. Shared: `naf_fx.gd`, `ribbon.gdshader`, this file.
|
||||||
|
|
||||||
|
- Resolute: `naf_resolute.glb`, `naf_resolute.gd`, `resolute.tscn`
|
||||||
|
- Kestrel: `kestrel_ec01_escape_capsule.glb`, `kestrel_ec01.gd`, `kestrel.tscn`
|
||||||
|
- Hongqi: `hongqi.glb`, `hongqi.gd`, `hongqi.tscn`
|
||||||
|
- Soyuz: `soyuz.glb`, `soyuz.gd`, `soyuz.tscn`
|
||||||
|
- Phantom II: `shrike.glb`, `shrike.gd`, `shrike.tscn`
|
||||||
|
|
||||||
|
Textures and the 0..1 clips are already in the glb. Play the scene before judging the jets. Additive ribbons disappear on the default grey sky. The tscn world is near-black on purpose.
|
||||||
|
|
||||||
|
Play, then:
|
||||||
|
|
||||||
|
- Resolute: `drive_on`, throttle 0.8. White core, blue sheath, flickering. Not a cloud of quads.
|
||||||
|
- Resolute: `rcs_direction` Starboard. Only the bells pushing that way light.
|
||||||
|
- Resolute: `pdc_fire`. Separate bolts, not one beam. `chaff_fire` is a small silver cloud.
|
||||||
|
- Kestrel: `abort_burn`. Four motors, white throat into an orange plume.
|
||||||
|
- Hongqi: `drive_on`, throttle 0.8. Gold core, cinnabar sheath, on the main bell and both outboards.
|
||||||
|
- Soyuz: `drive_on` is a short chemical plume on the service engine. `abort_burn` lights the six landing motors.
|
||||||
|
- Phantom II: `drive_on` is the ice/blue torch out the tail. `canopy_open` and `gear_deploy` are the two clips.
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
extends Node3D
|
||||||
|
## Hongqi 红旗-07. Godot 4.7. Units: 1 m.
|
||||||
|
## Axes: nose +Y, starboard +X, dorsal +Z. Aft exhaust is -Y.
|
||||||
|
## Clips are 0..1 on the AnimationPlayer imported with the glb.
|
||||||
|
## Particles are spawned on the Vfx sockets at runtime. They are not in the mesh.
|
||||||
|
## Drive is a cinnabar torch: white-gold core, red sheath, plus a smaller plume on each outboard bell.
|
||||||
|
## See PARTICLE_FX.md.
|
||||||
|
|
||||||
|
const Fx = preload("res://naf_fx.gd")
|
||||||
|
|
||||||
|
@export_range(0.0, 1.0) var gear_deploy := 1.0 : set = set_gear_deploy
|
||||||
|
@export_range(0.0, 1.0) var radiators_deploy := 1.0 : set = set_radiators_deploy
|
||||||
|
@export_range(0.0, 1.0) var hatch_boarding_port := 0.0 : set = set_hatch_boarding_port
|
||||||
|
@export_range(0.0, 1.0) var hatch_boarding_stbd := 0.0 : set = set_hatch_boarding_stbd
|
||||||
|
@export_range(0.0, 1.0) var hatch_airlock_open := 0.0 : set = set_hatch_airlock_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_deck_open := 0.0 : set = set_hatch_deck_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_ramp_open := 0.0 : set = set_hatch_ramp_open
|
||||||
|
@export_range(0.0, 1.0) var torpedo_doors_open := 0.0 : set = set_torpedo_doors_open
|
||||||
|
@export_range(0.0, 1.0) var throttle := 0.0
|
||||||
|
@export var drive_on := false
|
||||||
|
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
||||||
|
@export var pdc_fire := false
|
||||||
|
@export var chaff_fire := false
|
||||||
|
@export var battle_lights := false : set = set_battle_lights
|
||||||
|
|
||||||
|
var _heat := 0.0
|
||||||
|
var _spool := 0.0
|
||||||
|
var _time := 0.0
|
||||||
|
var _rad_mats: Array[StandardMaterial3D] = []
|
||||||
|
var _drive_socket: Node3D
|
||||||
|
var _drive_core: Node3D
|
||||||
|
var _drive_sheath: Node3D
|
||||||
|
var _drive_light: SpotLight3D
|
||||||
|
var _aux: Array[Dictionary] = []
|
||||||
|
var _rcs: Array[Node3D] = []
|
||||||
|
var _muzzles: Array[Node3D] = []
|
||||||
|
var _tracers: Array[Dictionary] = []
|
||||||
|
var _tracer_pool: Array[MeshInstance3D] = []
|
||||||
|
var _tracer_mesh: ArrayMesh
|
||||||
|
var _tracer_mat: StandardMaterial3D
|
||||||
|
var _chaff: Array[GPUParticles3D] = []
|
||||||
|
var _cabin_ready := false
|
||||||
|
var _cabin_lights: Array[Light3D] = []
|
||||||
|
var _cabin_rest: Array[Dictionary] = []
|
||||||
|
var _cabin_mats: Array[Dictionary] = []
|
||||||
|
|
||||||
|
const _RCS_THRUST := [
|
||||||
|
Vector3.ZERO,
|
||||||
|
Vector3(0, 1, 0),
|
||||||
|
Vector3(0, -1, 0),
|
||||||
|
Vector3(1, 0, 0),
|
||||||
|
Vector3(-1, 0, 0),
|
||||||
|
Vector3(0, 0, 1),
|
||||||
|
Vector3(0, 0, -1),
|
||||||
|
]
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_seek("gear_deploy", gear_deploy)
|
||||||
|
_seek("radiators_deploy", radiators_deploy)
|
||||||
|
_collect_glow()
|
||||||
|
_arm_fx()
|
||||||
|
_collect_cabin()
|
||||||
|
set_physics_process(true)
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
_time += delta
|
||||||
|
var power := throttle if drive_on else 0.0
|
||||||
|
var open := 0.0 if radiators_deploy < 0.15 else clampf((radiators_deploy - 0.15) / 0.7, 0.0, 1.0)
|
||||||
|
var hard := 0.0 if power < 0.72 else (power - 0.72) / 0.28
|
||||||
|
_heat = lerpf(_heat, hard * open, 1.0 - exp(-delta / (18.0 if hard * open > _heat else 22.0)))
|
||||||
|
var glow := 0.0 if _heat < 0.55 else pow((_heat - 0.55) / 0.45, 1.7)
|
||||||
|
for mat in _rad_mats:
|
||||||
|
mat.emission_enabled = glow > 0.02
|
||||||
|
mat.emission = Color(1.0, 0.35, 0.08)
|
||||||
|
mat.emission_energy_multiplier = glow * 1.15
|
||||||
|
var spool_target := 1.0 if power > 0.04 else 0.0
|
||||||
|
_spool = Fx.smooth(_spool, spool_target, delta, 0.26 if spool_target > 0.0 else 0.07)
|
||||||
|
var plume := power * _spool
|
||||||
|
if _drive_core:
|
||||||
|
var scale := _drive_reach_scale(_drive_socket, _drive_core)
|
||||||
|
Fx.set_spear(_drive_core, plume, scale, _time)
|
||||||
|
Fx.set_spear(_drive_sheath, plume * 0.82, scale, _time)
|
||||||
|
if _drive_light:
|
||||||
|
_drive_light.light_energy = plume * 5.0
|
||||||
|
for aux in _aux:
|
||||||
|
var socket := aux.socket as Node3D
|
||||||
|
var core := aux.core as Node3D
|
||||||
|
var sheath := aux.sheath as Node3D
|
||||||
|
var scale := _drive_reach_scale(socket, core)
|
||||||
|
Fx.set_spear(core, plume, scale, _time + 0.4)
|
||||||
|
Fx.set_spear(sheath, plume * 0.7, scale, _time + 0.4)
|
||||||
|
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
||||||
|
var ship := global_transform.basis
|
||||||
|
for ribbon in _rcs:
|
||||||
|
var want := 0.0
|
||||||
|
if thrust != Vector3.ZERO:
|
||||||
|
var exhaust: Vector3 = ship.inverse() * ribbon.global_transform.basis.y
|
||||||
|
if exhaust.dot(-thrust) > 0.7:
|
||||||
|
want = 1.0
|
||||||
|
var burn := float(ribbon.get_meta("burn"))
|
||||||
|
burn = Fx.smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
||||||
|
ribbon.set_meta("burn", burn)
|
||||||
|
Fx.set_spear(ribbon, burn, 1.0, _time)
|
||||||
|
for cloud in _chaff:
|
||||||
|
cloud.emitting = chaff_fire
|
||||||
|
_step_pdc(delta)
|
||||||
|
|
||||||
|
func set_battle_lights(v: bool) -> void:
|
||||||
|
battle_lights = v
|
||||||
|
if _cabin_ready:
|
||||||
|
_apply_cabin()
|
||||||
|
|
||||||
|
func _collect_cabin() -> void:
|
||||||
|
for n in find_children("*", "Light3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Nav") or nm.begins_with("Vfx"):
|
||||||
|
continue
|
||||||
|
if not (nm.begins_with("PointLight_") or nm.begins_with("CabinLamp") or nm == "CabinFill"):
|
||||||
|
continue
|
||||||
|
_cabin_lights.append(n)
|
||||||
|
_cabin_rest.append({"energy": n.light_energy, "color": n.light_color})
|
||||||
|
for n in find_children("*", "MeshInstance3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.find("Lamp") < 0 and not nm.begins_with("LightStrip_"):
|
||||||
|
continue
|
||||||
|
var mesh := n as MeshInstance3D
|
||||||
|
var mat := mesh.get_active_material(0)
|
||||||
|
if mat == null or not (mat is StandardMaterial3D):
|
||||||
|
continue
|
||||||
|
var copy := (mat as StandardMaterial3D).duplicate() as StandardMaterial3D
|
||||||
|
mesh.set_surface_override_material(0, copy)
|
||||||
|
_cabin_mats.append({"mat": copy, "energy": copy.emission_energy_multiplier, "color": copy.emission, "on": copy.emission_enabled})
|
||||||
|
_cabin_ready = true
|
||||||
|
|
||||||
|
func _apply_cabin() -> void:
|
||||||
|
var red := Color(0.769, 0.204, 0.173)
|
||||||
|
for i in _cabin_lights.size():
|
||||||
|
var lamp := _cabin_lights[i]
|
||||||
|
var rest: Dictionary = _cabin_rest[i]
|
||||||
|
if battle_lights:
|
||||||
|
lamp.light_color = red
|
||||||
|
lamp.light_energy = maxf(0.05, float(rest.energy) * 0.2)
|
||||||
|
else:
|
||||||
|
lamp.light_color = rest.color
|
||||||
|
lamp.light_energy = float(rest.energy)
|
||||||
|
for row in _cabin_mats:
|
||||||
|
var mat := row.mat as StandardMaterial3D
|
||||||
|
if battle_lights:
|
||||||
|
mat.emission_enabled = true
|
||||||
|
mat.emission = red
|
||||||
|
mat.emission_energy_multiplier = maxf(0.28, float(row.energy) * 0.45)
|
||||||
|
else:
|
||||||
|
mat.emission_enabled = bool(row.on)
|
||||||
|
mat.emission = row.color
|
||||||
|
mat.emission_energy_multiplier = float(row.energy)
|
||||||
|
|
||||||
|
func set_gear_deploy(v: float) -> void:
|
||||||
|
gear_deploy = v
|
||||||
|
_seek("gear_deploy", v)
|
||||||
|
|
||||||
|
func set_radiators_deploy(v: float) -> void:
|
||||||
|
radiators_deploy = v
|
||||||
|
_seek("radiators_deploy", v)
|
||||||
|
|
||||||
|
func set_hatch_boarding_port(v: float) -> void:
|
||||||
|
hatch_boarding_port = v
|
||||||
|
_seek("hatch_boarding_port", v)
|
||||||
|
|
||||||
|
func set_hatch_boarding_stbd(v: float) -> void:
|
||||||
|
hatch_boarding_stbd = v
|
||||||
|
_seek("hatch_boarding_stbd", v)
|
||||||
|
|
||||||
|
func set_hatch_airlock_open(v: float) -> void:
|
||||||
|
hatch_airlock_open = v
|
||||||
|
_seek("hatch_airlock_open", v)
|
||||||
|
|
||||||
|
func set_hatch_deck_open(v: float) -> void:
|
||||||
|
hatch_deck_open = v
|
||||||
|
_seek("hatch_deck_open", v)
|
||||||
|
|
||||||
|
func set_hatch_ramp_open(v: float) -> void:
|
||||||
|
hatch_ramp_open = v
|
||||||
|
_seek("hatch_ramp_open", v)
|
||||||
|
|
||||||
|
func set_torpedo_doors_open(v: float) -> void:
|
||||||
|
torpedo_doors_open = v
|
||||||
|
_seek("torpedo_doors_open", v)
|
||||||
|
|
||||||
|
func _player() -> AnimationPlayer:
|
||||||
|
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||||
|
if ap:
|
||||||
|
return ap
|
||||||
|
return find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
|
||||||
|
func _seek(clip: StringName, t: float) -> void:
|
||||||
|
var ap := _player()
|
||||||
|
if ap == null or not ap.has_animation(clip):
|
||||||
|
return
|
||||||
|
var len := ap.get_animation(clip).length
|
||||||
|
ap.play(clip)
|
||||||
|
ap.seek(clampf(t, 0.0, 1.0) * len, true)
|
||||||
|
ap.pause()
|
||||||
|
|
||||||
|
func _collect_glow() -> void:
|
||||||
|
for n in find_children("*", "MeshInstance3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if not nm.begins_with("Radiator_"):
|
||||||
|
continue
|
||||||
|
if nm.find("Fin_") < 0 and nm.find("Edge_") < 0:
|
||||||
|
continue
|
||||||
|
var mesh := n as MeshInstance3D
|
||||||
|
var mat := mesh.get_active_material(0)
|
||||||
|
if mat == null or not (mat is StandardMaterial3D):
|
||||||
|
continue
|
||||||
|
var copy := (mat as StandardMaterial3D).duplicate() as StandardMaterial3D
|
||||||
|
mesh.set_surface_override_material(0, copy)
|
||||||
|
_rad_mats.append(copy)
|
||||||
|
|
||||||
|
func _arm_fx() -> void:
|
||||||
|
_tracer_mesh = Fx.tracer_mesh()
|
||||||
|
_tracer_mat = Fx.tracer_mat()
|
||||||
|
var gold := Color(1.0, 0.965, 0.831, 0.95)
|
||||||
|
var gold_mid := Color(1.0, 0.72, 0.35, 0.4)
|
||||||
|
var gold_tip := Color(0.9, 0.4, 0.1, 0.0)
|
||||||
|
var red := Color(0.886, 0.231, 0.184, 0.9)
|
||||||
|
var red_mid := Color(0.55, 0.08, 0.05, 0.35)
|
||||||
|
var red_tip := Color(0.25, 0.02, 0.01, 0.0)
|
||||||
|
var drive := find_child("Vfx_DrivePlume", true, false) as Node3D
|
||||||
|
if drive:
|
||||||
|
_drive_socket = drive
|
||||||
|
_drive_core = Fx.spear(drive, 30.0, 1.15, gold, gold_mid, gold_tip, 0.92, 3, 1.0, "DriveCore")
|
||||||
|
_drive_sheath = Fx.spear(drive, 22.0, 2.7, red, red_mid, red_tip, 0.5, 3, 2.0, "DriveSheath")
|
||||||
|
_drive_light = Fx.spot(drive, 0.35, Color(1.0, 0.882, 0.69), 0.55, 7.5, 2.0)
|
||||||
|
for n in find_children("*", "Node3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm == "Vfx_Drive_Port" or nm == "Vfx_Drive_Stbd":
|
||||||
|
_aux.append({
|
||||||
|
"socket": n,
|
||||||
|
"core": Fx.spear(n, 12.0, 0.55, gold, gold_mid, gold_tip, 0.9, 3, 3.0, "DriveCore"),
|
||||||
|
"sheath": Fx.spear(n, 8.0, 1.2, red, red_mid, red_tip, 0.45, 3, 4.0, "DriveSheath"),
|
||||||
|
})
|
||||||
|
elif _is_rcs_socket(n):
|
||||||
|
_rcs.append(Fx.spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, 1.0, "Spear"))
|
||||||
|
elif nm.begins_with("PDT_") and nm.ends_with("_Muzzle"):
|
||||||
|
_muzzles.append(n)
|
||||||
|
n.set_meta("acc", randf() * 0.02)
|
||||||
|
elif nm == "Vfx_Chaff":
|
||||||
|
_chaff.append(Fx.chaff(n))
|
||||||
|
|
||||||
|
func _is_rcs_socket(n: Node) -> bool:
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Vfx_Drive") or nm == "Vfx_Chaff" or nm.begins_with("Vfx_Abort"):
|
||||||
|
return false
|
||||||
|
return nm.ends_with("_Vfx") or nm.begins_with("Vfx_RCS_")
|
||||||
|
|
||||||
|
func _drive_reach_scale(socket: Node3D, core: Node3D) -> float:
|
||||||
|
if socket == null or core == null:
|
||||||
|
return 1.0
|
||||||
|
var length := float(core.get_meta("len"))
|
||||||
|
var exhaust: Vector3 = (socket.global_transform.basis * core.basis.y).normalized()
|
||||||
|
if exhaust.y >= -0.35:
|
||||||
|
return 1.0
|
||||||
|
# Pad is world y=-2.45. Clip only when the bell is above it.
|
||||||
|
# At the origin the bells sit below that plane, so the torch stays full length.
|
||||||
|
var room := (socket.global_position.y - (-2.45 + 0.22)) / -exhaust.y
|
||||||
|
if room <= 0.0:
|
||||||
|
return 1.0
|
||||||
|
return maxf(0.9, minf(length, room)) / length
|
||||||
|
|
||||||
|
func _step_pdc(delta: float) -> void:
|
||||||
|
if pdc_fire:
|
||||||
|
for mz in _muzzles:
|
||||||
|
var acc := float(mz.get_meta("acc")) - delta
|
||||||
|
var shots := 0
|
||||||
|
while acc <= 0.0 and shots < 2 and _tracers.size() < 90:
|
||||||
|
acc += 1.0 / 42.0
|
||||||
|
shots += 1
|
||||||
|
_spawn_tracer(mz)
|
||||||
|
mz.set_meta("acc", acc)
|
||||||
|
for i in range(_tracers.size() - 1, -1, -1):
|
||||||
|
var t: Dictionary = _tracers[i]
|
||||||
|
t.life = float(t.life) - delta
|
||||||
|
var mesh := t.mesh as MeshInstance3D
|
||||||
|
if float(t.life) <= 0.0:
|
||||||
|
mesh.visible = false
|
||||||
|
_tracer_pool.append(mesh)
|
||||||
|
_tracers.remove_at(i)
|
||||||
|
continue
|
||||||
|
mesh.position += (t.dir as Vector3) * float(t.speed) * delta
|
||||||
|
|
||||||
|
func _spawn_tracer(mz: Node3D) -> void:
|
||||||
|
var mesh := _take_tracer()
|
||||||
|
var world_dir: Vector3 = (mz.global_transform.basis * Fx.exhaust_local(mz)).normalized()
|
||||||
|
var shot := Fx.cone(world_dir, 1.15)
|
||||||
|
var origin := mz.global_position + world_dir * 0.18
|
||||||
|
var local_dir: Vector3 = global_transform.basis.inverse() * shot
|
||||||
|
mesh.position = to_local(origin)
|
||||||
|
mesh.basis = Fx.basis_y(local_dir)
|
||||||
|
mesh.scale = Vector3(1, randf_range(1.5, 2.3), 1)
|
||||||
|
mesh.visible = true
|
||||||
|
_tracers.append({"mesh": mesh, "dir": local_dir, "life": randf_range(0.12, 0.2), "speed": randf_range(90.0, 140.0)})
|
||||||
|
|
||||||
|
func _take_tracer() -> MeshInstance3D:
|
||||||
|
if _tracer_pool.size() > 0:
|
||||||
|
return _tracer_pool.pop_back()
|
||||||
|
var m := MeshInstance3D.new()
|
||||||
|
m.mesh = _tracer_mesh
|
||||||
|
m.material_override = _tracer_mat
|
||||||
|
m.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
|
m.visible = false
|
||||||
|
add_child(m)
|
||||||
|
return m
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://d2ecdbay4q31q
|
||||||
Binary file not shown.
@@ -0,0 +1,45 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://bc0c4fwo6oom2"
|
||||||
|
path="res://.godot/imported/hongqi.glb-60257baeac74884c31f76642ce55bf18.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://hongqi.glb"
|
||||||
|
dest_files=["res://.godot/imported/hongqi.glb-60257baeac74884c31f76642ce55bf18.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/root_script=null
|
||||||
|
mesh_library/use_node_names_as_mesh_names=false
|
||||||
|
array_mesh/deduplicate_surfaces=true
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
nodes/import_as_skeleton_bones=false
|
||||||
|
nodes/use_name_suffixes=true
|
||||||
|
nodes/use_node_type_suffixes=true
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=1
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=false
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
animation/import_rest_as_RESET=false
|
||||||
|
import_script/path=""
|
||||||
|
materials/extract=0
|
||||||
|
materials/extract_format=0
|
||||||
|
materials/extract_path=""
|
||||||
|
_subresources={}
|
||||||
|
gltf/naming_version=2
|
||||||
|
gltf/embedded_image_handling=1
|
||||||
|
gltf/texture_map_mode=1
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene format=3 uid="uid://xxp0p0vb72tn"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bc0c4fwo6oom2" path="res://hongqi.glb" id="1_glb"]
|
||||||
|
[ext_resource type="Script" uid="uid://d2ecdbay4q31q" path="res://hongqi.gd" id="2_gd"]
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="env"]
|
||||||
|
background_mode = 1
|
||||||
|
background_color = Color(0.012, 0.014, 0.018, 1)
|
||||||
|
ambient_light_source = 2
|
||||||
|
ambient_light_color = Color(0.62, 0.7, 0.8, 1)
|
||||||
|
ambient_light_energy = 0.2
|
||||||
|
tonemap_mode = 2
|
||||||
|
|
||||||
|
[node name="Hongqi" instance=ExtResource("1_glb")]
|
||||||
|
script = ExtResource("2_gd")
|
||||||
|
|
||||||
|
[node name="HangarSky" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("env")
|
||||||
|
|
||||||
|
[node name="Key" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(0.8, -0.5, 0.33, 0, 0.55, 0.84, -0.6, -0.67, 0.44, 0, 30, 12)
|
||||||
|
light_color = Color(0.82, 0.88, 1, 1)
|
||||||
|
light_energy = 1.15
|
||||||
|
shadow_enabled = true
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene format=3 uid="uid://xg7scvaiinq2"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dcr7fsl5qx4ir" path="res://kestrel_ec01_escape_capsule.glb" id="1_glb"]
|
||||||
|
[ext_resource type="Script" uid="uid://c4ve1ai244iw6" path="res://kestrel_ec01.gd" id="2_gd"]
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="env"]
|
||||||
|
background_mode = 1
|
||||||
|
background_color = Color(0.012, 0.014, 0.018, 1)
|
||||||
|
ambient_light_source = 2
|
||||||
|
ambient_light_color = Color(0.62, 0.7, 0.8, 1)
|
||||||
|
ambient_light_energy = 0.22
|
||||||
|
tonemap_mode = 2
|
||||||
|
|
||||||
|
[node name="Kestrel" instance=ExtResource("1_glb")]
|
||||||
|
script = ExtResource("2_gd")
|
||||||
|
|
||||||
|
[node name="HangarSky" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("env")
|
||||||
|
|
||||||
|
[node name="Key" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(0.8, -0.5, 0.33, 0, 0.55, 0.84, -0.6, -0.67, 0.44, 2, 8, 4)
|
||||||
|
light_color = Color(0.9, 0.86, 0.78, 1)
|
||||||
|
light_energy = 1.15
|
||||||
|
shadow_enabled = true
|
||||||
@@ -2,11 +2,13 @@ extends Node3D
|
|||||||
## Kestrel EC-01 — analog 0..1 clip driver.
|
## Kestrel EC-01 — analog 0..1 clip driver.
|
||||||
## Nodes: hatch_crew, hatch_ladder, hatch_dock, LandingGear, SeatCamera.
|
## Nodes: hatch_crew, hatch_ladder, hatch_dock, LandingGear, SeatCamera.
|
||||||
##
|
##
|
||||||
## Effect controls:
|
## Particle look is PARTICLE_FX.md. RCS is a short crossed ribbon. Abort is two tapered ribbons plus sparks.
|
||||||
## rcs_direction fires only the bells that push that way. Off fires nothing.
|
## rcs_direction fires only the bells that push that way. Off fires nothing.
|
||||||
## abort_burn holds all four abort motors.
|
## abort_burn holds all four abort motors. The flame ramps in over 0.22 s and out over 0.28 s.
|
||||||
## Ship axes: nose +Y, starboard +X, dorsal +Z.
|
## Ship axes: nose +Y, starboard +X, dorsal +Z.
|
||||||
|
|
||||||
|
const Fx = preload("res://naf_fx.gd")
|
||||||
|
|
||||||
@export_range(0.0, 1.0) var gear_deploy := 1.0 : set = set_gear_deploy
|
@export_range(0.0, 1.0) var gear_deploy := 1.0 : set = set_gear_deploy
|
||||||
@export_range(0.0, 1.0) var hatch_crew_open := 0.0 : set = set_hatch_crew_open
|
@export_range(0.0, 1.0) var hatch_crew_open := 0.0 : set = set_hatch_crew_open
|
||||||
@export_range(0.0, 1.0) var hatch_ladder_deploy := 0.0 : set = set_hatch_ladder_deploy
|
@export_range(0.0, 1.0) var hatch_ladder_deploy := 0.0 : set = set_hatch_ladder_deploy
|
||||||
@@ -17,8 +19,10 @@ extends Node3D
|
|||||||
## Cabin strips and fill. On drops them to a low watch. It does not swap to battle red.
|
## Cabin strips and fill. On drops them to a low watch. It does not swap to battle red.
|
||||||
@export var cabin_dim := false : set = set_cabin_dim
|
@export var cabin_dim := false : set = set_cabin_dim
|
||||||
|
|
||||||
var _rcs: Array[GPUParticles3D] = []
|
var _rcs: Array[Node3D] = []
|
||||||
var _abort: Array[GPUParticles3D] = []
|
var _aborts: Array[Dictionary] = []
|
||||||
|
var _abort_hold := 0.0
|
||||||
|
var _time := 0.0
|
||||||
var _cabin_ready := false
|
var _cabin_ready := false
|
||||||
var _cabin_lights: Array[Light3D] = []
|
var _cabin_lights: Array[Light3D] = []
|
||||||
var _cabin_rest: Array[Dictionary] = []
|
var _cabin_rest: Array[Dictionary] = []
|
||||||
@@ -36,21 +40,56 @@ const _RCS_THRUST := [
|
|||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_seek("gear_deploy", gear_deploy)
|
_seek("gear_deploy", gear_deploy)
|
||||||
_arm_all("Vfx_RCS_*", 8, 0.14, 8.0, 14.0, 7.0, Color(0.86, 0.95, 1.0), 1.2, _rcs)
|
_arm_fx()
|
||||||
_arm_all("Vfx_Abort_*", 20, 0.18, 12.0, 22.0, 7.0, Color(1.0, 0.62, 0.3), 3.0, _abort)
|
|
||||||
_collect_cabin()
|
_collect_cabin()
|
||||||
|
|
||||||
func _physics_process(_delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
|
_time += delta
|
||||||
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
||||||
var ship := global_transform.basis
|
var ship := global_transform.basis
|
||||||
for p in _rcs:
|
for ribbon in _rcs:
|
||||||
if thrust == Vector3.ZERO:
|
var want := 0.0
|
||||||
p.emitting = false
|
if thrust != Vector3.ZERO:
|
||||||
else:
|
var exhaust: Vector3 = ship.inverse() * ribbon.global_transform.basis.y
|
||||||
var exhaust := ship.inverse() * p.global_transform.basis.y
|
if exhaust.dot(-thrust) > 0.7:
|
||||||
p.emitting = exhaust.dot(-thrust) > 0.7
|
want = 1.0
|
||||||
for p in _abort:
|
var burn := float(ribbon.get_meta("burn"))
|
||||||
p.emitting = abort_burn
|
burn = Fx.smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
||||||
|
ribbon.set_meta("burn", burn)
|
||||||
|
Fx.set_spear(ribbon, burn, 1.0, _time)
|
||||||
|
if abort_burn:
|
||||||
|
_abort_hold = minf(1.0, _abort_hold + delta / 0.22)
|
||||||
|
elif _abort_hold > 0.0:
|
||||||
|
_abort_hold = maxf(0.0, _abort_hold - delta / 0.28)
|
||||||
|
var on := _abort_hold > 0.03
|
||||||
|
var flick := 0.86 + 0.14 * sin(_time * 41.0)
|
||||||
|
var sheath_scale := 0.9 + 0.1 * sin(_time * 19.0)
|
||||||
|
for a in _aborts:
|
||||||
|
var core := a.core as Node3D
|
||||||
|
var sheath := a.sheath as Node3D
|
||||||
|
Fx.set_opacity(core, _abort_hold * 0.95 * flick if on else 0.0)
|
||||||
|
Fx.set_opacity(sheath, _abort_hold * 0.82 * flick if on else 0.0)
|
||||||
|
sheath.scale = Vector3(1, sheath_scale, 1)
|
||||||
|
var sparks := a.sparks as GPUParticles3D
|
||||||
|
var sheath_sparks := a.sheath_sparks as GPUParticles3D
|
||||||
|
sparks.emitting = on
|
||||||
|
sheath_sparks.emitting = on
|
||||||
|
var proc := sparks.process_material as ParticleProcessMaterial
|
||||||
|
var proc2 := sheath_sparks.process_material as ParticleProcessMaterial
|
||||||
|
if proc:
|
||||||
|
proc.color = Color(1, 1, 1, _abort_hold * 0.55)
|
||||||
|
if proc2:
|
||||||
|
proc2.color = Color(1, 1, 1, _abort_hold * 0.55)
|
||||||
|
var flash := a.flash as MeshInstance3D
|
||||||
|
flash.visible = on
|
||||||
|
var fmat := flash.material_override as StandardMaterial3D
|
||||||
|
if fmat:
|
||||||
|
var tint := fmat.albedo_color
|
||||||
|
tint.a = _abort_hold * 0.45
|
||||||
|
fmat.albedo_color = tint
|
||||||
|
flash.scale = Vector3.ONE * (0.06 + _abort_hold * 0.04)
|
||||||
|
var light := a.light as SpotLight3D
|
||||||
|
light.light_energy = _abort_hold * 8.0 if on else 0.0
|
||||||
|
|
||||||
func set_cabin_dim(v: bool) -> void:
|
func set_cabin_dim(v: bool) -> void:
|
||||||
cabin_dim = v
|
cabin_dim = v
|
||||||
@@ -135,57 +174,50 @@ func set_instruments_demo(v: float) -> void:
|
|||||||
func _seek(clip: StringName, t: float) -> void:
|
func _seek(clip: StringName, t: float) -> void:
|
||||||
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||||
if ap == null:
|
if ap == null:
|
||||||
return
|
ap = find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
if not ap.has_animation(clip):
|
if ap == null or not ap.has_animation(clip):
|
||||||
return
|
return
|
||||||
var len := ap.get_animation(clip).length
|
var len := ap.get_animation(clip).length
|
||||||
ap.play(clip)
|
ap.play(clip)
|
||||||
ap.seek(clampf(t, 0.0, 1.0) * len, true)
|
ap.seek(clampf(t, 0.0, 1.0) * len, true)
|
||||||
ap.pause()
|
ap.pause()
|
||||||
|
|
||||||
func _arm_all(pattern: String, amount: int, life: float, vmin: float, vmax: float, spread: float, color: Color, reach: float, into: Array[GPUParticles3D]) -> void:
|
func _arm_fx() -> void:
|
||||||
for n in find_children(pattern, "Node3D", true, false):
|
for n in find_children("*", "Node3D", true, false):
|
||||||
if n is GPUParticles3D:
|
var nm := String(n.name)
|
||||||
continue
|
if nm.begins_with("Vfx_RCS_"):
|
||||||
var p := _make_plume(n, amount, life, vmin, vmax, spread, color, reach)
|
_rcs.append(Fx.spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, 1.0, "Spear"))
|
||||||
if p:
|
elif nm.begins_with("Vfx_Abort_"):
|
||||||
into.append(p)
|
_aborts.append(_make_abort(n))
|
||||||
|
|
||||||
func _make_plume(socket: Node3D, amount: int, life: float, vmin: float, vmax: float, spread: float, color: Color, reach: float) -> GPUParticles3D:
|
func _make_abort(socket: Node3D) -> Dictionary:
|
||||||
if socket.get_node_or_null("Plume"):
|
# Core is the white-hot throat. Sheath is the orange plume that opens toward the tip.
|
||||||
return null
|
var core := Fx.taper_ribbon(socket, 2.1, 0.06, 0.20, Color(1.0, 0.97, 0.92, 1.0), Color(1.0, 0.45, 0.12, 0.72), Color(0.40, 0.08, 0.02, 0.0), 0.95, 3, "AbortCore")
|
||||||
var p := GPUParticles3D.new()
|
var sheath := Fx.taper_ribbon(socket, 5.2, 0.10, 0.78, Color(1.0, 0.55, 0.18, 1.0), Color(1.0, 0.28, 0.06, 0.55), Color(0.35, 0.06, 0.01, 0.0), 0.82, 3, "AbortSheath")
|
||||||
p.name = "Plume"
|
var sparks := Fx.sparks(socket, "AbortCoreSparks", 10, 0.15, 18.0, 28.0, 1.6, 0.05, 6.0)
|
||||||
p.amount = amount
|
var sheath_sparks := Fx.sparks(socket, "AbortSheathSparks", 18, 0.26, 14.0, 22.0, 4.5, 0.09, 8.0)
|
||||||
p.lifetime = life
|
for p in [sparks, sheath_sparks]:
|
||||||
p.visibility_aabb = AABB(Vector3(-reach, 0, -reach), Vector3(reach * 2.0, reach * 4.0, reach * 2.0))
|
var proc := p.process_material as ParticleProcessMaterial
|
||||||
var proc := ParticleProcessMaterial.new()
|
if proc:
|
||||||
proc.direction = Vector3(0, 1, 0)
|
proc.color_ramp = _abort_ramp()
|
||||||
proc.spread = spread
|
return {
|
||||||
proc.initial_velocity_min = vmin
|
"core": core,
|
||||||
proc.initial_velocity_max = vmax
|
"sheath": sheath,
|
||||||
proc.gravity = Vector3.ZERO
|
"sparks": sparks,
|
||||||
proc.scale_min = 0.08
|
"sheath_sparks": sheath_sparks,
|
||||||
proc.scale_max = 0.32
|
"flash": Fx.abort_flash(socket),
|
||||||
proc.color = color
|
"light": Fx.spot(socket, 0.08, Color(1.0, 0.416, 0.157), 0.5, 6.5, 1.8),
|
||||||
p.process_material = proc
|
}
|
||||||
var draw := StandardMaterial3D.new()
|
|
||||||
draw.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
func _abort_ramp() -> GradientTexture1D:
|
||||||
draw.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
|
var g := Gradient.new()
|
||||||
draw.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
g.offsets = PackedFloat32Array([0.0, 0.15, 0.45, 1.0])
|
||||||
draw.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
|
g.colors = PackedColorArray([
|
||||||
draw.albedo_color = color
|
Color(1.0, 0.97, 0.92, 1.0),
|
||||||
p.material_override = draw
|
Color(1.0, 0.46, 0.12, 0.9),
|
||||||
p.draw_pass_1 = QuadMesh.new()
|
Color(0.55, 0.16, 0.05, 0.45),
|
||||||
p.emitting = false
|
Color(0.25, 0.05, 0.01, 0.0),
|
||||||
var dir := socket.position
|
])
|
||||||
if dir.length() < 0.001:
|
var tex := GradientTexture1D.new()
|
||||||
dir = Vector3.UP
|
tex.gradient = g
|
||||||
dir = (socket.quaternion.inverse() * dir).normalized()
|
return tex
|
||||||
var y := dir
|
|
||||||
var up := Vector3.FORWARD if absf(y.dot(Vector3.UP)) > 0.9 else Vector3.UP
|
|
||||||
var x := up.cross(y).normalized()
|
|
||||||
var z := x.cross(y).normalized()
|
|
||||||
p.basis = Basis(x, y, z)
|
|
||||||
socket.add_child(p)
|
|
||||||
return p
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://c4ve1ai244iw6
|
||||||
Binary file not shown.
+4
-4
@@ -3,13 +3,13 @@
|
|||||||
importer="scene"
|
importer="scene"
|
||||||
importer_version=1
|
importer_version=1
|
||||||
type="PackedScene"
|
type="PackedScene"
|
||||||
uid="uid://cg7e2b3ydpaly"
|
uid="uid://dcr7fsl5qx4ir"
|
||||||
path="res://.godot/imported/kestrel_ec01_escape_capsule.glb-9ad6fcc806e750bf2e77f048f3d58804.scn"
|
path="res://.godot/imported/kestrel_ec01_escape_capsule.glb-515b5d663b088267edb64b261b63e5ee.scn"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://kestrel_ec01_godot/kestrel_ec01_escape_capsule.glb"
|
source_file="res://kestrel_ec01_escape_capsule.glb"
|
||||||
dest_files=["res://.godot/imported/kestrel_ec01_escape_capsule.glb-9ad6fcc806e750bf2e77f048f3d58804.scn"]
|
dest_files=["res://.godot/imported/kestrel_ec01_escape_capsule.glb-515b5d663b088267edb64b261b63e5ee.scn"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
extends RefCounted
|
||||||
|
## Hangar particle look for Resolute and Kestrel. See PARTICLE_FX.md.
|
||||||
|
## Drive, RCS, and abort flames are crossed additive ribbons. They are not GPUParticles.
|
||||||
|
|
||||||
|
const RIBBON := preload("res://ribbon.gdshader")
|
||||||
|
|
||||||
|
static func exhaust_local(socket: Node3D) -> Vector3:
|
||||||
|
var dir := socket.position
|
||||||
|
if dir.length() < 0.001:
|
||||||
|
dir = Vector3.UP
|
||||||
|
return (socket.quaternion.inverse() * dir).normalized()
|
||||||
|
|
||||||
|
static func basis_y(dir: Vector3) -> Basis:
|
||||||
|
var y := dir.normalized()
|
||||||
|
if y.length() < 0.001:
|
||||||
|
y = Vector3.UP
|
||||||
|
var up := Vector3.FORWARD if absf(y.dot(Vector3.UP)) > 0.9 else Vector3.UP
|
||||||
|
var x := up.cross(y).normalized()
|
||||||
|
var z := x.cross(y).normalized()
|
||||||
|
return Basis(x, y, z)
|
||||||
|
|
||||||
|
static func cone(dir: Vector3, deg: float) -> Vector3:
|
||||||
|
var axis := dir.normalized()
|
||||||
|
var up := Vector3.RIGHT if absf(axis.dot(Vector3.UP)) > 0.9 else Vector3.UP
|
||||||
|
var tangent := axis.cross(up).normalized()
|
||||||
|
var bitangent := axis.cross(tangent).normalized()
|
||||||
|
var a := randf() * TAU
|
||||||
|
var r := randf() * deg_to_rad(deg)
|
||||||
|
return (axis + tangent * (cos(a) * r) + bitangent * (sin(a) * r)).normalized()
|
||||||
|
|
||||||
|
static func _ribbon_mat(root: Color, mid: Color, tip: Color) -> ShaderMaterial:
|
||||||
|
var mat := ShaderMaterial.new()
|
||||||
|
mat.shader = RIBBON
|
||||||
|
mat.set_shader_parameter("root_color", root)
|
||||||
|
mat.set_shader_parameter("mid_color", mid)
|
||||||
|
mat.set_shader_parameter("tip_color", tip)
|
||||||
|
mat.set_shader_parameter("opacity", 0.0)
|
||||||
|
return mat
|
||||||
|
|
||||||
|
static func _sheet(length: float, width: float) -> ArrayMesh:
|
||||||
|
var half := width * 0.5
|
||||||
|
var st := SurfaceTool.new()
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
st.set_uv(Vector2(0, 0))
|
||||||
|
st.add_vertex(Vector3(-half, 0, 0))
|
||||||
|
st.set_uv(Vector2(1, 0))
|
||||||
|
st.add_vertex(Vector3(half, 0, 0))
|
||||||
|
st.set_uv(Vector2(1, 1))
|
||||||
|
st.add_vertex(Vector3(half, length, 0))
|
||||||
|
st.set_uv(Vector2(0, 1))
|
||||||
|
st.add_vertex(Vector3(-half, length, 0))
|
||||||
|
st.add_index(0)
|
||||||
|
st.add_index(1)
|
||||||
|
st.add_index(2)
|
||||||
|
st.add_index(0)
|
||||||
|
st.add_index(2)
|
||||||
|
st.add_index(3)
|
||||||
|
return st.commit()
|
||||||
|
|
||||||
|
static func _taper(length: float, w0: float, w1: float) -> ArrayMesh:
|
||||||
|
var rows := 11
|
||||||
|
var st := SurfaceTool.new()
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
for i in rows:
|
||||||
|
var t := float(i) / float(rows - 1)
|
||||||
|
var y := t * length
|
||||||
|
var w := (w0 + (w1 - w0) * pow(t, 0.7)) * 0.5
|
||||||
|
var v := t
|
||||||
|
st.set_uv(Vector2(0, v))
|
||||||
|
st.add_vertex(Vector3(-w, y, 0))
|
||||||
|
st.set_uv(Vector2(1, v))
|
||||||
|
st.add_vertex(Vector3(w, y, 0))
|
||||||
|
for i in rows - 1:
|
||||||
|
var a := i * 2
|
||||||
|
st.add_index(a)
|
||||||
|
st.add_index(a + 1)
|
||||||
|
st.add_index(a + 2)
|
||||||
|
st.add_index(a + 1)
|
||||||
|
st.add_index(a + 3)
|
||||||
|
st.add_index(a + 2)
|
||||||
|
return st.commit()
|
||||||
|
|
||||||
|
static func _crossed(socket: Node3D, mesh: ArrayMesh, mat: ShaderMaterial, planes: int, name: String, length: float, opacity: float, seed: float) -> Node3D:
|
||||||
|
var g := Node3D.new()
|
||||||
|
g.name = name
|
||||||
|
g.set_meta("mat", mat)
|
||||||
|
g.set_meta("len", length)
|
||||||
|
g.set_meta("opacity", opacity)
|
||||||
|
g.set_meta("seed", seed)
|
||||||
|
g.set_meta("burn", 0.0)
|
||||||
|
for i in planes:
|
||||||
|
var m := MeshInstance3D.new()
|
||||||
|
m.mesh = mesh
|
||||||
|
m.material_override = mat
|
||||||
|
m.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
|
m.extra_cull_margin = 8.0
|
||||||
|
m.rotation.y = (float(i) / float(planes)) * PI
|
||||||
|
g.add_child(m)
|
||||||
|
g.basis = basis_y(exhaust_local(socket))
|
||||||
|
g.visible = false
|
||||||
|
socket.add_child(g)
|
||||||
|
return g
|
||||||
|
|
||||||
|
static func spear(socket: Node3D, length: float, width: float, root: Color, mid: Color, tip: Color, opacity: float, planes: int, seed: float, node_name: String = "Spear") -> Node3D:
|
||||||
|
if socket.get_node_or_null(node_name):
|
||||||
|
return socket.get_node(node_name) as Node3D
|
||||||
|
return _crossed(socket, _sheet(length, width), _ribbon_mat(root, mid, tip), planes, node_name, length, opacity, seed)
|
||||||
|
|
||||||
|
static func taper_ribbon(socket: Node3D, length: float, w0: float, w1: float, root: Color, mid: Color, tip: Color, opacity: float, planes: int, node_name: String) -> Node3D:
|
||||||
|
if socket.get_node_or_null(node_name):
|
||||||
|
return socket.get_node(node_name) as Node3D
|
||||||
|
return _crossed(socket, _taper(length, w0, w1), _ribbon_mat(root, mid, tip), planes, node_name, length, opacity, 1.0)
|
||||||
|
|
||||||
|
static func set_opacity(ribbon: Node3D, opacity: float) -> void:
|
||||||
|
if ribbon == null:
|
||||||
|
return
|
||||||
|
var mat := ribbon.get_meta("mat") as ShaderMaterial
|
||||||
|
var shown := opacity > 0.02
|
||||||
|
ribbon.visible = shown
|
||||||
|
mat.set_shader_parameter("opacity", opacity if shown else 0.0)
|
||||||
|
|
||||||
|
static func set_spear(ribbon: Node3D, amount: float, length_scale: float, time: float) -> void:
|
||||||
|
if ribbon == null:
|
||||||
|
return
|
||||||
|
var a := maxf(amount, 0.0)
|
||||||
|
if a < 0.02:
|
||||||
|
set_opacity(ribbon, 0.0)
|
||||||
|
return
|
||||||
|
var seed := float(ribbon.get_meta("seed"))
|
||||||
|
var flick := 0.82 + 0.18 * sin(time * 37.0 + seed * 2.1)
|
||||||
|
var base := float(ribbon.get_meta("opacity"))
|
||||||
|
var s := maxf(length_scale, 0.001)
|
||||||
|
ribbon.scale = Vector3(1, s, 1)
|
||||||
|
set_opacity(ribbon, base * minf(a, 1.0) * flick)
|
||||||
|
|
||||||
|
static func spot(socket: Node3D, along: float, color: Color, angle_rad: float, range_m: float, decay: float) -> SpotLight3D:
|
||||||
|
var existing := socket.get_node_or_null("ExhaustLight") as SpotLight3D
|
||||||
|
if existing:
|
||||||
|
return existing
|
||||||
|
var aim := basis_y(exhaust_local(socket))
|
||||||
|
var light := SpotLight3D.new()
|
||||||
|
light.name = "ExhaustLight"
|
||||||
|
light.light_color = color
|
||||||
|
light.light_energy = 0.0
|
||||||
|
light.spot_range = range_m
|
||||||
|
light.spot_angle = rad_to_deg(angle_rad)
|
||||||
|
light.spot_attenuation = decay
|
||||||
|
light.shadow_enabled = false
|
||||||
|
light.position = aim.y * along
|
||||||
|
var z := -aim.y
|
||||||
|
var up := aim.x
|
||||||
|
var x := up.cross(z).normalized()
|
||||||
|
var y := z.cross(x).normalized()
|
||||||
|
light.basis = Basis(x, y, z)
|
||||||
|
socket.add_child(light)
|
||||||
|
return light
|
||||||
|
|
||||||
|
static func soft_disc() -> Texture2D:
|
||||||
|
var img := Image.create(64, 64, false, Image.FORMAT_RGBA8)
|
||||||
|
for y in 64:
|
||||||
|
for x in 64:
|
||||||
|
var d := Vector2(x - 31.5, y - 31.5).length() / 32.0
|
||||||
|
var t := clampf(d, 0.0, 1.0)
|
||||||
|
var s := t * t * (3.0 - 2.0 * t)
|
||||||
|
var a := (1.0 - s) * (1.0 - s)
|
||||||
|
img.set_pixel(x, y, Color(1, 1, 1, a))
|
||||||
|
return ImageTexture.create_from_image(img)
|
||||||
|
|
||||||
|
static func _disc_mat() -> StandardMaterial3D:
|
||||||
|
var draw := StandardMaterial3D.new()
|
||||||
|
draw.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
draw.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
|
||||||
|
draw.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
draw.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
|
||||||
|
draw.vertex_color_use_as_albedo = true
|
||||||
|
draw.albedo_texture = soft_disc()
|
||||||
|
draw.albedo_color = Color.WHITE
|
||||||
|
draw.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||||
|
draw.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_DISABLED
|
||||||
|
return draw
|
||||||
|
|
||||||
|
static func _ramp(offsets: PackedFloat32Array, colors: PackedColorArray) -> GradientTexture1D:
|
||||||
|
var g := Gradient.new()
|
||||||
|
g.offsets = offsets
|
||||||
|
g.colors = colors
|
||||||
|
var tex := GradientTexture1D.new()
|
||||||
|
tex.gradient = g
|
||||||
|
return tex
|
||||||
|
|
||||||
|
static func sparks(socket: Node3D, node_name: String, amount: int, life: float, vmin: float, vmax: float, spread_deg: float, size: float, reach: float) -> GPUParticles3D:
|
||||||
|
var existing := socket.get_node_or_null(node_name) as GPUParticles3D
|
||||||
|
if existing:
|
||||||
|
return existing
|
||||||
|
var p := GPUParticles3D.new()
|
||||||
|
p.name = node_name
|
||||||
|
p.amount = amount
|
||||||
|
p.lifetime = life
|
||||||
|
p.explosiveness = 0.0
|
||||||
|
p.local_coords = true
|
||||||
|
p.visibility_aabb = AABB(Vector3(-reach, 0, -reach), Vector3(reach * 2.0, reach * 3.0, reach * 2.0))
|
||||||
|
var proc := ParticleProcessMaterial.new()
|
||||||
|
proc.direction = Vector3(0, 1, 0)
|
||||||
|
proc.spread = spread_deg
|
||||||
|
proc.initial_velocity_min = vmin
|
||||||
|
proc.initial_velocity_max = vmax
|
||||||
|
proc.gravity = Vector3.ZERO
|
||||||
|
proc.scale_min = 0.85
|
||||||
|
proc.scale_max = 1.15
|
||||||
|
proc.color = Color(1, 1, 1, 0.55)
|
||||||
|
p.process_material = proc
|
||||||
|
p.material_override = _disc_mat()
|
||||||
|
var quad := QuadMesh.new()
|
||||||
|
quad.size = Vector2(size, size)
|
||||||
|
p.draw_pass_1 = quad
|
||||||
|
p.emitting = false
|
||||||
|
p.basis = basis_y(exhaust_local(socket))
|
||||||
|
socket.add_child(p)
|
||||||
|
return p
|
||||||
|
|
||||||
|
static func abort_flash(socket: Node3D) -> MeshInstance3D:
|
||||||
|
var existing := socket.get_node_or_null("AbortFlash") as MeshInstance3D
|
||||||
|
if existing:
|
||||||
|
return existing
|
||||||
|
var aim := basis_y(exhaust_local(socket))
|
||||||
|
var mat := StandardMaterial3D.new()
|
||||||
|
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
mat.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
|
||||||
|
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
mat.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED
|
||||||
|
mat.albedo_texture = soft_disc()
|
||||||
|
mat.albedo_color = Color(1.0, 0.945, 0.867, 0.0)
|
||||||
|
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_DISABLED
|
||||||
|
var m := MeshInstance3D.new()
|
||||||
|
m.name = "AbortFlash"
|
||||||
|
var quad := QuadMesh.new()
|
||||||
|
quad.size = Vector2(1, 1)
|
||||||
|
m.mesh = quad
|
||||||
|
m.material_override = mat
|
||||||
|
m.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
|
m.position = aim.y * 0.02
|
||||||
|
m.scale = Vector3.ONE * 0.08
|
||||||
|
m.visible = false
|
||||||
|
socket.add_child(m)
|
||||||
|
return m
|
||||||
|
|
||||||
|
static func tracer_mesh() -> ArrayMesh:
|
||||||
|
var st := SurfaceTool.new()
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
var sides := 5
|
||||||
|
var r0 := 0.016
|
||||||
|
var r1 := 0.008
|
||||||
|
for i in sides:
|
||||||
|
var a0 := TAU * float(i) / float(sides)
|
||||||
|
var a1 := TAU * float(i + 1) / float(sides)
|
||||||
|
var p0 := Vector3(cos(a0) * r0, 0.0, sin(a0) * r0)
|
||||||
|
var p1 := Vector3(cos(a1) * r0, 0.0, sin(a1) * r0)
|
||||||
|
var q0 := Vector3(cos(a0) * r1, 1.0, sin(a0) * r1)
|
||||||
|
var q1 := Vector3(cos(a1) * r1, 1.0, sin(a1) * r1)
|
||||||
|
st.add_vertex(p0)
|
||||||
|
st.add_vertex(q0)
|
||||||
|
st.add_vertex(p1)
|
||||||
|
st.add_vertex(p1)
|
||||||
|
st.add_vertex(q0)
|
||||||
|
st.add_vertex(q1)
|
||||||
|
return st.commit()
|
||||||
|
|
||||||
|
static func tracer_mat() -> StandardMaterial3D:
|
||||||
|
var draw := StandardMaterial3D.new()
|
||||||
|
draw.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
draw.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
|
||||||
|
draw.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
draw.albedo_color = Color(1.0, 0.902, 0.690, 0.95)
|
||||||
|
draw.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||||
|
draw.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_DISABLED
|
||||||
|
return draw
|
||||||
|
|
||||||
|
static func chaff(socket: Node3D) -> GPUParticles3D:
|
||||||
|
var existing := socket.get_node_or_null("ChaffCloud") as GPUParticles3D
|
||||||
|
if existing:
|
||||||
|
return existing
|
||||||
|
var p := GPUParticles3D.new()
|
||||||
|
p.name = "ChaffCloud"
|
||||||
|
p.amount = 28
|
||||||
|
p.lifetime = 1.55
|
||||||
|
p.explosiveness = 0.0
|
||||||
|
p.local_coords = true
|
||||||
|
p.visibility_aabb = AABB(Vector3(-6, 0, -6), Vector3(12, 16, 12))
|
||||||
|
var proc := ParticleProcessMaterial.new()
|
||||||
|
proc.direction = Vector3(0, 1, 0)
|
||||||
|
proc.spread = 22.0
|
||||||
|
proc.initial_velocity_min = 3.0
|
||||||
|
proc.initial_velocity_max = 8.0
|
||||||
|
proc.gravity = Vector3.ZERO
|
||||||
|
proc.damping_min = 0.0
|
||||||
|
proc.damping_max = 0.0
|
||||||
|
proc.scale_min = 0.07
|
||||||
|
proc.scale_max = 0.16
|
||||||
|
proc.color = Color(0.90, 0.93, 0.97, 0.92)
|
||||||
|
proc.color_ramp = _ramp(
|
||||||
|
PackedFloat32Array([0.0, 0.55, 1.0]),
|
||||||
|
PackedColorArray([
|
||||||
|
Color(0.90, 0.93, 0.97, 0.92),
|
||||||
|
Color(0.90, 0.93, 0.97, 0.7),
|
||||||
|
Color(0.90, 0.93, 0.97, 0.0),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
p.process_material = proc
|
||||||
|
p.material_override = _disc_mat()
|
||||||
|
var quad := QuadMesh.new()
|
||||||
|
quad.size = Vector2(1, 1)
|
||||||
|
p.draw_pass_1 = quad
|
||||||
|
p.emitting = false
|
||||||
|
p.basis = basis_y(exhaust_local(socket))
|
||||||
|
socket.add_child(p)
|
||||||
|
return p
|
||||||
|
|
||||||
|
static func smooth(current: float, target: float, delta: float, seconds: float) -> float:
|
||||||
|
var k := 1.0 - exp(-delta / maxf(0.05, seconds))
|
||||||
|
return current + (target - current) * k
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://du4mcoyxwuwfl
|
||||||
@@ -0,0 +1,357 @@
|
|||||||
|
extends Node3D
|
||||||
|
## NAF Resolute CVT-07 — analog 0..1 clip driver.
|
||||||
|
## Keep node names stable: LandingGear, Radiators, hatch_airlock_outer, HelmCamera, PlayerSpawn.
|
||||||
|
##
|
||||||
|
## Effects are inspector toggles, not clips.
|
||||||
|
## Particle look is PARTICLE_FX.md. Drive and RCS are crossed additive ribbons, not sprays.
|
||||||
|
## drive_on plus throttle runs the plume. Radiator panels stay grey metal unless the burn is hard and has been hard for a while.
|
||||||
|
## rcs_direction fires only the bells that push that way. Off fires nothing.
|
||||||
|
## pdc_fire is a stream of short tracer bolts, not one beam. chaff_fire dumps the dispensers.
|
||||||
|
## Torpedo bodies are not particles. Use the torpedo door clip, then fly a real round later.
|
||||||
|
## Sockets: Vfx_DrivePlume, *_Vfx, PDT_*_Muzzle, Vfx_Chaff.
|
||||||
|
|
||||||
|
const Fx = preload("res://naf_fx.gd")
|
||||||
|
|
||||||
|
@export_range(0.0, 1.0) var gear_deploy := 1.0 : set = set_gear_deploy
|
||||||
|
@export_range(0.0, 1.0) var radiators_deploy := 1.0 : set = set_radiators_deploy
|
||||||
|
@export_range(0.0, 1.0) var hatch_airlock_open := 0.0 : set = set_hatch_airlock_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_dock_open := 0.0 : set = set_hatch_dock_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_cargo_open := 0.0 : set = set_hatch_cargo_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_deck_open := 0.0 : set = set_hatch_deck_open
|
||||||
|
@export_range(0.0, 1.0) var torpedo_doors_open := 0.0 : set = set_torpedo_doors_open
|
||||||
|
@export_range(0.0, 1.0) var throttle := 0.0
|
||||||
|
@export var drive_on := false
|
||||||
|
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
||||||
|
@export var pdc_fire := false
|
||||||
|
@export var chaff_fire := false
|
||||||
|
## Cabin practicals. Off is the normal warm watch. On drops the hab to dim red battle lighting.
|
||||||
|
@export var battle_lights := false : set = set_battle_lights
|
||||||
|
## Torch vector. Positive pitch swings the bell ventral and the nose down. Positive yaw swings the bell to starboard and the nose to port. No roll. Limit ±14°.
|
||||||
|
@export_range(-0.244, 0.244, 0.001) var gimbal_pitch := 0.0 : set = set_gimbal_pitch
|
||||||
|
@export_range(-0.244, 0.244, 0.001) var gimbal_yaw := 0.0 : set = set_gimbal_yaw
|
||||||
|
|
||||||
|
var _heat := 0.0
|
||||||
|
var _spool := 0.0
|
||||||
|
var _time := 0.0
|
||||||
|
var _rad_mats: Array[StandardMaterial3D] = []
|
||||||
|
var _drive_mats: Array[StandardMaterial3D] = []
|
||||||
|
var _drive_socket: Node3D
|
||||||
|
var _drive_core: Node3D
|
||||||
|
var _drive_sheath: Node3D
|
||||||
|
var _drive_light: SpotLight3D
|
||||||
|
var _rcs: Array[Node3D] = []
|
||||||
|
var _muzzles: Array[Node3D] = []
|
||||||
|
var _tracers: Array[Dictionary] = []
|
||||||
|
var _tracer_pool: Array[MeshInstance3D] = []
|
||||||
|
var _tracer_mesh: ArrayMesh
|
||||||
|
var _tracer_mat: StandardMaterial3D
|
||||||
|
var _chaff: Array[GPUParticles3D] = []
|
||||||
|
var _gimbal_rest: Basis
|
||||||
|
var _gimbal_ready := false
|
||||||
|
var _cabin_ready := false
|
||||||
|
var _cabin_lights: Array[Light3D] = []
|
||||||
|
var _cabin_rest: Array[Dictionary] = []
|
||||||
|
var _cabin_mats: Array[Dictionary] = []
|
||||||
|
|
||||||
|
const _RCS_THRUST := [
|
||||||
|
Vector3.ZERO,
|
||||||
|
Vector3(0, 1, 0),
|
||||||
|
Vector3(0, -1, 0),
|
||||||
|
Vector3(1, 0, 0),
|
||||||
|
Vector3(-1, 0, 0),
|
||||||
|
Vector3(0, 0, 1),
|
||||||
|
Vector3(0, 0, -1),
|
||||||
|
]
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_seek("gear_deploy", gear_deploy)
|
||||||
|
_seek("radiators_deploy", radiators_deploy)
|
||||||
|
_collect_glow()
|
||||||
|
_arm_fx()
|
||||||
|
var pivot := find_child("gimbal_pivot", true, false) as Node3D
|
||||||
|
if pivot:
|
||||||
|
_gimbal_rest = pivot.transform.basis
|
||||||
|
_gimbal_ready = true
|
||||||
|
_apply_gimbal()
|
||||||
|
_collect_cabin()
|
||||||
|
set_physics_process(true)
|
||||||
|
|
||||||
|
func set_gimbal_pitch(v: float) -> void:
|
||||||
|
gimbal_pitch = clampf(v, -0.244, 0.244)
|
||||||
|
if _gimbal_ready:
|
||||||
|
_apply_gimbal()
|
||||||
|
|
||||||
|
func set_gimbal_yaw(v: float) -> void:
|
||||||
|
gimbal_yaw = clampf(v, -0.244, 0.244)
|
||||||
|
if _gimbal_ready:
|
||||||
|
_apply_gimbal()
|
||||||
|
|
||||||
|
func _apply_gimbal() -> void:
|
||||||
|
var pivot := find_child("gimbal_pivot", true, false) as Node3D
|
||||||
|
if pivot == null:
|
||||||
|
return
|
||||||
|
var spin := Basis(Vector3.UP, gimbal_yaw) * Basis(Vector3.RIGHT, -gimbal_pitch)
|
||||||
|
pivot.transform.basis = _gimbal_rest * spin
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
_time += delta
|
||||||
|
var power := throttle if drive_on else 0.0
|
||||||
|
var open := 0.0 if radiators_deploy < 0.15 else clampf((radiators_deploy - 0.15) / 0.7, 0.0, 1.0)
|
||||||
|
var hard := 0.0 if power < 0.72 else (power - 0.72) / 0.28
|
||||||
|
var target := hard * open
|
||||||
|
var tau := 18.0 if target > _heat else 22.0
|
||||||
|
_heat = lerpf(_heat, target, 1.0 - exp(-delta / tau))
|
||||||
|
var glow := 0.0 if _heat < 0.55 else pow((_heat - 0.55) / 0.45, 1.7)
|
||||||
|
var hot := Color(1.0, 0.35, 0.08)
|
||||||
|
for mat in _rad_mats:
|
||||||
|
mat.emission_enabled = glow > 0.02
|
||||||
|
mat.emission = hot
|
||||||
|
mat.emission_energy_multiplier = glow * 1.15
|
||||||
|
for mat in _drive_mats:
|
||||||
|
mat.emission_enabled = power > 0.02
|
||||||
|
mat.emission = Color(0.55 * power, 0.72 * power, power)
|
||||||
|
mat.emission_energy_multiplier = power * 7.0
|
||||||
|
var spool_target := 1.0 if power > 0.04 else 0.0
|
||||||
|
_spool = Fx.smooth(_spool, spool_target, delta, 0.26 if spool_target > 0.0 else 0.07)
|
||||||
|
var plume := power * _spool
|
||||||
|
if _drive_core:
|
||||||
|
var scale := _drive_reach_scale()
|
||||||
|
Fx.set_spear(_drive_core, plume, scale, _time)
|
||||||
|
Fx.set_spear(_drive_sheath, plume * 0.82, scale, _time)
|
||||||
|
if _drive_light:
|
||||||
|
_drive_light.light_energy = plume * 5.0 if plume > 0.04 else 0.0
|
||||||
|
var reach := float(_drive_core.get_meta("len")) * scale
|
||||||
|
_drive_light.spot_range = minf(float(_drive_core.get_meta("len")), maxf(2.2, reach))
|
||||||
|
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
||||||
|
var ship := global_transform.basis
|
||||||
|
for ribbon in _rcs:
|
||||||
|
var want := 0.0
|
||||||
|
if thrust != Vector3.ZERO:
|
||||||
|
var exhaust: Vector3 = ship.inverse() * ribbon.global_transform.basis.y
|
||||||
|
if exhaust.dot(-thrust) > 0.7:
|
||||||
|
want = 1.0
|
||||||
|
var burn := float(ribbon.get_meta("burn"))
|
||||||
|
burn = Fx.smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
||||||
|
ribbon.set_meta("burn", burn)
|
||||||
|
Fx.set_spear(ribbon, burn, 1.0, _time)
|
||||||
|
_step_pdc(delta)
|
||||||
|
for cloud in _chaff:
|
||||||
|
cloud.emitting = chaff_fire
|
||||||
|
|
||||||
|
func set_battle_lights(v: bool) -> void:
|
||||||
|
battle_lights = v
|
||||||
|
if _cabin_ready:
|
||||||
|
_apply_cabin()
|
||||||
|
|
||||||
|
func _cabin_light(n: Node) -> bool:
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Nav"):
|
||||||
|
return false
|
||||||
|
if nm.begins_with("Light_") or nm == "HelmPracticalLight" or nm.begins_with("CabinLamp") or nm.begins_with("PointLight_") or nm == "CabinFill" or nm == "PanelLight" or nm == "AirlockFlood":
|
||||||
|
return true
|
||||||
|
var p := n.get_parent()
|
||||||
|
while p:
|
||||||
|
var pn := String(p.name)
|
||||||
|
if pn == "CompanionwayWalk" or pn == "Lights" or pn == "CabinLights":
|
||||||
|
return true
|
||||||
|
if pn.begins_with("Vfx"):
|
||||||
|
return false
|
||||||
|
p = p.get_parent()
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _cabin_mesh(n: Node) -> bool:
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Nav"):
|
||||||
|
return false
|
||||||
|
return "Lamp" in nm or nm.begins_with("LightStrip") or nm.begins_with("CabinGlow")
|
||||||
|
|
||||||
|
func _collect_cabin() -> void:
|
||||||
|
_cabin_lights.clear()
|
||||||
|
_cabin_rest.clear()
|
||||||
|
_cabin_mats.clear()
|
||||||
|
for n in find_children("*", "Light3D", true, false):
|
||||||
|
if not _cabin_light(n):
|
||||||
|
continue
|
||||||
|
_cabin_lights.append(n)
|
||||||
|
_cabin_rest.append({"energy": n.light_energy, "color": n.light_color})
|
||||||
|
for n in find_children("*", "MeshInstance3D", true, false):
|
||||||
|
if not _cabin_mesh(n):
|
||||||
|
continue
|
||||||
|
var mat := _override_std(n)
|
||||||
|
if mat == null:
|
||||||
|
continue
|
||||||
|
_cabin_mats.append({"mat": mat, "energy": mat.emission_energy_multiplier, "color": mat.emission, "on": mat.emission_enabled})
|
||||||
|
_cabin_ready = true
|
||||||
|
_apply_cabin()
|
||||||
|
|
||||||
|
func _apply_cabin() -> void:
|
||||||
|
for i in _cabin_lights.size():
|
||||||
|
var lamp := _cabin_lights[i]
|
||||||
|
var rest: Dictionary = _cabin_rest[i]
|
||||||
|
if battle_lights:
|
||||||
|
lamp.light_color = Color(1.0, 0.14, 0.1)
|
||||||
|
lamp.light_energy = maxf(0.05, float(rest.energy) * 0.2)
|
||||||
|
else:
|
||||||
|
lamp.light_color = rest.color
|
||||||
|
lamp.light_energy = float(rest.energy)
|
||||||
|
for row in _cabin_mats:
|
||||||
|
var mat := row.mat as StandardMaterial3D
|
||||||
|
if battle_lights:
|
||||||
|
mat.emission_enabled = true
|
||||||
|
mat.emission = Color(1.0, 0.13, 0.09)
|
||||||
|
mat.emission_energy_multiplier = maxf(0.28, float(row.energy) * 0.45)
|
||||||
|
else:
|
||||||
|
mat.emission_enabled = bool(row.on)
|
||||||
|
mat.emission = row.color
|
||||||
|
mat.emission_energy_multiplier = float(row.energy)
|
||||||
|
|
||||||
|
func set_gear_deploy(v: float) -> void:
|
||||||
|
gear_deploy = v
|
||||||
|
_seek("gear_deploy", v)
|
||||||
|
|
||||||
|
func set_radiators_deploy(v: float) -> void:
|
||||||
|
radiators_deploy = v
|
||||||
|
_seek("radiators_deploy", v)
|
||||||
|
|
||||||
|
func set_hatch_airlock_open(v: float) -> void:
|
||||||
|
hatch_airlock_open = v
|
||||||
|
_seek("hatch_airlock_open", v)
|
||||||
|
|
||||||
|
func set_hatch_dock_open(v: float) -> void:
|
||||||
|
hatch_dock_open = v
|
||||||
|
_seek("hatch_dock_open", v)
|
||||||
|
|
||||||
|
func set_hatch_cargo_open(v: float) -> void:
|
||||||
|
hatch_cargo_open = v
|
||||||
|
_seek("hatch_cargo_open", v)
|
||||||
|
|
||||||
|
func set_hatch_deck_open(v: float) -> void:
|
||||||
|
hatch_deck_open = v
|
||||||
|
_seek("hatch_deck_open", v)
|
||||||
|
|
||||||
|
func set_torpedo_doors_open(v: float) -> void:
|
||||||
|
torpedo_doors_open = v
|
||||||
|
_seek("torpedo_doors_open", v)
|
||||||
|
|
||||||
|
func _seek(clip: StringName, t: float) -> void:
|
||||||
|
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||||
|
if ap == null:
|
||||||
|
ap = find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
if ap == null or not ap.has_animation(clip):
|
||||||
|
return
|
||||||
|
var len := ap.get_animation(clip).length
|
||||||
|
ap.play(clip)
|
||||||
|
ap.seek(clampf(t, 0.0, 1.0) * len, true)
|
||||||
|
ap.pause()
|
||||||
|
|
||||||
|
func _collect_glow() -> void:
|
||||||
|
for n in find_children("*", "MeshInstance3D", true, false):
|
||||||
|
var mat := _override_std(n)
|
||||||
|
if mat == null:
|
||||||
|
continue
|
||||||
|
if n.name.begins_with("Radiator_") and ("Fin_" in n.name or "Edge_" in n.name or "Rail" in n.name):
|
||||||
|
_rad_mats.append(mat)
|
||||||
|
elif n.name == "DriveGlow" or n.name == "DrivePlasma":
|
||||||
|
_drive_mats.append(mat)
|
||||||
|
|
||||||
|
func _override_std(mesh: MeshInstance3D) -> StandardMaterial3D:
|
||||||
|
var mat := mesh.get_active_material(0)
|
||||||
|
if mat == null or not (mat is StandardMaterial3D):
|
||||||
|
return null
|
||||||
|
var copy := (mat as StandardMaterial3D).duplicate() as StandardMaterial3D
|
||||||
|
mesh.set_surface_override_material(0, copy)
|
||||||
|
return copy
|
||||||
|
|
||||||
|
func _is_rcs_socket(n: Node) -> bool:
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm == "Vfx_DrivePlume" or nm == "Vfx_Chaff" or nm.begins_with("Vfx_Abort"):
|
||||||
|
return false
|
||||||
|
return nm.ends_with("_Vfx") or nm.begins_with("Vfx_RCS_")
|
||||||
|
|
||||||
|
func _is_muzzle(n: Node) -> bool:
|
||||||
|
var nm := String(n.name)
|
||||||
|
return nm.begins_with("PDT_") and nm.ends_with("_Muzzle")
|
||||||
|
|
||||||
|
func _arm_fx() -> void:
|
||||||
|
_tracer_mesh = Fx.tracer_mesh()
|
||||||
|
_tracer_mat = Fx.tracer_mat()
|
||||||
|
var drive := find_child("Vfx_DrivePlume", true, false) as Node3D
|
||||||
|
if drive:
|
||||||
|
_drive_socket = drive
|
||||||
|
# Ice-white core, 38 m, three crossed planes. Blue sheath is shorter and wider.
|
||||||
|
_drive_core = Fx.spear(drive, 38.0, 1.35, Color(0.957, 0.973, 1.0, 0.95), Color(0.47, 0.71, 1.0, 0.35), Color(0.24, 0.47, 1.0, 0.0), 0.95, 3, 1.0, "DriveCore")
|
||||||
|
_drive_sheath = Fx.spear(drive, 26.0, 3.2, Color(0.353, 0.659, 1.0, 0.95), Color(0.17, 0.47, 1.0, 0.35), Color(0.08, 0.22, 0.55, 0.0), 0.5, 3, 2.0, "DriveSheath")
|
||||||
|
_drive_light = Fx.spot(drive, 0.35, Color(0.773, 0.863, 1.0), 0.3, 7.5, 2.0)
|
||||||
|
for n in find_children("*", "Node3D", true, false):
|
||||||
|
if _is_rcs_socket(n):
|
||||||
|
# 1.2 m pale spear, two crossed planes. One per bell.
|
||||||
|
_rcs.append(Fx.spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, 1.0, "Spear"))
|
||||||
|
elif _is_muzzle(n):
|
||||||
|
_muzzles.append(n)
|
||||||
|
n.set_meta("acc", randf() * 0.02)
|
||||||
|
elif String(n.name) == "Vfx_Chaff":
|
||||||
|
_chaff.append(Fx.chaff(n))
|
||||||
|
|
||||||
|
func _drive_reach_scale() -> float:
|
||||||
|
if _drive_socket == null or _drive_core == null:
|
||||||
|
return 1.0
|
||||||
|
var length := float(_drive_core.get_meta("len"))
|
||||||
|
var exhaust: Vector3 = (_drive_socket.global_transform.basis * _drive_core.basis.y).normalized()
|
||||||
|
if exhaust.y >= -0.35:
|
||||||
|
return 1.0
|
||||||
|
# Pad is world y=-2.45. Clip only when the bell is above it.
|
||||||
|
var room := (_drive_socket.global_position.y - (-2.45 + 0.22)) / -exhaust.y
|
||||||
|
if room <= 0.0:
|
||||||
|
return 1.0
|
||||||
|
var reach := maxf(0.9, minf(length, room))
|
||||||
|
return reach / length
|
||||||
|
|
||||||
|
func _take_tracer() -> MeshInstance3D:
|
||||||
|
if _tracer_pool.size() > 0:
|
||||||
|
return _tracer_pool.pop_back()
|
||||||
|
var m := MeshInstance3D.new()
|
||||||
|
m.mesh = _tracer_mesh
|
||||||
|
m.material_override = _tracer_mat
|
||||||
|
m.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
|
m.visible = false
|
||||||
|
add_child(m)
|
||||||
|
return m
|
||||||
|
|
||||||
|
func _step_pdc(delta: float) -> void:
|
||||||
|
if pdc_fire:
|
||||||
|
for mz in _muzzles:
|
||||||
|
var acc := float(mz.get_meta("acc")) - delta
|
||||||
|
var shots := 0
|
||||||
|
while acc <= 0.0 and shots < 2 and _tracers.size() < 90:
|
||||||
|
acc += 1.0 / 42.0
|
||||||
|
shots += 1
|
||||||
|
_spawn_tracer(mz)
|
||||||
|
mz.set_meta("acc", acc)
|
||||||
|
for i in range(_tracers.size() - 1, -1, -1):
|
||||||
|
var t: Dictionary = _tracers[i]
|
||||||
|
t.life = float(t.life) - delta
|
||||||
|
var mesh := t.mesh as MeshInstance3D
|
||||||
|
if float(t.life) <= 0.0:
|
||||||
|
mesh.visible = false
|
||||||
|
_tracer_pool.append(mesh)
|
||||||
|
_tracers.remove_at(i)
|
||||||
|
continue
|
||||||
|
var local_dir: Vector3 = t.dir
|
||||||
|
mesh.position += local_dir * float(t.speed) * delta
|
||||||
|
|
||||||
|
func _spawn_tracer(mz: Node3D) -> void:
|
||||||
|
var mesh := _take_tracer()
|
||||||
|
var world_dir: Vector3 = (mz.global_transform.basis * Fx.exhaust_local(mz)).normalized()
|
||||||
|
var shot := Fx.cone(world_dir, 1.15)
|
||||||
|
var origin := mz.global_position + world_dir * 0.18
|
||||||
|
var local_dir: Vector3 = global_transform.basis.inverse() * shot
|
||||||
|
mesh.position = to_local(origin)
|
||||||
|
mesh.basis = Fx.basis_y(local_dir)
|
||||||
|
mesh.scale = Vector3(1, randf_range(1.5, 2.3), 1)
|
||||||
|
mesh.visible = true
|
||||||
|
_tracers.append({
|
||||||
|
"mesh": mesh,
|
||||||
|
"dir": local_dir,
|
||||||
|
"life": randf_range(0.12, 0.2),
|
||||||
|
"speed": randf_range(90.0, 140.0),
|
||||||
|
})
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://xe0h4b0jupg0
|
||||||
Binary file not shown.
+4
-4
@@ -3,13 +3,13 @@
|
|||||||
importer="scene"
|
importer="scene"
|
||||||
importer_version=1
|
importer_version=1
|
||||||
type="PackedScene"
|
type="PackedScene"
|
||||||
uid="uid://doooawixeleqo"
|
uid="uid://dhoaoukb2mpvm"
|
||||||
path="res://.godot/imported/naf_resolute.glb-a69669a72fb82d4768ddc2df7ed0153e.scn"
|
path="res://.godot/imported/naf_resolute.glb-d143e75fe1b7e7fa78dade564883f596.scn"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://naf_resolute_godot/naf_resolute.glb"
|
source_file="res://naf_resolute.glb"
|
||||||
dest_files=["res://.godot/imported/naf_resolute.glb-a69669a72fb82d4768ddc2df7ed0153e.scn"]
|
dest_files=["res://.godot/imported/naf_resolute.glb-d143e75fe1b7e7fa78dade564883f596.scn"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
; Godot 4.7 fleet. Open this folder as a project. Main scene is Hongqi.
|
||||||
|
; Units are meters. See EXPORT_NOTES.txt.
|
||||||
|
|
||||||
|
config_version=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
config/name="NAF Fleet"
|
||||||
|
config/description="Hongqi, Resolute, Kestrel, Soyuz-MS, and Phantom II. Textures and 0..1 clips are in the glb files. Drive, RCS, abort, tracers, and chaff spawn at play."
|
||||||
|
config/features=PackedStringArray("4.7", "Forward Plus")
|
||||||
|
run/main_scene="res://hongqi.tscn"
|
||||||
|
|
||||||
|
[rendering]
|
||||||
|
|
||||||
|
renderer/rendering_method="forward_plus"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cgevlt7c231mf"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dhoaoukb2mpvm" path="res://naf_resolute.glb" id="1_glb"]
|
||||||
|
[ext_resource type="Script" uid="uid://xe0h4b0jupg0" path="res://naf_resolute.gd" id="2_gd"]
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="env"]
|
||||||
|
background_mode = 1
|
||||||
|
background_color = Color(0.012, 0.014, 0.018, 1)
|
||||||
|
ambient_light_source = 2
|
||||||
|
ambient_light_color = Color(0.62, 0.7, 0.8, 1)
|
||||||
|
ambient_light_energy = 0.2
|
||||||
|
tonemap_mode = 2
|
||||||
|
|
||||||
|
[node name="Resolute" instance=ExtResource("1_glb")]
|
||||||
|
script = ExtResource("2_gd")
|
||||||
|
|
||||||
|
[node name="HangarSky" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("env")
|
||||||
|
|
||||||
|
[node name="Key" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(0.8, -0.5, 0.33, 0, 0.55, 0.84, -0.6, -0.67, 0.44, 0, 30, 12)
|
||||||
|
light_color = Color(0.82, 0.88, 1, 1)
|
||||||
|
light_energy = 1.15
|
||||||
|
shadow_enabled = true
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
shader_type spatial;
|
||||||
|
render_mode blend_add, unshaded, cull_disabled, depth_draw_never, fog_disabled;
|
||||||
|
|
||||||
|
// UV.y = 0 at the socket, 1 at the tip. UV.x = 0..1 across the ribbon.
|
||||||
|
uniform vec4 root_color : source_color = vec4(1.0, 1.0, 1.0, 0.95);
|
||||||
|
uniform vec4 mid_color : source_color = vec4(0.47, 0.71, 1.0, 0.35);
|
||||||
|
uniform vec4 tip_color : source_color = vec4(0.16, 0.31, 1.0, 0.0);
|
||||||
|
uniform float opacity : hint_range(0.0, 2.0, 0.001) = 0.0;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
float v = UV.y;
|
||||||
|
float edge = abs(UV.x - 0.5) * 2.0;
|
||||||
|
float side = 1.0 - smoothstep(0.36, 1.0, edge);
|
||||||
|
vec3 col = mix(root_color.rgb, mid_color.rgb, smoothstep(0.0, 0.45, v));
|
||||||
|
col = mix(col, tip_color.rgb, smoothstep(0.35, 1.0, v));
|
||||||
|
float a = mix(root_color.a, mid_color.a, smoothstep(0.05, 0.45, v));
|
||||||
|
a = mix(a, tip_color.a, smoothstep(0.45, 1.0, v));
|
||||||
|
ALBEDO = col;
|
||||||
|
ALPHA = clamp(a * side * opacity, 0.0, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dkfm1ub4lbf33
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
extends Node3D
|
||||||
|
## NAF Phantom II (shrike). Godot 4.7. Units: 1 m.
|
||||||
|
## Axes: nose +Z, belly -Y, starboard +X. The torch exhausts -Z.
|
||||||
|
## rcs_direction uses those axes. Nose thrust is +Z. Dorsal thrust is +Y.
|
||||||
|
## See PARTICLE_FX.md.
|
||||||
|
|
||||||
|
const Fx = preload("res://naf_fx.gd")
|
||||||
|
|
||||||
|
@export_range(0.0, 1.0) var gear_deploy := 1.0 : set = set_gear_deploy
|
||||||
|
@export_range(0.0, 1.0) var canopy_open := 0.0 : set = set_canopy_open
|
||||||
|
@export_range(0.0, 1.0) var throttle := 0.0
|
||||||
|
@export var drive_on := false
|
||||||
|
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
||||||
|
@export var pdc_fire := false
|
||||||
|
@export var chaff_fire := false
|
||||||
|
@export var cabin_dim := false : set = set_cabin_dim
|
||||||
|
|
||||||
|
var _time := 0.0
|
||||||
|
var _spool := 0.0
|
||||||
|
var _drive_socket: Node3D
|
||||||
|
var _drive_core: Node3D
|
||||||
|
var _drive_sheath: Node3D
|
||||||
|
var _drive_light: SpotLight3D
|
||||||
|
var _rcs: Array[Node3D] = []
|
||||||
|
var _muzzles: Array[Node3D] = []
|
||||||
|
var _tracers: Array[Dictionary] = []
|
||||||
|
var _tracer_pool: Array[MeshInstance3D] = []
|
||||||
|
var _tracer_mesh: ArrayMesh
|
||||||
|
var _tracer_mat: StandardMaterial3D
|
||||||
|
var _chaff: Array[GPUParticles3D] = []
|
||||||
|
var _cabin_ready := false
|
||||||
|
var _cabin_lights: Array[Light3D] = []
|
||||||
|
var _cabin_rest: Array[Dictionary] = []
|
||||||
|
|
||||||
|
const _RCS_THRUST := [
|
||||||
|
Vector3.ZERO,
|
||||||
|
Vector3(0, 0, 1),
|
||||||
|
Vector3(0, 0, -1),
|
||||||
|
Vector3(1, 0, 0),
|
||||||
|
Vector3(-1, 0, 0),
|
||||||
|
Vector3(0, 1, 0),
|
||||||
|
Vector3(0, -1, 0),
|
||||||
|
]
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_seek("gear_deploy", gear_deploy)
|
||||||
|
_arm_fx()
|
||||||
|
_collect_cabin()
|
||||||
|
set_physics_process(true)
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
_time += delta
|
||||||
|
var power := throttle if drive_on else 0.0
|
||||||
|
var spool_target := 1.0 if power > 0.04 else 0.0
|
||||||
|
_spool = Fx.smooth(_spool, spool_target, delta, 0.26 if spool_target > 0.0 else 0.07)
|
||||||
|
var plume := power * _spool
|
||||||
|
if _drive_core:
|
||||||
|
Fx.set_spear(_drive_core, plume, 1.0, _time)
|
||||||
|
Fx.set_spear(_drive_sheath, plume * 0.82, 1.0, _time)
|
||||||
|
if _drive_light:
|
||||||
|
_drive_light.light_energy = plume * 5.0
|
||||||
|
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
||||||
|
var ship := global_transform.basis
|
||||||
|
for ribbon in _rcs:
|
||||||
|
var want := 0.0
|
||||||
|
if thrust != Vector3.ZERO:
|
||||||
|
var exhaust: Vector3 = ship.inverse() * ribbon.global_transform.basis.y
|
||||||
|
if exhaust.dot(-thrust) > 0.7:
|
||||||
|
want = 1.0
|
||||||
|
var burn := float(ribbon.get_meta("burn"))
|
||||||
|
burn = Fx.smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
||||||
|
ribbon.set_meta("burn", burn)
|
||||||
|
Fx.set_spear(ribbon, burn, 1.0, _time)
|
||||||
|
for cloud in _chaff:
|
||||||
|
cloud.emitting = chaff_fire
|
||||||
|
_step_pdc(delta)
|
||||||
|
|
||||||
|
func set_cabin_dim(v: bool) -> void:
|
||||||
|
cabin_dim = v
|
||||||
|
if _cabin_ready:
|
||||||
|
for i in _cabin_lights.size():
|
||||||
|
var lamp := _cabin_lights[i]
|
||||||
|
var rest: Dictionary = _cabin_rest[i]
|
||||||
|
lamp.light_energy = float(rest.energy) * (0.16 if cabin_dim else 1.0)
|
||||||
|
lamp.light_color = rest.color
|
||||||
|
|
||||||
|
func _collect_cabin() -> void:
|
||||||
|
for n in find_children("*", "Light3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("PointLight_") or nm.begins_with("CabinLamp") or nm == "CabinFill":
|
||||||
|
_cabin_lights.append(n)
|
||||||
|
_cabin_rest.append({"energy": n.light_energy, "color": n.light_color})
|
||||||
|
_cabin_ready = true
|
||||||
|
|
||||||
|
func set_gear_deploy(v: float) -> void:
|
||||||
|
gear_deploy = v
|
||||||
|
_seek("gear_deploy", v)
|
||||||
|
|
||||||
|
func set_canopy_open(v: float) -> void:
|
||||||
|
canopy_open = v
|
||||||
|
_seek("canopy_open", v)
|
||||||
|
|
||||||
|
func _seek(clip: StringName, t: float) -> void:
|
||||||
|
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||||
|
if ap == null:
|
||||||
|
ap = find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
if ap == null or not ap.has_animation(clip):
|
||||||
|
return
|
||||||
|
ap.play(clip)
|
||||||
|
ap.seek(clampf(t, 0.0, 1.0) * ap.get_animation(clip).length, true)
|
||||||
|
ap.pause()
|
||||||
|
|
||||||
|
func _arm_fx() -> void:
|
||||||
|
_tracer_mesh = Fx.tracer_mesh()
|
||||||
|
_tracer_mat = Fx.tracer_mat()
|
||||||
|
var drive := find_child("Vfx_DrivePlume", true, false) as Node3D
|
||||||
|
if drive:
|
||||||
|
_drive_socket = drive
|
||||||
|
_drive_core = Fx.spear(drive, 22.0, 0.68, Color(0.957, 0.973, 1.0, 0.95), Color(0.47, 0.71, 1.0, 0.35), Color(0.24, 0.47, 1.0, 0.0), 0.92, 3, 1.0, "DriveCore")
|
||||||
|
_drive_sheath = Fx.spear(drive, 14.0, 1.7, Color(0.353, 0.659, 1.0, 0.9), Color(0.17, 0.47, 1.0, 0.35), Color(0.08, 0.22, 0.55, 0.0), 0.5, 3, 2.0, "DriveSheath")
|
||||||
|
_drive_light = Fx.spot(drive, 0.3, Color(0.773, 0.863, 1.0), 0.35, 7.5, 2.0)
|
||||||
|
for n in find_children("*", "Node3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Vfx_RCS_"):
|
||||||
|
_rcs.append(Fx.spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, 1.0, "Spear"))
|
||||||
|
elif nm.begins_with("PDT_") and nm.ends_with("_Muzzle"):
|
||||||
|
_muzzles.append(n)
|
||||||
|
n.set_meta("acc", randf() * 0.02)
|
||||||
|
elif nm == "Vfx_Chaff":
|
||||||
|
_chaff.append(Fx.chaff(n))
|
||||||
|
|
||||||
|
func _step_pdc(delta: float) -> void:
|
||||||
|
if pdc_fire:
|
||||||
|
for mz in _muzzles:
|
||||||
|
var acc := float(mz.get_meta("acc")) - delta
|
||||||
|
var shots := 0
|
||||||
|
while acc <= 0.0 and shots < 2 and _tracers.size() < 40:
|
||||||
|
acc += 1.0 / 42.0
|
||||||
|
shots += 1
|
||||||
|
var mesh := _take_tracer()
|
||||||
|
var world_dir: Vector3 = (mz.global_transform.basis * Fx.exhaust_local(mz)).normalized()
|
||||||
|
var shot := Fx.cone(world_dir, 1.15)
|
||||||
|
var origin := mz.global_position + world_dir * 0.18
|
||||||
|
var local_dir: Vector3 = global_transform.basis.inverse() * shot
|
||||||
|
mesh.position = to_local(origin)
|
||||||
|
mesh.basis = Fx.basis_y(local_dir)
|
||||||
|
mesh.scale = Vector3(1, randf_range(1.5, 2.3), 1)
|
||||||
|
mesh.visible = true
|
||||||
|
_tracers.append({"mesh": mesh, "dir": local_dir, "life": randf_range(0.12, 0.2), "speed": randf_range(90.0, 140.0)})
|
||||||
|
mz.set_meta("acc", acc)
|
||||||
|
for i in range(_tracers.size() - 1, -1, -1):
|
||||||
|
var t: Dictionary = _tracers[i]
|
||||||
|
t.life = float(t.life) - delta
|
||||||
|
var mesh := t.mesh as MeshInstance3D
|
||||||
|
if float(t.life) <= 0.0:
|
||||||
|
mesh.visible = false
|
||||||
|
_tracer_pool.append(mesh)
|
||||||
|
_tracers.remove_at(i)
|
||||||
|
continue
|
||||||
|
mesh.position += (t.dir as Vector3) * float(t.speed) * delta
|
||||||
|
|
||||||
|
func _take_tracer() -> MeshInstance3D:
|
||||||
|
if _tracer_pool.size() > 0:
|
||||||
|
return _tracer_pool.pop_back()
|
||||||
|
var m := MeshInstance3D.new()
|
||||||
|
m.mesh = _tracer_mesh
|
||||||
|
m.material_override = _tracer_mat
|
||||||
|
m.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
|
m.visible = false
|
||||||
|
add_child(m)
|
||||||
|
return m
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cxr0um6cwvfkv
|
||||||
Binary file not shown.
@@ -0,0 +1,45 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://crwynydci5r5y"
|
||||||
|
path="res://.godot/imported/shrike.glb-afe83605e4be6db26bf4e422fef66872.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://shrike.glb"
|
||||||
|
dest_files=["res://.godot/imported/shrike.glb-afe83605e4be6db26bf4e422fef66872.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/root_script=null
|
||||||
|
mesh_library/use_node_names_as_mesh_names=false
|
||||||
|
array_mesh/deduplicate_surfaces=true
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
nodes/import_as_skeleton_bones=false
|
||||||
|
nodes/use_name_suffixes=true
|
||||||
|
nodes/use_node_type_suffixes=true
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=1
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=false
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
animation/import_rest_as_RESET=false
|
||||||
|
import_script/path=""
|
||||||
|
materials/extract=0
|
||||||
|
materials/extract_format=0
|
||||||
|
materials/extract_path=""
|
||||||
|
_subresources={}
|
||||||
|
gltf/naming_version=2
|
||||||
|
gltf/embedded_image_handling=1
|
||||||
|
gltf/texture_map_mode=1
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene format=3 uid="uid://b228nn7odpif5"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://crwynydci5r5y" path="res://shrike.glb" id="1_glb"]
|
||||||
|
[ext_resource type="Script" uid="uid://cxr0um6cwvfkv" path="res://shrike.gd" id="2_gd"]
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="env"]
|
||||||
|
background_mode = 1
|
||||||
|
background_color = Color(0.012, 0.014, 0.018, 1)
|
||||||
|
ambient_light_source = 2
|
||||||
|
ambient_light_color = Color(0.62, 0.7, 0.8, 1)
|
||||||
|
ambient_light_energy = 0.2
|
||||||
|
tonemap_mode = 2
|
||||||
|
|
||||||
|
[node name="Shrike" instance=ExtResource("1_glb")]
|
||||||
|
script = ExtResource("2_gd")
|
||||||
|
|
||||||
|
[node name="HangarSky" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("env")
|
||||||
|
|
||||||
|
[node name="Key" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(0.8, -0.5, 0.33, 0, 0.55, 0.84, -0.6, -0.67, 0.44, 6, 8, 4)
|
||||||
|
light_color = Color(0.82, 0.88, 1, 1)
|
||||||
|
light_energy = 1.15
|
||||||
|
shadow_enabled = true
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
extends Node3D
|
||||||
|
## Soyuz-MS. Godot 4.7. Units: 1 m.
|
||||||
|
## Axes: dock / forward +Z, dorsal +Y, starboard +X. The service engine exhausts -Z.
|
||||||
|
## rcs_direction uses those axes. Nose thrust is +Z, not +Y.
|
||||||
|
## Drive is a chemical torch (short, warm). Abort motors are the same ribbons as Kestrel.
|
||||||
|
## See PARTICLE_FX.md.
|
||||||
|
|
||||||
|
const Fx = preload("res://naf_fx.gd")
|
||||||
|
|
||||||
|
@export_range(0.0, 1.0) var hatch_crew_open := 0.0 : set = set_hatch_crew_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_transfer_open := 0.0 : set = set_hatch_transfer_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_dock_open := 0.0 : set = set_hatch_dock_open
|
||||||
|
@export_range(0.0, 1.0) var throttle := 0.0
|
||||||
|
@export var drive_on := false
|
||||||
|
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
||||||
|
@export var abort_burn := false
|
||||||
|
|
||||||
|
var _time := 0.0
|
||||||
|
var _spool := 0.0
|
||||||
|
var _abort_hold := 0.0
|
||||||
|
var _drive_socket: Node3D
|
||||||
|
var _drive_core: Node3D
|
||||||
|
var _drive_sheath: Node3D
|
||||||
|
var _drive_light: SpotLight3D
|
||||||
|
var _rcs: Array[Node3D] = []
|
||||||
|
var _aborts: Array[Dictionary] = []
|
||||||
|
|
||||||
|
const _RCS_THRUST := [
|
||||||
|
Vector3.ZERO,
|
||||||
|
Vector3(0, 0, 1),
|
||||||
|
Vector3(0, 0, -1),
|
||||||
|
Vector3(1, 0, 0),
|
||||||
|
Vector3(-1, 0, 0),
|
||||||
|
Vector3(0, 1, 0),
|
||||||
|
Vector3(0, -1, 0),
|
||||||
|
]
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_arm_fx()
|
||||||
|
set_physics_process(true)
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
_time += delta
|
||||||
|
var power := throttle if drive_on else 0.0
|
||||||
|
var spool_target := 1.0 if power > 0.04 else 0.0
|
||||||
|
_spool = Fx.smooth(_spool, spool_target, delta, 0.2 if spool_target > 0.0 else 0.12)
|
||||||
|
var plume := power * _spool
|
||||||
|
if _drive_core:
|
||||||
|
Fx.set_spear(_drive_core, plume, 1.0, _time)
|
||||||
|
Fx.set_spear(_drive_sheath, plume * 0.82, 1.0, _time)
|
||||||
|
if _drive_light:
|
||||||
|
_drive_light.light_energy = plume * 6.0
|
||||||
|
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
||||||
|
var ship := global_transform.basis
|
||||||
|
for ribbon in _rcs:
|
||||||
|
var want := 0.0
|
||||||
|
if thrust != Vector3.ZERO:
|
||||||
|
var exhaust: Vector3 = ship.inverse() * ribbon.global_transform.basis.y
|
||||||
|
if exhaust.dot(-thrust) > 0.7:
|
||||||
|
want = 1.0
|
||||||
|
var burn := float(ribbon.get_meta("burn"))
|
||||||
|
burn = Fx.smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
||||||
|
ribbon.set_meta("burn", burn)
|
||||||
|
Fx.set_spear(ribbon, burn, 1.0, _time)
|
||||||
|
if abort_burn:
|
||||||
|
_abort_hold = minf(1.0, _abort_hold + delta / 0.22)
|
||||||
|
elif _abort_hold > 0.0:
|
||||||
|
_abort_hold = maxf(0.0, _abort_hold - delta / 0.28)
|
||||||
|
var on := _abort_hold > 0.03
|
||||||
|
var flick := 0.86 + 0.14 * sin(_time * 41.0)
|
||||||
|
var sheath_scale := 0.9 + 0.1 * sin(_time * 19.0)
|
||||||
|
for a in _aborts:
|
||||||
|
var core := a.core as Node3D
|
||||||
|
var sheath := a.sheath as Node3D
|
||||||
|
Fx.set_opacity(core, _abort_hold * 0.95 * flick if on else 0.0)
|
||||||
|
Fx.set_opacity(sheath, _abort_hold * 0.82 * flick if on else 0.0)
|
||||||
|
sheath.scale = Vector3(1, sheath_scale, 1)
|
||||||
|
var sparks := a.sparks as GPUParticles3D
|
||||||
|
var sheath_sparks := a.sheath_sparks as GPUParticles3D
|
||||||
|
sparks.emitting = on
|
||||||
|
sheath_sparks.emitting = on
|
||||||
|
var flash := a.flash as MeshInstance3D
|
||||||
|
flash.visible = on
|
||||||
|
var fmat := flash.material_override as StandardMaterial3D
|
||||||
|
if fmat:
|
||||||
|
var tint := fmat.albedo_color
|
||||||
|
tint.a = _abort_hold * 0.45
|
||||||
|
fmat.albedo_color = tint
|
||||||
|
flash.scale = Vector3.ONE * (0.06 + _abort_hold * 0.04)
|
||||||
|
var light := a.light as SpotLight3D
|
||||||
|
light.light_energy = _abort_hold * 8.0 if on else 0.0
|
||||||
|
|
||||||
|
func set_hatch_crew_open(v: float) -> void:
|
||||||
|
hatch_crew_open = v
|
||||||
|
_seek("hatch_crew_open", v)
|
||||||
|
|
||||||
|
func set_hatch_transfer_open(v: float) -> void:
|
||||||
|
hatch_transfer_open = v
|
||||||
|
_seek("hatch_transfer_open", v)
|
||||||
|
|
||||||
|
func set_hatch_dock_open(v: float) -> void:
|
||||||
|
hatch_dock_open = v
|
||||||
|
_seek("hatch_dock_open", v)
|
||||||
|
|
||||||
|
func _seek(clip: StringName, t: float) -> void:
|
||||||
|
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||||
|
if ap == null:
|
||||||
|
ap = find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
if ap == null or not ap.has_animation(clip):
|
||||||
|
return
|
||||||
|
ap.play(clip)
|
||||||
|
ap.seek(clampf(t, 0.0, 1.0) * ap.get_animation(clip).length, true)
|
||||||
|
ap.pause()
|
||||||
|
|
||||||
|
func _arm_fx() -> void:
|
||||||
|
var drive := find_child("Vfx_DrivePlume", true, false) as Node3D
|
||||||
|
if drive:
|
||||||
|
_drive_socket = drive
|
||||||
|
_drive_core = Fx.spear(drive, 7.0, 0.5, Color(1.0, 0.953, 0.831, 0.95), Color(1.0, 0.72, 0.4, 0.4), Color(1.0, 0.45, 0.1, 0.0), 0.92, 4, 1.0, "DriveCore")
|
||||||
|
_drive_sheath = Fx.spear(drive, 5.0, 1.25, Color(0.91, 0.353, 0.07, 0.9), Color(0.7, 0.2, 0.02, 0.35), Color(0.3, 0.05, 0.0, 0.0), 0.6, 3, 2.0, "DriveSheath")
|
||||||
|
_drive_light = Fx.spot(drive, 0.2, Color(1.0, 0.627, 0.29), 0.5, 6.0, 2.0)
|
||||||
|
for n in find_children("*", "Node3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Vfx_RCS_"):
|
||||||
|
_rcs.append(Fx.spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, 1.0, "Spear"))
|
||||||
|
elif nm.begins_with("Vfx_Abort_"):
|
||||||
|
_aborts.append(_make_abort(n))
|
||||||
|
|
||||||
|
func _make_abort(socket: Node3D) -> Dictionary:
|
||||||
|
var core := Fx.taper_ribbon(socket, 2.1, 0.06, 0.20, Color(1.0, 0.97, 0.92, 1.0), Color(1.0, 0.45, 0.12, 0.72), Color(0.40, 0.08, 0.02, 0.0), 0.95, 3, "AbortCore")
|
||||||
|
var sheath := Fx.taper_ribbon(socket, 5.2, 0.10, 0.78, Color(1.0, 0.55, 0.18, 1.0), Color(1.0, 0.28, 0.06, 0.55), Color(0.35, 0.06, 0.01, 0.0), 0.82, 3, "AbortSheath")
|
||||||
|
var sparks := Fx.sparks(socket, "AbortCoreSparks", 10, 0.15, 18.0, 28.0, 1.6, 0.05, 6.0)
|
||||||
|
var sheath_sparks := Fx.sparks(socket, "AbortSheathSparks", 18, 0.26, 14.0, 22.0, 4.5, 0.09, 8.0)
|
||||||
|
for p in [sparks, sheath_sparks]:
|
||||||
|
var proc := p.process_material as ParticleProcessMaterial
|
||||||
|
if proc:
|
||||||
|
proc.color_ramp = _abort_ramp()
|
||||||
|
var flash := Fx.abort_flash(socket)
|
||||||
|
var light := Fx.spot(socket, 0.08, Color(1.0, 0.416, 0.157), 0.5, 6.5, 1.8)
|
||||||
|
light.light_energy = 0.0
|
||||||
|
return {"core": core, "sheath": sheath, "sparks": sparks, "sheath_sparks": sheath_sparks, "flash": flash, "light": light}
|
||||||
|
|
||||||
|
func _abort_ramp() -> GradientTexture1D:
|
||||||
|
var g := Gradient.new()
|
||||||
|
g.offsets = PackedFloat32Array([0.0, 0.15, 0.45, 1.0])
|
||||||
|
g.colors = PackedColorArray([
|
||||||
|
Color(1.0, 0.97, 0.92, 1.0),
|
||||||
|
Color(1.0, 0.46, 0.12, 0.9),
|
||||||
|
Color(0.55, 0.16, 0.05, 0.45),
|
||||||
|
Color(0.25, 0.05, 0.01, 0.0),
|
||||||
|
])
|
||||||
|
var tex := GradientTexture1D.new()
|
||||||
|
tex.gradient = g
|
||||||
|
return tex
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://c8ywu0poa4ix8
|
||||||
Binary file not shown.
@@ -3,13 +3,13 @@
|
|||||||
importer="scene"
|
importer="scene"
|
||||||
importer_version=1
|
importer_version=1
|
||||||
type="PackedScene"
|
type="PackedScene"
|
||||||
uid="uid://dnx6e6lksuu1s"
|
uid="uid://1v5lob6vv8do"
|
||||||
path="res://.godot/imported/Young.glb-b6edcd46f48fe0cdfa3811c30f939a9f.scn"
|
path="res://.godot/imported/soyuz.glb-570645bd68031fcc55fb6f400b810e4d.scn"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://Young.glb"
|
source_file="res://soyuz.glb"
|
||||||
dest_files=["res://.godot/imported/Young.glb-b6edcd46f48fe0cdfa3811c30f939a9f.scn"]
|
dest_files=["res://.godot/imported/soyuz.glb-570645bd68031fcc55fb6f400b810e4d.scn"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cy1epmtsn7v6l"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://1v5lob6vv8do" path="res://soyuz.glb" id="1_glb"]
|
||||||
|
[ext_resource type="Script" uid="uid://c8ywu0poa4ix8" path="res://soyuz.gd" id="2_gd"]
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="env"]
|
||||||
|
background_mode = 1
|
||||||
|
background_color = Color(0.012, 0.014, 0.018, 1)
|
||||||
|
ambient_light_source = 2
|
||||||
|
ambient_light_color = Color(0.62, 0.7, 0.8, 1)
|
||||||
|
ambient_light_energy = 0.2
|
||||||
|
tonemap_mode = 2
|
||||||
|
|
||||||
|
[node name="Soyuz" instance=ExtResource("1_glb")]
|
||||||
|
script = ExtResource("2_gd")
|
||||||
|
|
||||||
|
[node name="HangarSky" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("env")
|
||||||
|
|
||||||
|
[node name="Key" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(0.8, -0.5, 0.33, 0, 0.55, 0.84, -0.6, -0.67, 0.44, 8, 6, 10)
|
||||||
|
light_color = Color(0.9, 0.86, 0.78, 1)
|
||||||
|
light_energy = 1.2
|
||||||
|
shadow_enabled = true
|
||||||
LFS
BIN
Binary file not shown.
@@ -0,0 +1,45 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://bs88v1pkp2opr"
|
||||||
|
path="res://.godot/imported/Young_2077.glb-7aaf8ddc11fa9df7f27a6eddd82a45e1.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077.glb"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077.glb-7aaf8ddc11fa9df7f27a6eddd82a45e1.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/root_script=null
|
||||||
|
mesh_library/use_node_names_as_mesh_names=false
|
||||||
|
array_mesh/deduplicate_surfaces=true
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
nodes/import_as_skeleton_bones=false
|
||||||
|
nodes/use_name_suffixes=true
|
||||||
|
nodes/use_node_type_suffixes=true
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=1
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=false
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
animation/import_rest_as_RESET=false
|
||||||
|
import_script/path=""
|
||||||
|
materials/extract=0
|
||||||
|
materials/extract_format=0
|
||||||
|
materials/extract_path=""
|
||||||
|
_subresources={}
|
||||||
|
gltf/naming_version=2
|
||||||
|
gltf/embedded_image_handling=1
|
||||||
|
gltf/texture_map_mode=1
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bso5nwkt6po7x"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_blast.png-3d97ed2a2bd10a15bf176b41e0fdab9d.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "d6aa4936eecbaaa962450eb3e1c70146"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_blast.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_blast.png-3d97ed2a2bd10a15bf176b41e0fdab9d.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,43 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://spi08lnpt7ip"
|
||||||
|
path="res://.godot/imported/Young_2077_brick.png-5bd8d5b5b7f73ec03855fa602284c10b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "d5d24c0b9814f804ebb2a8fdd4fa12bb"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_brick.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_brick.png-5bd8d5b5b7f73ec03855fa602284c10b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://20f80n21d46j"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_core.png-e72e5e3deb547c04117a9ec816abec93.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "a1aaf16e4c18b59bf59e878b180d3b23"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_core.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_core.png-e72e5e3deb547c04117a9ec816abec93.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ca75rn0cohjl"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_cyber.png-9c506a502f916ddac2efe6e19e6917bb.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "e54379ab4ae723f367d3894c1a48fa7a"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_cyber.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_cyber.png-9c506a502f916ddac2efe6e19e6917bb.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dxsyl2preexp2"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_cyber_emit.png-df6c0f98bc5e1718b500f219d9492002.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "d1dd210d6b1312cb342b56d02bd5e651"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_cyber_emit.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_cyber_emit.png-df6c0f98bc5e1718b500f219d9492002.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bb47b3lobt6ob"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_elev.png-f073fc23f2a7ed2242a006b15a04f33a.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "70018ee84a8d696aa8074e8537169d35"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_elev.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_elev.png-f073fc23f2a7ed2242a006b15a04f33a.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://5571ygam76sn"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_hab.png-98f1c86cd37466f995035871e568b19f.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "9971c02f32fc1b9273da18b44f70b799"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_hab.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_hab.png-98f1c86cd37466f995035871e568b19f.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cvkudulwacfv5"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_landing.png-580f79ac1d84cdcf6026c083dab58a6e.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "394b2553df45a36f87aaf744f66d9127"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_landing.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_landing.png-580f79ac1d84cdcf6026c083dab58a6e.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b00u6ue1l0dq2"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_machine.png-ed5dad4accd44c63ee43273a867dfdf8.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "a342ebe8aa499c8528995a13673b2207"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_machine.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_machine.png-ed5dad4accd44c63ee43273a867dfdf8.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,43 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c5dr10k3lhptk"
|
||||||
|
path="res://.godot/imported/Young_2077_marble.png-83b704b5d1e6327653959f27f34d7431.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "f75e368114340503aa12341952279afc"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_marble.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_marble.png-83b704b5d1e6327653959f27f34d7431.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d1trkcen3ftf"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_panel.png-3bc818c4d058dd91ff53f23af7976a19.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "1f1b250323961ccbece0c86fa3a5daaa"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_panel.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_panel.png-3bc818c4d058dd91ff53f23af7976a19.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cd607o8u8rnoe"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_pillar.png-181f4f5801bd3fb0749f2f8b9b2edcbd.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "d0bc4594760a3d101a509620d3a59c41"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_pillar.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_pillar.png-181f4f5801bd3fb0749f2f8b9b2edcbd.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://6vsodm2wgqha"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_plate.png-a671f10c81a24b0ce917c36a71378e17.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "b69b538b9646c2643dd3a44e989afe6b"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_plate.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_plate.png-a671f10c81a24b0ce917c36a71378e17.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bqsegbrmbkqn0"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_rib.png-20cb6beac176f488f2bc480ee52c8e51.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "0a10e5a2421515b9986f9096b28acdd0"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_rib.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_rib.png-20cb6beac176f488f2bc480ee52c8e51.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://15ilicb345pk"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_school.png-416a9d9b58fc0973e773fb09e02320f8.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "3a198c9bd0dd8a5657af7f912658012f"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_school.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_school.png-416a9d9b58fc0973e773fb09e02320f8.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bs18lajcddph4"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_soffit.png-20eed9b10c026b621e80aa7dcbfa7a76.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "d059e980ca332a87943e2c317ea20c04"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_soffit.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_soffit.png-20eed9b10c026b621e80aa7dcbfa7a76.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bp74jds8n4ce8"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_street.png-d6fec24cdd742f3d7bb7390de10ed9a8.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "00aa3f3fb0dda12f43fcf9a1ab7c40ef"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_street.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_street.png-d6fec24cdd742f3d7bb7390de10ed9a8.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bimksgdbf1toc"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_street_emit.png-3e9ca62bdd797e7f860ab16a042cfdd3.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "7ab4e5e3387bed15e47ab02d1f26e204"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_street_emit.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_street_emit.png-3e9ca62bdd797e7f860ab16a042cfdd3.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://clrnbwgdfs57j"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_tread.png-441a0e32e9847091495aa6d16b8aeec8.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "8ea9242b8f4a9bd0cc4d3511094a6240"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_tread.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_tread.png-441a0e32e9847091495aa6d16b8aeec8.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dqkp7k533fddb"
|
||||||
|
path.s3tc="res://.godot/imported/Young_2077_yard.png-45f15ec3da2cd2ee01e105cea210a6cc.s3tc.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={
|
||||||
|
"md5": "917e826f5261f55e5698d9c20883ad3e"
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Young_2077_yard.png"
|
||||||
|
dest_files=["res://.godot/imported/Young_2077_yard.png-45f15ec3da2cd2ee01e105cea210a6cc.s3tc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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=0
|
||||||
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
@@ -1,44 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://bfr7h1gbevuic"
|
|
||||||
path.s3tc="res://.godot/imported/Young_pillar.png-1c3150bd45e98aca1d846e0b5a0c9178.s3tc.ctex"
|
|
||||||
metadata={
|
|
||||||
"imported_formats": ["s3tc_bptc"],
|
|
||||||
"vram_texture": true
|
|
||||||
}
|
|
||||||
generator_parameters={
|
|
||||||
"md5": "36f0e00e59ff8e4325668b21fb73a729"
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://Young_pillar.png"
|
|
||||||
dest_files=["res://.godot/imported/Young_pillar.png-1c3150bd45e98aca1d846e0b5a0c9178.s3tc.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=2
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=true
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
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=0
|
|
||||||
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
@@ -1,44 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://iafexcw8xydf"
|
|
||||||
path.s3tc="res://.godot/imported/Young_soffit.png-0fcd7172b7cbeec36d04ea1d2e219b40.s3tc.ctex"
|
|
||||||
metadata={
|
|
||||||
"imported_formats": ["s3tc_bptc"],
|
|
||||||
"vram_texture": true
|
|
||||||
}
|
|
||||||
generator_parameters={
|
|
||||||
"md5": "1099b9de5b0852886fc0903387f86192"
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://Young_soffit.png"
|
|
||||||
dest_files=["res://.godot/imported/Young_soffit.png-0fcd7172b7cbeec36d04ea1d2e219b40.s3tc.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=2
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=true
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
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=0
|
|
||||||
LFS
BIN
Binary file not shown.
@@ -1,44 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://csgo5g6vok50k"
|
|
||||||
path.s3tc="res://.godot/imported/Young_tread.png-3adc5022f76b3593991df42a6b1d8417.s3tc.ctex"
|
|
||||||
metadata={
|
|
||||||
"imported_formats": ["s3tc_bptc"],
|
|
||||||
"vram_texture": true
|
|
||||||
}
|
|
||||||
generator_parameters={
|
|
||||||
"md5": "310d47ab0b64b919a20a6c9f5c20d4a1"
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://Young_tread.png"
|
|
||||||
dest_files=["res://.godot/imported/Young_tread.png-3adc5022f76b3593991df42a6b1d8417.s3tc.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=2
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=true
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
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=0
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Brachistochrone — NAF ship assets
|
||||||
|
|
||||||
|
Rebuilt 2026-09-20 in App Builder from the Expert-mode handoff. Procedural Three.js is the source of truth (`src/ship/*`, `src/capsule/buildCapsule.ts`). Do not hand-edit GLBs.
|
||||||
|
|
||||||
|
## Frame
|
||||||
|
|
||||||
|
- 1 glTF unit = 1 meter, Y-up
|
||||||
|
- **Kestrel EC-01:** heat shield at Y≈0, nose +Y, crew backs on the shield, gaze +Y, visors +Z
|
||||||
|
- **Resolute CVT-07:** +Y = nose / thrust up, decks = XZ stacked on Y, +X starboard, +Z dorsal
|
||||||
|
- Faction: North American Federation. Charcoal hull `#2a3038`, liner `#6a727c`
|
||||||
|
- No keel gun. No planet in the hangar preview. Default view is solid exterior.
|
||||||
|
|
||||||
|
## Resolute
|
||||||
|
|
||||||
|
- Length ~46 m, beam ~13 m, height ~11.4 m
|
||||||
|
- Superellipse loft, `SEGS=8`, `SUPER_EXP=0.22`
|
||||||
|
- `WALL=0.58 m`, floor extra inset `0.30 m`
|
||||||
|
- 11 decks, pitch 2.65 m, room height 2.42 m
|
||||||
|
- Companionway: well 0.95 × 3.35 m, rise 0.221 m, run 0.255 m, 12 treads, trunk walls, switchback per deck
|
||||||
|
- Cabins on decks 03/04 against port/starboard inner hull; corridor + stair in the center
|
||||||
|
- Dorsal airlock on storage deck 07, walk-through vestibule + round hatches
|
||||||
|
|
||||||
|
## Kestrel
|
||||||
|
|
||||||
|
- ~4.55 m capsule, four Gemini L-couches
|
||||||
|
- Cabin lights ~0.28 intensity (dim)
|
||||||
|
- Glass opacity 0.20, roughness 0.07, metalness 0, hull holes behind panes
|
||||||
|
- Sitting bulkhead at z≈−0.36, stow lids in a line
|
||||||
|
- Docking probe stays in the tunnel
|
||||||
|
|
||||||
|
## Clips (seek 0..1)
|
||||||
|
|
||||||
|
Kestrel: `gear_deploy` `hatch_crew_open` `hatch_ladder_deploy` `hatch_dock_open` `instruments_demo`
|
||||||
|
|
||||||
|
Resolute: `gear_deploy` `radiators_deploy` `hatch_airlock_open` `hatch_dock_open` `hatch_cargo_open` `torpedo_doors_open`
|
||||||
|
|
||||||
|
Named cameras: `SeatCamera` (Kestrel), `HelmCamera` + `PlayerSpawn` (Resolute)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
Godot 4.7 project. Units: 1 = 1 meter. Open this folder (it has project.godot). Hongqi is the main scene. The other ships are resolute.tscn, kestrel.tscn, soyuz.tscn, and shrike.tscn. Do not re-import these GLBs into the hangar. These .tscn files are ordinary Godot 4.7 scenes; replace the whole folder if an older copy still says a scene is corrupt.
|
||||||
|
|
||||||
|
Each scene is a glb (mesh, embedded PNG textures, 0..1 animation clips) plus a script that spawns particles on the Vfx sockets when you press Play. Textures are inside the glb. Ribbons are not. Drive, RCS, and abort flames are crossed additive ribbons from naf_fx.gd and ribbon.gdshader. Chaff and abort sparks are the only GPUParticles. PDC is a stream of short tracer bolts, not one beam. The tscn adds a near-black world so the additive jets show. Godot's default grey sky washes them out.
|
||||||
|
|
||||||
|
Press Play, then use the inspector. Drive On plus Throttle runs the torch. The 0..1 sliders seek the imported clips. Gear starts down. Hatches start shut. Hongqi radiators start open.
|
||||||
|
|
||||||
|
Keep these files together. The scripts preload naf_fx.gd from this folder.
|
||||||
|
|
||||||
|
| Scene | Open | Axes |
|
||||||
|
|---|---|---|
|
||||||
|
| resolute.tscn | NAF Resolute | nose +Y, starboard +X, dorsal +Z |
|
||||||
|
| kestrel.tscn | Kestrel EC-01 | nose +Y, starboard +X, dorsal +Z |
|
||||||
|
| hongqi.tscn | 红旗-07 | nose +Y, starboard +X, dorsal +Z. Root scale 1.14, same as the hangar. |
|
||||||
|
| soyuz.tscn | Soyuz-MS | dock +Z, dorsal +Y, starboard +X. Nose thrust is +Z. |
|
||||||
|
| shrike.tscn | Phantom II | nose +Z, belly -Y, starboard +X. Nose thrust is +Z. |
|
||||||
|
|
||||||
|
Rest pose: gear down where the ship has gear, hatches shut, torpedo doors shut, canopy shut, effects off. Hongqi radiators start open. Sliders seek the imported AnimationPlayer. If Godot nests that player, the scripts look it up with find_child.
|
||||||
|
|
||||||
|
Hongqi clips: gear_deploy, radiators_deploy, hatch_boarding_port, hatch_boarding_stbd, hatch_airlock_open, hatch_deck_open, hatch_ramp_open, torpedo_doors_open.
|
||||||
|
Hongqi sockets: Vfx_DrivePlume (main bell, exhaust -Y), Vfx_Drive_Port, Vfx_Drive_Stbd, Vfx_Chaff, RCS *_Vfx on the grafted clusters, PDT_*_Muzzle.
|
||||||
|
Hongqi drive_on plus throttle is a gold core inside a cinnabar sheath. Outboard bells use a shorter copy of the same plume. battle_lights goes dim cinnabar, not the Resolute red.
|
||||||
|
|
||||||
|
Soyuz clips: hatch_crew_open, hatch_transfer_open, hatch_dock_open.
|
||||||
|
Soyuz sockets: Vfx_DrivePlume (exhaust -Z, short chemical plume), Vfx_RCS_*, Vfx_Abort_0..5 (the six soft-landing motors). abort_burn is that landing burn.
|
||||||
|
|
||||||
|
Shrike clips: gear_deploy, canopy_open.
|
||||||
|
Shrike sockets: Vfx_DrivePlume (exhaust -Z), Vfx_RCS_*, PDT_L_Muzzle, PDT_R_Muzzle, Vfx_Chaff.
|
||||||
|
|
||||||
|
Resolute and Kestrel are unchanged in behavior. See PARTICLE_FX.md for ribbon sizes, which bell fires, and what not to replace with a particle spray.
|
||||||
|
|
||||||
|
Exhaust direction is the socket's local position, rotated by the inverse of its quaternion. Do not aim the ribbons by the ship's nose axis. A bell fires only when that exhaust dots the negated thrust by more than 0.7.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
NAF Kestrel EC-01 — Godot GLB
|
||||||
|
Units: 1 = 1 m. Y-up. Docking collar is +Y. Visors are +Z. Heat shield is -Y.
|
||||||
|
This file is the original mesh with hangar fixes baked into node transforms:
|
||||||
|
- Nav lights (port, starboard, white) sit outside the hull, above the heat shield.
|
||||||
|
- RCS bells seated on their housings and aimed outward.
|
||||||
|
- Abort motors yawed off the RCS line, fairings on the skin.
|
||||||
|
- Blade and VHF antennas pulled onto the hull. S-band seated on the shoulder.
|
||||||
|
- Docking hatch lid and probe seated. Probe stays a child of DockHatch, so hatch_dock_open still carries it.
|
||||||
|
Animations: gear_deploy, hatch_crew_open, hatch_ladder_deploy, hatch_dock_open, instruments_demo.
|
||||||
|
kestrel_ec01.gd drives those clips 0..1.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# Particle FX handoff — Resolute and Kestrel
|
||||||
|
|
||||||
|
The hangar look is **not a GPUParticles spray**. Drive, RCS, and the abort flame are additive ribbon meshes parented to the Vfx sockets. A spray of quads (what the old Godot scripts built) reads as smoke. The hangar reads as a solid jet.
|
||||||
|
|
||||||
|
Source of truth: `public/hangar-fx.js`, function `updateFx`. Several helpers in that file are **dead** and must not be ported:
|
||||||
|
|
||||||
|
- `makeDrivePlume` / `stepDrivePlume` — unused. The live drive is `makeSpear`.
|
||||||
|
- `makeRcsJet` / `stepRcsJet` — unused. The live RCS jet is `makeSpear`.
|
||||||
|
- `makePdcStream` / `stepPdcStream` — unused. The live PDC is `spawnTracer`.
|
||||||
|
- `burstChaff` — unused. The live chaff is `makeChaffCloud`.
|
||||||
|
|
||||||
|
The scripts in this folder already implement the live path:
|
||||||
|
|
||||||
|
- `naf_fx.gd` builds ribbons, abort sparks, chaff, tracer bolts, and the exhaust light.
|
||||||
|
- `ribbon.gdshader` is the additive gradient. UV.y 0 is the socket, 1 is the tip. Edges fade so the ribbon is a soft streak, not a rectangle.
|
||||||
|
- `naf_resolute.gd` arms drive, RCS, PDC, chaff.
|
||||||
|
- `kestrel_ec01.gd` arms RCS and the four abort motors.
|
||||||
|
|
||||||
|
Do not replace the ribbons with `GPUParticles3D`. Particles are only used where the hangar uses points: abort sparks and chaff.
|
||||||
|
|
||||||
|
## Axes and which bell fires
|
||||||
|
|
||||||
|
Ship space, same as the GLB: nose +Y, starboard +X, dorsal +Z. Port is -X. Ventral is -Z.
|
||||||
|
|
||||||
|
`rcs_direction` is a thrust, not an exhaust. A bell fires only when its exhaust direction (ribbon local +Y, in ship space) dots the **negated** thrust by more than 0.7. Off fires nothing. They do not all fire together.
|
||||||
|
|
||||||
|
| Direction | Thrust | Bells that fire |
|
||||||
|
|---|---|---|
|
||||||
|
| Nose | +Y | exhaust mostly -Y |
|
||||||
|
| Tail | -Y | exhaust mostly +Y |
|
||||||
|
| Starboard | +X | exhaust mostly -X |
|
||||||
|
| Port | -X | exhaust mostly +X |
|
||||||
|
| Dorsal | +Z | exhaust mostly -Z |
|
||||||
|
| Ventral | -Z | exhaust mostly +Z |
|
||||||
|
|
||||||
|
Exhaust direction comes from the socket, not from a guess. Take the socket's local `position`, rotate it by the inverse of the socket quaternion, and normalize. That is the nozzle axis. Aim the ribbon so its +Y matches. The socket stays put, so a gimballed drive bell carries the plume with it.
|
||||||
|
|
||||||
|
Burn does not pop. Ease toward the target with `1 - exp(-dt / tau)`. Tau is 0.08 s turning on, 0.14 s turning off.
|
||||||
|
|
||||||
|
Opacity flickers. For drive and RCS: `0.82 + 0.18 * sin(time * 37 + seed * 2.1)`. Hide the ribbon when the flickered opacity drops under 0.02.
|
||||||
|
|
||||||
|
## Resolute drive
|
||||||
|
|
||||||
|
Socket `Vfx_DrivePlume`. Two ribbons, each three double-sided planes spun by `i / 3 * PI` (0°, 60°, 120°). Additive, unshaded, no depth write, depth test on, no fog.
|
||||||
|
|
||||||
|
| Piece | Length | Width | Planes | Base opacity | Color along the jet |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Core | 38 m | 1.35 m | 3 | 0.95 | white `f4f8ff` at the throat, ice blue at mid, clear at the tip |
|
||||||
|
| Sheath | 26 m | 3.2 m | 3 | 0.50 | saturated blue `5aa8ff`, wider and shorter than the core |
|
||||||
|
|
||||||
|
`drive_on` with `throttle` is the power. Spool eases toward 1 over 0.26 s when power > 0.04, and back to 0 over 0.07 s. Visible amount is `power * spool`. The sheath uses 0.82 of that amount.
|
||||||
|
|
||||||
|
If the world exhaust points down (`world_dir.y < -0.35`) and the bell is above the hangar pad, shorten both ribbons by the same factor so they stop at the deck instead of spearing it. Deck is world y = -2.45, with 0.22 m of clearance. Minimum reach 0.9 m. If the bell is already below that plane (the ship sitting at the origin in these scenes), do not clip: the torch stays full length. The sheath mesh is already the shorter one. Do **not** multiply its scale by 26/38 a second time. In the hangar the ship stands nose-up on the pad, so the torch points at the deck and this clip is why the plume is a short bright jet under the bell instead of a 38 m lance. In space, or with the ship at the scene origin, exhaust is not aimed at a pad under the bell and the full length shows.
|
||||||
|
|
||||||
|
Spot light on the socket, 0.35 m out along the exhaust, color `c5dcff`, cone half-angle 0.3 rad, range follows the clipped length (at least 2.2 m, at most 38 m), decay 2. Energy is `5 * plume amount`. Godot's tonemapper is harsher than the hangar. If it blooms white, lower the light energy. Do not dim the ribbon to compensate.
|
||||||
|
|
||||||
|
`DriveGlow` and `DrivePlasma` are materials, not particles. Emission color `(0.55, 0.72, 1.0) * power`, energy `power * 7`. Radiator fins stay cold unless throttle has been above 0.72 with the panels open. That heat path is already in `naf_resolute.gd`. Leave it.
|
||||||
|
|
||||||
|
## Resolute RCS
|
||||||
|
|
||||||
|
One ribbon per socket whose name ends in `_Vfx` or starts with `Vfx_RCS_`. Skip `Vfx_DrivePlume`, `Vfx_Chaff`, and anything named `Vfx_Abort_`.
|
||||||
|
|
||||||
|
Length 1.2 m, width 0.09 m, **two** planes (0° and 90°). Base opacity 0.82. Color `dcefff` at the throat, fading to pale blue, then clear. Same flicker and burn ease as the drive. No gravity, no spread. It should look like a pencil of light out of the bell, not a cone of sparks.
|
||||||
|
|
||||||
|
## Resolute PDC
|
||||||
|
|
||||||
|
Not a beam and not a particle system. Each `PDT_*_Muzzle` fires discrete bolts at 42 Hz, at most 2 per frame, 90 bolts alive across the whole ship.
|
||||||
|
|
||||||
|
Each bolt is a 5-sided additive cone, color `ffe6b0`, radius 0.016 at the tail and 0.008 at the nose, then scaled in length to a random 1.5–2.3 m. Life 0.12–0.2 s. Speed 90–140 m/s. Direction is the muzzle exhaust, jittered in a 1.15° cone. Spawn 0.18 m ahead of the socket. When `pdc_fire` turns off, stop spawning and let the live bolts finish.
|
||||||
|
|
||||||
|
Turret slew is not part of this handoff.
|
||||||
|
|
||||||
|
## Resolute chaff
|
||||||
|
|
||||||
|
Socket `Vfx_Chaff`. This one **is** particles, because the hangar cloud is points.
|
||||||
|
|
||||||
|
28 soft discs. Life about 1.55 s (the hangar rolls 1.35–1.75). Speed 3–8 m/s. Spread 22°. No gravity, no damping. Size 0.07–0.16 m. Color `(0.90, 0.93, 0.97)`, additive. Hold alpha until 55% of life, then fade to nothing. `chaff_fire` is the continuous cloud.
|
||||||
|
|
||||||
|
## Kestrel RCS
|
||||||
|
|
||||||
|
Same ribbon as Resolute RCS, on every `Vfx_RCS_*` socket. Kestrel has no drive, no PDC, and no chaff.
|
||||||
|
|
||||||
|
## Kestrel abort
|
||||||
|
|
||||||
|
Socket per motor: `Vfx_Abort_FR`, `Vfx_Abort_FL`, `Vfx_Abort_RR`, `Vfx_Abort_RL`. `abort_burn` ramps a hold value up over 0.22 s and down over 0.28 s. All four motors use that one value.
|
||||||
|
|
||||||
|
Each motor has:
|
||||||
|
|
||||||
|
| Piece | What it is |
|
||||||
|
|---|---|
|
||||||
|
| Core ribbon | Tapered, 2.1 m. Width 0.06 m at the throat, 0.20 m at the tip. Width along the length is `w0 + (w1 - w0) * t^0.7`. Three planes. White-hot `fff4e6` at the root, orange, then clear. Opacity `hold * 0.95 * flick`. |
|
||||||
|
| Sheath ribbon | Tapered, 5.2 m. Width 0.10 → 0.78 m, same exponent. Orange `ff6418`. Opacity `hold * 0.82 * flick`. Its length scale breathes: `0.9 + 0.1 * sin(time * 19)`. |
|
||||||
|
| Core sparks | 10 round additive points. Life 0.12–0.18 s (use 0.15). Speed 18–28 m/s. Spread 1.6°. Size about 0.05 m. |
|
||||||
|
| Sheath sparks | 18 points. Life 0.20–0.32 s (use 0.26). Speed 14–22 m/s. Spread 4.5°. Size about 0.09 m. |
|
||||||
|
| Color of sparks | White `(1, 0.97, 0.92)` at birth, orange `(1, 0.46, 0.12)` by mid-life, dark red `(0.55, 0.16, 0.05)` at the end. Opacity `hold * 0.55`. |
|
||||||
|
| Flash | Soft additive disc on the throat, color `fff1dd`, opacity `hold * 0.45`, scale `0.06 + hold * 0.04` m. |
|
||||||
|
| Light | Spot, color `ff6a28`, energy `hold * 8`, half-angle 0.5 rad, range 6.5 m, decay 1.8, sitting 0.08 m out from the socket. |
|
||||||
|
|
||||||
|
Abort flicker is **not** the RCS flicker. Use `0.86 + 0.14 * sin(time * 41)`.
|
||||||
|
|
||||||
|
The ribbons are the flame. The sparks are a thin hot core inside them. If you only keep the sparks, it will look like the old spray.
|
||||||
|
|
||||||
|
## Hongqi, Soyuz, Phantom II
|
||||||
|
|
||||||
|
Same ribbon code as above (`naf_fx.gd`). Do not invent a second particle style. Sizes match `hangar-fx.js`.
|
||||||
|
|
||||||
|
### Hongqi
|
||||||
|
|
||||||
|
Axes: nose +Y, starboard +X, dorsal +Z. Torch exhaust -Y.
|
||||||
|
|
||||||
|
| Socket | Piece | Length | Width | Planes | Color |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Vfx_DrivePlume | Core | 30 m | 1.15 m | 3 | gold-white `fff6d4` |
|
||||||
|
| Vfx_DrivePlume | Sheath | 22 m | 2.7 m | 3 | cinnabar `e23b2f` |
|
||||||
|
| Vfx_Drive_Port / Vfx_Drive_Stbd | Core | 12 m | 0.55 m | 3 | same gold |
|
||||||
|
| Vfx_Drive_Port / Vfx_Drive_Stbd | Sheath | 8 m | 1.2 m | 3 | same cinnabar |
|
||||||
|
|
||||||
|
RCS is the Resolute pencil (1.2 m, two planes) on every `*_Vfx` / `Vfx_RCS_*` socket. PDC is the same 42 Hz tracer stream on `PDT_*_Muzzle`. Chaff is the Resolute cloud on `Vfx_Chaff`. `battle_lights` uses cinnabar `c4342c`, not Resolute red. Radiator fins glow only after a hard burn, same rule as Resolute.
|
||||||
|
|
||||||
|
### Soyuz-MS
|
||||||
|
|
||||||
|
Axes: dock +Z, dorsal +Y, starboard +X. Service-engine exhaust -Z. `rcs_direction` Nose is +Z thrust.
|
||||||
|
|
||||||
|
| Socket | Piece | Length | Width | Planes | Color |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Vfx_DrivePlume | Core | 7 m | 0.5 m | 4 | warm `fff3d4` |
|
||||||
|
| Vfx_DrivePlume | Sheath | 5 m | 1.25 m | 3 | chemical orange `e85a12` |
|
||||||
|
|
||||||
|
`Vfx_Abort_0` … `Vfx_Abort_5` are the six soft-landing motors. They use the Kestrel abort recipe (tapered core, orange sheath, sparks). `abort_burn` holds all six.
|
||||||
|
|
||||||
|
### Phantom II (shrike)
|
||||||
|
|
||||||
|
Axes: nose +Z, belly -Y, starboard +X. Torch exhaust -Z. `rcs_direction` Nose is +Z thrust. Dorsal is +Y.
|
||||||
|
|
||||||
|
| Socket | Piece | Length | Width | Planes | Color |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Vfx_DrivePlume | Core | 22 m | 0.68 m | 3 | ice `f4f8ff` |
|
||||||
|
| Vfx_DrivePlume | Sheath | 14 m | 1.7 m | 3 | blue `5aa8ff` |
|
||||||
|
|
||||||
|
RCS, the two `PDT_*_Muzzle` guns, and `Vfx_Chaff` match Resolute. `cabin_dim` only lowers the cabin lights. There is no battle-red swap.
|
||||||
|
|
||||||
|
## What to give Godot 4.7
|
||||||
|
|
||||||
|
One folder, with `project.godot`. Open it in Godot 4.7. Hongqi is the main scene. Shared: `naf_fx.gd`, `ribbon.gdshader`, this file.
|
||||||
|
|
||||||
|
- Resolute: `naf_resolute.glb`, `naf_resolute.gd`, `resolute.tscn`
|
||||||
|
- Kestrel: `kestrel_ec01_escape_capsule.glb`, `kestrel_ec01.gd`, `kestrel.tscn`
|
||||||
|
- Hongqi: `hongqi.glb`, `hongqi.gd`, `hongqi.tscn`
|
||||||
|
- Soyuz: `soyuz.glb`, `soyuz.gd`, `soyuz.tscn`
|
||||||
|
- Phantom II: `shrike.glb`, `shrike.gd`, `shrike.tscn`
|
||||||
|
|
||||||
|
Textures and the 0..1 clips are already in the glb. Play the scene before judging the jets. Additive ribbons disappear on the default grey sky. The tscn world is near-black on purpose.
|
||||||
|
|
||||||
|
Play, then:
|
||||||
|
|
||||||
|
- Resolute: `drive_on`, throttle 0.8. White core, blue sheath, flickering. Not a cloud of quads.
|
||||||
|
- Resolute: `rcs_direction` Starboard. Only the bells pushing that way light.
|
||||||
|
- Resolute: `pdc_fire`. Separate bolts, not one beam. `chaff_fire` is a small silver cloud.
|
||||||
|
- Kestrel: `abort_burn`. Four motors, white throat into an orange plume.
|
||||||
|
- Hongqi: `drive_on`, throttle 0.8. Gold core, cinnabar sheath, on the main bell and both outboards.
|
||||||
|
- Soyuz: `drive_on` is a short chemical plume on the service engine. `abort_burn` lights the six landing motors.
|
||||||
|
- Phantom II: `drive_on` is the ice/blue torch out the tail. `canopy_open` and `gear_deploy` are the two clips.
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
extends Node3D
|
||||||
|
## Hongqi 红旗-07. Godot 4.7. Units: 1 m.
|
||||||
|
## Axes: nose +Y, starboard +X, dorsal +Z. Aft exhaust is -Y.
|
||||||
|
## Clips are 0..1 on the AnimationPlayer imported with the glb.
|
||||||
|
## Particles are spawned on the Vfx sockets at runtime. They are not in the mesh.
|
||||||
|
## Drive is a cinnabar torch: white-gold core, red sheath, plus a smaller plume on each outboard bell.
|
||||||
|
## See PARTICLE_FX.md.
|
||||||
|
|
||||||
|
const Fx = preload("res://naf_fx.gd")
|
||||||
|
|
||||||
|
@export_range(0.0, 1.0) var gear_deploy := 1.0 : set = set_gear_deploy
|
||||||
|
@export_range(0.0, 1.0) var radiators_deploy := 1.0 : set = set_radiators_deploy
|
||||||
|
@export_range(0.0, 1.0) var hatch_boarding_port := 0.0 : set = set_hatch_boarding_port
|
||||||
|
@export_range(0.0, 1.0) var hatch_boarding_stbd := 0.0 : set = set_hatch_boarding_stbd
|
||||||
|
@export_range(0.0, 1.0) var hatch_airlock_open := 0.0 : set = set_hatch_airlock_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_deck_open := 0.0 : set = set_hatch_deck_open
|
||||||
|
@export_range(0.0, 1.0) var hatch_ramp_open := 0.0 : set = set_hatch_ramp_open
|
||||||
|
@export_range(0.0, 1.0) var torpedo_doors_open := 0.0 : set = set_torpedo_doors_open
|
||||||
|
@export_range(0.0, 1.0) var throttle := 0.0
|
||||||
|
@export var drive_on := false
|
||||||
|
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
||||||
|
@export var pdc_fire := false
|
||||||
|
@export var chaff_fire := false
|
||||||
|
@export var battle_lights := false : set = set_battle_lights
|
||||||
|
|
||||||
|
var _heat := 0.0
|
||||||
|
var _spool := 0.0
|
||||||
|
var _time := 0.0
|
||||||
|
var _rad_mats: Array[StandardMaterial3D] = []
|
||||||
|
var _drive_socket: Node3D
|
||||||
|
var _drive_core: Node3D
|
||||||
|
var _drive_sheath: Node3D
|
||||||
|
var _drive_light: SpotLight3D
|
||||||
|
var _aux: Array[Dictionary] = []
|
||||||
|
var _rcs: Array[Node3D] = []
|
||||||
|
var _muzzles: Array[Node3D] = []
|
||||||
|
var _tracers: Array[Dictionary] = []
|
||||||
|
var _tracer_pool: Array[MeshInstance3D] = []
|
||||||
|
var _tracer_mesh: ArrayMesh
|
||||||
|
var _tracer_mat: StandardMaterial3D
|
||||||
|
var _chaff: Array[GPUParticles3D] = []
|
||||||
|
var _cabin_ready := false
|
||||||
|
var _cabin_lights: Array[Light3D] = []
|
||||||
|
var _cabin_rest: Array[Dictionary] = []
|
||||||
|
var _cabin_mats: Array[Dictionary] = []
|
||||||
|
|
||||||
|
const _RCS_THRUST := [
|
||||||
|
Vector3.ZERO,
|
||||||
|
Vector3(0, 1, 0),
|
||||||
|
Vector3(0, -1, 0),
|
||||||
|
Vector3(1, 0, 0),
|
||||||
|
Vector3(-1, 0, 0),
|
||||||
|
Vector3(0, 0, 1),
|
||||||
|
Vector3(0, 0, -1),
|
||||||
|
]
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_seek("gear_deploy", gear_deploy)
|
||||||
|
_seek("radiators_deploy", radiators_deploy)
|
||||||
|
_collect_glow()
|
||||||
|
_arm_fx()
|
||||||
|
_collect_cabin()
|
||||||
|
set_physics_process(true)
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
_time += delta
|
||||||
|
var power := throttle if drive_on else 0.0
|
||||||
|
var open := 0.0 if radiators_deploy < 0.15 else clampf((radiators_deploy - 0.15) / 0.7, 0.0, 1.0)
|
||||||
|
var hard := 0.0 if power < 0.72 else (power - 0.72) / 0.28
|
||||||
|
_heat = lerpf(_heat, hard * open, 1.0 - exp(-delta / (18.0 if hard * open > _heat else 22.0)))
|
||||||
|
var glow := 0.0 if _heat < 0.55 else pow((_heat - 0.55) / 0.45, 1.7)
|
||||||
|
for mat in _rad_mats:
|
||||||
|
mat.emission_enabled = glow > 0.02
|
||||||
|
mat.emission = Color(1.0, 0.35, 0.08)
|
||||||
|
mat.emission_energy_multiplier = glow * 1.15
|
||||||
|
var spool_target := 1.0 if power > 0.04 else 0.0
|
||||||
|
_spool = Fx.smooth(_spool, spool_target, delta, 0.26 if spool_target > 0.0 else 0.07)
|
||||||
|
var plume := power * _spool
|
||||||
|
if _drive_core:
|
||||||
|
var scale := _drive_reach_scale(_drive_socket, _drive_core)
|
||||||
|
Fx.set_spear(_drive_core, plume, scale, _time)
|
||||||
|
Fx.set_spear(_drive_sheath, plume * 0.82, scale, _time)
|
||||||
|
if _drive_light:
|
||||||
|
_drive_light.light_energy = plume * 5.0
|
||||||
|
for aux in _aux:
|
||||||
|
var socket := aux.socket as Node3D
|
||||||
|
var core := aux.core as Node3D
|
||||||
|
var sheath := aux.sheath as Node3D
|
||||||
|
var scale := _drive_reach_scale(socket, core)
|
||||||
|
Fx.set_spear(core, plume, scale, _time + 0.4)
|
||||||
|
Fx.set_spear(sheath, plume * 0.7, scale, _time + 0.4)
|
||||||
|
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
||||||
|
var ship := global_transform.basis
|
||||||
|
for ribbon in _rcs:
|
||||||
|
var want := 0.0
|
||||||
|
if thrust != Vector3.ZERO:
|
||||||
|
var exhaust: Vector3 = ship.inverse() * ribbon.global_transform.basis.y
|
||||||
|
if exhaust.dot(-thrust) > 0.7:
|
||||||
|
want = 1.0
|
||||||
|
var burn := float(ribbon.get_meta("burn"))
|
||||||
|
burn = Fx.smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
||||||
|
ribbon.set_meta("burn", burn)
|
||||||
|
Fx.set_spear(ribbon, burn, 1.0, _time)
|
||||||
|
for cloud in _chaff:
|
||||||
|
cloud.emitting = chaff_fire
|
||||||
|
_step_pdc(delta)
|
||||||
|
|
||||||
|
func set_battle_lights(v: bool) -> void:
|
||||||
|
battle_lights = v
|
||||||
|
if _cabin_ready:
|
||||||
|
_apply_cabin()
|
||||||
|
|
||||||
|
func _collect_cabin() -> void:
|
||||||
|
for n in find_children("*", "Light3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Nav") or nm.begins_with("Vfx"):
|
||||||
|
continue
|
||||||
|
if not (nm.begins_with("PointLight_") or nm.begins_with("CabinLamp") or nm == "CabinFill"):
|
||||||
|
continue
|
||||||
|
_cabin_lights.append(n)
|
||||||
|
_cabin_rest.append({"energy": n.light_energy, "color": n.light_color})
|
||||||
|
for n in find_children("*", "MeshInstance3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.find("Lamp") < 0 and not nm.begins_with("LightStrip_"):
|
||||||
|
continue
|
||||||
|
var mesh := n as MeshInstance3D
|
||||||
|
var mat := mesh.get_active_material(0)
|
||||||
|
if mat == null or not (mat is StandardMaterial3D):
|
||||||
|
continue
|
||||||
|
var copy := (mat as StandardMaterial3D).duplicate() as StandardMaterial3D
|
||||||
|
mesh.set_surface_override_material(0, copy)
|
||||||
|
_cabin_mats.append({"mat": copy, "energy": copy.emission_energy_multiplier, "color": copy.emission, "on": copy.emission_enabled})
|
||||||
|
_cabin_ready = true
|
||||||
|
|
||||||
|
func _apply_cabin() -> void:
|
||||||
|
var red := Color(0.769, 0.204, 0.173)
|
||||||
|
for i in _cabin_lights.size():
|
||||||
|
var lamp := _cabin_lights[i]
|
||||||
|
var rest: Dictionary = _cabin_rest[i]
|
||||||
|
if battle_lights:
|
||||||
|
lamp.light_color = red
|
||||||
|
lamp.light_energy = maxf(0.05, float(rest.energy) * 0.2)
|
||||||
|
else:
|
||||||
|
lamp.light_color = rest.color
|
||||||
|
lamp.light_energy = float(rest.energy)
|
||||||
|
for row in _cabin_mats:
|
||||||
|
var mat := row.mat as StandardMaterial3D
|
||||||
|
if battle_lights:
|
||||||
|
mat.emission_enabled = true
|
||||||
|
mat.emission = red
|
||||||
|
mat.emission_energy_multiplier = maxf(0.28, float(row.energy) * 0.45)
|
||||||
|
else:
|
||||||
|
mat.emission_enabled = bool(row.on)
|
||||||
|
mat.emission = row.color
|
||||||
|
mat.emission_energy_multiplier = float(row.energy)
|
||||||
|
|
||||||
|
func set_gear_deploy(v: float) -> void:
|
||||||
|
gear_deploy = v
|
||||||
|
_seek("gear_deploy", v)
|
||||||
|
|
||||||
|
func set_radiators_deploy(v: float) -> void:
|
||||||
|
radiators_deploy = v
|
||||||
|
_seek("radiators_deploy", v)
|
||||||
|
|
||||||
|
func set_hatch_boarding_port(v: float) -> void:
|
||||||
|
hatch_boarding_port = v
|
||||||
|
_seek("hatch_boarding_port", v)
|
||||||
|
|
||||||
|
func set_hatch_boarding_stbd(v: float) -> void:
|
||||||
|
hatch_boarding_stbd = v
|
||||||
|
_seek("hatch_boarding_stbd", v)
|
||||||
|
|
||||||
|
func set_hatch_airlock_open(v: float) -> void:
|
||||||
|
hatch_airlock_open = v
|
||||||
|
_seek("hatch_airlock_open", v)
|
||||||
|
|
||||||
|
func set_hatch_deck_open(v: float) -> void:
|
||||||
|
hatch_deck_open = v
|
||||||
|
_seek("hatch_deck_open", v)
|
||||||
|
|
||||||
|
func set_hatch_ramp_open(v: float) -> void:
|
||||||
|
hatch_ramp_open = v
|
||||||
|
_seek("hatch_ramp_open", v)
|
||||||
|
|
||||||
|
func set_torpedo_doors_open(v: float) -> void:
|
||||||
|
torpedo_doors_open = v
|
||||||
|
_seek("torpedo_doors_open", v)
|
||||||
|
|
||||||
|
func _player() -> AnimationPlayer:
|
||||||
|
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||||
|
if ap:
|
||||||
|
return ap
|
||||||
|
return find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
|
||||||
|
func _seek(clip: StringName, t: float) -> void:
|
||||||
|
var ap := _player()
|
||||||
|
if ap == null or not ap.has_animation(clip):
|
||||||
|
return
|
||||||
|
var len := ap.get_animation(clip).length
|
||||||
|
ap.play(clip)
|
||||||
|
ap.seek(clampf(t, 0.0, 1.0) * len, true)
|
||||||
|
ap.pause()
|
||||||
|
|
||||||
|
func _collect_glow() -> void:
|
||||||
|
for n in find_children("*", "MeshInstance3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if not nm.begins_with("Radiator_"):
|
||||||
|
continue
|
||||||
|
if nm.find("Fin_") < 0 and nm.find("Edge_") < 0:
|
||||||
|
continue
|
||||||
|
var mesh := n as MeshInstance3D
|
||||||
|
var mat := mesh.get_active_material(0)
|
||||||
|
if mat == null or not (mat is StandardMaterial3D):
|
||||||
|
continue
|
||||||
|
var copy := (mat as StandardMaterial3D).duplicate() as StandardMaterial3D
|
||||||
|
mesh.set_surface_override_material(0, copy)
|
||||||
|
_rad_mats.append(copy)
|
||||||
|
|
||||||
|
func _arm_fx() -> void:
|
||||||
|
_tracer_mesh = Fx.tracer_mesh()
|
||||||
|
_tracer_mat = Fx.tracer_mat()
|
||||||
|
var gold := Color(1.0, 0.965, 0.831, 0.95)
|
||||||
|
var gold_mid := Color(1.0, 0.72, 0.35, 0.4)
|
||||||
|
var gold_tip := Color(0.9, 0.4, 0.1, 0.0)
|
||||||
|
var red := Color(0.886, 0.231, 0.184, 0.9)
|
||||||
|
var red_mid := Color(0.55, 0.08, 0.05, 0.35)
|
||||||
|
var red_tip := Color(0.25, 0.02, 0.01, 0.0)
|
||||||
|
var drive := find_child("Vfx_DrivePlume", true, false) as Node3D
|
||||||
|
if drive:
|
||||||
|
_drive_socket = drive
|
||||||
|
_drive_core = Fx.spear(drive, 30.0, 1.15, gold, gold_mid, gold_tip, 0.92, 3, 1.0, "DriveCore")
|
||||||
|
_drive_sheath = Fx.spear(drive, 22.0, 2.7, red, red_mid, red_tip, 0.5, 3, 2.0, "DriveSheath")
|
||||||
|
_drive_light = Fx.spot(drive, 0.35, Color(1.0, 0.882, 0.69), 0.55, 7.5, 2.0)
|
||||||
|
for n in find_children("*", "Node3D", true, false):
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm == "Vfx_Drive_Port" or nm == "Vfx_Drive_Stbd":
|
||||||
|
_aux.append({
|
||||||
|
"socket": n,
|
||||||
|
"core": Fx.spear(n, 12.0, 0.55, gold, gold_mid, gold_tip, 0.9, 3, 3.0, "DriveCore"),
|
||||||
|
"sheath": Fx.spear(n, 8.0, 1.2, red, red_mid, red_tip, 0.45, 3, 4.0, "DriveSheath"),
|
||||||
|
})
|
||||||
|
elif _is_rcs_socket(n):
|
||||||
|
_rcs.append(Fx.spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, 1.0, "Spear"))
|
||||||
|
elif nm.begins_with("PDT_") and nm.ends_with("_Muzzle"):
|
||||||
|
_muzzles.append(n)
|
||||||
|
n.set_meta("acc", randf() * 0.02)
|
||||||
|
elif nm == "Vfx_Chaff":
|
||||||
|
_chaff.append(Fx.chaff(n))
|
||||||
|
|
||||||
|
func _is_rcs_socket(n: Node) -> bool:
|
||||||
|
var nm := String(n.name)
|
||||||
|
if nm.begins_with("Vfx_Drive") or nm == "Vfx_Chaff" or nm.begins_with("Vfx_Abort"):
|
||||||
|
return false
|
||||||
|
return nm.ends_with("_Vfx") or nm.begins_with("Vfx_RCS_")
|
||||||
|
|
||||||
|
func _drive_reach_scale(socket: Node3D, core: Node3D) -> float:
|
||||||
|
if socket == null or core == null:
|
||||||
|
return 1.0
|
||||||
|
var length := float(core.get_meta("len"))
|
||||||
|
var exhaust: Vector3 = (socket.global_transform.basis * core.basis.y).normalized()
|
||||||
|
if exhaust.y >= -0.35:
|
||||||
|
return 1.0
|
||||||
|
# Pad is world y=-2.45. Clip only when the bell is above it.
|
||||||
|
# At the origin the bells sit below that plane, so the torch stays full length.
|
||||||
|
var room := (socket.global_position.y - (-2.45 + 0.22)) / -exhaust.y
|
||||||
|
if room <= 0.0:
|
||||||
|
return 1.0
|
||||||
|
return maxf(0.9, minf(length, room)) / length
|
||||||
|
|
||||||
|
func _step_pdc(delta: float) -> void:
|
||||||
|
if pdc_fire:
|
||||||
|
for mz in _muzzles:
|
||||||
|
var acc := float(mz.get_meta("acc")) - delta
|
||||||
|
var shots := 0
|
||||||
|
while acc <= 0.0 and shots < 2 and _tracers.size() < 90:
|
||||||
|
acc += 1.0 / 42.0
|
||||||
|
shots += 1
|
||||||
|
_spawn_tracer(mz)
|
||||||
|
mz.set_meta("acc", acc)
|
||||||
|
for i in range(_tracers.size() - 1, -1, -1):
|
||||||
|
var t: Dictionary = _tracers[i]
|
||||||
|
t.life = float(t.life) - delta
|
||||||
|
var mesh := t.mesh as MeshInstance3D
|
||||||
|
if float(t.life) <= 0.0:
|
||||||
|
mesh.visible = false
|
||||||
|
_tracer_pool.append(mesh)
|
||||||
|
_tracers.remove_at(i)
|
||||||
|
continue
|
||||||
|
mesh.position += (t.dir as Vector3) * float(t.speed) * delta
|
||||||
|
|
||||||
|
func _spawn_tracer(mz: Node3D) -> void:
|
||||||
|
var mesh := _take_tracer()
|
||||||
|
var world_dir: Vector3 = (mz.global_transform.basis * Fx.exhaust_local(mz)).normalized()
|
||||||
|
var shot := Fx.cone(world_dir, 1.15)
|
||||||
|
var origin := mz.global_position + world_dir * 0.18
|
||||||
|
var local_dir: Vector3 = global_transform.basis.inverse() * shot
|
||||||
|
mesh.position = to_local(origin)
|
||||||
|
mesh.basis = Fx.basis_y(local_dir)
|
||||||
|
mesh.scale = Vector3(1, randf_range(1.5, 2.3), 1)
|
||||||
|
mesh.visible = true
|
||||||
|
_tracers.append({"mesh": mesh, "dir": local_dir, "life": randf_range(0.12, 0.2), "speed": randf_range(90.0, 140.0)})
|
||||||
|
|
||||||
|
func _take_tracer() -> MeshInstance3D:
|
||||||
|
if _tracer_pool.size() > 0:
|
||||||
|
return _tracer_pool.pop_back()
|
||||||
|
var m := MeshInstance3D.new()
|
||||||
|
m.mesh = _tracer_mesh
|
||||||
|
m.material_override = _tracer_mat
|
||||||
|
m.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
|
m.visible = false
|
||||||
|
add_child(m)
|
||||||
|
return m
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://d2ecdbay4q31q
|
||||||
Binary file not shown.
@@ -0,0 +1,45 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://bc0c4fwo6oom2"
|
||||||
|
path="res://.godot/imported/hongqi.glb-60257baeac74884c31f76642ce55bf18.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://hongqi.glb"
|
||||||
|
dest_files=["res://.godot/imported/hongqi.glb-60257baeac74884c31f76642ce55bf18.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/root_script=null
|
||||||
|
mesh_library/use_node_names_as_mesh_names=false
|
||||||
|
array_mesh/deduplicate_surfaces=true
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
nodes/import_as_skeleton_bones=false
|
||||||
|
nodes/use_name_suffixes=true
|
||||||
|
nodes/use_node_type_suffixes=true
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=1
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=false
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
animation/import_rest_as_RESET=false
|
||||||
|
import_script/path=""
|
||||||
|
materials/extract=0
|
||||||
|
materials/extract_format=0
|
||||||
|
materials/extract_path=""
|
||||||
|
_subresources={}
|
||||||
|
gltf/naming_version=2
|
||||||
|
gltf/embedded_image_handling=1
|
||||||
|
gltf/texture_map_mode=1
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user