Quality Gate / Quality Gate (pull_request) Successful in 6m55s
New visual test site under game/world/sites: flat sandy floor at -40 m, calm surface at 0 m, fixed daytime sun ahead of and above the submarine. Light shafts come from a shadow-only surface occluder whose alpha-scissor ripples open only inside a pool a few tens of metres ahead; a subtractive FogVolume cancels the global volumetric fog above the water, and an unshaded ceiling mesh shows the lit underside of the surface inside that pool. A composed world (sunlit_shallows_world.tscn) mounts the submarine, cockpit and camera rig in it, with the projectors kept out of the fog. Cockpit: the porthole glass moves to its own render layer with a rougher material and the porthole fill light excludes that layer, so interior lamps no longer mirror as hard specular points. Quality gate: new integration suite for the site (42 checks), three glass assertions in the foundation suite, and the Input-access architecture rule now matches both path separators so the gate runs on Windows. project.godot raises the volumetric fog froxel grid to 128 so the shafts stay readable. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
85 lines
2.7 KiB
GDScript
85 lines
2.7 KiB
GDScript
class_name CockpitPressureBulkhead
|
|
extends Node3D
|
|
|
|
## Imported pressure hull (end cap, knuckle and barrel in one shell) and the
|
|
## porthole bolted into it. Both models are instanced by `cockpit_interior.tscn`.
|
|
|
|
## The porthole glass lives alone on render layer 2 so interior lights that
|
|
## sit right behind it (the cool porthole fill) can leave it out of their cull
|
|
## mask instead of mirroring themselves as a hard specular point.
|
|
const PORTHOLE_GLASS_RENDER_LAYER_BIT := 1 << 1
|
|
## Imported glass is near-mirror smooth; a little roughness turns the remaining
|
|
## lamp reflections into soft smears rather than pinpoints.
|
|
const PORTHOLE_GLASS_ROUGHNESS := 0.45
|
|
|
|
const CockpitMeshFactory = preload(
|
|
"res://game/presentation/cockpit/cockpit_mesh_factory.gd"
|
|
)
|
|
|
|
const WHITEBOX_NAMES: Array[String] = [
|
|
"LeftBulkhead",
|
|
"RightBulkhead",
|
|
"TopBulkhead",
|
|
"BottomBulkhead",
|
|
]
|
|
|
|
var _hull_shells: Array[Node] = []
|
|
var _porthole_frame: MeshInstance3D
|
|
var _porthole_glass: MeshInstance3D
|
|
|
|
|
|
func _ready() -> void:
|
|
var hull := get_node_or_null("HullWall")
|
|
if hull != null:
|
|
_hull_shells = hull.find_children("*", "MeshInstance3D", true, false)
|
|
_porthole_frame = _find_mesh("Porthole")
|
|
_porthole_glass = _find_mesh("PortholeGlass")
|
|
if _hull_shells.is_empty() or _porthole_frame == null or _porthole_glass == null:
|
|
push_error("Pressure hull or porthole model is missing a node")
|
|
return
|
|
_porthole_glass.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
|
_porthole_glass.layers = PORTHOLE_GLASS_RENDER_LAYER_BIT
|
|
var glass_material := CockpitMeshFactory.own_surface_material(_porthole_glass)
|
|
if glass_material != null:
|
|
glass_material.roughness = PORTHOLE_GLASS_ROUGHNESS
|
|
|
|
|
|
func glass_is_on_dedicated_layer() -> bool:
|
|
return _porthole_glass != null and _porthole_glass.layers == PORTHOLE_GLASS_RENDER_LAYER_BIT
|
|
|
|
|
|
func glass_roughness() -> float:
|
|
if _porthole_glass == null:
|
|
return 0.0
|
|
var material := _porthole_glass.get_active_material(0) as BaseMaterial3D
|
|
return 0.0 if material == null else material.roughness
|
|
|
|
|
|
func panel_count() -> int:
|
|
return _hull_shells.size()
|
|
|
|
|
|
func hull_shell() -> MeshInstance3D:
|
|
return null if _hull_shells.is_empty() else _hull_shells[0] as MeshInstance3D
|
|
|
|
|
|
func porthole_frame() -> MeshInstance3D:
|
|
return _porthole_frame
|
|
|
|
|
|
func porthole_glass() -> MeshInstance3D:
|
|
return _porthole_glass
|
|
|
|
|
|
func rectangular_whitebox_is_hidden() -> bool:
|
|
for node_name: String in WHITEBOX_NAMES:
|
|
var whitebox := get_parent().get_node_or_null(node_name) as MeshInstance3D
|
|
if whitebox != null and whitebox.visible:
|
|
return false
|
|
return true
|
|
|
|
|
|
func _find_mesh(mesh_name: String) -> MeshInstance3D:
|
|
var matches := find_children(mesh_name, "MeshInstance3D", true, false)
|
|
return null if matches.is_empty() else matches[0] as MeshInstance3D
|