2 changed files with 243 additions and 5 deletions
@ -0,0 +1,185 @@
|
||||
# Dynamic Camera Zoom Implementation Plan |
||||
|
||||
## Overview |
||||
Replace fixed zoom behavior with adaptive zoom speed and minimum distance based on current camera distance and target characteristics. |
||||
|
||||
## Goals |
||||
- Zoom speed adapts to current distance (faster when far, slower when close) |
||||
- Minimum camera distance based on target's physical size (radius) |
||||
- Prevent zooming through body surfaces |
||||
- Maintain smooth, controllable zoom with delta clamping |
||||
|
||||
## Changes Required |
||||
|
||||
### 1. Add New Constants (src/renderer.cpp) |
||||
Add after existing camera constants: |
||||
|
||||
```cpp |
||||
// Dynamic zoom settings |
||||
#define ZOOM_PERCENTAGE 0.07f // 7% of current distance per keypress |
||||
#define ZOOM_MIN_DELTA 0.05f // Minimum zoom delta (prevents too-slow zoom) |
||||
#define ZOOM_MAX_DELTA 5.0f // Maximum zoom delta (prevents too-fast zoom) |
||||
|
||||
// Minimum distance settings (in render units) |
||||
#define MIN_DISTANCE_BODY_RADIUS_MULT 2.75f // 2.75x body radius minimum |
||||
#define MIN_DISTANCE_SPACECRAFT_DEFAULT 0.5f // Fallback for orphaned spacecraft |
||||
#define MIN_DISTANCE_SPACECRAFT_PARENT_RADIUS_MULT 0.1f // Scale parent radius for spacecraft |
||||
``` |
||||
|
||||
### 2. Add Zoom Direction Enum (src/renderer.cpp) |
||||
Local enum for improved readability: |
||||
|
||||
```cpp |
||||
enum ZoomDirection { |
||||
ZOOM_IN = -1.0f, |
||||
ZOOM_OUT = 1.0f |
||||
}; |
||||
``` |
||||
|
||||
### 3. Modify `zoom_camera()` Function |
||||
**Location:** src/renderer.cpp:223 |
||||
|
||||
**Changes:** |
||||
- Update signature: `static void zoom_camera(RenderState* render_state, SimulationState* sim, ZoomDirection direction)` |
||||
- Replace fixed `CAMERA_ZOOM_DELTA` with dynamic calculation |
||||
- Add minimum distance based on target radius |
||||
|
||||
**Implementation:** |
||||
```cpp |
||||
static void zoom_camera(RenderState* render_state, SimulationState* sim, ZoomDirection direction) { |
||||
Vector3 to_target = Vector3Subtract(render_state->camera.target, render_state->camera.position); |
||||
Vector3 direction_vec = Vector3Normalize(to_target); |
||||
float camera_distance = Vector3Length(to_target); |
||||
|
||||
// Calculate dynamic zoom delta based on current distance |
||||
float raw_delta = camera_distance * ZOOM_PERCENTAGE * (float)direction; |
||||
|
||||
// Clamp delta to reasonable bounds |
||||
float distance_delta = raw_delta; |
||||
if (fabsf(distance_delta) < ZOOM_MIN_DELTA) { |
||||
distance_delta = ZOOM_MIN_DELTA * (float)direction; |
||||
} |
||||
if (fabsf(distance_delta) > ZOOM_MAX_DELTA) { |
||||
distance_delta = ZOOM_MAX_DELTA * (float)direction; |
||||
} |
||||
|
||||
// Get target-specific minimum distance |
||||
float min_dist = get_target_min_distance(render_state, sim); |
||||
|
||||
// Check if zoom would exceed minimum distance |
||||
if (direction == ZOOM_IN && camera_distance - distance_delta <= min_dist) { |
||||
distance_delta = camera_distance - min_dist - 0.001f; |
||||
} |
||||
|
||||
render_state->camera.position = Vector3Add(render_state->camera.position, Vector3Scale(direction_vec, distance_delta)); |
||||
|
||||
if (render_state->camera_target_enabled) { |
||||
render_state->camera_offset = Vector3Subtract( |
||||
render_state->camera.position, |
||||
render_state->camera.target |
||||
); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### 4. Add `get_target_min_distance()` Helper Function |
||||
**Location:** Add new static function before `zoom_camera()` |
||||
|
||||
```cpp |
||||
static float get_target_min_distance(RenderState* render_state, SimulationState* sim) { |
||||
float target_radius = 0.0f; |
||||
|
||||
if (render_state->selected_body_index >= 0) { |
||||
CelestialBody* body = &sim->bodies[render_state->selected_body_index]; |
||||
target_radius = scale_radius(body->radius, render_state->distance_scale); |
||||
} else if (render_state->selected_craft_index >= 0) { |
||||
Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index]; |
||||
if (craft->parent_index >= 0 && craft->parent_index < sim->body_count) { |
||||
CelestialBody* parent = &sim->bodies[craft->parent_index]; |
||||
target_radius = scale_radius(parent->radius, render_state->distance_scale); |
||||
target_radius *= MIN_DISTANCE_SPACECRAFT_PARENT_RADIUS_MULT; |
||||
} else { |
||||
// Spacecraft without parent (shouldn't happen in valid configs) |
||||
return MIN_DISTANCE_SPACECRAFT_DEFAULT; |
||||
} |
||||
} else { |
||||
// No target selected, use existing fallback |
||||
return CAMERA_MIN_DISTANCE; |
||||
} |
||||
|
||||
return target_radius * MIN_DISTANCE_BODY_RADIUS_MULT; |
||||
} |
||||
``` |
||||
|
||||
### 5. Update `update_camera()` Calls |
||||
**Location:** src/renderer.cpp:277-282 |
||||
|
||||
**Change from:** |
||||
```cpp |
||||
if (IsKeyDown(KEY_UP)) { |
||||
zoom_camera(render_state, CAMERA_ZOOM_DELTA); |
||||
} |
||||
if (IsKeyDown(KEY_DOWN)) { |
||||
zoom_camera(render_state, -CAMERA_ZOOM_DELTA); |
||||
} |
||||
``` |
||||
|
||||
**Change to:** |
||||
```cpp |
||||
if (IsKeyDown(KEY_UP)) { |
||||
zoom_camera(render_state, sim, ZOOM_IN); |
||||
} |
||||
if (IsKeyDown(KEY_DOWN)) { |
||||
zoom_camera(render_state, sim, ZOOM_OUT); |
||||
} |
||||
``` |
||||
|
||||
### 6. Remove/Update Old Constants |
||||
**Location:** src/renderer.cpp:8 |
||||
|
||||
**Remove:** `#define CAMERA_ZOOM_DELTA 2.0f` (replaced by dynamic calculation) |
||||
**Keep:** `#define CAMERA_MIN_DISTANCE 0.1f` (as fallback when no target) |
||||
|
||||
## Technical Details |
||||
|
||||
### Dynamic Zoom Calculation |
||||
1. Calculate raw delta: `current_distance * ZOOM_PERCENTAGE * direction` |
||||
2. Clamp delta between `ZOOM_MIN_DELTA` and `ZOOM_MAX_DELTA` |
||||
3. Ensure sign matches zoom direction |
||||
|
||||
### Minimum Distance Logic |
||||
1. For bodies: `body_radius * MIN_DISTANCE_BODY_RADIUS_MULT` |
||||
2. For spacecraft: `parent_radius * MIN_DISTANCE_SPACECRAFT_PARENT_RADIUS_MULT * MIN_DISTANCE_BODY_RADIUS_MULT` |
||||
3. For no target: `CAMERA_MIN_DISTANCE` (existing fallback) |
||||
|
||||
### Sign Convention |
||||
- `ZOOM_IN (-1.0)`: Move camera closer to target (decrease distance) |
||||
- `ZOOM_OUT (+1.0)`: Move camera farther from target (increase distance) |
||||
|
||||
### Edge Cases |
||||
- Spacecraft without parent: Uses `MIN_DISTANCE_SPACECRAFT_DEFAULT` |
||||
- No target selected: Uses existing `CAMERA_MIN_DISTANCE` |
||||
- Very close zoom: Delta clamping prevents too-slow movement |
||||
|
||||
## Implementation Order |
||||
1. Add new constants and enum to renderer.cpp |
||||
2. Implement `get_target_min_distance()` helper |
||||
3. Modify `zoom_camera()` function signature and implementation |
||||
4. Update `update_camera()` calls to `zoom_camera()` |
||||
5. Remove old `CAMERA_ZOOM_DELTA` constant |
||||
6. Build and test |
||||
|
||||
## Testing Checklist |
||||
- [ ] Zoom in/out works smoothly with new percentage-based behavior |
||||
- [ ] Zoom speed adapts based on current distance (faster when far, slower when close) |
||||
- [ ] Cannot zoom through body surface (minimum distance enforced) |
||||
- [ ] Spacecraft zoom uses parent body radius (scaled down) |
||||
- [ ] Zoom delta clamping prevents too-fast or too-slow zoom |
||||
- [ ] No target selected still works (uses existing fallbacks) |
||||
- [ ] Build succeeds without errors |
||||
|
||||
## Future Enhancements (Not in scope) |
||||
- Use parent's SOI radius instead of radius for more physically meaningful bounds |
||||
- Consider orbital characteristics (semi-major axis) for context-aware minimums |
||||
- Add configurable zoom speed presets (slow/normal/fast) |
||||
- Smooth interpolation when zooming rapidly |
||||
Loading…
Reference in new issue