Browse Source
- Create ui_renderer.cpp/h with all raygui UI panel rendering functions - Remove UI rendering code from renderer.cpp (421 lines moved) - Remove UI state fields from RenderState, create separate UIState struct - Remove raygui dependency from renderer module - Update main.cpp to initialize UIState and call UI render functions - Update documentation to reflect new module structure - Improve separation of concerns between 3D rendering and UI overlaysmain
7 changed files with 491 additions and 443 deletions
@ -0,0 +1,421 @@
|
||||
#include "ui_renderer.h" |
||||
#include "spacecraft.h" |
||||
#include "maneuver.h" |
||||
#include <cstdio> |
||||
#include <cstring> |
||||
#include <cmath> |
||||
|
||||
// raygui implementation (header-only library)
|
||||
#define RAYGUI_IMPLEMENTATION |
||||
#include "raygui.h" |
||||
|
||||
// Render simulation information overlay
|
||||
void render_info(SimulationState* sim) { |
||||
// Panel dimensions
|
||||
int panel_width = 300; |
||||
int panel_height = 300; |
||||
int panel_x = 10; |
||||
int panel_y = GetScreenHeight() - panel_height - 10; |
||||
|
||||
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height }; |
||||
|
||||
// Draw window
|
||||
if (GuiWindowBox(panel_bounds, "Info")) { |
||||
// Window box close button doesn't close this panel
|
||||
} |
||||
|
||||
// Prepare info text
|
||||
char info_text[1024]; |
||||
char temp_buffer[512]; |
||||
|
||||
snprintf(info_text, sizeof(info_text), "Orbital Mechanics Simulation\n\n"); |
||||
|
||||
// Config name
|
||||
if (sim->config_name[0] != '\0') { |
||||
snprintf(temp_buffer, sizeof(temp_buffer), "Config: %s\n\n", sim->config_name); |
||||
strcat(info_text, temp_buffer); |
||||
} |
||||
|
||||
// Simulation time (in days)
|
||||
double days = sim->time / 86400.0; // seconds to days
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "Time: %.2f days\n", days); |
||||
strcat(info_text, temp_buffer); |
||||
|
||||
// Body count
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "Bodies: %d\n", sim->body_count); |
||||
strcat(info_text, temp_buffer); |
||||
|
||||
// FPS
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "FPS: %d\n\n", GetFPS()); |
||||
strcat(info_text, temp_buffer); |
||||
|
||||
// Controls
|
||||
strcat(info_text, "Controls:\n"); |
||||
strcat(info_text, " Arrows: Rotate/Zoom\n"); |
||||
strcat(info_text, " Space: Pause/Resume\n"); |
||||
strcat(info_text, " +/-: Speed control\n"); |
||||
strcat(info_text, " ESC: Quit\n"); |
||||
|
||||
// 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); |
||||
} |
||||
|
||||
// Render body list UI panel
|
||||
void render_body_list_ui(SimulationState* sim, RenderState* render_state, UIState* ui_state) { |
||||
// Panel dimensions
|
||||
int panel_width = 200; |
||||
int panel_height = 400; |
||||
int panel_x = 10; |
||||
int panel_y = 10; |
||||
|
||||
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height }; |
||||
|
||||
// Create list string for raygui (NULL-terminated)
|
||||
// Include both bodies and spacecraft
|
||||
int total_items = sim->body_count + sim->craft_count; |
||||
int buffer_size = total_items * (64 + 3 + 1) + 1; // Extra space for "🚀 " prefix
|
||||
char* list_text = (char*)malloc(buffer_size); |
||||
if (!list_text) return; |
||||
|
||||
list_text[0] = '\0'; |
||||
int offset = 0; |
||||
int item_index = 0; |
||||
|
||||
// Add bodies to list
|
||||
for (int i = 0; i < sim->body_count; i++) { |
||||
if (item_index > 0) { |
||||
offset += snprintf(list_text + offset, buffer_size - offset, ";"); |
||||
} |
||||
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)
|
||||
GuiWindowBox(panel_bounds, "Objects"); |
||||
|
||||
// Draw list view (inside window, leave space for title bar)
|
||||
Rectangle list_bounds = { |
||||
(float)panel_x + 4, |
||||
(float)panel_y + 25, |
||||
(float)panel_width - 8, |
||||
(float)panel_height - 29 |
||||
}; |
||||
|
||||
int previous_active = ui_state->body_list_active; |
||||
GuiListView(list_bounds, list_text, &ui_state->body_list_scroll, &ui_state->body_list_active); |
||||
|
||||
// Check if an item was selected
|
||||
if (ui_state->body_list_active >= 0 && ui_state->body_list_active != previous_active) { |
||||
if (ui_state->body_list_active < sim->body_count) { |
||||
// 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; |
||||
printf("Camera follow enabled for body: %s\n", sim->bodies[render_state->selected_body_index].name); |
||||
} else { |
||||
// A spacecraft was selected
|
||||
int craft_index = ui_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(list_text); |
||||
} |
||||
|
||||
// Render body information UI panel
|
||||
void render_body_info_ui(SimulationState* sim, RenderState* render_state) { |
||||
// Panel dimensions
|
||||
int panel_width = 250; |
||||
int panel_height = 300; |
||||
int panel_x = GetScreenWidth() - panel_width - 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 }; |
||||
|
||||
// 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)
|
||||
GuiWindowBox(panel_bounds, body->name); |
||||
|
||||
// Prepare info text
|
||||
char info_text[1024]; |
||||
char temp_buffer[256]; |
||||
|
||||
snprintf(info_text, sizeof(info_text), "Name: %s", body->name); |
||||
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "Mass: %.2e kg", body->mass); |
||||
strcat(info_text, "\n"); |
||||
strcat(info_text, temp_buffer); |
||||
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "Radius: %.2e m", body->radius); |
||||
strcat(info_text, "\n"); |
||||
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); |
||||
} |
||||
|
||||
// Orbital elements
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "Eccentricity: %.3f", body->eccentricity); |
||||
strcat(info_text, "\n"); |
||||
strcat(info_text, temp_buffer); |
||||
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "Semi-major axis: %.2e m", body->semi_major_axis); |
||||
strcat(info_text, "\n"); |
||||
strcat(info_text, temp_buffer); |
||||
|
||||
// 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)
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "local_position: (%.2e, %.2e, %.2e)", |
||||
craft->local_position.x, craft->local_position.y, craft->local_position.z); |
||||
strcat(info_text, "\n"); |
||||
strcat(info_text, temp_buffer); |
||||
|
||||
// Velocity (Local)
|
||||
double vel_mag = vec3_magnitude(craft->local_velocity); |
||||
snprintf(temp_buffer, sizeof(temp_buffer), "local_velocity: %.2f m/s", vel_mag); |
||||
strcat(info_text, "\n"); |
||||
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; |
||||
} |
||||
} |
||||
|
||||
// Render maneuver list UI panel
|
||||
void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state) { |
||||
if (render_state->selected_craft_index < 0 || render_state->selected_craft_index >= sim->craft_count) { |
||||
return; // No spacecraft selected
|
||||
} |
||||
|
||||
int panel_width = 300; |
||||
int panel_height = 400; |
||||
int panel_x = GetScreenWidth() - panel_width - 10; |
||||
int panel_y = 320; // Below info panel
|
||||
|
||||
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height }; |
||||
|
||||
Spacecraft* craft = &sim->spacecraft[render_state->selected_craft_index]; |
||||
|
||||
// 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++; |
||||
} |
||||
} |
||||
|
||||
if (craft_maneuver_count == 0) { |
||||
// No maneuvers for this spacecraft
|
||||
Rectangle text_bounds = { |
||||
(float)panel_x + 8, |
||||
(float)panel_y + 25, |
||||
(float)panel_width - 16, |
||||
(float)panel_height - 29 |
||||
}; |
||||
GuiLabel(text_bounds, "No maneuvers defined"); |
||||
return; |
||||
} |
||||
|
||||
// Create maneuver list string for raygui
|
||||
int buffer_size = craft_maneuver_count * (64 + 10 + 1) + 1; |
||||
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_width - 8, |
||||
(float)panel_height - 100 // Leave space for details below
|
||||
}; |
||||
|
||||
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); |
||||
} |
||||
@ -0,0 +1,20 @@
|
||||
#ifndef UI_RENDERER_H |
||||
#define UI_RENDERER_H |
||||
|
||||
#include "simulation.h" |
||||
#include "renderer.h" |
||||
#include "raylib.h" |
||||
|
||||
// UI state
|
||||
struct UIState { |
||||
int body_list_scroll; // Scroll position for body list
|
||||
int body_list_active; // Active item index in body list
|
||||
}; |
||||
|
||||
// UI rendering functions
|
||||
void render_info(SimulationState* sim); |
||||
void render_body_list_ui(SimulationState* sim, RenderState* render_state, UIState* ui_state); |
||||
void render_body_info_ui(SimulationState* sim, RenderState* render_state); |
||||
void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state); |
||||
|
||||
#endif |
||||
Loading…
Reference in new issue