Browse Source

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
main
cinnaboot 6 months ago
parent
commit
98905fc846
  1. 12
      src/main.cpp
  2. 256
      src/renderer.cpp
  3. 6
      src/ui_renderer.cpp
  4. 119
      tests/test_orbit_rendering.cpp
  5. 37
      tests/test_orbit_rendering.toml

12
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

256
src/renderer.cpp

@ -3,6 +3,9 @@
#include <cmath>
#include <cstdio>
// 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) {

6
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);
}
}

119
tests/test_orbit_rendering.cpp

@ -0,0 +1,119 @@
#include <catch2/catch_test_macros.hpp>
#include "../src/physics.h"
#include "../src/simulation.h"
#include "../src/spacecraft.h"
#include "../src/config_loader.h"
#include <cmath>
#include <iostream>
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);
}

37
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
}
Loading…
Cancel
Save