From 98905fc846055c4ea123fd72c994918a60562b21 Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Mon, 26 Jan 2026 14:39:23 -0500 Subject: [PATCH] Phase 0: Refactor update_camera() with helper functions - Add TrackingMode and RenderFrameMode enums to renderer.h - Remove old state fields (was_following_body, previous_selected_body) - Add new state fields (camera_mode, frame_mode, last_target_index, local_frame_parent_index) - Update main.cpp and ui_renderer.cpp to use new field names - Refactor update_camera() with helper functions: - detect_camera_mode() - has_target_changed() - update_camera_target() - rotate_camera_orbitally() - zoom_camera() - update_last_target() - update_camera_frame_mode() - detect_render_frame_mode() - Add local frame transformation helpers: - get_local_frame_scale() - sim_to_render_local() - Add SOI_SCALE_TARGET constant --- src/main.cpp | 12 +- src/renderer.cpp | 256 ++++++++++++++++++++------------ src/ui_renderer.cpp | 6 +- tests/test_orbit_rendering.cpp | 119 +++++++++++++++ tests/test_orbit_rendering.toml | 37 +++++ 5 files changed, 326 insertions(+), 104 deletions(-) create mode 100644 tests/test_orbit_rendering.cpp create mode 100644 tests/test_orbit_rendering.toml diff --git a/src/main.cpp b/src/main.cpp index bc0d460..e7300d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,11 +52,13 @@ void run_gui_simulation(SimulationState* sim) { // Initialize render state render_state.selected_body_index = root_body_index >= 0 ? root_body_index : -1; - render_state.selected_craft_index = -1; // No spacecraft selected initially - render_state.previous_selected_body = -1; // Previous selected body index - render_state.camera_follow_body = root_body_index >= 0; // Follow root body if found - render_state.camera_offset = (Vector3){ 0, 50, 100 }; // Default offset - render_state.was_following_body = false; + render_state.selected_craft_index = -1; + render_state.camera_target_enabled = root_body_index >= 0; + render_state.camera_mode = root_body_index >= 0 ? TRACKING_BODY : TRACKING_FREE; + render_state.frame_mode = RENDER_FRAME_GLOBAL; + render_state.last_target_index = root_body_index >= 0 ? root_body_index : -1; + render_state.local_frame_parent_index = -1; + render_state.camera_offset = (Vector3){ 0, 50, 100 }; // Initialize UI state ui_state.body_list_scroll = 0; // Initial scroll position diff --git a/src/renderer.cpp b/src/renderer.cpp index 2ddedde..f85b1cc 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -3,6 +3,9 @@ #include #include +// Scale target for local frame rendering +static const double SOI_SCALE_TARGET = 100.0; + // Forward declarations static Vector3 sim_to_render(Vec3 pos, double scale); @@ -96,142 +99,184 @@ void setup_camera(RenderState* render_state) { } } -// Update camera with keyboard/mouse controls -void update_camera(RenderState* render_state, SimulationState* sim) { - float angle_speed = 0.02f; - - // Handle camera follow state transitions - if (render_state->camera_follow_body && !render_state->was_following_body) { - // Just started following - store current offset - if (render_state->selected_body_index >= 0 && render_state->selected_body_index < sim->body_count) { - CelestialBody* body = &sim->bodies[render_state->selected_body_index]; - Vector3 body_pos = sim_to_render(body->global_position, render_state->distance_scale); - render_state->camera.target = body_pos; - render_state->camera_offset = Vector3Subtract(render_state->camera.position, body_pos); - } 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]; - Vector3 craft_pos = sim_to_render(craft->global_position, render_state->distance_scale); - render_state->camera.target = craft_pos; - render_state->camera_offset = Vector3Subtract(render_state->camera.position, craft_pos); +static TrackingMode detect_camera_mode(RenderState* render_state) { + if (render_state->camera_target_enabled) { + if (render_state->selected_craft_index >= 0) { + return TRACKING_CRAFT; + } else if (render_state->selected_body_index >= 0) { + return TRACKING_BODY; } } + return TRACKING_FREE; +} - // Preserve distance when switching to different body or spacecraft - if (render_state->camera_follow_body) { - if (render_state->selected_body_index >= 0 && - render_state->selected_body_index != render_state->previous_selected_body && - render_state->selected_body_index < sim->body_count) { - // Body selection changed - recalculate offset to maintain distance - Vector3 body_pos = sim_to_render(sim->bodies[render_state->selected_body_index].global_position, render_state->distance_scale); - render_state->camera.target = body_pos; - render_state->camera.position = Vector3Add(body_pos, render_state->camera_offset); - } else if (render_state->selected_craft_index >= 0 && sim->craft_count > 0) { - // Spacecraft selected - update camera to follow it - Vector3 craft_pos = sim_to_render(sim->spacecraft[render_state->selected_craft_index].global_position, render_state->distance_scale); - render_state->camera.target = craft_pos; - render_state->camera.position = Vector3Add(craft_pos, render_state->camera_offset); - } +static bool has_target_changed(RenderState* render_state) { + int current_target = -1; + if (render_state->selected_body_index >= 0) { + current_target = render_state->selected_body_index; + } else if (render_state->selected_craft_index >= 0) { + current_target = -(render_state->selected_craft_index + 1); } + return current_target != render_state->last_target_index; +} - // Update target position when following body - if (render_state->camera_follow_body && render_state->selected_body_index >= 0 && - render_state->selected_body_index < sim->body_count) { +static void update_camera_target(RenderState* render_state, SimulationState* sim) { + if (render_state->camera_mode == TRACKING_BODY && render_state->selected_body_index >= 0) { CelestialBody* body = &sim->bodies[render_state->selected_body_index]; Vector3 body_pos = sim_to_render(body->global_position, render_state->distance_scale); render_state->camera.target = body_pos; render_state->camera.position = Vector3Add(body_pos, render_state->camera_offset); - } - - // Update target position when following spacecraft - if (render_state->camera_follow_body && render_state->selected_craft_index >= 0 && - render_state->selected_craft_index < sim->craft_count) { + } else if (render_state->camera_mode == TRACKING_CRAFT && render_state->selected_craft_index >= 0) { Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index]; Vector3 craft_pos = sim_to_render(craft->global_position, render_state->distance_scale); render_state->camera.target = craft_pos; render_state->camera.position = Vector3Add(craft_pos, render_state->camera_offset); } +} - // Camera rotation using camera's up vector +static void rotate_camera_orbitally(RenderState* render_state, float angle) { Vector3 to_camera = Vector3Subtract(render_state->camera.position, render_state->camera.target); float camera_distance = Vector3Length(to_camera); + Vector3 forward = Vector3Normalize(to_camera); - // Rotate around target using camera's up vector - if (IsKeyDown(KEY_LEFT)) { - Vector3 forward = Vector3Normalize(to_camera); + float cos_a = cosf(angle); + float sin_a = sinf(angle); - // Rotate forward vector around camera's up axis (horizontal orbit) - float cos_a = cosf(angle_speed); - float sin_a = sinf(angle_speed); + Vector3 new_forward = Vector3Add( + Vector3Scale(forward, cos_a), + Vector3Scale(Vector3CrossProduct(render_state->camera.up, forward), sin_a) + ); - Vector3 new_forward = Vector3Add( - Vector3Scale(forward, cos_a), - Vector3Scale(Vector3CrossProduct(render_state->camera.up, forward), sin_a) + render_state->camera.position = Vector3Add( + render_state->camera.target, + Vector3Scale(new_forward, camera_distance) + ); + + if (render_state->camera_target_enabled) { + render_state->camera_offset = Vector3Subtract( + render_state->camera.position, + render_state->camera.target ); + } +} + +static void zoom_camera(RenderState* render_state, float distance_delta) { + Vector3 to_target = Vector3Subtract(render_state->camera.target, render_state->camera.position); + Vector3 direction = Vector3Normalize(to_target); + float camera_distance = Vector3Length(to_target); + + if (distance_delta > 0 && camera_distance <= 10.0f) return; + + render_state->camera.position = Vector3Add(render_state->camera.position, Vector3Scale(direction, distance_delta)); - render_state->camera.position = Vector3Add( - render_state->camera.target, - Vector3Scale(new_forward, camera_distance) + if (render_state->camera_target_enabled) { + render_state->camera_offset = Vector3Subtract( + render_state->camera.position, + render_state->camera.target ); + } +} + +static void update_last_target(RenderState* render_state) { + if (render_state->selected_body_index >= 0) { + render_state->last_target_index = render_state->selected_body_index; + } else if (render_state->selected_craft_index >= 0) { + render_state->last_target_index = -(render_state->selected_craft_index + 1); + } else { + render_state->last_target_index = -1; + } +} + +static RenderFrameMode detect_render_frame_mode(RenderState* render_state, SimulationState* sim) { + if (!render_state->camera_target_enabled) { + return RENDER_FRAME_GLOBAL; + } + + if (render_state->camera_mode == TRACKING_BODY) { + CelestialBody* body = NULL; + if (render_state->selected_body_index >= 0) { + body = &sim->bodies[render_state->selected_body_index]; + } - if (render_state->camera_follow_body) { - render_state->camera_offset = Vector3Subtract( - render_state->camera.position, - render_state->camera.target - ); + if (body && body->parent_index >= 0) { + return RENDER_FRAME_LOCAL; } } - if (IsKeyDown(KEY_RIGHT)) { - Vector3 forward = Vector3Normalize(to_camera); - // Rotate forward vector around camera's up axis (horizontal orbit) - float cos_a = cosf(-angle_speed); - float sin_a = sinf(-angle_speed); + if (render_state->camera_mode == TRACKING_CRAFT) { + Spacecraft* craft = NULL; + if (render_state->selected_craft_index >= 0) { + craft = &sim->spacecraft[render_state->selected_craft_index]; + } - Vector3 new_forward = Vector3Add( - Vector3Scale(forward, cos_a), - Vector3Scale(Vector3CrossProduct(render_state->camera.up, forward), sin_a) - ); + if (craft && craft->parent_index >= 0) { + return RENDER_FRAME_LOCAL; + } + } - render_state->camera.position = Vector3Add( - render_state->camera.target, - Vector3Scale(new_forward, camera_distance) - ); + return RENDER_FRAME_GLOBAL; +} - if (render_state->camera_follow_body) { - render_state->camera_offset = Vector3Subtract( - render_state->camera.position, - render_state->camera.target - ); +static void update_camera_frame_mode(RenderState* render_state, SimulationState* sim) { + RenderFrameMode new_frame_mode = detect_render_frame_mode(render_state, sim); + + if (new_frame_mode != render_state->frame_mode) { + render_state->frame_mode = new_frame_mode; + + if (new_frame_mode == RENDER_FRAME_LOCAL) { + if (render_state->camera_mode == TRACKING_BODY && render_state->selected_body_index >= 0) { + CelestialBody* body = &sim->bodies[render_state->selected_body_index]; + render_state->local_frame_parent_index = body->parent_index; + } else if (render_state->camera_mode == TRACKING_CRAFT && render_state->selected_craft_index >= 0) { + Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index]; + render_state->local_frame_parent_index = craft->parent_index; + } + } else { + render_state->local_frame_parent_index = -1; } } +} - // Zoom in/out with up/down keys - if (IsKeyDown(KEY_UP) && camera_distance > 10.0f) { - Vector3 direction = Vector3Normalize(Vector3Subtract(render_state->camera.target, render_state->camera.position)); - render_state->camera.position = Vector3Add(render_state->camera.position, Vector3Scale(direction, 2.0f)); +// Update camera with keyboard/mouse controls +void update_camera(RenderState* render_state, SimulationState* sim) { + float angle_speed = 0.02f; + + render_state->camera_mode = detect_camera_mode(render_state); - if (render_state->camera_follow_body) { - render_state->camera_offset = Vector3Subtract( - render_state->camera.position, - render_state->camera.target - ); + bool target_changed = has_target_changed(render_state); + + if (render_state->camera_target_enabled) { + if (target_changed) { + update_camera_target(render_state, sim); + } else if (render_state->camera_mode == TRACKING_BODY && render_state->selected_body_index >= 0) { + CelestialBody* body = &sim->bodies[render_state->selected_body_index]; + Vector3 body_pos = sim_to_render(body->global_position, render_state->distance_scale); + render_state->camera.target = body_pos; + render_state->camera.position = Vector3Add(body_pos, render_state->camera_offset); + } else if (render_state->camera_mode == TRACKING_CRAFT && render_state->selected_craft_index >= 0) { + Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index]; + Vector3 craft_pos = sim_to_render(craft->global_position, render_state->distance_scale); + render_state->camera.target = craft_pos; + render_state->camera.position = Vector3Add(craft_pos, render_state->camera_offset); } } + + if (IsKeyDown(KEY_LEFT)) { + rotate_camera_orbitally(render_state, angle_speed); + } + if (IsKeyDown(KEY_RIGHT)) { + rotate_camera_orbitally(render_state, -angle_speed); + } + + if (IsKeyDown(KEY_UP)) { + zoom_camera(render_state, 2.0f); + } if (IsKeyDown(KEY_DOWN)) { - Vector3 direction = Vector3Normalize(Vector3Subtract(render_state->camera.position, render_state->camera.target)); - render_state->camera.position = Vector3Add(render_state->camera.position, Vector3Scale(direction, 2.0f)); - - if (render_state->camera_follow_body) { - render_state->camera_offset = Vector3Subtract( - render_state->camera.position, - render_state->camera.target - ); - } + zoom_camera(render_state, -2.0f); } - // Store previous follow state and selected body - render_state->was_following_body = render_state->camera_follow_body; - render_state->previous_selected_body = render_state->selected_body_index; + update_last_target(render_state); + update_camera_frame_mode(render_state, sim); } // Transform from simulation coordinates (XY plane) to render coordinates (XZ plane) @@ -244,7 +289,24 @@ Vector3 sim_to_render(Vec3 pos, double scale) { }; } +static double get_local_frame_scale(CelestialBody* parent) { + if (!parent) return 1e-9; + double soi_radius = parent->soi_radius; + if (soi_radius > 0) { + return SOI_SCALE_TARGET / soi_radius; + } + + return 1e-9; +} + +static Vector3 sim_to_render_local(Vec3 local_pos, double scale) { + return (Vector3){ + (float)(local_pos.x * scale), + (float)(local_pos.z * scale), + (float)(-local_pos.y * scale) + }; +} // Scale a radius for rendering (with minimum visible size) float scale_radius(double radius, double scale) { diff --git a/src/ui_renderer.cpp b/src/ui_renderer.cpp index 033d17d..ea1d276 100644 --- a/src/ui_renderer.cpp +++ b/src/ui_renderer.cpp @@ -129,7 +129,8 @@ void render_body_list_ui(SimulationState* sim, RenderState* render_state, UIStat // A body was selected render_state->selected_body_index = ui_state->body_list_active; render_state->selected_craft_index = -1; - render_state->camera_follow_body = true; + render_state->camera_target_enabled = true; + render_state->camera_mode = TRACKING_BODY; printf("Camera follow enabled for body: %s\n", sim->bodies[render_state->selected_body_index].name); } else { // A spacecraft was selected @@ -137,7 +138,8 @@ void render_body_list_ui(SimulationState* sim, RenderState* render_state, UIStat if (craft_index >= 0 && craft_index < sim->craft_count) { render_state->selected_body_index = -1; render_state->selected_craft_index = craft_index; - render_state->camera_follow_body = true; + render_state->camera_target_enabled = true; + render_state->camera_mode = TRACKING_CRAFT; printf("Camera follow enabled for spacecraft: %s\n", sim->spacecraft[craft_index].name); } } diff --git a/tests/test_orbit_rendering.cpp b/tests/test_orbit_rendering.cpp new file mode 100644 index 0000000..34975cc --- /dev/null +++ b/tests/test_orbit_rendering.cpp @@ -0,0 +1,119 @@ +#include +#include "../src/physics.h" +#include "../src/simulation.h" +#include "../src/spacecraft.h" +#include "../src/config_loader.h" +#include +#include + +TEST_CASE("Debug orbit rendering coordinates", "[debug][rendering]") { + const double TIME_STEP = 60.0; + SimulationState* sim = create_simulation(10, 10, 0, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_orbit_rendering.toml")); + + Spacecraft* craft = &sim->spacecraft[0]; + CelestialBody* parent = &sim->bodies[craft->parent_index]; + + // Calculate orbit parameters (same as render_orbit() does) + Vec3 r_vec = craft->local_position; + Vec3 velocity = craft->local_velocity; + Vec3 parent_pos = parent->global_position; + double r = vec3_magnitude(r_vec); + double v = vec3_magnitude(velocity); + double mu = G * parent->mass; + + // Calculate eccentricity vector + double v_squared = v * v; + double r_dot_v = vec3_dot(r_vec, velocity); + Vec3 e_vec = { + (v_squared - mu / r) * r_vec.x - r_dot_v * velocity.x, + (v_squared - mu / r) * r_vec.y - r_dot_v * velocity.y, + (v_squared - mu / r) * r_vec.z - r_dot_v * velocity.z + }; + double e = vec3_magnitude(e_vec) / mu; + + // Calculate semi-major axis + double specific_energy = (v * v) / 2.0 - mu / r; + double a = -mu / (2.0 * specific_energy); + + INFO("=== Orbit Parameters ==="); + INFO("Semi-major axis (a): " << a << " m"); + INFO("Eccentricity (e): " << e); + INFO("Orbital radius (r): " << r << " m"); + INFO("Velocity (v): " << v << " m/s"); + + // Calculate rendering scale factors + double distance_scale = 1e-9; + double size_scale = 0.02; + + INFO(""); + INFO("=== Rendering Scale Factors ==="); + INFO("Distance scale (linear): " << distance_scale); + INFO("Size scale (log): " << size_scale); + + // Calculate Earth's rendered size + float earth_render_radius = size_scale * log10(parent->radius); + INFO("Earth rendered radius: " << earth_render_radius << " units"); + + // Generate orbit points at 0°, 90°, 180°, 270° + double b = a * sqrt(1.0 - e * e); + double c = a * e; + + INFO(""); + INFO("=== Orbit Point Coordinates ==="); + + for (int i = 0; i < 4; i++) { + double theta = (double)i / 4.0 * 2.0 * M_PI; + + // Orbital plane coordinates + double x_orbit = a * cos(theta) - c; + double y_orbit = b * sin(theta); + + // Sim coordinates (using orbital_to_cartesian logic with circular orbit fix) + Vec3 e_dir = vec3_normalize(e_vec); + Vec3 periapsis_dir; + + // For circular orbits, use position direction as periapsis direction + if (vec3_magnitude(e_dir) < 0.001) { + periapsis_dir = vec3_normalize(r_vec); + } else { + periapsis_dir = e_dir; + } + + Vec3 h_vec = vec3_cross(r_vec, velocity); + Vec3 normal = vec3_normalize(h_vec); + Vec3 q_vec = vec3_cross(normal, periapsis_dir); + + Vec3 sim_pos = { + periapsis_dir.x * x_orbit + q_vec.x * y_orbit + parent_pos.x, + periapsis_dir.y * x_orbit + q_vec.y * y_orbit + parent_pos.y, + periapsis_dir.z * x_orbit + q_vec.z * y_orbit + parent_pos.z + }; + + // Render coordinates (using sim_to_render logic) + double render_x = sim_pos.x * distance_scale; + double render_y = sim_pos.z * distance_scale; + double render_z = -sim_pos.y * distance_scale; + + double distance_from_center = sqrt(render_x*render_x + render_y*render_y + render_z*render_z); + + std::cout << "\nPoint " << i << " (" << (i * 90) << "°):" << std::endl; + std::cout << " Orbit plane: (" << x_orbit << ", " << y_orbit << ") m" << std::endl; + std::cout << " Sim pos: (" << sim_pos.x << ", " << sim_pos.y << ", " << sim_pos.z << ") m" << std::endl; + std::cout << " Render pos: (" << render_x << ", " << render_y << ", " << render_z << ") units" << std::endl; + std::cout << " Distance from center: " << distance_from_center << " units" << std::endl; + std::cout << " % of Earth radius: " << (distance_from_center / earth_render_radius * 100.0) << "%" << std::endl; + } + + INFO(""); + INFO("=== Scaling Diagnosis ==="); + double orbit_radius_render = r * distance_scale; + INFO("Orbital radius (render units): " << orbit_radius_render); + INFO("Ratio to Earth rendered radius: " << (orbit_radius_render / earth_render_radius)); + INFO("Earth rendered radius: " << earth_render_radius); + + REQUIRE(true); + + destroy_simulation(sim); +} diff --git a/tests/test_orbit_rendering.toml b/tests/test_orbit_rendering.toml new file mode 100644 index 0000000..7ed0b31 --- /dev/null +++ b/tests/test_orbit_rendering.toml @@ -0,0 +1,37 @@ +# Test Configuration: Orbit Rendering Debug +# Sun + Earth + LEO Satellite +# Used for diagnosing orbit coordinate generation and scaling + +[[bodies]] +name = "Sun" +mass = 1.989e30 +radius = 6.96e8 +parent_index = -1 +color = { r = 1.0, g = 1.0, b = 0.0 } +orbit = { + semi_major_axis = 0.0, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +[[bodies]] +name = "Earth" +mass = 5.972e24 +radius = 6.371e6 +parent_index = 0 +color = { r = 0.0, g = 0.5, b = 1.0 } +orbit = { + semi_major_axis = 1.496e11, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +[[spacecraft]] +name = "LEO_Satellite" +mass = 1000.0 +parent_index = 1 +orbit = { + altitude = 400000.0, + eccentricity = 0.0, + true_anomaly = 0.0 +}