12 KiB
Rendering System - Technical Reference
Overview
3D visualization system using raylib for interactive orbital mechanics simulation. Supports linear distance scaling, relative rendering with child indicators, orbit path rendering, spacecraft tracking, and maneuver planning visualization.
Core Data Structure
RenderState (renderer.h)
struct RenderState {
Camera3D camera;
double distance_scale; // Scale factor for distances
double size_scale; // Scale factor for body sizes
int selected_body_index; // -1 = no selection
bool camera_target_enabled; // Whether camera follows selected body
Vector3 camera_offset; // Offset from target when following body
int last_target_index; // Tracks body index for change detection
Texture2D maneuver_textures[6]; // One per burn direction
bool texture_loaded;
};
UIState (ui_renderer.h)
struct UIState {
int body_list_scroll; // Scroll position for body list
int body_list_active; // Active item index in body list
int selected_craft_index; // Selected spacecraft index (-1 = no selection)
int maneuver_list_active;
int maneuver_list_scroll;
int selected_maneuver_index;
int cached_body_count, cached_craft_count;
int cached_maneuver_count, cached_executed_count;
char* body_list_buffer; int body_list_buffer_size;
char* craft_list_buffer; int craft_list_buffer_size;
char* body_dropdown_buffer; int body_dropdown_buffer_size;
char* maneuver_list_buffer; int maneuver_list_buffer_size;
ManeuverDialogState maneuver_dialog;
};
ManeuverDialogState (ui_renderer.h)
struct ManeuverDialogState {
bool open;
ManeuverDialogTab active_tab;
int craft_index, direction_active;
double delta_v, trigger_value;
int edit_maneuver_index;
int target_body_index, current_body_index, transfer_body_index;
bool show_hohmann_preview;
HohmannTransfer last_calc;
bool show_preview, preview_valid;
OrbitalElements preview_elements;
char error_message[256];
bool show_error, show_delete_confirm;
};
Coordinate Transformation
Simulation to Render Coordinates
The simulation uses an XY plane while rendering uses XZ plane (Y is up in raylib).
Transformation:
- Simulation (X, Y, Z) → Render (X, Z, -Y)
- 90-degree rotation around X-axis
Implementation:
Vector3 sim_to_render(Vec3 pos, double scale) {
return (Vector3){
(float)(pos.x * scale),
(float)(pos.z * scale),
(float)(-pos.y * scale)
};
}
Scaling Factors
Distance Scale: 1e-9 (1 render unit = 1 billion meters)
- Optimized for solar system scale
- Used for both position transformations and radius scaling
Size Scale: Same as distance scale (linear scaling)
Radius Scaling:
float scale_radius(double radius, double scale) {
return (float)(radius * scale);
}
Uses linear scaling for consistent representation at all scales.
Camera System
Camera Setup
- Position: (0, CAMERA_INITIAL_POSITION_Y, CAMERA_INITIAL_POSITION_Z) initially
- Target: Origin (0, 0, 0)
- Up vector: (0, 1, 0)
- FOV: CAMERA_INITIAL_FOV (45 degrees)
- Projection: Perspective
Camera Control Constants
- Orbit rotation speed: CAMERA_ORBIT_ANGLE_SPEED (0.02 radians per keypress)
- Zoom percentage: ZOOM_PERCENTAGE (7% of current distance per keypress)
- Zoom delta limits: ZOOM_MIN_DELTA (0.05) to ZOOM_MAX_DELTA (5.0)
- Initial height factor: CAMERA_INITIAL_HEIGHT_FACTOR (0.3)
- Minimum distance fallback: CAMERA_MIN_DISTANCE (0.1 for no target)
Camera Controls
Arrow Keys:
- Left/Right: Orbit around target (horizontal rotation)
- Up/Down: Zoom in/out (preserve viewing angle)
- Zoom speed is percentage-based: faster when far, slower when close
- Zoom delta clamped to prevent too-slow or too-fast movement
Camera Follow Mode:
- Tracks selected body only (spacecraft selection handled by UI)
- When spacecraft selected, camera follows spacecraft's parent body
- Preserves relative offset when following
- Updates target position each frame to follow moving objects
- Maintains camera distance when switching between bodies
- Initial camera position accounts for body size when switching targets
Camera Rotation Logic:
- Uses camera's up vector for horizontal orbit
- Rotates forward vector around up axis
- Preserves camera distance from target
Follow State Transitions:
- Enable follow: Store current offset from target
- Body switch: Recalculate offset using get_initial_camera_distance()
- Frame update: Move camera to maintain offset from moving target
- Spacecraft selection: Camera focuses on spacecraft's parent body
Object Rendering
Celestial Bodies
Rendering:
- Wireframe spheres (
DrawSphereWires) - Color from body's RGB values
- Position scaled and transformed to render coordinates
- Radius scaled with minimum visible size
Order: All bodies rendered after orbit paths
Spacecraft
Rendering:
- Only visible via child indicators when parent body is selected
- No separate 3D or screen-space rendering
- Orbits rendered as cyan (0, 255, 255) lines
Purpose: Spacecraft are tracked via orbit paths and child indicators only
Maneuver Markers
Rendering:
- Screen-space 2D overlay using
DrawTexturePro() - Size based on delta-v magnitude (20-60 pixels, clamped)
- Only shown for unexecuted maneuvers
- Off-screen culling (skips rendering if outside viewport)
Color Coding:
- PROGRADE: Green (0, 255, 0)
- RETROGRADE: Red (255, 0, 0)
- NORMAL: Yellow (255, 255, 0)
- ANTINORMAL: Orange (255, 165, 0)
- RADIAL_IN: Magenta (255, 0, 255)
- RADIAL_OUT: Cyan (0, 255, 255)
- CUSTOM: White (255, 255, 255)
Render Order: Top layer, rendered as 2D overlays after 3D scene
Child Indicators
Screen-space 2D overlays for children of selected body (similar to NASA Eyes).
Rendering:
- NASA Eyes-style hollow circles
- Radius: 20px, Thickness: 2px
- Text label centered inside indicator
- White color for bodies, cyan (0, 255, 255) for spacecraft
- Only shown when body selected (not when spacecraft selected)
- Uses
GetWorldToScreen()for 2D positioning afterEndMode3D()
Purpose: Indicates location of children bodies/spacecraft that may be off-screen or hard to locate
Orbit Rendering
Orbital Basis Calculation
Components:
periapsis_dir: Normalized eccentricity vector (points toward periapsis)normal: Normalized angular momentum vector (orbit plane normal)q_vec: Cross product of normal and periapsis_dir (completes basis)
Purpose: Defines orbital plane for transforming orbital elements to Cartesian coordinates
Elliptical Orbits (e < 0.98)
Parameters:
- Semi-major axis:
a = -μ / (2 * specific_energy) - Semi-minor axis:
b = a * sqrt(1 - e²) - Focus offset:
c = a * e
Rendering:
- 100 segments
- Full orbit (0 to 2π)
- Drawn as series of line segments in orbital basis
Parabolic Orbits (0.98 ≤ e ≤ 1.02)
Parameters:
- Semi-latus rectum:
p = h² / μ
Rendering:
- 80 segments
- True anomaly range: -π0.95 to π0.95 (avoid singularity at ±π)
- Escape trajectory path
Hyperbolic Orbits (e > 1.02)
Parameters:
- Semi-major axis:
a = μ / (2 * (-specific_energy))(negative for hyperbola) - Semi-latus rectum:
p = a * (1 - e²) - Max true anomaly:
acos(-1/e) * 0.95
Rendering:
- 60 segments
- Shows asymptotic behavior approaching true anomaly limit
- Fast escape trajectory visualization
Orbit Color: Half-intensity body color (128 alpha)
Render Order
- Reference grid
- Orbit paths (for all bodies with parents)
- Bodies
- Maneuver markers
- Child indicators (screen-space overlays)
Relative Rendering
When a body is selected, the scene is rendered relative to that body:
- Selected body is rendered at origin (0, 0, 0)
- Children are rendered relative to selected body
- Children's orbits are rendered around origin
- Uses full float precision for small orbital distances (e.g., LEO)
When a spacecraft is selected:
- Parent body is rendered relative to spacecraft
- Spacecraft's orbit is rendered around parent
- No sibling spacecraft rendered
Benefits:
- Improved visibility of small orbits (LEO, moon orbits)
- Better float precision for local coordinate systems
- Automatic camera positioning based on children distances
UI System (ui_renderer + raygui)
Info Panel (Bottom-Left)
Content:
- Simulation time (in days)
- Body count
- FPS
- Controls reference
- Config file name
Dimensions: 300×300 pixels
Objects List (Top-Left)
Content:
- All celestial bodies (no prefix)
- All spacecraft (🚀 prefix)
- Scrollable list view
Interaction:
- Clicking enables camera follow
- Updates selected_body_index or selected_craft_index
- Triggers camera offset calculation
Dimensions: 200×400 pixels
Info Panel (Top-Right)
Content (Body Selected):
- Name, mass, radius
- Position (global for root, local for children)
- Velocity magnitude
- Eccentricity, semi-major axis
- Parent body name
- SOI radius
Content (Spacecraft Selected):
- Name, mass
- Local position and velocity
- Parent body name
- Pending/Executed maneuver counts
Empty: Displays placeholder panel when nothing selected
Dimensions: 250×300 pixels
Maneuver List (Below Info Panel)
Content (Spacecraft Selected Only):
- List of maneuvers for selected spacecraft
- Status prefix: [PENDING] or [DONE]
- Maneuver names
Details:
- Next pending maneuver: name and delta-v
- Last executed maneuver: name, delta-v, execution time
- "No maneuvers defined" if none exist
Dimensions: 300×400 pixels Position: Y=320 (below info panel)
Dropdown lists use cached buffers, rebuilt only when object counts change.
Rendering Pipeline
Main Loop Sequence
- BeginDrawing: Clear background to black
- BeginMode3D: Enter 3D rendering
- Draw Reference Grid: Subtle gray grid lines
- Render Orbit Paths: Calculate and draw all orbits
- Render Bodies: Draw all celestial bodies
- Render Maneuver Markers: Draw pending maneuver indicators
- EndMode3D: Exit 3D rendering
- Render UI Panels: Draw all raygui panels
- Render Child Indicators: Draw child overlays
- EndDrawing: Complete frame
Performance Considerations
- 60 FPS target
- Wireframe rendering for faster performance
- Minimum visible size prevents tiny bodies
- Clipped orbit segments to avoid infinite paths
Module Functions
Renderer Module (renderer.cpp/h)
Initialization
init_renderer(width, height, title)- Setup raylib windowclose_renderer()- Cleanup raylibsetup_camera(render_state)- Initialize camera parameters
Camera
update_camera(render_state, sim)- Handle input and camera followget_initial_camera_distance(body, sim, render_state)- Auto-position camera based on children distance
Rendering
render_maneuver_marker_screen_space(craft, maneuver, render_state)- Draw burn indicator as 2D overlayrender_simulation(sim, render_state)- Full scene rendering (3D + 2D overlays)
Scaling
scale_position(pos, scale)- Transform simulation to render positionscale_radius(radius, scale)- Apply size scaling with minimum
UI Renderer Module (ui_renderer.cpp/h)
UI Rendering
render_info(sim)- Bottom-left info panelrender_body_list_ui(sim, render_state, ui_state)- Objects selection panelrender_body_info_ui(sim, render_state)- Top-right info panelrender_maneuver_list_ui(sim, render_state)- Maneuver list panel
Note: UI rendering functions are called after render_simulation() to ensure UI overlays appear on top of the 3D scene.
Technical Notes
Raylib Integration
- Header-only raygui for UI
- raymath for vector operations
- Perspective 3D camera
- Wireframe sphere rendering
Color Handling
- Body colors: float RGB (0.0-1.0) → byte RGB (0-255)
- Orbit colors: 50% intensity, 50% alpha
- Spacecraft: Fixed cyan
- Maneuvers: Direction-based colors with alpha
Reference Grid
- Subtle gray lines (20, 20, 20) for minor axes
- Brighter axes (40, 40, 40) for X and Z zero lines
- Extends from -500 to 500 in both directions
- Helps orient user in 3D space
Memory Management
UI buffers persist between frames and rebuild when object counts change. Buffers freed on shutdown via ui_state_free_buffers().
TODO Items
-
Distant Bodies: When body selected, show indicators for bodies that are not children (currently only shows direct children)
-
Smooth Frame Transitions: Add interpolation when switching between different selected bodies (instant switch currently)