Integrate gauges into cockpit dashboard

This commit is contained in:
chindit committed 2026-10-03 09:40:36 +02:00
1 parent 0b79bf9b82
commit 53288b48c8
7 files changed
+213 -17

No files matched your search

+1 -1
View File
@@ -20,7 +20,7 @@ production sous `game/` n'en dépend.
télémétrie typés. télémétrie typés.
- Habitacle 3D solidaire de la coque : hublot occultant, consoles, commandes, - Habitacle 3D solidaire de la coque : hublot occultant, consoles, commandes,
éclairage, structure de coque renforcée, conduites, jauges analogiques et éclairage, structure de coque renforcée, conduites, jauges analogiques et
emplacement réservé au sonar. tableau d'instruments structuré, et emplacement réservé au sonar.
- Instruments physiques à valeurs réelles ; le HUD reste réservé aux alertes, - Instruments physiques à valeurs réelles ; le HUD reste réservé aux alertes,
à l'éjection et aux états terminaux. à l'éjection et aux états terminaux.
- Pause qui suspend les ticks, confirmations de sortie et premier remappage avec - Pause qui suspend les ticks, confirmations de sortie et premier remappage avec
+3
View File
@@ -811,6 +811,9 @@ Complément du 3 octobre 2026 : le plafond, le boîtier supérieur et les faces
jauges utilisent des textures bitmap dédiées. Les cadrans reçoivent en plus des jauges utilisent des textures bitmap dédiées. Les cadrans reçoivent en plus des
graduations, une aiguille peinte et une vitre distincte ; le plafonnier est graduations, une aiguille peinte et une vitre distincte ; le plafonnier est
réduit et protégé par une grille pour conserver des proportions crédibles. réduit et protégé par une grille pour conserver des proportions crédibles.
Les jauges de charge batterie et de pression coque sont regroupées dans un
binnacle solidaire du tableau de bord, hors du hublot. La façade du tableau est
structurée par des rails, séparateurs, vis et banques de commutateurs physiques.
- Construire une coque intérieure 3D fixe par rapport au véhicule, indépendante - Construire une coque intérieure 3D fixe par rapport au véhicule, indépendante
du regard de la caméra. du regard de la caméra.
@@ -12,8 +12,19 @@ var _pressure_needle: Node3D
func _init(materials: CockpitMaterialPalette) -> void: func _init(materials: CockpitMaterialPalette) -> void:
_materials = materials _materials = materials
name = "AnalogGauges" name = "AnalogGauges"
_battery_needle = _add_gauge("BatteryGauge", Vector3(-0.34, 0.48, -1.14), "BAT") _build_binnacle()
_pressure_needle = _add_gauge("PressureGauge", Vector3(0.34, 0.48, -1.14), "BAR") _battery_needle = _add_gauge(
"BatteryGauge",
Vector3(-0.095, 0.315, 0.145),
"%"
)
_pressure_needle = _add_gauge(
"PressureGauge",
Vector3(0.095, 0.315, 0.145),
"BAR"
)
_add_binnacle_label("CHARGE BAT.", Vector3(-0.095, 0.235, 0.160))
_add_binnacle_label("PRESSION COQUE", Vector3(0.095, 0.235, 0.160))
func present(battery_ratio: float, pressure_ratio: float) -> void: func present(battery_ratio: float, pressure_ratio: float) -> void:
@@ -25,11 +36,39 @@ func battery_needle_degrees() -> float:
return 0.0 if _battery_needle == null else _battery_needle.rotation_degrees.z return 0.0 if _battery_needle == null else _battery_needle.rotation_degrees.z
func _build_binnacle() -> void:
CockpitMeshFactory.add_box(
"BinnacleHousing",
self,
Vector3(0.0, 0.290, 0.095),
Vector3(0.40, 0.180, 0.105),
_materials.overhead_steel
)
CockpitMeshFactory.add_box(
"BinnacleFoot",
self,
Vector3(0.0, 0.205, 0.075),
Vector3(0.47, 0.040, 0.14),
_materials.dark_steel
)
func _add_binnacle_label(caption: String, position: Vector3) -> void:
var label := Label3D.new()
label.name = "%sLabel" % caption.to_pascal_case().replace(" ", "")
label.text = caption
label.font_size = 12
label.pixel_size = 0.00055
label.modulate = Color(0.76, 0.72, 0.60)
label.position = position
add_child(label)
func _add_gauge(gauge_name: String, position: Vector3, caption: String) -> Node3D: func _add_gauge(gauge_name: String, position: Vector3, caption: String) -> Node3D:
var gauge := Node3D.new() var gauge := Node3D.new()
gauge.name = gauge_name gauge.name = gauge_name
gauge.position = position gauge.position = position
gauge.scale = Vector3(0.78, 0.78, 0.78) gauge.scale = Vector3(0.62, 0.62, 0.62)
add_child(gauge) add_child(gauge)
CockpitMeshFactory.add_box( CockpitMeshFactory.add_box(
"MountingPlate", "MountingPlate",
@@ -0,0 +1,114 @@
class_name CockpitDashboardDetailing
extends Node3D
const CockpitMaterialPalette = preload(
"res://game/presentation/cockpit/cockpit_material_palette.gd"
)
const CockpitMeshFactory = preload(
"res://game/presentation/cockpit/cockpit_mesh_factory.gd"
)
var _materials: CockpitMaterialPalette
var _control_count := 0
func _init(materials: CockpitMaterialPalette) -> void:
_materials = materials
name = "DashboardDetailing"
_build_structural_frame()
_build_switch_banks()
func control_count() -> int:
return _control_count
func apply_screen_materials(dashboard: Node3D) -> void:
for screen_name: String in ["ResourcesScreen", "NavigationScreen", "IntegrityScreen"]:
var screen := dashboard.get_node_or_null(screen_name) as MeshInstance3D
if screen != null:
screen.material_override = _materials.instrument_screen
func _build_structural_frame() -> void:
CockpitMeshFactory.add_box(
"TopRail",
self,
Vector3(0.0, 0.255, 0.115),
Vector3(1.50, 0.055, 0.055),
_materials.dark_steel
)
CockpitMeshFactory.add_box(
"BottomRail",
self,
Vector3(0.0, -0.205, 0.115),
Vector3(1.50, 0.050, 0.055),
_materials.dark_steel
)
for x: float in [-0.755, 0.755]:
CockpitMeshFactory.add_box(
"SideRail",
self,
Vector3(x, 0.025, 0.115),
Vector3(0.050, 0.46, 0.055),
_materials.dark_steel
)
for x: float in [-0.238, 0.238]:
CockpitMeshFactory.add_box(
"ScreenSeparator",
self,
Vector3(x, 0.015, 0.118),
Vector3(0.038, 0.39, 0.048),
_materials.brass
)
for screw_position: Vector2 in [
Vector2(-0.72, -0.18),
Vector2(-0.72, 0.23),
Vector2(0.72, -0.18),
Vector2(0.72, 0.23),
Vector2(-0.24, -0.18),
Vector2(0.24, -0.18),
]:
CockpitMeshFactory.add_cylinder(
"PanelScrew",
self,
Vector3(screw_position.x, screw_position.y, 0.151),
0.010,
0.010,
_materials.brass,
Vector3(90.0, 0.0, 0.0)
)
func _build_switch_banks() -> void:
_add_switch_bank(-0.59, "AUX")
_add_switch_bank(0.59, "POMPES")
func _add_switch_bank(center_x: float, caption: String) -> void:
CockpitMeshFactory.add_box(
"SwitchPlate",
self,
Vector3(center_x, -0.205, 0.145),
Vector3(0.25, 0.060, 0.020),
_materials.overhead_steel
)
for offset: float in [-0.075, -0.025, 0.025, 0.075]:
CockpitMeshFactory.add_cylinder(
"ToggleSwitch",
self,
Vector3(center_x + offset, -0.195, 0.171),
0.006,
0.030,
_materials.dark_steel,
Vector3(90.0, 0.0, 0.0)
)
_control_count += 1
var label := Label3D.new()
label.name = "%sLabel" % caption.to_pascal_case()
label.text = caption
label.font_size = 10
label.pixel_size = 0.00055
label.modulate = Color(0.72, 0.69, 0.56)
label.position = Vector3(center_x, -0.235, 0.165)
add_child(label)
+24 -12
View File
@@ -5,6 +5,9 @@ const DiveUpdate = preload("res://game/domain/dive/dive_update.gd")
const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd") const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd")
const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd") const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd")
const CockpitAnalogGauges = preload("res://game/presentation/cockpit/cockpit_analog_gauges.gd") const CockpitAnalogGauges = preload("res://game/presentation/cockpit/cockpit_analog_gauges.gd")
const CockpitDashboardDetailing = preload(
"res://game/presentation/cockpit/cockpit_dashboard_detailing.gd"
)
const CockpitMaterialPalette = preload("res://game/presentation/cockpit/cockpit_material_palette.gd") const CockpitMaterialPalette = preload("res://game/presentation/cockpit/cockpit_material_palette.gd")
const CockpitMeshFactory = preload("res://game/presentation/cockpit/cockpit_mesh_factory.gd") const CockpitMeshFactory = preload("res://game/presentation/cockpit/cockpit_mesh_factory.gd")
@@ -14,9 +17,8 @@ var _dark_steel: Material
var _overhead_steel: Material var _overhead_steel: Material
var _brass: Material var _brass: Material
var _copper: Material var _copper: Material
var _rubber: Material
var _warm_emission: StandardMaterial3D
var _analog_gauges: CockpitAnalogGauges var _analog_gauges: CockpitAnalogGauges
var _dashboard_detailing: CockpitDashboardDetailing
var _warm_practical: OmniLight3D var _warm_practical: OmniLight3D
var _emergency_light: OmniLight3D var _emergency_light: OmniLight3D
@@ -28,7 +30,7 @@ func _ready() -> void:
_build_life_support_pipes() _build_life_support_pipes()
_build_overhead_console() _build_overhead_console()
_build_panel_hardware() _build_panel_hardware()
_build_analog_gauges() _build_dashboard_instrumentation()
_build_practical_lights() _build_practical_lights()
@@ -74,6 +76,14 @@ func textures_are_loaded() -> bool:
) )
func dashboard_control_count() -> int:
return 0 if _dashboard_detailing == null else _dashboard_detailing.control_count()
func gauges_are_dashboard_mounted() -> bool:
return _analog_gauges != null and _analog_gauges.get_parent() == get_parent().get_node("Dashboard")
func _create_materials() -> void: func _create_materials() -> void:
_materials = CockpitMaterialPalette.new() _materials = CockpitMaterialPalette.new()
_painted_steel = _materials.painted_steel _painted_steel = _materials.painted_steel
@@ -81,8 +91,6 @@ func _create_materials() -> void:
_overhead_steel = _materials.overhead_steel _overhead_steel = _materials.overhead_steel
_brass = _materials.brass _brass = _materials.brass
_copper = _materials.copper _copper = _materials.copper
_rubber = _materials.rubber
_warm_emission = _materials.warm_emission
func _apply_materials_to_existing_cockpit() -> void: func _apply_materials_to_existing_cockpit() -> void:
@@ -91,7 +99,6 @@ func _apply_materials_to_existing_cockpit() -> void:
[ [
"CabinShell/LeftWall", "CabinShell/LeftWall",
"CabinShell/RightWall", "CabinShell/RightWall",
"Dashboard/MainPanel",
"LeftConsole", "LeftConsole",
"RightConsole", "RightConsole",
] ]
@@ -112,6 +119,7 @@ func _apply_materials_to_existing_cockpit() -> void:
[ [
"CabinShell/Ceiling", "CabinShell/Ceiling",
"PortholeAssembly/TopBulkhead", "PortholeAssembly/TopBulkhead",
"Dashboard/MainPanel",
] ]
) )
_apply_material_to_paths( _apply_material_to_paths(
@@ -125,8 +133,6 @@ func _apply_materials_to_existing_cockpit() -> void:
"VerticalKnob", "VerticalKnob",
] ]
) )
func _apply_material_to_paths(material: Material, paths: Array[String]) -> void: func _apply_material_to_paths(material: Material, paths: Array[String]) -> void:
var cockpit := get_parent() var cockpit := get_parent()
for path: String in paths: for path: String in paths:
@@ -241,7 +247,7 @@ func _build_overhead_console() -> void:
root, root,
Vector3(0.0, 0.895, -0.985), Vector3(0.0, 0.895, -0.985),
Vector3(0.13, 0.010, 0.026), Vector3(0.13, 0.010, 0.026),
_warm_emission, _materials.warm_emission,
Vector3(-8.0, 0.0, 0.0) Vector3(-8.0, 0.0, 0.0)
) )
for guard_index in 5: for guard_index in 5:
@@ -262,7 +268,7 @@ func _build_overhead_console() -> void:
Vector3(x, 0.965, -0.985), Vector3(x, 0.965, -0.985),
0.004, 0.004,
0.018, 0.018,
_rubber, _materials.rubber,
Vector3(90.0, 0.0, 0.0) Vector3(90.0, 0.0, 0.0)
) )
@@ -302,9 +308,15 @@ func _build_panel_hardware() -> void:
root.add_child(label) root.add_child(label)
func _build_analog_gauges() -> void: func _build_dashboard_instrumentation() -> void:
var dashboard := get_parent().get_node_or_null("Dashboard") as Node3D
if dashboard == null:
return
_dashboard_detailing = CockpitDashboardDetailing.new(_materials)
dashboard.add_child(_dashboard_detailing)
_dashboard_detailing.apply_screen_materials(dashboard)
_analog_gauges = CockpitAnalogGauges.new(_materials) _analog_gauges = CockpitAnalogGauges.new(_materials)
add_child(_analog_gauges) dashboard.add_child(_analog_gauges)
func _build_practical_lights() -> void: func _build_practical_lights() -> void:
@@ -17,6 +17,7 @@ var rubber: Material
var gauge_face: StandardMaterial3D var gauge_face: StandardMaterial3D
var gauge_glass: StandardMaterial3D var gauge_glass: StandardMaterial3D
var needle_paint: StandardMaterial3D var needle_paint: StandardMaterial3D
var instrument_screen: ShaderMaterial
var warm_emission: StandardMaterial3D var warm_emission: StandardMaterial3D
@@ -65,6 +66,7 @@ func _init() -> void:
) )
gauge_glass = _glass_material() gauge_glass = _glass_material()
needle_paint = _plain_material(Color(0.68, 0.055, 0.022), 0.12, 0.44) needle_paint = _plain_material(Color(0.68, 0.055, 0.022), 0.12, 0.44)
instrument_screen = _screen_material()
warm_emission = _emissive_material(Color(0.96, 0.52, 0.18), 2.4) warm_emission = _emissive_material(Color(0.96, 0.52, 0.18), 2.4)
@@ -167,6 +169,30 @@ func _glass_material() -> StandardMaterial3D:
return material return material
func _screen_material() -> ShaderMaterial:
var shader := Shader.new()
shader.code = """
shader_type spatial;
render_mode diffuse_burley, specular_schlick_ggx;
void fragment() {
vec2 centered = UV - vec2(0.5);
float vignette = 1.0 - smoothstep(0.28, 0.72, length(centered));
float scanline = 0.94 + 0.06 * sin(UV.y * 720.0);
float edge = mix(0.68, 1.0, vignette);
vec3 phosphor = vec3(0.025, 0.105, 0.11) * edge * scanline;
ALBEDO = phosphor;
EMISSION = phosphor * 0.38;
METALLIC = 0.08;
ROUGHNESS = 0.18;
SPECULAR = 0.82;
}
"""
var material := ShaderMaterial.new()
material.shader = shader
return material
func _emissive_material(color: Color, energy: float) -> StandardMaterial3D: func _emissive_material(color: Color, energy: float) -> StandardMaterial3D:
var material := StandardMaterial3D.new() var material := StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
@@ -16,7 +16,7 @@ const ProductionInputAdapter = preload(
"res://game/infrastructure/input/production_input_adapter.gd" "res://game/infrastructure/input/production_input_adapter.gd"
) )
const TEST_COUNT := 56 const TEST_COUNT := 58
var _failures: Array[String] = [] var _failures: Array[String] = []
@@ -112,6 +112,8 @@ func _test_vehicle_slice(composition: VerticalSliceRoot) -> void:
_expect(detailing.pipe_segment_count() >= 13, "cockpit exposes a complete life-support pipe run") _expect(detailing.pipe_segment_count() >= 13, "cockpit exposes a complete life-support pipe run")
_expect(absf(detailing.battery_needle_degrees()) > 100.0, "analog battery gauge follows the dive snapshot") _expect(absf(detailing.battery_needle_degrees()) > 100.0, "analog battery gauge follows the dive snapshot")
_expect(detailing.textures_are_loaded(), "cockpit loads its overhead and analog gauge textures") _expect(detailing.textures_are_loaded(), "cockpit loads its overhead and analog gauge textures")
_expect(detailing.gauges_are_dashboard_mounted(), "analog gauges are mounted on the dashboard")
_expect(detailing.dashboard_control_count() >= 8, "dashboard exposes physical auxiliary controls")
_expect("kWh" in instruments.resources_text() and "kW" in instruments.resources_text(), "battery instrument has exact units") _expect("kWh" in instruments.resources_text() and "kW" in instruments.resources_text(), "battery instrument has exact units")
_expect("L/min" in instruments.resources_text(), "oxygen instrument has volume and consumption") _expect("L/min" in instruments.resources_text(), "oxygen instrument has volume and consumption")
_expect("bar" in instruments.integrity_text(), "hull instrument has current and certified pressure") _expect("bar" in instruments.integrity_text(), "hull instrument has current and certified pressure")