Browse Source

Implement relative rendering with linear scaling and child indicators

- Change scale_radius() from logarithmic to linear scaling
- Add get_initial_camera_distance() for auto camera positioning
- Implement selected-body-relative rendering (body at origin)
- Add child indicators with hollow circles and text labels
- Restructure render_simulation() with conditional logic
- Fix zoom camera min_distance check for both directions
- Camera automatically positioned based on average child distance
main
cinnaboot 5 months ago
parent
commit
0ec7646fca
  1. 184
      src/renderer.cpp
  2. 2
      src/renderer.h

184
src/renderer.cpp

@ -6,6 +6,51 @@
// Forward declarations
static Vector3 sim_to_render(Vec3 pos, double scale);
static void draw_child_indicator(Vector2 screen_pos, const char* name, Color color) {
const float radius = 20.0f;
DrawCircleLinesV(screen_pos, radius, color);
int font_size = 10;
int text_width = MeasureText(name, font_size);
Vector2 text_pos = {screen_pos.x - text_width/2, screen_pos.y - font_size/2};
DrawText(name, (int)text_pos.x, (int)text_pos.y, font_size, color);
}
void render_child_indicators(SimulationState* sim, RenderState* render_state) {
if (render_state->selected_body_index < 0 || render_state->selected_body_index >= sim->body_count) {
return;
}
CelestialBody* selected = &sim->bodies[render_state->selected_body_index];
Color body_color = WHITE;
Color craft_color = (Color){0, 255, 255, 255};
// Render child body indicators
for (int i = 0; i < sim->body_count; i++) {
CelestialBody* child = &sim->bodies[i];
if (child->parent_index == render_state->selected_body_index) {
Vec3 child_rel = vec3_sub(child->global_position, selected->global_position);
Vector3 child_pos_3d = sim_to_render(child_rel, render_state->distance_scale);
Vector2 screen_pos = GetWorldToScreen(child_pos_3d, render_state->camera);
draw_child_indicator(screen_pos, child->name, body_color);
}
}
// Render child spacecraft indicators
for (int i = 0; i < sim->craft_count; i++) {
Spacecraft* craft = &sim->spacecraft[i];
if (craft->parent_index == render_state->selected_body_index) {
Vec3 craft_rel = vec3_sub(craft->global_position, selected->global_position);
Vector3 craft_pos_3d = sim_to_render(craft_rel, render_state->distance_scale);
Vector2 screen_pos = GetWorldToScreen(craft_pos_3d, render_state->camera);
draw_child_indicator(screen_pos, craft->name, craft_color);
}
}
}
// Initialize raylib window
void init_renderer(int width, int height, const char* title) {
InitWindow(width, height, title);
@ -109,9 +154,14 @@ static bool has_target_changed(RenderState* render_state) {
static void update_camera_target(RenderState* render_state, SimulationState* sim) {
if (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);
// Camera target is at origin for selected body
render_state->camera.target = (Vector3){0, 0, 0};
// Set initial camera position based on children distance
float distance = get_initial_camera_distance(body, sim, render_state);
render_state->camera.position = (Vector3){0, distance * 0.3f, distance};
render_state->camera_offset = render_state->camera.position;
} else if (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);
@ -151,7 +201,8 @@ static void zoom_camera(RenderState* render_state, float distance_delta) {
Vector3 direction = Vector3Normalize(to_target);
float camera_distance = Vector3Length(to_target);
if (distance_delta > 0 && camera_distance <= 10.0f) return;
float min_distance = 0.1f;
if (camera_distance - distance_delta <= min_distance) return;
render_state->camera.position = Vector3Add(render_state->camera.position, Vector3Scale(direction, distance_delta));
@ -183,11 +234,11 @@ void update_camera(RenderState* render_state, SimulationState* sim) {
if (target_changed) {
update_camera_target(render_state, sim);
} else if (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);
// Body selected - camera orbits around origin (body at 0,0,0 in render space)
render_state->camera.target = (Vector3){0, 0, 0};
render_state->camera.position = render_state->camera_offset;
} else if (render_state->selected_craft_index >= 0) {
// Spacecraft selected - camera orbits around spacecraft
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;
@ -212,6 +263,41 @@ void update_camera(RenderState* render_state, SimulationState* sim) {
update_last_target(render_state);
}
// Calculate initial camera distance based on children of the selected body
float get_initial_camera_distance(CelestialBody* body, SimulationState* sim, RenderState* render_state) {
int body_index = body - sim->bodies;
// Calculate average distance to children (bodies and spacecraft)
int child_count = 0;
double total_distance = 0.0;
// Check body children
for (int i = 0; i < sim->body_count; i++) {
if (sim->bodies[i].parent_index == body_index) {
child_count++;
double dist = vec3_distance(sim->bodies[i].global_position, body->global_position);
total_distance += dist * render_state->distance_scale;
}
}
// Check spacecraft children
for (int i = 0; i < sim->craft_count; i++) {
if (sim->spacecraft[i].parent_index == body_index) {
child_count++;
double dist = vec3_distance(sim->spacecraft[i].global_position, body->global_position);
total_distance += dist * render_state->distance_scale;
}
}
if (child_count > 0) {
return (float)(total_distance / child_count);
}
// No children - use 100x body radius as fallback
float body_radius = scale_radius(body->radius, render_state->distance_scale);
return body_radius * 100.0f;
}
// Transform from simulation coordinates (XY plane) to render coordinates (XZ plane)
// Rotation matrix: 90 degrees around X-axis maps Y -> Z
Vector3 sim_to_render(Vec3 pos, double scale) {
@ -222,11 +308,9 @@ Vector3 sim_to_render(Vec3 pos, double scale) {
};
}
// Scale a radius for rendering (with minimum visible size)
// Scale a radius for rendering (linear scaling)
float scale_radius(double radius, double scale) {
float scaled = (float)(scale * log10(radius));
float min_radius = 0.01f; // Minimum visible radius
return (scaled > min_radius) ? scaled : min_radius;
return (float)(radius * scale);
}
// Render a single celestial body
@ -450,6 +534,7 @@ Color get_body_orbit_color(CelestialBody* body) {
};
}
// FIXME: orbital vector calculations should be in orbital_mechanics.h
void render_orbit(Vec3 position, Vec3 velocity, Vec3 parent_position,
double parent_mass, Color orbit_color, RenderState* render_state) {
Vec3 r_vec = vec3_sub(position, parent_position);
@ -521,29 +606,63 @@ void render_simulation(SimulationState* sim, RenderState* render_state) {
DrawLine3D((Vector3){0, 0, -500.0f}, (Vector3){0, 0, 500.0f}, (Color){40, 40, 40, 255});
DrawLine3D((Vector3){-500.0f, 0, 0}, (Vector3){500.0f, 0, 0}, (Color){40, 40, 40, 255});
// Render orbit paths first
for (int i = 0; i < sim->body_count; i++) {
CelestialBody* body = &sim->bodies[i];
if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[body->parent_index];
render_orbit(body->global_position, body->local_velocity, parent->global_position,
parent->mass, get_body_orbit_color(body), render_state);
if (render_state->selected_body_index >= 0) {
// === BODY SELECTED MODE ===
CelestialBody* selected = &sim->bodies[render_state->selected_body_index];
// Render selected body at origin
Vector3 origin = {0, 0, 0};
float radius = scale_radius(selected->radius, render_state->distance_scale);
Color selected_color = {(unsigned char)(selected->color[0]*255), (unsigned char)(selected->color[1]*255), (unsigned char)(selected->color[2]*255), 255};
DrawSphere(origin, radius, selected_color);
// Render child bodies and their orbits
for (int i = 0; i < sim->body_count; i++) {
CelestialBody* child = &sim->bodies[i];
if (child->parent_index == render_state->selected_body_index) {
Vec3 child_rel = vec3_sub(child->global_position, selected->global_position);
Vector3 child_pos = sim_to_render(child_rel, render_state->distance_scale);
float child_radius = scale_radius(child->radius, render_state->distance_scale);
Color child_color = {(unsigned char)(child->color[0]*255), (unsigned char)(child->color[1]*255), (unsigned char)(child->color[2]*255), 255};
DrawSphere(child_pos, child_radius, child_color);
Vec3 origin_vec = {0, 0, 0};
render_orbit(child_rel, child->local_velocity, origin_vec, selected->mass, child_color, render_state);
}
}
}
// Render spacecraft orbits
for (int i = 0; i < sim->craft_count; i++) {
Spacecraft* craft = &sim->spacecraft[i];
if (craft->parent_index >= 0 && craft->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[craft->parent_index];
render_orbit(craft->global_position, craft->local_velocity, parent->global_position,
parent->mass, (Color){0, 255, 255, 128}, render_state);
// Render child spacecraft and their orbits
for (int i = 0; i < sim->craft_count; i++) {
Spacecraft* craft = &sim->spacecraft[i];
if (craft->parent_index == render_state->selected_body_index) {
Vec3 craft_rel = vec3_sub(craft->global_position, selected->global_position);
// Craft will be rendered in screen space after EndMode3D
// Just draw orbit here
Vec3 origin_vec = {0, 0, 0};
render_orbit(craft_rel, craft->local_velocity, origin_vec, selected->mass, (Color){0, 255, 255, 128}, render_state);
}
}
}
// Render all bodies
for (int i = 0; i < sim->body_count; i++) {
render_body(&sim->bodies[i], render_state);
} else if (render_state->selected_craft_index >= 0) {
// === SPACECRAFT SELECTED MODE ===
Spacecraft* selected = &sim->spacecraft[render_state->selected_craft_index];
// Render parent body (for reference)
if (selected->parent_index >= 0 && selected->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[selected->parent_index];
Vec3 parent_rel = vec3_sub(parent->global_position, selected->global_position);
Vector3 parent_pos = sim_to_render(parent_rel, render_state->distance_scale);
float parent_radius = scale_radius(parent->radius, render_state->distance_scale);
Color parent_color = {(unsigned char)(parent->color[0]*255), (unsigned char)(parent->color[1]*255), (unsigned char)(parent->color[2]*255), 255};
DrawSphere(parent_pos, parent_radius, parent_color);
// Render spacecraft's orbit around parent
Vec3 origin_vec = {0, 0, 0};
render_orbit(origin_vec, selected->local_velocity, parent_rel, parent->mass, (Color){0, 255, 255, 128}, render_state);
}
}
EndMode3D();
@ -561,4 +680,7 @@ void render_simulation(SimulationState* sim, RenderState* render_state) {
render_maneuver_marker_screen_space(craft, maneuver, render_state);
}
}
// Render child indicators
render_child_indicators(sim, render_state);
}

2
src/renderer.h

@ -39,6 +39,7 @@ float calculate_spacecraft_angle(Vec3 velocity, Camera3D camera);
// Camera setup and control
void setup_camera(RenderState* render_state);
void update_camera(RenderState* render_state, SimulationState* sim);
float get_initial_camera_distance(CelestialBody* body, SimulationState* sim, RenderState* render_state);
// Rendering functions
void render_body(CelestialBody* body, RenderState* render_state);
@ -48,6 +49,7 @@ void render_orbit(Vec3 position, Vec3 velocity, Vec3 parent_position, double par
Color get_body_orbit_color(CelestialBody* body);
// NOTE: needs to be called between begin_frame() and end_frame()
void render_simulation(SimulationState* sim, RenderState* render_state);
void render_child_indicators(SimulationState* sim, RenderState* render_state);
void begin_frame();
void end_frame();

Loading…
Cancel
Save