2 changed files with 46 additions and 610 deletions
@ -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 |
||||
Loading…
Reference in new issue