vibe coding an orbital mechanics simulation to try out claude code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

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:

  1. Enable follow: Store current offset from target
  2. Body switch: Recalculate offset using get_initial_camera_distance()
  3. Frame update: Move camera to maintain offset from moving target
  4. 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 after EndMode3D()

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

  1. Reference grid
  2. Orbit paths (for all bodies with parents)
  3. Bodies
  4. Maneuver markers
  5. 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

  1. BeginDrawing: Clear background to black
  2. BeginMode3D: Enter 3D rendering
  3. Draw Reference Grid: Subtle gray grid lines
  4. Render Orbit Paths: Calculate and draw all orbits
  5. Render Bodies: Draw all celestial bodies
  6. Render Maneuver Markers: Draw pending maneuver indicators
  7. EndMode3D: Exit 3D rendering
  8. Render UI Panels: Draw all raygui panels
  9. Render Child Indicators: Draw child overlays
  10. 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 window
  • close_renderer() - Cleanup raylib
  • setup_camera(render_state) - Initialize camera parameters

Camera

  • update_camera(render_state, sim) - Handle input and camera follow
  • get_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 overlay
  • render_simulation(sim, render_state) - Full scene rendering (3D + 2D overlays)

Scaling

  • scale_position(pos, scale) - Transform simulation to render position
  • scale_radius(radius, scale) - Apply size scaling with minimum

UI Renderer Module (ui_renderer.cpp/h)

UI Rendering

  • render_info(sim) - Bottom-left info panel
  • render_body_list_ui(sim, render_state, ui_state) - Objects selection panel
  • render_body_info_ui(sim, render_state) - Top-right info panel
  • render_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

  1. Distant Bodies: When body selected, show indicators for bodies that are not children (currently only shows direct children)

  2. Smooth Frame Transitions: Add interpolation when switching between different selected bodies (instant switch currently)