11 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
int selected_craft_index; // -1 = no selection
int last_target_index; // Tracks body or craft index (negative = spacecraft)
int body_list_scroll; // Scroll position for body list
int body_list_active; // Active item index in body list
bool camera_target_enabled; // Whether camera follows selected body/craft
Vector3 camera_offset; // Offset from target when following body
};
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, 50, 100) initially
- Target: Origin (0, 0, 0)
- Up vector: (0, 1, 0)
- FOV: 45 degrees
- Projection: Perspective
Camera Controls
Arrow Keys:
- Left/Right: Orbit around target (horizontal rotation)
- Up/Down: Zoom in/out (preserve viewing angle)
Camera Follow Mode:
- Tracks selected body or spacecraft
- Preserves relative offset when following
- Updates target position each frame to follow moving objects
- Maintains camera distance when switching between bodies
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 to maintain distance
- Frame update: Move camera to maintain offset from moving target
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:
- Screen-space 2D overlay using
DrawTexturePro() - Constant screen size: 40 pixels (independent of camera zoom)
- Cyan color (0, 255, 255)
- Rotated to face velocity direction
- Off-screen culling (skips rendering if outside viewport)
Purpose: UI indicator for spacecraft that remains visible at all zoom levels
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, after spacecraft (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
- Spacecraft
- 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)
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 Spacecraft: Draw all spacecraft
- Render Maneuver Markers: Draw pending maneuver indicators
- EndMode3D: Exit 3D rendering
- Render UI Panels: Draw all raygui panels
- EndDrawing: Complete frame
Performance Considerations
- 60 FPS target
- Wireframe rendering for faster performance
- Minimum visible size prevents tiny bodies
- Fixed spacecraft marker size for visibility at distance
- 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_body(body, render_state)- Draw single celestial bodyrender_spacecraft_screen_space(craft, render_state)- Draw spacecraft marker as 2D overlayrender_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 panels allocate temporary buffers for lists
- List text freed after rendering
- No persistent UI state allocation
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)