Files
Ransom-s-wake/game/presentation/cockpit/cockpit_mesh_factory.gd
OlivierandClaude Opus 5.5 2c1ea50aba
Quality Gate / Quality Gate (pull_request) Successful in 7m6s
feat(cockpit): replace procedural cockpit with twelve scripted models
Instance twelve Blender-scripted glTF models in the cockpit scene and
rebuild the cabin around them: gauge panel, sonar, switch panel, alert
box, speaker, porthole, hull wall, caged lamps, gas bottle, handrails
and side consoles. Presentation only; no domain, application or world
rule changes beyond enabling glow in the test environment.

- Needles, alert lamps and sonar screen are driven by the existing
  snapshots; the alert box now carries SYS/ATTN/ALRM.
- Depth dial reads percent of certified depth.
- Sonar display restyled as a turquoise holographic relief with a
  shader grid and height scale; SubViewport matches the glass (960x604).
- Cabin lighting retuned: warm pooled lamp light, cool porthole rim,
  lamp shadows, dial backlight.
- Procedural stand-ins removed (bulkhead, tanks, consoles, levers,
  indicator columns, placeholder pipe run, dial face PNGs).
- Tests adapted where nodes disappeared; one assertion removed
  (pipe_segment_count) until the pipe kit is placed.
- Model .glb files are committed without LFS; textures Godot extracts
  next to them at import are ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:46:46 +02:00

114 lines
2.9 KiB
GDScript

class_name CockpitMeshFactory
extends RefCounted
static func add_box(
node_name: String,
parent: Node3D,
position: Vector3,
size: Vector3,
material: Material,
rotation_degrees: Vector3 = Vector3.ZERO
) -> MeshInstance3D:
var instance := MeshInstance3D.new()
instance.name = node_name
var mesh := BoxMesh.new()
mesh.size = size
instance.mesh = mesh
instance.material_override = material
instance.position = position
instance.rotation_degrees = rotation_degrees
parent.add_child(instance)
return instance
static func add_cylinder(
node_name: String,
parent: Node3D,
position: Vector3,
radius: float,
height: float,
material: Material,
rotation_degrees: Vector3 = Vector3.ZERO
) -> MeshInstance3D:
var instance := MeshInstance3D.new()
instance.name = node_name
var mesh := CylinderMesh.new()
mesh.top_radius = radius
mesh.bottom_radius = radius
mesh.height = height
mesh.radial_segments = 16
instance.mesh = mesh
instance.material_override = material
instance.position = position
instance.rotation_degrees = rotation_degrees
parent.add_child(instance)
return instance
static func add_cylinder_between(
node_name: String,
parent: Node3D,
start: Vector3,
end: Vector3,
radius: float,
material: Material
) -> MeshInstance3D:
var direction := end - start
var instance := add_cylinder(
node_name,
parent,
(start + end) * 0.5,
radius,
direction.length(),
material
)
if not direction.is_zero_approx():
instance.quaternion = Quaternion(Vector3.UP, direction.normalized())
return instance
static func add_sphere(
node_name: String,
parent: Node3D,
position: Vector3,
radius: float,
material: Material
) -> MeshInstance3D:
var instance := MeshInstance3D.new()
instance.name = node_name
var mesh := SphereMesh.new()
mesh.radius = radius
mesh.height = radius * 2.0
mesh.radial_segments = 16
mesh.rings = 8
instance.mesh = mesh
instance.material_override = material
instance.position = position
parent.add_child(instance)
return instance
## Gives an imported mesh instance its own copy of its first surface material,
## so driving a property (emission) never edits the resource shared by the model.
static func own_surface_material(instance: MeshInstance3D) -> BaseMaterial3D:
if instance == null or instance.mesh == null or instance.mesh.get_surface_count() == 0:
return null
var shared := instance.mesh.surface_get_material(0) as BaseMaterial3D
if shared == null:
return null
var owned := shared.duplicate() as BaseMaterial3D
instance.set_surface_override_material(0, owned)
return owned
## Stops every mesh under `root` (root included) from casting shadows.
static func disable_shadow_casting(root: Node) -> void:
if root == null:
return
var meshes: Array[Node] = root.find_children("*", "MeshInstance3D", true, false)
meshes.append(root)
for node: Node in meshes:
if node is MeshInstance3D:
(node as MeshInstance3D).cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF