You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.8 KiB
6.8 KiB
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:
// 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:
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_DELTAwith dynamic calculation - Add minimum distance based on target radius
Implementation:
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()
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:
if (IsKeyDown(KEY_UP)) {
zoom_camera(render_state, CAMERA_ZOOM_DELTA);
}
if (IsKeyDown(KEY_DOWN)) {
zoom_camera(render_state, -CAMERA_ZOOM_DELTA);
}
Change to:
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
- Calculate raw delta:
current_distance * ZOOM_PERCENTAGE * direction - Clamp delta between
ZOOM_MIN_DELTAandZOOM_MAX_DELTA - Ensure sign matches zoom direction
Minimum Distance Logic
- For bodies:
body_radius * MIN_DISTANCE_BODY_RADIUS_MULT - For spacecraft:
parent_radius * MIN_DISTANCE_SPACECRAFT_PARENT_RADIUS_MULT * MIN_DISTANCE_BODY_RADIUS_MULT - 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
- Add new constants and enum to renderer.cpp
- Implement
get_target_min_distance()helper - Modify
zoom_camera()function signature and implementation - Update
update_camera()calls tozoom_camera() - Remove old
CAMERA_ZOOM_DELTAconstant - 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