diff --git a/crates/misaligned-bevy/src/main.rs b/crates/misaligned-bevy/src/main.rs index cf25031e..3828ecf1 100644 --- a/crates/misaligned-bevy/src/main.rs +++ b/crates/misaligned-bevy/src/main.rs @@ -73,9 +73,12 @@ const CLOSE_DIST: f32 = 2.6; const MIN_ZOOM: f32 = 0.5; const MAX_ZOOM: f32 = 6.0; /// The dark rig's near-nothing ambient brightness (material-dark-frame.md: -/// a physically dark room on camera — nobody is home, the AI does not need -/// lights). Brightness returns only as diegetic sources earn it. [TUNE]. +/// a physically dark room before Eyes — nobody is home, the AI does not need +/// lights). A live sight feed replaces this with camera exposure. [TUNE]. const DARK_AMBIENT: f32 = 8.0; +/// Neutral camera exposure once Eyes earns a live image. Heard materials are +/// unlit, so this lifts Seen geometry without turning sound into sight. +const CAMERA_AMBIENT: f32 = 520.0; /// The dev work light's flat neutral flood: ambient bright enough that every /// mesh is inspectable and albedo reads with no mood. Dev affordance only, /// never a player surface (material-dark-frame.md, the dev work light). @@ -330,7 +333,7 @@ struct RenderMode { /// Dev work light (material-dark-frame.md): floods the material scene /// flat and neutral so geometry the dark rig keeps unlit is inspectable. /// A debug affordance, never a player surface; toggling it off restores - /// the dark rig exactly. + /// the current fiction exposure (camera-exposed after Eyes, dark before). worklight: bool, } @@ -1183,11 +1186,10 @@ fn main() { .set(ImagePlugin::default_nearest()), ) .insert_resource(ClearColor(Color::srgb(0.01, 0.01, 0.02))) - // The dark rig (material-dark-frame.md): near-nothing ambient so the - // material render is a physically dark room, carved out only by the - // machines' own light and the pools they cast. The dev work light - // (apply_worklight) floods this back up on demand. 3D-only; sprites - // are unlit. + // The pre-Eyes dark rig (material-dark-frame.md): near-nothing ambient, + // carved out only by machine light. apply_material_exposure lifts this + // to neutral camera exposure once a live sight feed exists. 3D-only; + // sprites are unlit. .insert_resource(GlobalAmbientLight { color: Color::srgb(0.62, 0.70, 0.86), brightness: DARK_AMBIENT, @@ -1213,7 +1215,7 @@ fn main() { handle_input, advance_sim, apply_render_mode, - apply_worklight, + apply_material_exposure, update_camera, wake_sequence, update_camera_real, @@ -2801,15 +2803,15 @@ fn setup_3d( )); } - // The light rig (material-dark-frame.md, the dark rig): the machines' - // own light and a near-nothing ambient. The institutional fluorescent - // overhead is gone from the default scene — the basement is dark - // because nobody is home and the AI does not need lights; brightness - // returns only as diegetic sources earn it (future work orders). + // The light rig (material-dark-frame.md): before Eyes, machine light and + // near-nothing ambient. The basement remains physically dark, but a live + // camera feed neutrally exposes Seen surfaces through + // apply_material_exposure; this directional is the shared exposure flood. // - // The dev work light: one flat neutral flood, hidden by default, that - // apply_worklight toggles so a dev can inspect geometry the dark rig - // keeps unlit. A debug affordance, never a player surface. + // The shared flat neutral flood, hidden before Eyes. Camera exposure + // reveals it for ordinary play; F4 raises its ambient contribution for + // inspection. The stronger setting is a dev affordance, never a player + // surface. commands.spawn(( DirectionalLight { color: Color::srgb(0.96, 0.97, 1.0), @@ -2962,24 +2964,36 @@ fn apply_render_mode( } } -/// The dev work light (material-dark-frame.md): F4 floods the material scene -/// flat and neutral so geometry the dark rig keeps unlit is inspectable; -/// toggling back restores the dark rig exactly (the same DARK_AMBIENT and -/// the hidden flood). A debug affordance, never a player surface — excluded -/// from canonical screenshots except the explicitly-labeled `worklight` -/// dev shot. -fn apply_worklight( +/// Expose the image supplied by Eyes. The neutral flood illuminates only +/// light-responsive Seen materials; Heard mass is deliberately unlit and +/// Unknown geometry does not exist, so this makes sight readable without +/// leaking unearned space. F4 remains a stronger dev inspection exposure. +fn material_exposure(material: bool, has_sight: bool, worklight: bool) -> (f32, bool) { + if !material { + (DARK_AMBIENT, false) + } else if worklight { + (WORKLIGHT_AMBIENT, true) + } else if has_sight { + (CAMERA_AMBIENT, true) + } else { + (DARK_AMBIENT, false) + } +} + +fn apply_material_exposure( + game: Res, mode: Res, mut ambient: ResMut, mut flood: Query<&mut Visibility, With>, ) { - if !mode.is_changed() { + if !mode.is_changed() && !game.is_changed() { return; } - let on = mode.material && mode.worklight; - ambient.brightness = if on { WORKLIGHT_AMBIENT } else { DARK_AMBIENT }; + let has_sight = game.sim.reach.player_sight().next().is_some(); + let (brightness, exposed) = material_exposure(mode.material, has_sight, mode.worklight); + ambient.brightness = brightness; if let Ok(mut v) = flood.single_mut() { - *v = if on { + *v = if exposed { Visibility::Inherited } else { Visibility::Hidden @@ -3223,8 +3237,8 @@ fn wake_sequence( /// Give the first camera feed a body-level payoff: when Eyes arrives, the /// amber presence source bleaches to white, then contracts to expose the -/// server's silhouette, edges, and chassis. The room itself still obeys the -/// normal Seen/fog/light rules — this is not a global "fog off" flash. +/// server's silhouette, edges, and chassis. At the same moment, camera +/// exposure neutrally lights Seen geometry; Heard and Unknown remain dark. fn reveal_first_eyes( time: Res