Add realistic cockpit surface textures
This commit is contained in:
1 parent
5455ae7928
commit
0b79bf9b82
8 files changed
+160
-23
No files matched your search
@@ -807,6 +807,11 @@ jauges analogiques fonctionnelles, des matériaux métalliques patinés et un
|
|||||||
éclairage pratique chaud/froid. La lisibilité du hublot et des instruments a
|
éclairage pratique chaud/froid. La lisibilité du hublot et des instruments a
|
||||||
été revue nativement en Forward+ à 1280 × 720.
|
été revue nativement en Forward+ à 1280 × 720.
|
||||||
|
|
||||||
|
Complément du 3 octobre 2026 : le plafond, le boîtier supérieur et les faces de
|
||||||
|
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
|
||||||
|
réduit et protégé par une grille pour conserver des proportions crédibles.
|
||||||
|
|
||||||
- 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.
|
||||||
- Remplacer le cercle Canvas par un hublot 3D occultant avec cadre, joint, verre,
|
- Remplacer le cercle Canvas par un hublot 3D occultant avec cadre, joint, verre,
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Cockpit texture provenance
|
||||||
|
|
||||||
|
These textures were generated specifically for Ransom's Wake on 3 October 2026
|
||||||
|
with OpenAI's built-in image generation tool. No third-party image was used as
|
||||||
|
an input or reference.
|
||||||
|
|
||||||
|
## `naval_painted_steel_albedo.png`
|
||||||
|
|
||||||
|
Prompt summary: seamless, orthographic PBR-style albedo of dark blue-green
|
||||||
|
naval painted steel, with restrained salt-air oxidation, micro-scratches,
|
||||||
|
paint chips and grime; neutral illumination; no borders, rivets, text or
|
||||||
|
watermark.
|
||||||
|
|
||||||
|
## `analog_gauge_face_albedo.png`
|
||||||
|
|
||||||
|
Prompt summary: orthographic vintage naval gauge face in aged ivory enamel,
|
||||||
|
with fine crazing, soot, speckling and a faint printed arc; no bezel, needle,
|
||||||
|
hub, glass, text, numbers, logo or watermark.
|
||||||
|
|
||||||
|
Both source outputs were resized to 1024 × 1024 and stripped of metadata for
|
||||||
|
runtime use. The original generated outputs remain in the local Codex image
|
||||||
|
generation store and are not required by the game.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
@@ -12,7 +12,7 @@ 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), "V")
|
_battery_needle = _add_gauge("BatteryGauge", Vector3(-0.34, 0.48, -1.14), "BAT")
|
||||||
_pressure_needle = _add_gauge("PressureGauge", Vector3(0.34, 0.48, -1.14), "BAR")
|
_pressure_needle = _add_gauge("PressureGauge", Vector3(0.34, 0.48, -1.14), "BAR")
|
||||||
|
|
||||||
|
|
||||||
@@ -70,10 +70,13 @@ func _add_gauge(gauge_name: String, position: Vector3, caption: String) -> Node3
|
|||||||
face_mesh.height = 0.039
|
face_mesh.height = 0.039
|
||||||
face_mesh.radial_segments = 32
|
face_mesh.radial_segments = 32
|
||||||
face.mesh = face_mesh
|
face.mesh = face_mesh
|
||||||
face.material_override = _materials.ivory
|
face.material_override = _materials.gauge_face
|
||||||
face.rotation_degrees.x = 90.0
|
face.rotation_degrees.x = 90.0
|
||||||
face.position.z = 0.003
|
face.position.z = 0.003
|
||||||
gauge.add_child(face)
|
gauge.add_child(face)
|
||||||
|
_add_scale_label(gauge, "0", Vector3(-0.052, -0.018, 0.028))
|
||||||
|
_add_scale_label(gauge, "50", Vector3(0.0, 0.052, 0.028))
|
||||||
|
_add_scale_label(gauge, "100", Vector3(0.052, -0.018, 0.028))
|
||||||
var needle_root := Node3D.new()
|
var needle_root := Node3D.new()
|
||||||
needle_root.name = "Needle"
|
needle_root.name = "Needle"
|
||||||
needle_root.position.z = 0.027
|
needle_root.position.z = 0.027
|
||||||
@@ -81,9 +84,9 @@ func _add_gauge(gauge_name: String, position: Vector3, caption: String) -> Node3
|
|||||||
CockpitMeshFactory.add_box(
|
CockpitMeshFactory.add_box(
|
||||||
"NeedleBlade",
|
"NeedleBlade",
|
||||||
needle_root,
|
needle_root,
|
||||||
Vector3(0.0, 0.033, 0.0),
|
Vector3(0.0, 0.034, 0.0),
|
||||||
Vector3(0.012, 0.064, 0.008),
|
Vector3(0.008, 0.068, 0.006),
|
||||||
_materials.red_emission
|
_materials.needle_paint
|
||||||
)
|
)
|
||||||
CockpitMeshFactory.add_cylinder(
|
CockpitMeshFactory.add_cylinder(
|
||||||
"NeedleHub",
|
"NeedleHub",
|
||||||
@@ -100,11 +103,35 @@ func _add_gauge(gauge_name: String, position: Vector3, caption: String) -> Node3
|
|||||||
label.font_size = 12
|
label.font_size = 12
|
||||||
label.pixel_size = 0.0007
|
label.pixel_size = 0.0007
|
||||||
label.modulate = Color(0.11, 0.12, 0.10)
|
label.modulate = Color(0.11, 0.12, 0.10)
|
||||||
label.position = Vector3(0.0, -0.045, 0.032)
|
label.position = Vector3(0.0, -0.044, 0.032)
|
||||||
gauge.add_child(label)
|
gauge.add_child(label)
|
||||||
|
var glass := MeshInstance3D.new()
|
||||||
|
glass.name = "GaugeGlass"
|
||||||
|
var glass_mesh := CylinderMesh.new()
|
||||||
|
glass_mesh.top_radius = 0.081
|
||||||
|
glass_mesh.bottom_radius = 0.081
|
||||||
|
glass_mesh.height = 0.004
|
||||||
|
glass_mesh.radial_segments = 40
|
||||||
|
glass.mesh = glass_mesh
|
||||||
|
glass.material_override = _materials.gauge_glass
|
||||||
|
glass.rotation_degrees.x = 90.0
|
||||||
|
glass.position.z = 0.036
|
||||||
|
glass.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
|
gauge.add_child(glass)
|
||||||
return needle_root
|
return needle_root
|
||||||
|
|
||||||
|
|
||||||
|
func _add_scale_label(gauge: Node3D, text: String, position: Vector3) -> void:
|
||||||
|
var label := Label3D.new()
|
||||||
|
label.name = "Scale%s" % text
|
||||||
|
label.text = text
|
||||||
|
label.font_size = 9
|
||||||
|
label.pixel_size = 0.00048
|
||||||
|
label.modulate = Color(0.09, 0.085, 0.065)
|
||||||
|
label.position = position
|
||||||
|
gauge.add_child(label)
|
||||||
|
|
||||||
|
|
||||||
func _set_needle_ratio(needle: Node3D, ratio: float) -> void:
|
func _set_needle_ratio(needle: Node3D, ratio: float) -> void:
|
||||||
if needle != null:
|
if needle != null:
|
||||||
needle.rotation_degrees.z = lerpf(125.0, -125.0, clampf(ratio, 0.0, 1.0))
|
needle.rotation_degrees.z = lerpf(125.0, -125.0, clampf(ratio, 0.0, 1.0))
|
||||||
@@ -11,6 +11,7 @@ const CockpitMeshFactory = preload("res://game/presentation/cockpit/cockpit_mesh
|
|||||||
var _materials: CockpitMaterialPalette
|
var _materials: CockpitMaterialPalette
|
||||||
var _painted_steel: Material
|
var _painted_steel: Material
|
||||||
var _dark_steel: Material
|
var _dark_steel: Material
|
||||||
|
var _overhead_steel: Material
|
||||||
var _brass: Material
|
var _brass: Material
|
||||||
var _copper: Material
|
var _copper: Material
|
||||||
var _rubber: Material
|
var _rubber: Material
|
||||||
@@ -65,10 +66,19 @@ func battery_needle_degrees() -> float:
|
|||||||
return 0.0 if _analog_gauges == null else _analog_gauges.battery_needle_degrees()
|
return 0.0 if _analog_gauges == null else _analog_gauges.battery_needle_degrees()
|
||||||
|
|
||||||
|
|
||||||
|
func textures_are_loaded() -> bool:
|
||||||
|
return (
|
||||||
|
_materials != null
|
||||||
|
and _materials.overhead_steel.albedo_texture != null
|
||||||
|
and _materials.gauge_face.albedo_texture != null
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _create_materials() -> void:
|
func _create_materials() -> void:
|
||||||
_materials = CockpitMaterialPalette.new()
|
_materials = CockpitMaterialPalette.new()
|
||||||
_painted_steel = _materials.painted_steel
|
_painted_steel = _materials.painted_steel
|
||||||
_dark_steel = _materials.dark_steel
|
_dark_steel = _materials.dark_steel
|
||||||
|
_overhead_steel = _materials.overhead_steel
|
||||||
_brass = _materials.brass
|
_brass = _materials.brass
|
||||||
_copper = _materials.copper
|
_copper = _materials.copper
|
||||||
_rubber = _materials.rubber
|
_rubber = _materials.rubber
|
||||||
@@ -89,16 +99,21 @@ func _apply_materials_to_existing_cockpit() -> void:
|
|||||||
_apply_material_to_paths(
|
_apply_material_to_paths(
|
||||||
_dark_steel,
|
_dark_steel,
|
||||||
[
|
[
|
||||||
"CabinShell/Ceiling",
|
|
||||||
"CabinShell/RearBulkhead",
|
"CabinShell/RearBulkhead",
|
||||||
"PortholeAssembly/LeftBulkhead",
|
"PortholeAssembly/LeftBulkhead",
|
||||||
"PortholeAssembly/RightBulkhead",
|
"PortholeAssembly/RightBulkhead",
|
||||||
"PortholeAssembly/TopBulkhead",
|
|
||||||
"PortholeAssembly/BottomBulkhead",
|
"PortholeAssembly/BottomBulkhead",
|
||||||
"ThrottleLever",
|
"ThrottleLever",
|
||||||
"VerticalLever",
|
"VerticalLever",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
_apply_material_to_paths(
|
||||||
|
_overhead_steel,
|
||||||
|
[
|
||||||
|
"CabinShell/Ceiling",
|
||||||
|
"PortholeAssembly/TopBulkhead",
|
||||||
|
]
|
||||||
|
)
|
||||||
_apply_material_to_paths(
|
_apply_material_to_paths(
|
||||||
_brass,
|
_brass,
|
||||||
[
|
[
|
||||||
@@ -216,27 +231,37 @@ func _build_overhead_console() -> void:
|
|||||||
CockpitMeshFactory.add_box(
|
CockpitMeshFactory.add_box(
|
||||||
"BreakerHousing",
|
"BreakerHousing",
|
||||||
root,
|
root,
|
||||||
Vector3(0.0, 0.90, -1.08),
|
Vector3(0.0, 0.94, -1.06),
|
||||||
Vector3(0.66, 0.13, 0.27),
|
Vector3(0.30, 0.09, 0.12),
|
||||||
_painted_steel,
|
_overhead_steel,
|
||||||
Vector3(-8.0, 0.0, 0.0)
|
Vector3(-8.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
CockpitMeshFactory.add_box(
|
CockpitMeshFactory.add_box(
|
||||||
"WarmLightDiffuser",
|
"WarmLightDiffuser",
|
||||||
root,
|
root,
|
||||||
Vector3(0.0, 0.835, -0.89),
|
Vector3(0.0, 0.895, -0.985),
|
||||||
Vector3(0.38, 0.018, 0.065),
|
Vector3(0.13, 0.010, 0.026),
|
||||||
_warm_emission,
|
_warm_emission,
|
||||||
Vector3(-8.0, 0.0, 0.0)
|
Vector3(-8.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
for switch_index in 7:
|
for guard_index in 5:
|
||||||
var x := -0.24 + switch_index * 0.08
|
var guard_x := -0.05 + guard_index * 0.025
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"LampGuard",
|
||||||
|
root,
|
||||||
|
Vector3(guard_x, 0.896, -0.968),
|
||||||
|
Vector3(0.005, 0.045, 0.008),
|
||||||
|
_dark_steel,
|
||||||
|
Vector3(-8.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
for switch_index in 5:
|
||||||
|
var x := -0.10 + switch_index * 0.05
|
||||||
CockpitMeshFactory.add_cylinder(
|
CockpitMeshFactory.add_cylinder(
|
||||||
"BreakerSwitch",
|
"BreakerSwitch",
|
||||||
root,
|
root,
|
||||||
Vector3(x, 0.83, -0.88),
|
Vector3(x, 0.965, -0.985),
|
||||||
0.006,
|
0.004,
|
||||||
0.035,
|
0.018,
|
||||||
_rubber,
|
_rubber,
|
||||||
Vector3(90.0, 0.0, 0.0)
|
Vector3(90.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
class_name CockpitMaterialPalette
|
class_name CockpitMaterialPalette
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
|
const NAVAL_STEEL_TEXTURE_PATH := (
|
||||||
|
"res://game/presentation/cockpit/assets/naval_painted_steel_albedo.png"
|
||||||
|
)
|
||||||
|
const GAUGE_FACE_TEXTURE_PATH := (
|
||||||
|
"res://game/presentation/cockpit/assets/analog_gauge_face_albedo.png"
|
||||||
|
)
|
||||||
|
|
||||||
var painted_steel: Material
|
var painted_steel: Material
|
||||||
var dark_steel: Material
|
var dark_steel: Material
|
||||||
|
var overhead_steel: StandardMaterial3D
|
||||||
var brass: Material
|
var brass: Material
|
||||||
var copper: Material
|
var copper: Material
|
||||||
var rubber: Material
|
var rubber: Material
|
||||||
var ivory: Material
|
var gauge_face: StandardMaterial3D
|
||||||
|
var gauge_glass: StandardMaterial3D
|
||||||
|
var needle_paint: StandardMaterial3D
|
||||||
var warm_emission: StandardMaterial3D
|
var warm_emission: StandardMaterial3D
|
||||||
var red_emission: StandardMaterial3D
|
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
@@ -26,6 +35,13 @@ func _init() -> void:
|
|||||||
0.44,
|
0.44,
|
||||||
0.08
|
0.08
|
||||||
)
|
)
|
||||||
|
overhead_steel = _textured_material(
|
||||||
|
_load_texture(NAVAL_STEEL_TEXTURE_PATH),
|
||||||
|
Color(0.64, 0.69, 0.68),
|
||||||
|
0.64,
|
||||||
|
0.56,
|
||||||
|
Vector3(1.6, 1.6, 1.6)
|
||||||
|
)
|
||||||
brass = _weathered_material(
|
brass = _weathered_material(
|
||||||
Color(0.28, 0.18, 0.045),
|
Color(0.28, 0.18, 0.045),
|
||||||
Color(0.08, 0.16, 0.13),
|
Color(0.08, 0.16, 0.13),
|
||||||
@@ -41,9 +57,15 @@ func _init() -> void:
|
|||||||
0.12
|
0.12
|
||||||
)
|
)
|
||||||
rubber = _plain_material(Color(0.012, 0.018, 0.018), 0.0, 0.92)
|
rubber = _plain_material(Color(0.012, 0.018, 0.018), 0.0, 0.92)
|
||||||
ivory = _plain_material(Color(0.72, 0.69, 0.56), 0.05, 0.63)
|
gauge_face = _textured_material(
|
||||||
|
_load_texture(GAUGE_FACE_TEXTURE_PATH),
|
||||||
|
Color(0.78, 0.74, 0.64),
|
||||||
|
0.02,
|
||||||
|
0.72
|
||||||
|
)
|
||||||
|
gauge_glass = _glass_material()
|
||||||
|
needle_paint = _plain_material(Color(0.68, 0.055, 0.022), 0.12, 0.44)
|
||||||
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)
|
||||||
red_emission = _emissive_material(Color(0.92, 0.055, 0.025), 3.6)
|
|
||||||
|
|
||||||
|
|
||||||
func _weathered_material(
|
func _weathered_material(
|
||||||
@@ -110,6 +132,41 @@ func _plain_material(
|
|||||||
return material
|
return material
|
||||||
|
|
||||||
|
|
||||||
|
func _textured_material(
|
||||||
|
texture: Texture2D,
|
||||||
|
tint: Color,
|
||||||
|
metallic: float,
|
||||||
|
roughness: float,
|
||||||
|
uv_scale: Vector3 = Vector3.ONE
|
||||||
|
) -> StandardMaterial3D:
|
||||||
|
var material := StandardMaterial3D.new()
|
||||||
|
material.albedo_color = tint
|
||||||
|
material.albedo_texture = texture
|
||||||
|
material.metallic = metallic
|
||||||
|
material.roughness = roughness
|
||||||
|
material.uv1_scale = uv_scale
|
||||||
|
material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC
|
||||||
|
return material
|
||||||
|
|
||||||
|
|
||||||
|
func _load_texture(resource_path: String) -> ImageTexture:
|
||||||
|
var image := Image.load_from_file(resource_path)
|
||||||
|
if image == null or image.is_empty():
|
||||||
|
push_error("Unable to load cockpit texture: %s" % resource_path)
|
||||||
|
return null
|
||||||
|
return ImageTexture.create_from_image(image)
|
||||||
|
|
||||||
|
|
||||||
|
func _glass_material() -> StandardMaterial3D:
|
||||||
|
var material := StandardMaterial3D.new()
|
||||||
|
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
material.albedo_color = Color(0.42, 0.68, 0.66, 0.13)
|
||||||
|
material.metallic = 0.12
|
||||||
|
material.roughness = 0.08
|
||||||
|
material.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||||
|
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 := 55
|
const TEST_COUNT := 56
|
||||||
|
|
||||||
var _failures: Array[String] = []
|
var _failures: Array[String] = []
|
||||||
|
|
||||||
@@ -111,6 +111,7 @@ func _test_vehicle_slice(composition: VerticalSliceRoot) -> void:
|
|||||||
_expect(detailing.pressure_hull_rib_count() >= 6, "cockpit exposes reinforced pressure-hull structure")
|
_expect(detailing.pressure_hull_rib_count() >= 6, "cockpit exposes reinforced pressure-hull structure")
|
||||||
_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("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")
|
||||||
|
|||||||
Reference in new issue
Block a user