Browse Source

Add spacecraft rendering with maneuver visualization

- Spacecraft rendered as cyan wireframe spheres (fixed size for visibility)
- Maneuver markers as colored cubes (direction-based colors: green=prograde, red=retrograde, etc.)
- Object list now includes spacecraft with '🚀 ' prefix
- Camera follow extended to support spacecraft tracking
- Spacecraft info panel displays local coordinates and maneuver counts
- Maneuver list panel shows pending/executed status with details
- Panel renamed from 'Bodies' to 'Objects'
main
cinnaboot 6 months ago
parent
commit
b96f1cfe8c
  1. 1
      src/main.cpp
  2. 466
      src/renderer.cpp
  3. 6
      src/renderer.h

1
src/main.cpp

@ -47,6 +47,7 @@ void run_gui_simulation(SimulationState* sim) {
// Initialize UI state // Initialize UI state
render_state.selected_body_index = root_body_index >= 0 ? root_body_index : -1; 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.previous_selected_body = -1; // Previous selected body index
render_state.body_list_scroll = 0; // Initial scroll position render_state.body_list_scroll = 0; // Initial scroll position
render_state.body_list_active = root_body_index >= 0 ? root_body_index : -1; // Sync with selected body render_state.body_list_active = root_body_index >= 0 ? root_body_index : -1; // Sync with selected body

466
src/renderer.cpp

@ -46,21 +46,32 @@ void update_camera(RenderState* render_state, SimulationState* sim) {
Vector3 body_pos = sim_to_render(body->position, render_state->distance_scale); Vector3 body_pos = sim_to_render(body->position, render_state->distance_scale);
render_state->camera.target = body_pos; render_state->camera.target = body_pos;
render_state->camera_offset = Vector3Subtract(render_state->camera.position, 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->position, render_state->distance_scale);
render_state->camera.target = craft_pos;
render_state->camera_offset = Vector3Subtract(render_state->camera.position, craft_pos);
} }
} }
// Preserve distance when switching to different body // Preserve distance when switching to different body or spacecraft
if (render_state->camera_follow_body && render_state->selected_body_index >= 0 && if (render_state->camera_follow_body) {
render_state->selected_body_index != render_state->previous_selected_body && if (render_state->selected_body_index >= 0 &&
render_state->selected_body_index < sim->body_count) { render_state->selected_body_index != render_state->previous_selected_body &&
// Body selection changed - recalculate offset to maintain distance render_state->selected_body_index < sim->body_count) {
Vector3 body_pos = sim_to_render(sim->bodies[render_state->selected_body_index].position, render_state->distance_scale); // Body selection changed - recalculate offset to maintain distance
render_state->camera.target = body_pos; Vector3 body_pos = sim_to_render(sim->bodies[render_state->selected_body_index].position, render_state->distance_scale);
// Use current offset to maintain distance to new body render_state->camera.target = body_pos;
render_state->camera.position = Vector3Add(body_pos, render_state->camera_offset); 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].position, render_state->distance_scale);
render_state->camera.target = craft_pos;
render_state->camera.position = Vector3Add(craft_pos, render_state->camera_offset);
}
} }
// Update target position when following // Update target position when following body
if (render_state->camera_follow_body && render_state->selected_body_index >= 0 && if (render_state->camera_follow_body && render_state->selected_body_index >= 0 &&
render_state->selected_body_index < sim->body_count) { render_state->selected_body_index < sim->body_count) {
CelestialBody* body = &sim->bodies[render_state->selected_body_index]; CelestialBody* body = &sim->bodies[render_state->selected_body_index];
@ -69,6 +80,15 @@ void update_camera(RenderState* render_state, SimulationState* sim) {
render_state->camera.position = Vector3Add(body_pos, render_state->camera_offset); 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) {
Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index];
Vector3 craft_pos = sim_to_render(craft->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 // Camera rotation using camera's up vector
Vector3 to_camera = Vector3Subtract(render_state->camera.position, render_state->camera.target); Vector3 to_camera = Vector3Subtract(render_state->camera.position, render_state->camera.target);
float camera_distance = Vector3Length(to_camera); float camera_distance = Vector3Length(to_camera);
@ -186,6 +206,59 @@ void render_body(CelestialBody* body, RenderState* render_state) {
DrawSphereWires(position, radius, 16, 16, color); DrawSphereWires(position, radius, 16, 16, color);
} }
// Render a single spacecraft as a simple geometric marker
void render_spacecraft(Spacecraft* craft, RenderState* render_state) {
Vector3 position = sim_to_render(craft->position, render_state->distance_scale);
// Fixed size for visibility at solar system distances
float marker_radius = 5.0f;
Color spacecraft_color = { 0, 255, 255, 255 }; // Cyan
DrawSphereWires(position, marker_radius, 8, 8, spacecraft_color);
}
// Render a maneuver marker for a spacecraft
void render_maneuver_marker(Spacecraft* craft, Maneuver* maneuver, RenderState* render_state) {
if (maneuver->executed) {
return; // Don't show markers for executed maneuvers
}
Vector3 position = sim_to_render(craft->position, render_state->distance_scale);
// Size based on delta-v magnitude
float marker_size = (float)(maneuver->delta_v / 100.0f); // Scale by delta-v
if (marker_size < 2.0f) marker_size = 2.0f;
if (marker_size > 10.0f) marker_size = 10.0f;
// Color based on direction
Color marker_color;
switch (maneuver->direction) {
case BURN_PROGRADE:
marker_color = (Color){ 0, 255, 0, 200 }; // Green
break;
case BURN_RETROGRADE:
marker_color = (Color){ 255, 0, 0, 200 }; // Red
break;
case BURN_NORMAL:
marker_color = (Color){ 255, 255, 0, 200 }; // Yellow
break;
case BURN_ANTINORMAL:
marker_color = (Color){ 255, 165, 0, 200 }; // Orange
break;
case BURN_RADIAL_IN:
marker_color = (Color){ 255, 0, 255, 200 }; // Magenta
break;
case BURN_RADIAL_OUT:
marker_color = (Color){ 0, 255, 255, 200 }; // Cyan
break;
default:
marker_color = (Color){ 255, 255, 255, 200 }; // White
break;
}
DrawCube(position, marker_size, marker_size, marker_size, marker_color);
}
struct OrbitalBasis { struct OrbitalBasis {
Vec3 periapsis_dir; Vec3 periapsis_dir;
Vec3 normal; Vec3 normal;
@ -388,12 +461,27 @@ void render_simulation(SimulationState* sim, RenderState* render_state) {
render_body(&sim->bodies[i], render_state); render_body(&sim->bodies[i], render_state);
} }
// Render all spacecraft (after bodies for visibility)
for (int i = 0; i < sim->craft_count; i++) {
render_spacecraft(&sim->spacecraft[i], render_state);
}
// Render maneuver markers (top layer)
for (int i = 0; i < sim->maneuver_count; i++) {
Maneuver* maneuver = &sim->maneuvers[i];
if (!maneuver->executed && maneuver->craft_index >= 0 && maneuver->craft_index < sim->craft_count) {
Spacecraft* craft = &sim->spacecraft[maneuver->craft_index];
render_maneuver_marker(craft, maneuver, render_state);
}
}
EndMode3D(); EndMode3D();
// Render UI panels (always shown) // Render UI panels (always shown)
render_info(sim); render_info(sim);
render_body_list_ui(sim, render_state); render_body_list_ui(sim, render_state);
render_body_info_ui(sim, render_state); render_body_info_ui(sim, render_state);
render_maneuver_list_ui(sim, render_state);
EndDrawing(); EndDrawing();
} }
@ -466,23 +554,35 @@ void render_body_list_ui(SimulationState* sim, RenderState* render_state) {
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height }; Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
// Create body list string for raygui (NULL-terminated) // Create list string for raygui (NULL-terminated)
int buffer_size = sim->body_count * (64 + 1) + 1; // Include both bodies and spacecraft
char* body_list_text = (char*)malloc(buffer_size); int total_items = sim->body_count + sim->craft_count;
if (!body_list_text) return; int buffer_size = total_items * (64 + 3 + 1) + 1; // Extra space for "🚀 " prefix
char* list_text = (char*)malloc(buffer_size);
if (!list_text) return;
body_list_text[0] = '\0'; list_text[0] = '\0';
int offset = 0; int offset = 0;
int item_index = 0;
// Add bodies to list
for (int i = 0; i < sim->body_count; i++) { for (int i = 0; i < sim->body_count; i++) {
if (i > 0) { if (item_index > 0) {
offset += snprintf(body_list_text + offset, buffer_size - offset, ";"); offset += snprintf(list_text + offset, buffer_size - offset, ";");
} }
offset += snprintf(body_list_text + offset, buffer_size - offset, "%s", sim->bodies[i].name); offset += snprintf(list_text + offset, buffer_size - offset, "%s", sim->bodies[i].name);
item_index++;
}
// Add spacecraft to list with "🚀 " prefix
for (int i = 0; i < sim->craft_count; i++) {
offset += snprintf(list_text + offset, buffer_size - offset, ";");
offset += snprintf(list_text + offset, buffer_size - offset, "🚀 %s", sim->spacecraft[i].name);
item_index++;
} }
// Draw window (no close button - panels always shown) // Draw window (no close button - panels always shown)
GuiWindowBox(panel_bounds, "Bodies"); GuiWindowBox(panel_bounds, "Objects");
// Draw list view (inside window, leave space for title bar) // Draw list view (inside window, leave space for title bar)
Rectangle list_bounds = { Rectangle list_bounds = {
@ -493,118 +593,306 @@ void render_body_list_ui(SimulationState* sim, RenderState* render_state) {
}; };
int previous_active = render_state->body_list_active; int previous_active = render_state->body_list_active;
GuiListView(list_bounds, body_list_text, &render_state->body_list_scroll, &render_state->body_list_active); GuiListView(list_bounds, list_text, &render_state->body_list_scroll, &render_state->body_list_active);
// Check if a body was selected (active changed from -1 or previous value) // Check if an item was selected
if (render_state->body_list_active >= 0 && render_state->body_list_active < sim->body_count && if (render_state->body_list_active >= 0 && render_state->body_list_active != previous_active) {
render_state->body_list_active != previous_active) { if (render_state->body_list_active < sim->body_count) {
render_state->selected_body_index = render_state->body_list_active; // A body was selected
// Enable camera follow when body is selected render_state->selected_body_index = render_state->body_list_active;
render_state->camera_follow_body = true; render_state->selected_craft_index = -1;
printf("Camera follow enabled for: %s\n", sim->bodies[render_state->selected_body_index].name); render_state->camera_follow_body = true;
printf("Camera follow enabled for body: %s\n", sim->bodies[render_state->selected_body_index].name);
} else {
// A spacecraft was selected
int craft_index = render_state->body_list_active - sim->body_count;
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;
printf("Camera follow enabled for spacecraft: %s\n", sim->spacecraft[craft_index].name);
}
}
} }
free(body_list_text); free(list_text);
} }
// Render body information UI panel // Render body information UI panel
void render_body_info_ui(SimulationState* sim, RenderState* render_state) { void render_body_info_ui(SimulationState* sim, RenderState* render_state) {
if (render_state->selected_body_index < 0 || render_state->selected_body_index >= sim->body_count) {
// No body selected - render empty panel
int panel_width = 250;
int panel_height = 300;
int panel_x = GetScreenWidth() - panel_width - 10;
int panel_y = 10;
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
GuiWindowBox(panel_bounds, "Info");
return;
}
// Panel dimensions // Panel dimensions
int panel_width = 250; int panel_width = 250;
int panel_height = 300; int panel_height = 300;
int panel_x = GetScreenWidth() - panel_width - 10; int panel_x = GetScreenWidth() - panel_width - 10;
int panel_y = 10; int panel_y = 10;
if ((render_state->selected_body_index < 0 || render_state->selected_body_index >= sim->body_count) &&
(render_state->selected_craft_index < 0 || render_state->selected_craft_index >= sim->craft_count)) {
// No body or spacecraft selected - render empty panel
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
GuiWindowBox(panel_bounds, "Info");
return;
}
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height }; Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
CelestialBody* body = &sim->bodies[render_state->selected_body_index]; // Check if a body is selected
if (render_state->selected_body_index >= 0 && render_state->selected_body_index < sim->body_count) {
CelestialBody* body = &sim->bodies[render_state->selected_body_index];
// Draw window (no close button - panels always shown) // Draw window (no close button - panels always shown)
GuiWindowBox(panel_bounds, body->name); GuiWindowBox(panel_bounds, body->name);
// Prepare info text // Prepare info text
char info_text[1024]; char info_text[1024];
char temp_buffer[256]; char temp_buffer[256];
snprintf(info_text, sizeof(info_text), "Name: %s", body->name); snprintf(info_text, sizeof(info_text), "Name: %s", body->name);
snprintf(temp_buffer, sizeof(temp_buffer), "Mass: %.2e kg", body->mass); snprintf(temp_buffer, sizeof(temp_buffer), "Mass: %.2e kg", body->mass);
strcat(info_text, "\n"); strcat(info_text, "\n");
strcat(info_text, temp_buffer); strcat(info_text, temp_buffer);
snprintf(temp_buffer, sizeof(temp_buffer), "Radius: %.2e m", body->radius); snprintf(temp_buffer, sizeof(temp_buffer), "Radius: %.2e m", body->radius);
strcat(info_text, "\n"); strcat(info_text, "\n");
strcat(info_text, temp_buffer); strcat(info_text, temp_buffer);
if (body->parent_index <= 0) {
// Position
snprintf(temp_buffer, sizeof(temp_buffer), "Position: (%.2e, %.2e, %.2e)",
body->position.x, body->position.y, body->position.z);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Velocity
double vel_mag = vec3_magnitude(body->velocity);
snprintf(temp_buffer, sizeof(temp_buffer), "Velocity: %.2f m/s", vel_mag);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
} else {
// Position (Local)
snprintf(temp_buffer, sizeof(temp_buffer), "local_position: (%.2e, %.2e, %.2e)",
body->local_position.x, body->local_position.y, body->local_position.z);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Velocity (Local)
double parent_vel_mag = vec3_magnitude(sim->bodies[body->parent_index].velocity);
double vel_mag = vec3_magnitude(body->velocity) - parent_vel_mag;
snprintf(temp_buffer, sizeof(temp_buffer), "local_velocity: %.2f m/s", vel_mag);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
}
if (body->parent_index <= 0) { // Orbital elements
// Position snprintf(temp_buffer, sizeof(temp_buffer), "Eccentricity: %.3f", body->eccentricity);
snprintf(temp_buffer, sizeof(temp_buffer), "Position: (%.2e, %.2e, %.2e)",
body->position.x, body->position.y, body->position.z);
strcat(info_text, "\n"); strcat(info_text, "\n");
strcat(info_text, temp_buffer); strcat(info_text, temp_buffer);
// Velocity snprintf(temp_buffer, sizeof(temp_buffer), "Semi-major axis: %.2e m", body->semi_major_axis);
double vel_mag = vec3_magnitude(body->velocity);
snprintf(temp_buffer, sizeof(temp_buffer), "Velocity: %.2f m/s", vel_mag);
strcat(info_text, "\n"); strcat(info_text, "\n");
strcat(info_text, temp_buffer); strcat(info_text, temp_buffer);
} else {
// Parent body
if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: %s", sim->bodies[body->parent_index].name);
} else {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: None");
}
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// SOI
snprintf(temp_buffer, sizeof(temp_buffer), "SOI radius: %.2e m", body->soi_radius);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Draw info text (inside window, leave space for title bar)
Rectangle text_bounds = {
(float)panel_x + 8,
(float)panel_y + 25,
(float)panel_width - 16,
(float)panel_height - 29
};
GuiLabel(text_bounds, info_text);
return;
}
// Check if a spacecraft is selected
if (render_state->selected_craft_index >= 0 && render_state->selected_craft_index < sim->craft_count) {
Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index];
// Draw window (no close button - panels always shown)
GuiWindowBox(panel_bounds, craft->name);
// Prepare info text
char info_text[1024];
char temp_buffer[256];
snprintf(info_text, sizeof(info_text), "Name: %s", craft->name);
snprintf(temp_buffer, sizeof(temp_buffer), "Mass: %.2e kg", craft->mass);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Position (Local) // Position (Local)
snprintf(temp_buffer, sizeof(temp_buffer), "local_position: (%.2e, %.2e, %.2e)", snprintf(temp_buffer, sizeof(temp_buffer), "local_position: (%.2e, %.2e, %.2e)",
body->local_position.x, body->local_position.y, body->local_position.z); craft->local_position.x, craft->local_position.y, craft->local_position.z);
strcat(info_text, "\n"); strcat(info_text, "\n");
strcat(info_text, temp_buffer); strcat(info_text, temp_buffer);
// Velocity (Local) // Velocity (Local)
double parent_vel_mag = vec3_magnitude(sim->bodies[body->parent_index].velocity); double vel_mag = vec3_magnitude(craft->local_velocity);
double vel_mag = vec3_magnitude(body->velocity) - parent_vel_mag;
snprintf(temp_buffer, sizeof(temp_buffer), "local_velocity: %.2f m/s", vel_mag); snprintf(temp_buffer, sizeof(temp_buffer), "local_velocity: %.2f m/s", vel_mag);
strcat(info_text, "\n"); strcat(info_text, "\n");
strcat(info_text, temp_buffer); strcat(info_text, temp_buffer);
// Parent body
if (craft->parent_index >= 0 && craft->parent_index < sim->body_count) {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: %s", sim->bodies[craft->parent_index].name);
} else {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: None");
}
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Maneuver info
int pending_maneuvers = 0;
int executed_maneuvers = 0;
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == render_state->selected_craft_index) {
if (sim->maneuvers[i].executed) {
executed_maneuvers++;
} else {
pending_maneuvers++;
}
}
}
snprintf(temp_buffer, sizeof(temp_buffer), "Pending maneuvers: %d", pending_maneuvers);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
snprintf(temp_buffer, sizeof(temp_buffer), "Executed maneuvers: %d", executed_maneuvers);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Draw info text (inside window, leave space for title bar)
Rectangle text_bounds = {
(float)panel_x + 8,
(float)panel_y + 25,
(float)panel_width - 16,
(float)panel_height - 29
};
GuiLabel(text_bounds, info_text);
return;
} }
}
// Orbital elements // Render maneuver list UI panel
snprintf(temp_buffer, sizeof(temp_buffer), "Eccentricity: %.3f", body->eccentricity); void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state) {
strcat(info_text, "\n"); if (render_state->selected_craft_index < 0 || render_state->selected_craft_index >= sim->craft_count) {
strcat(info_text, temp_buffer); return; // No spacecraft selected
}
snprintf(temp_buffer, sizeof(temp_buffer), "Semi-major axis: %.2e m", body->semi_major_axis); int panel_width = 300;
strcat(info_text, "\n"); int panel_height = 400;
strcat(info_text, temp_buffer); int panel_x = GetScreenWidth() - panel_width - 10;
int panel_y = 320; // Below info panel
// Parent body Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: %s", sim->bodies[body->parent_index].name); Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index];
} else {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: None"); // Create title with spacecraft name
char title[128];
snprintf(title, sizeof(title), "Maneuvers: %s", craft->name);
// Draw window
GuiWindowBox(panel_bounds, title);
// Count maneuvers for this spacecraft
int craft_maneuver_count = 0;
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == render_state->selected_craft_index) {
craft_maneuver_count++;
}
} }
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// SOI if (craft_maneuver_count == 0) {
snprintf(temp_buffer, sizeof(temp_buffer), "SOI radius: %.2e m", body->soi_radius); // No maneuvers for this spacecraft
strcat(info_text, "\n"); Rectangle text_bounds = {
strcat(info_text, temp_buffer); (float)panel_x + 8,
(float)panel_y + 25,
(float)panel_width - 16,
(float)panel_height - 29
};
GuiLabel(text_bounds, "No maneuvers defined");
return;
}
// Draw info text (inside window, leave space for title bar) // Create maneuver list string for raygui
Rectangle text_bounds = { int buffer_size = craft_maneuver_count * (64 + 10 + 1) + 1;
(float)panel_x + 8, char* maneuver_list_text = (char*)malloc(buffer_size);
if (!maneuver_list_text) return;
maneuver_list_text[0] = '\0';
int offset = 0;
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == render_state->selected_craft_index) {
if (offset > 0) {
offset += snprintf(maneuver_list_text + offset, buffer_size - offset, ";");
}
const char* status_char = sim->maneuvers[i].executed ? "[DONE]" : "[PENDING]";
offset += snprintf(maneuver_list_text + offset, buffer_size - offset, "%s %s",
status_char, sim->maneuvers[i].name);
}
}
// Draw list view (inside window, leave space for title bar)
Rectangle list_bounds = {
(float)panel_x + 4,
(float)panel_y + 25, (float)panel_y + 25,
(float)panel_width - 16, (float)panel_width - 8,
(float)panel_height - 29 (float)panel_height - 100 // Leave space for details below
}; };
GuiLabel(text_bounds, info_text); int maneuver_scroll = 0;
int maneuver_active = -1;
GuiListView(list_bounds, maneuver_list_text, &maneuver_scroll, &maneuver_active);
// Show details for first pending or last executed maneuver
char details_text[512];
details_text[0] = '\0';
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == render_state->selected_craft_index) {
if (sim->maneuvers[i].executed) {
snprintf(details_text, sizeof(details_text),
"Last executed:\n%s\nΔv: %.1f m/s\nTime: %.1f s",
sim->maneuvers[i].name, sim->maneuvers[i].delta_v, sim->maneuvers[i].executed_time);
} else {
snprintf(details_text, sizeof(details_text),
"Next pending:\n%s\nΔv: %.1f m/s",
sim->maneuvers[i].name, sim->maneuvers[i].delta_v);
break; // Show first pending
}
}
}
if (details_text[0] != '\0') {
Rectangle details_bounds = {
(float)panel_x + 8,
(float)panel_y + (float)panel_height - 70,
(float)panel_width - 16,
65
};
GuiLabel(details_bounds, details_text);
}
free(maneuver_list_text);
} }

6
src/renderer.h

@ -2,6 +2,8 @@
#define RENDERER_H #define RENDERER_H
#include "simulation.h" #include "simulation.h"
#include "spacecraft.h"
#include "maneuver.h"
#include "raylib.h" #include "raylib.h"
// Rendering state // Rendering state
@ -10,6 +12,7 @@ struct RenderState {
double distance_scale; // Scale factor for distances double distance_scale; // Scale factor for distances
double size_scale; // Scale factor for body sizes double size_scale; // Scale factor for body sizes
int selected_body_index; // -1 = no selection int selected_body_index; // -1 = no selection
int selected_craft_index; // -1 = no selection
int previous_selected_body; // Previous selected body index int previous_selected_body; // Previous selected body index
int body_list_scroll; // Scroll position for body list int body_list_scroll; // Scroll position for body list
int body_list_active; // Active item index in body list int body_list_active; // Active item index in body list
@ -28,12 +31,15 @@ void update_camera(RenderState* render_state, SimulationState* sim);
// Rendering functions // Rendering functions
void render_body(CelestialBody* body, RenderState* render_state); void render_body(CelestialBody* body, RenderState* render_state);
void render_spacecraft(Spacecraft* craft, RenderState* render_state);
void render_maneuver_marker(Spacecraft* craft, Maneuver* maneuver, RenderState* render_state);
void render_simulation(SimulationState* sim, RenderState* render_state); void render_simulation(SimulationState* sim, RenderState* render_state);
void render_info(SimulationState* sim); void render_info(SimulationState* sim);
// UI rendering functions // UI rendering functions
void render_body_list_ui(SimulationState* sim, RenderState* render_state); void render_body_list_ui(SimulationState* sim, RenderState* render_state);
void render_body_info_ui(SimulationState* sim, RenderState* render_state); void render_body_info_ui(SimulationState* sim, RenderState* render_state);
void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state);
// Scaling functions // Scaling functions
Vector3 scale_position(Vec3 pos, double scale); Vector3 scale_position(Vec3 pos, double scale);

Loading…
Cancel
Save