# Recovery integration API All scene types below are exported from `hexwork_render::scene`. ```rust pub enum SemanticRole { Player, Actor, Threat, Npc, Item, Connector, Interaction } pub struct EntityDescriptor { pub id: u64, pub anchor: [f32; 3], // centered feet/access-cell XY, unchanged Z pub height: f32, pub glyph: char, pub role: SemanticRole, pub local_light: u8, } pub struct SceneBounds { pub min: [i32;3], pub max: [i32;3] } // inclusive bounds, matching worldgen Bounds/Session TacticalContext pub struct RenderScratch { /* retained private buffers */ } // RenderScratch: Default + Debug; half_samples() and cell_roles() accessors pub fn render_scene( grid: &hexwork_core::voxel::ChunkedGrid, view: &hexwork_render::camera::View, palette: &hexwork_render::palette::Palette, framebuffer: &mut hexwork_render::FrameBuffer, lights: &[(i32,i32,i32,u8)], entities: &[EntityDescriptor], bounds: &SceneBounds, scratch: &mut RenderScratch, ); pub fn entity_eligible(grid: &ChunkedGrid, player_eye: [f32;3], entity: &EntityDescriptor, bounds: &SceneBounds) -> bool; pub fn eligibility_samples(entity: &EntityDescriptor) -> [[f32;3];2]; pub fn sample_visible(grid: &ChunkedGrid, eye: [f32;3], point: [f32;3], bounds: &SceneBounds) -> bool; // Pair positive light at point with sample_visible for that SAME point. pub fn ascii_scene(fb: &mut FrameBuffer, scratch: &RenderScratch); pub fn semantic_color(role: SemanticRole, local_light: u8, palette: &Palette) -> [u8;3]; pub fn semantic_ascii(role: SemanticRole, glyph: char) -> char; pub fn edge_join(mask: u8) -> char; // N=1,E=2,S=4,W=8 ``` Actor positions are occupancy coordinates (Session rounds to cells). Apply `camera::voxel_center([f32;3]) -> [f32;3]` once: XY +0.5, Z unchanged. Camera helper positions and descriptor anchors are already centered. `camera::chase_orbit_elevated(actor_pos,yaw,elevation) -> View` and `camera::collision_safe_chase(grid,actor_pos,yaw,elevation) -> View` use centered positions. Eligibility samples: actors/player torso min(height/2,0.9) and head min(max(height-0.1,0),1.6), item +0.15, connector/interaction +0.5. Helper tests either sample LOS, positive descriptor light, active bounds and 48m player-eye range; active-location membership remains caller-owned. Descriptor light must represent eligible locally lit samples, not camera light. For exact per-sample correspondence, Entry should test each returned sample's light and LOS together. Display frustum is never part of eligibility. Production render is perspective only; existing tactical_map remains separate. FP excludes Player role. ANSI exports: ```rust pub enum ColorMode { TrueColor, Ansi256, NoColor } pub fn encode_full_mode(fb: &FrameBuffer, out: &mut String, mode: ColorMode); pub fn encode_diff_mode(prev: &FrameBuffer, next: &FrameBuffer, out: &mut String, mode: ColorMode); ``` Modes live in `hexwork_render::ansi`. Force full on mode changes. Run `ascii_scene` rather than a glyph-density conversion that discards semantic punctuation. No-color preserves glyphs and emits explicit bold/normal plus final reset.