From 2b68e2e579d3bbecee1daaf92dce7e9301ed606c Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Wed, 28 Jan 2026 10:41:55 -0500 Subject: [PATCH] Update rendering documentation to reflect relative rendering implementation --- docs/planning/local_rendering_frame.md | 596 ------------------------- docs/rendering.md | 60 ++- 2 files changed, 46 insertions(+), 610 deletions(-) delete mode 100644 docs/planning/local_rendering_frame.md diff --git a/docs/planning/local_rendering_frame.md b/docs/planning/local_rendering_frame.md deleted file mode 100644 index e6ad658..0000000 --- a/docs/planning/local_rendering_frame.md +++ /dev/null @@ -1,596 +0,0 @@ -# Local Rendering Frame Implementation Plan - -## Overview -Implement local coordinate rendering when following bodies, using SOI-based scaling for improved orbit visibility. Refactor camera update logic to remove clunky state tracking. - -## Problem Statement - -### Current Issues -1. **Float Precision Loss**: LEO orbit (6.8e6 m) scaled by 1e-9 → 0.0068 render units (5% of Earth radius) -2. **Camera State Clunkiness**: `was_following_body` and `previous_selected_body` require frame-to-frame comparison -3. **Code Duplication**: Follow logic repeated 4x, rotation logic duplicated 2x, zoom logic duplicated 2x - -### Root Cause -- Global scale factor (1e-9) optimized for solar system view -- When zoomed in on local orbits, precision is insufficient for smooth visualization - ---- - -## Design Goals - -1. **Precision**: Use local coordinates with larger scale factor when following bodies -2. **Clarity**: Maintain visual balance between bodies and their local orbits -3. **Cleanliness**: Remove redundant state tracking and code duplication -4. **Extensibility**: Enable future nested local frames if needed - ---- - -## Phase 0: Refactor `update_camera()` - -### Current Problems -1. State tracking clunkiness: `was_following_body` and `previous_selected_body` require frame-to-frame comparison -2. Code duplication: Follow logic repeated 4x, rotation logic duplicated 2x, zoom logic duplicated 2x -3. Offset updates scattered: Camera offset updated in 6 places with identical logic - -### Refactoring Strategy - -**Simplified State Management:** -- Remove `was_following_body` from `RenderState` -- Remove `previous_selected_body` from `RenderState` -- Add `last_target_index` to `RenderState` (single int tracking body or spacecraft) -- Add `camera_mode` enum: `CAMERA_FREE`, `CAMERA_FOLLOW_BODY`, `CAMERA_FOLLOW_CRAFT` - -**New Helper Functions:** -1. `detect_camera_mode()` → returns camera mode from render state -2. `get_camera_target()` → returns target position Vector3 -3. `has_target_changed()` → checks last_target_index vs current -4. `update_camera_target()` → handles all follow logic -5. `rotate_camera_orbitally()` → abstracts KEY_LEFT/RIGHT -6. `zoom_camera()` → abstracts KEY_UP/DOWN -7. `update_follow_offset()` → extracts repeated offset update logic -8. `update_last_target()` → updates tracking for next frame - -**Refactored Structure:** -```cpp -void update_camera(RenderState* render_state, SimulationState* sim) { - // 1. Update frame mode (for later phases) - render_state->camera_mode = detect_camera_mode(render_state, sim); - - // 2. Handle following (handles both global and local frames) - if (render_state->camera_follow_body) { - update_camera_target(render_state, sim); - } - - // 3. Handle rotation - if (IsKeyDown(KEY_LEFT)) { - rotate_camera_orbitally(render_state, angle_speed); - } else if (IsKeyDown(KEY_RIGHT)) { - rotate_camera_orbitally(render_state, -angle_speed); - } - - // 4. Handle zoom - if (IsKeyDown(KEY_UP)) { - zoom_camera(render_state, -2.0f); - } else if (IsKeyDown(KEY_DOWN)) { - zoom_camera(render_state, 2.0f); - } - - // 5. Update last target for next frame - update_last_target(render_state); -} -``` - ---- - -## Phase 1: Infrastructure Setup - -### 1.1 Add Rendering Mode Enum and Fields - -**src/renderer.h:** -```cpp -enum CameraMode { - CAMERA_FREE, - CAMERA_FOLLOW_BODY, - CAMERA_FOLLOW_CRAFT -}; - -enum RenderFrameMode { - RENDER_FRAME_GLOBAL, - RENDER_FRAME_LOCAL -}; - -struct RenderState { - // ... existing fields ... - CameraMode camera_mode; - RenderFrameMode frame_mode; - int last_target_index; // Tracks body or craft index (negative = spacecraft) - int local_frame_parent_index; // Body index for local frame -}; -``` - -### 1.2 Add Detection and Scale Functions - -**New functions in src/renderer.cpp:** - -```cpp -static CameraMode detect_camera_mode(RenderState* render_state, SimulationState* sim) { - if (!render_state->camera_follow_body) return CAMERA_FREE; - if (render_state->selected_body_index >= 0) return CAMERA_FOLLOW_BODY; - if (render_state->selected_craft_index >= 0) return CAMERA_FOLLOW_CRAFT; - return CAMERA_FREE; -} - -static RenderFrameMode detect_render_frame_mode(CameraMode mode, int body_index, SimulationState* sim) { - if (mode != CAMERA_FOLLOW_BODY) return RENDER_FRAME_GLOBAL; - if (body_index < 0 || body_index >= sim->body_count) return RENDER_FRAME_GLOBAL; - if (sim->bodies[body_index].parent_index < 0) return RENDER_FRAME_GLOBAL; // Root body (Sun) - return RENDER_FRAME_LOCAL; -} - -// Target: SOI occupies ~100 units in render space for visibility -static double get_local_frame_scale(SimulationState* sim, int body_index) { - CelestialBody* body = &sim->bodies[body_index]; - double soi_radius = body->soi_radius; - - // Target: 1.0 × SOI radius → 100.0 render units - return 100.0 / soi_radius; -} -``` - ---- - -## Phase 2: Local Frame Coordinate Transformation - -### 2.1 Add Local Transform Function - -**src/renderer.cpp:** -```cpp -static Vector3 sim_to_render_local(Vec3 local_pos, double local_scale) { - return (Vector3){ - (float)(local_pos.x * local_scale), - (float)(local_pos.z * local_scale), - (float)(-local_pos.y * local_scale) - }; -} -``` - -### 2.2 Modify Orbit Rendering for Local Frame - -**Add new function:** -```cpp -static void render_orbit_local(Vec3 local_position, Vec3 local_velocity, - double parent_mass, Color orbit_color, - double local_scale, RenderState* render_state) { - Vec3 r_vec = local_position; - double r = vec3_magnitude(r_vec); - double v = vec3_magnitude(local_velocity); - - if (r < 1.0) return; - - // Calculate orbit parameters (same as global version) - double mu = G * parent_mass; - double specific_energy = (v * v) / 2.0 - mu / r; - double v_squared = v * v; - double r_dot_v = vec3_dot(r_vec, local_velocity); - Vec3 e_vec = { - (v_squared - mu / r) * r_vec.x - r_dot_v * local_velocity.x, - (v_squared - mu / r) * r_vec.y - r_dot_v * local_velocity.y, - (v_squared - mu / r) * r_vec.z - r_dot_v * local_velocity.z - }; - double e = vec3_magnitude(e_vec) / mu; - - OrbitalBasis basis = calculate_orbital_basis(r_vec, local_velocity, e_vec); - - // Render with local scale and origin at (0,0,0) - if (e < 0.98) { - double a = -mu / (2.0 * specific_energy); - if (a <= 0.0) return; - render_elliptical_orbit_local(a, e, basis, local_scale, render_state, orbit_color); - } else if (e > 1.02) { - double a = mu / (2.0 * (-specific_energy)); - double p = a * (1.0 - e * e); - if (p <= 0.0) return; - render_hyperbolic_orbit_local(p, e, basis, local_scale, render_state, orbit_color); - } else { - Vec3 h_vec = vec3_cross(r_vec, local_velocity); - double h_squared = vec3_dot(h_vec, h_vec); - double p = h_squared / mu; - if (p <= 0.0) return; - render_parabolic_orbit_local(p, basis, local_scale, render_state, orbit_color); - } -} -``` - -**Add local orbit drawing functions:** -```cpp -static void draw_orbit_segment_local(double x1, double y1, double x2, double y2, - OrbitalBasis basis, double local_scale, - RenderState* render_state, Color color) { - // Convert from orbital plane to render coords (no parent offset) - Vec3 p1_local = { - basis.periapsis_dir.x * x1 + basis.q_vec.x * y1, - basis.periapsis_dir.y * x1 + basis.q_vec.y * y1, - basis.periapsis_dir.z * x1 + basis.q_vec.z * y1 - }; - Vec3 p2_local = { - basis.periapsis_dir.x * x2 + basis.q_vec.x * y2, - basis.periapsis_dir.y * x2 + basis.q_vec.y * y2, - basis.periapsis_dir.z * x2 + basis.q_vec.z * y2 - }; - - Vector3 p1 = sim_to_render_local(p1_local, local_scale); - Vector3 p2 = sim_to_render_local(p2_local, local_scale); - - DrawLine3D(p1, p2, color); -} - -static void render_elliptical_orbit_local(double a, double e, OrbitalBasis basis, - double local_scale, RenderState* render_state, Color color) { - double b = a * sqrt(1.0 - e * e); - double c = a * e; - int segments = 100; - - for (int i = 0; i < segments; i++) { - float theta1 = (float)i / segments * 2.0f * PI; - float theta2 = (float)(i + 1) / segments * 2.0f * PI; - double x1 = a * cos(theta1) - c; - double y1 = b * sin(theta1); - double x2 = a * cos(theta2) - c; - double y2 = b * sin(theta2); - draw_orbit_segment_local(x1, y1, x2, y2, basis, local_scale, render_state, color); - } -} -``` - -(Also add `render_hyperbolic_orbit_local()` and `render_parabolic_orbit_local()` - similar to existing global versions but using `draw_orbit_segment_local()`) - ---- - -## Phase 3: Local Frame Body Rendering - -### 3.1 Add Local Body Rendering Function - -**src/renderer.cpp:** -```cpp -static void render_body_local(CelestialBody* body, int local_parent_index, - double local_scale, RenderState* render_state) { - Vector3 position; - - if (body->parent_index == local_parent_index) { - // Direct child of followed body - use local position - position = sim_to_render_local(body->local_position, local_scale); - } else if (body->parent_index < 0) { - // Root body (Sun) - at origin in local frame - position = (Vector3){0.0f, 0.0f, 0.0f}; - } else { - // Other bodies - TODO: decide whether to render or skip - // For now: skip (will be very far off-screen) - return; - } - - float radius = scale_radius(body->radius, render_state->size_scale); - Color color = { - (unsigned char)(body->color[0] * 255), - (unsigned char)(body->color[1] * 255), - (unsigned char)(body->color[2] * 255), - 255 - }; - - DrawSphereWires(position, radius, 16, 16, color); -} -``` - ---- - -## Phase 4: Integration - Render Simulation Router - -### 4.1 Modify `render_simulation()` to Route by Frame Mode - -**src/renderer.cpp:** -```cpp -void render_simulation(SimulationState* sim, RenderState* render_state) { - // Update rendering mode - render_state->camera_mode = detect_camera_mode(render_state, sim); - render_state->frame_mode = detect_render_frame_mode( - render_state->camera_mode, - render_state->selected_body_index, - sim - ); - - BeginMode3D(render_state->camera); - - // Draw reference grid (in both modes) - for (int i = -50; i <= 50; i++) { - // ... existing grid code ... - } - - // Route to appropriate rendering function - if (render_state->frame_mode == RENDER_FRAME_LOCAL) { - render_simulation_local(sim, render_state); - } else { - render_simulation_global(sim, render_state); // existing implementation - } - - EndMode3D(); - - // Spacecraft and maneuvers (screen-space, shared between modes) - for (int i = 0; i < sim->craft_count; i++) { - render_spacecraft_screen_space(&sim->spacecraft[i], render_state); - } - // ... maneuver markers ... -} - -static void render_simulation_local(SimulationState* sim, RenderState* render_state) { - int parent_index = render_state->local_frame_parent_index; - double local_scale = get_local_frame_scale(sim, parent_index); - - // Render followed body at origin - CelestialBody* parent = &sim->bodies[parent_index]; - Vector3 origin = (Vector3){0.0f, 0.0f, 0.0f}; - float parent_radius = scale_radius(parent->radius, render_state->size_scale); - Color parent_color = { - (unsigned char)(parent->color[0] * 255), - (unsigned char)(parent->color[1] * 255), - (unsigned char)(parent->color[2] * 255), - 255 - }; - DrawSphereWires(origin, parent_radius, 16, 16, parent_color); - - // Render orbits of children (not followed body's orbit) - for (int i = 0; i < sim->body_count; i++) { - CelestialBody* body = &sim->bodies[i]; - if (body->parent_index == parent_index) { - render_orbit_local(body->local_position, body->local_velocity, - parent->mass, get_body_orbit_color(body), - local_scale, render_state); - } - } - - // Render spacecraft orbits - for (int i = 0; i < sim->craft_count; i++) { - Spacecraft* craft = &sim->spacecraft[i]; - if (craft->parent_index == parent_index) { - render_orbit_local(craft->local_position, craft->local_velocity, - parent->mass, (Color){0, 255, 255, 128}, - local_scale, render_state); - } - } - - // Render children bodies - for (int i = 0; i < sim->body_count; i++) { - if (i != parent_index) { - render_body_local(&sim->bodies[i], parent_index, local_scale, render_state); - } - } -} -``` - -**Extract current `render_simulation()` into `render_simulation_global()`** - ---- - -## Phase 5: Camera Integration - -### 5.1 Modify `update_camera()` for Frame Transitions - -**Add to `update_camera()` helper functions (Phase 0 refactor):** - -```cpp -static void update_camera_frame_mode(RenderState* render_state, SimulationState* sim) { - // Detect if we need to switch frame modes - int new_parent_index = -1; - - if (render_state->camera_mode == CAMERA_FOLLOW_BODY && - render_state->selected_body_index >= 0) { - // Check if following a non-root body - int body_index = render_state->selected_body_index; - if (body_index < sim->body_count && - sim->bodies[body_index].parent_index >= 0) { - new_parent_index = body_index; - } - } - - // Check for mode switch - bool mode_changed = (new_parent_index != render_state->local_frame_parent_index); - - if (mode_changed) { - render_state->local_frame_parent_index = new_parent_index; - render_state->frame_mode = detect_render_frame_mode( - render_state->camera_mode, - render_state->selected_body_index, - sim - ); - - // When switching to local frame: set camera target to origin - if (render_state->frame_mode == RENDER_FRAME_LOCAL) { - render_state->camera.target = (Vector3){0.0f, 0.0f, 0.0f}; - } - // When switching to global frame: target set by update_camera_target() - } -} -``` - -**Integrate into refactored `update_camera()`:** -```cpp -void update_camera(RenderState* render_state, SimulationState* sim) { - // 0. Update frame mode - render_state->camera_mode = detect_camera_mode(render_state, sim); - update_camera_frame_mode(render_state, sim); - - // 1. Handle following (handles both global and local frames) - if (render_state->camera_follow_body) { - update_camera_target(render_state, sim); - } - - // 2. Handle rotation - // ... existing rotation logic ... - - // 3. Handle zoom - // ... existing zoom logic ... -} -``` - -**Update `update_camera_target()` to handle local frame:** -```cpp -static void update_camera_target(RenderState* render_state, SimulationState* sim) { - Vector3 target_pos; - bool has_target = false; - - if (render_state->frame_mode == RENDER_FRAME_LOCAL) { - // Local frame: target is always origin - target_pos = (Vector3){0.0f, 0.0f, 0.0f}; - has_target = true; - } else { - // Global frame: get target from body or spacecraft - if (render_state->selected_body_index >= 0 && - render_state->selected_body_index < sim->body_count) { - CelestialBody* body = &sim->bodies[render_state->selected_body_index]; - target_pos = sim_to_render(body->global_position, render_state->distance_scale); - has_target = true; - } else if (render_state->selected_craft_index >= 0 && - render_state->selected_craft_index < sim->craft_count) { - Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index]; - target_pos = sim_to_render(craft->global_position, render_state->distance_scale); - has_target = true; - } - } - - if (has_target) { - // Check if target changed - bool target_changed = has_target_changed(render_state); - - if (target_changed) { - // Preserve camera distance by updating offset - Vector3 to_camera = Vector3Subtract(render_state->camera.position, target_pos); - render_state->camera_offset = to_camera; - } - - render_state->camera.target = target_pos; - render_state->camera.position = Vector3Add(target_pos, render_state->camera_offset); - } -} -``` - ---- - -## Phase 6: Testing (After Implementation) - -### 6.1 Manual Testing Checklist -1. Start simulation with Earth selected -2. Verify camera follows Earth in local frame -3. Zoom in to see LEO orbit clearly visible -4. Select Sun from UI → verify switch to global frame -5. Rotate/zoom controls work in both frames -6. Orbits render correctly in both frames -7. Earth's orbit around Sun omitted in local frame -8. Spacecraft billboard moves correctly with simulation - ---- - -## Design Decisions - -1. **Local Frame Scale Factor**: SOI-based scaling targeting ~100 render units - - Defined as constant at top of renderer.cpp - - Allows easy adjustment based on visual feedback - -2. **Frame Levels**: Single-level local frame only - - Following Earth shows Earth + spacecraft at LEO - - TODO: Support nested local frames (viewing Moon while following Earth) - -3. **Followed Body's Orbit**: Omitted in local frame - - Since we're now the reference frame, Earth's orbit around Sun is confusing - -4. **Other Bodies in Local Frame**: Skipped for now (TODO) - - Distant bodies will be very far off-screen - - TODO: Decide whether to render using global coordinates or skip - -5. **Transition Behavior**: Instant switch between frames - - TODO: Add smooth interpolation if jarring - ---- - -## Expected Outcomes - -### LEO Orbit Visibility (400km altitude) -- **Current**: 0.0068 render units (5% of Earth radius) -- **After Local Frame**: ~67 render units (50% of Earth radius) -- **Improvement**: ~1000x more visible - -### Float Precision -- Local frame positions: ~6.8e6 m → 67 render units (scale 1e-7) -- Precision at 67 units: ~0.0001 (1/670000) -- Orbit precision: 67 × 0.0001 = 0.0067 units -- Result: High precision, smooth orbits - -### Code Quality -- Reduced camera duplication by ~60% -- Eliminated clunky state tracking -- Clear separation of global/local rendering -- Extensible for future nested local frames - ---- - -## File Summary - -### New Files -- `docs/planning/local_rendering_frame.md` - This planning document - -### Modified Files - -#### src/renderer.h -- Add `CameraMode` enum -- Add `RenderFrameMode` enum -- Add fields to `RenderState` struct: - - Remove: `was_following_body`, `previous_selected_body` - - Add: `camera_mode`, `frame_mode`, `last_target_index`, `local_frame_parent_index` - -#### src/renderer.cpp -- **Phase 0**: Refactor update_camera() with helper functions: - - Add SOI_SCALE_TARGET define at top - - Implement helper functions: detect_camera_mode, get_camera_target, has_target_changed, - update_camera_target, rotate_camera_orbitally, zoom_camera, update_follow_offset, update_last_target - - Refactor update_camera() to use helpers - -- **Phase 1**: Add detection/scale functions: - - Add detect_render_frame_mode() - - Add get_local_frame_scale() - - Add sim_to_render_local() - -- **Phase 2**: Add local coordinate transformation functions: - - Add draw_orbit_segment_local() - - Add render_elliptical_orbit_local() - - Add render_hyperbolic_orbit_local() - - Add render_parabolic_orbit_local() - - Add render_orbit_local() - -- **Phase 3**: Add local body rendering: - - Add render_body_local() - -- **Phase 4**: Modify render_simulation() routing: - - Extract current logic to render_simulation_global() - - Add render_simulation_local() - - Modify render_simulation() to route by frame mode - -- **Phase 5**: Update camera frame mode handling: - - Add update_camera_frame_mode() - - Update update_camera_target() to handle local frame - - Integrate frame mode updates into camera update - -#### src/main.cpp -- Update initialization to remove old state tracking: - - Remove: `was_following_body` initialization - - Remove: `previous_selected_body` initialization - - Add: Initialize new RenderState fields (camera_mode, frame_mode, etc.) - -#### src/ui_renderer.cpp -- Update references to removed state fields if any -- Ensure compatibility with new camera_mode system - ---- - -## TODO Items - -1. **Nested Local Frames**: Support 2-level local frames (viewing Moon while following Earth) -2. **Distant Bodies in Local Frame**: Decide whether to render distant bodies using global coordinates or skip -3. **Smooth Frame Transitions**: Add interpolation when switching between global and local frames -4. **Test Coverage**: Add unit tests for local frame rendering after manual verification diff --git a/docs/rendering.md b/docs/rendering.md index 47ee8ba..32041f2 100644 --- a/docs/rendering.md +++ b/docs/rendering.md @@ -1,7 +1,7 @@ # Rendering System - Technical Reference ## Overview -3D visualization system using raylib for interactive orbital mechanics simulation. Supports logarithmic distance scaling, orbit path rendering, spacecraft tracking, and maneuver planning visualization. +3D visualization system using raylib for interactive orbital mechanics simulation. Supports linear distance scaling, relative rendering with child indicators, orbit path rendering, spacecraft tracking, and maneuver planning visualization. ## Core Data Structure @@ -13,12 +13,11 @@ struct RenderState { double size_scale; // Scale factor for body sizes int selected_body_index; // -1 = no selection int selected_craft_index; // -1 = no selection - int previous_selected_body; // Previous selected body index + int last_target_index; // Tracks body or craft index (negative = spacecraft) int body_list_scroll; // Scroll position for body list int body_list_active; // Active item index in body list - bool camera_follow_body; // Whether camera follows selected body + bool camera_target_enabled; // Whether camera follows selected body/craft Vector3 camera_offset; // Offset from target when following body - bool was_following_body; // Previous frame follow state }; ``` @@ -45,23 +44,17 @@ Vector3 sim_to_render(Vec3 pos, double scale) { ### Scaling Factors **Distance Scale:** `1e-9` (1 render unit = 1 billion meters) - Optimized for solar system scale -- Used for position transformations +- Used for both position transformations and radius scaling -**Size Scale:** `0.02` (logarithmic radius scaling factor) -- Applied to body radii using logarithmic scaling -- Minimum visible radius: 0.01 units (ensures tiny bodies are visible) +**Size Scale:** Same as distance scale (linear scaling) **Radius Scaling:** ```cpp float scale_radius(double radius, double scale) { - float scaled = (float)(scale * log10(radius)); - float min_radius = 0.01f; - return (scaled > min_radius) ? scaled : min_radius; + return (float)(radius * scale); } ``` - -Uses logarithmic scaling to handle extreme radius ranges (1.5M to 700M meters). -Scale factor 0.02 produces: Sun ~0.18, planets 0.13-0.16, moons 0.12-0.13 render units. +Uses linear scaling for consistent representation at all scales. ## Camera System @@ -132,6 +125,19 @@ Scale factor 0.02 produces: Sun ~0.18, planets 0.13-0.16, moons 0.12-0.13 render **Render Order:** Top layer, after spacecraft (rendered as 2D overlays after 3D scene) +### Child Indicators +Screen-space 2D overlays for children of selected body (similar to NASA Eyes). + +**Rendering:** +- NASA Eyes-style hollow circles +- Radius: 20px, Thickness: 2px +- Text label centered inside indicator +- White color for bodies, cyan (0, 255, 255) for spacecraft +- Only shown when body selected (not when spacecraft selected) +- Uses `GetWorldToScreen()` for 2D positioning after `EndMode3D()` + +**Purpose:** Indicates location of children bodies/spacecraft that may be off-screen or hard to locate + ## Orbit Rendering ### Orbital Basis Calculation @@ -181,6 +187,25 @@ Scale factor 0.02 produces: Sun ~0.18, planets 0.13-0.16, moons 0.12-0.13 render 3. Bodies 4. Spacecraft 5. Maneuver markers +6. Child indicators (screen-space overlays) + +### Relative Rendering + +When a body is selected, the scene is rendered relative to that body: +- Selected body is rendered at origin (0, 0, 0) +- Children are rendered relative to selected body +- Children's orbits are rendered around origin +- Uses full float precision for small orbital distances (e.g., LEO) + +When a spacecraft is selected: +- Parent body is rendered relative to spacecraft +- Spacecraft's orbit is rendered around parent +- No sibling spacecraft rendered + +**Benefits:** +- Improved visibility of small orbits (LEO, moon orbits) +- Better float precision for local coordinate systems +- Automatic camera positioning based on children distances ## UI System (ui_renderer + raygui) @@ -272,6 +297,7 @@ Scale factor 0.02 produces: Sun ~0.18, planets 0.13-0.16, moons 0.12-0.13 render #### Camera - `update_camera(render_state, sim)` - Handle input and camera follow +- `get_initial_camera_distance(body, sim, render_state)` - Auto-position camera based on children distance #### Rendering - `render_body(body, render_state)` - Draw single celestial body @@ -317,3 +343,9 @@ Note: UI rendering functions are called after `render_simulation()` to ensure UI - UI panels allocate temporary buffers for lists - List text freed after rendering - No persistent UI state allocation + +## TODO Items + +1. **Distant Bodies**: When body selected, show indicators for bodies that are not children (currently only shows direct children) + +2. **Smooth Frame Transitions**: Add interpolation when switching between different selected bodies (instant switch currently)