Browse Source

Initial commit

Orbital mechanics simulation with 2-body physics and SOI transitions.

Core Features:
- 2-body gravitational physics with sphere of influence transitions
- Real-time 3D visualization using raylib
- Configurable star systems via text files
- Interactive controls (camera, pause, speed)

Technical Implementation:
- C-style C++ (structs and functions, no classes)
- Modular architecture (physics, bodies, config loader, renderer)
- Euler integration for orbital mechanics
- SOI detection using Hill sphere approximation

Configuration System:
- Solar system with realistic data (Sun, 8 planets, 5 major moons)
- Binary star system example
- Easy to create custom systems via simple text format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
main
cinnaboot 6 months ago
commit
e8a56c93c1
  1. 14
      .gitignore
  2. 3
      .gitmodules
  3. 59
      Makefile
  4. 144
      README.md
  5. 20
      configs/example_binary_star.txt
  6. 31
      configs/solar_system.txt
  7. 235
      docs/implementation_plan.md
  8. 1
      ext/raylib
  9. 133
      src/bodies.cpp
  10. 38
      src/bodies.h
  11. 138
      src/config_loader.cpp
  12. 19
      src/config_loader.h
  13. 90
      src/main.cpp
  14. 71
      src/physics.cpp
  15. 28
      src/physics.h
  16. 159
      src/renderer.cpp
  17. 32
      src/renderer.h

14
.gitignore vendored

@ -0,0 +1,14 @@
# Build artifacts
build/
orbit_sim
# Editor/IDE files
.vscode/
.idea/
*.swp
*.swo
*~
# OS files
.DS_Store
Thumbs.db

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "ext/raylib"]
path = ext/raylib
url = https://github.com/raysan5/raylib.git

59
Makefile

@ -0,0 +1,59 @@
# Compiler and flags
CXX = g++
CXXFLAGS = -Wall -Wextra -std=c++11 -I./src -I./ext/raylib/src
LDFLAGS = -L./ext/raylib/src -lraylib -lm -lpthread -ldl -lrt -lX11
# Directories
SRC_DIR = src
BUILD_DIR = build
RAYLIB_DIR = ext/raylib/src
TARGET = orbit_sim
# Source files
SOURCES = $(SRC_DIR)/main.cpp \
$(SRC_DIR)/physics.cpp \
$(SRC_DIR)/bodies.cpp \
$(SRC_DIR)/config_loader.cpp \
$(SRC_DIR)/renderer.cpp
# Object files
OBJECTS = $(SOURCES:$(SRC_DIR)/%.cpp=$(BUILD_DIR)/%.o)
# Default target
all: raylib $(BUILD_DIR) $(TARGET)
# Build raylib
raylib:
@if [ ! -f $(RAYLIB_DIR)/libraylib.a ]; then \
echo "Building raylib..."; \
cd $(RAYLIB_DIR) && $(MAKE) PLATFORM=PLATFORM_DESKTOP; \
fi
# Create build directory
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
# Link the executable
$(TARGET): $(OBJECTS) raylib
$(CXX) $(OBJECTS) -o $(TARGET) $(LDFLAGS)
# Compile source files
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# Clean build files
clean:
rm -rf $(BUILD_DIR) $(TARGET)
# Clean everything including raylib
clean-all: clean
cd $(RAYLIB_DIR) && $(MAKE) clean
# Rebuild
rebuild: clean all
# Run the simulation
run: $(TARGET)
./$(TARGET)
.PHONY: all clean clean-all rebuild run raylib

144
README.md

@ -0,0 +1,144 @@
# Orbital Mechanics Simulation
A 3D orbital mechanics simulation using a 2-body gravitational model with sphere of influence (SOI) transitions. Features real-time visualization of celestial bodies using raylib.
## Features
- **2-body gravitational physics** with Euler integration
- **Sphere of influence (SOI)** transitions between gravitational parents
- **3D real-time visualization** using raylib
- **Configurable star systems** via simple text files
- **Interactive camera controls** (rotate, zoom)
- **Simulation controls** (pause, resume, speed adjustment)
- Solar system and binary star example configurations
## Dependencies (Debian 13)
Install the required packages:
```bash
sudo apt-get update
sudo apt-get install -y \
build-essential \
g++ \
make \
libraylib-dev \
libx11-dev \
libxcursor-dev \
libxrandr-dev \
libxinerama-dev \
libxi-dev \
libgl1-mesa-dev \
libglu1-mesa-dev
```
If `libraylib-dev` is not available in the repositories, you can build raylib from source:
```bash
# Install raylib build dependencies
sudo apt-get install -y cmake git
# Clone and build raylib
git clone https://github.com/raysan5/raylib.git
cd raylib
mkdir build && cd build
cmake .. -DBUILD_SHARED_LIBS=ON
make
sudo make install
sudo ldconfig
cd ../..
```
## Building
```bash
make
```
This will create the `orbit_sim` executable in the project directory.
## Running
Run with the default solar system configuration:
```bash
./orbit_sim
```
Run with a custom configuration file:
```bash
./orbit_sim configs/example_binary_star.txt
```
## Controls
- **Arrow Keys**: Rotate and zoom camera
- **Space**: Pause/Resume simulation
- **+/-**: Speed up/slow down simulation
- **I**: Toggle info display
- **ESC**: Quit
## Configuration File Format
Configuration files define celestial bodies in a simple text format:
```
# Comments start with #
# Format: name mass(kg) radius(m) x(m) y(m) z(m) parent_index r g b
Sun 1.989e30 6.96e8 0 0 0 -1 1.0 1.0 0.0
Earth 5.972e24 6.371e6 1.496e11 0 0 0 0.0 0.5 1.0
Moon 7.342e22 1.737e6 1.500e11 0 0 1 0.7 0.7 0.7
```
Fields:
- **name**: Body name (string, no spaces)
- **mass**: Mass in kilograms
- **radius**: Radius in meters
- **x, y, z**: Initial position in meters
- **parent_index**: Index of gravitational parent (-1 for root bodies like stars)
- **r, g, b**: RGB color values (0.0 to 1.0)
Velocities are calculated automatically for circular orbits.
## Project Structure
```
claudes_game/
├── src/
│ ├── main.cpp - Main program loop
│ ├── physics.cpp/h - Vector math and physics
│ ├── bodies.cpp/h - Celestial bodies and simulation
│ ├── config_loader.cpp/h - Configuration file parser
│ └── renderer.cpp/h - 3D rendering with raylib
├── configs/
│ ├── solar_system.txt - Solar system configuration
│ └── example_binary_star.txt - Binary star example
├── docs/
│ └── implementation_plan.md - Detailed implementation plan
├── Makefile
└── README.md
```
## Technical Details
- **Physics**: 2-body gravitational model using Newton's law of gravitation
- **Integration**: Euler method with configurable time step (default: 60 seconds)
- **SOI Detection**: Hill sphere approximation for sphere of influence
- **Rendering**: Logarithmic distance scaling and exponential size scaling for visualization
- **Language**: C-style C++ (structs and functions, no classes or templates)
## Future Enhancements
- Quaternion-based rotations for realistic body orientation
- Orbit trail rendering
- N-body simulation mode
- More accurate integration methods (RK4, Verlet)
- Save/load simulation state
- Interactive body selection and information display
- Multiple reference frames
## License
This project is provided as-is for educational and research purposes.

20
configs/example_binary_star.txt

@ -0,0 +1,20 @@
# Binary Star System with Planets
# A simple example of two stars orbiting each other with planets around them
# Star A (yellow, index 0) - positioned to the right
StarA 1.5e30 8.0e8 3.0e11 0 0 -1 1.0 1.0 0.2
# Star B (blue, index 1) - positioned to the left
StarB 1.2e30 7.0e8 -3.7e11 0 0 -1 0.3 0.5 1.0
# Planet orbiting Star A (index 2, parent is StarA at index 0)
PlanetA1 6.0e24 7.0e6 3.5e11 0 0 0 0.8 0.3 0.2
# Planet orbiting Star B (index 3, parent is StarB at index 1)
PlanetB1 4.0e24 6.0e6 -4.2e11 0 0 1 0.2 0.8 0.6
# Moon orbiting PlanetA1 (index 4, parent is PlanetA1 at index 2)
MoonA1 1.0e23 2.0e6 3.52e11 0 0 2 0.7 0.7 0.7
# Second planet orbiting Star A (index 5, parent is StarA at index 0)
PlanetA2 8.0e24 8.0e6 4.0e11 0 0 0 0.5 0.6 0.3

31
configs/solar_system.txt

@ -0,0 +1,31 @@
# Solar System Configuration
# Format: name mass(kg) radius(m) x(m) y(m) z(m) parent_index r g b
# parent_index: -1 for Sun, 0 for Sun's children, etc.
# Colors are RGB values from 0.0 to 1.0
# The Sun (index 0)
Sun 1.989e30 6.96e8 0 0 0 -1 1.0 1.0 0.0
# Inner planets
Mercury 3.285e23 2.4397e6 5.791e10 0 0 0 0.5 0.5 0.5
Venus 4.867e24 6.0518e6 1.082e11 0 0 0 0.9 0.7 0.3
Earth 5.972e24 6.371e6 1.496e11 0 0 0 0.0 0.5 1.0
Mars 6.39e23 3.3895e6 2.279e11 0 0 0 0.8 0.3 0.1
# Outer planets
Jupiter 1.898e27 6.9911e7 7.785e11 0 0 0 0.9 0.7 0.5
Saturn 5.683e26 5.8232e7 1.434e12 0 0 0 0.9 0.8 0.6
Uranus 8.681e25 2.5362e7 2.871e12 0 0 0 0.5 0.8 0.9
Neptune 1.024e26 2.4622e7 4.495e12 0 0 0 0.2 0.4 0.9
# Earth's Moon (index 9, parent is Earth at index 3)
Moon 7.342e22 1.737e6 1.500e11 0 0 3 0.7 0.7 0.7
# Jupiter's major moons (parent is Jupiter at index 5)
Io 8.93e22 1.822e6 8.207e11 0 0 5 0.9 0.9 0.3
Europa 4.80e22 1.561e6 8.456e11 0 0 5 0.8 0.8 0.7
Ganymede 1.48e23 2.634e6 8.853e11 0 0 5 0.6 0.6 0.5
Callisto 1.08e23 2.410e6 9.670e11 0 0 5 0.5 0.5 0.4
# Saturn's largest moon (parent is Saturn at index 6)
Titan 1.345e23 2.575e6 1.556e12 0 0 6 0.9 0.6 0.3

235
docs/implementation_plan.md

@ -0,0 +1,235 @@
# Orbital Mechanics Simulation - Implementation Plan
## Project Overview
A 3D orbital mechanics simulation using 2-body gravitational model with sphere of influence (SOI) transitions. Real-time visualization of the solar system (Sun, planets, moons) using raylib with C-style C++.
## Technical Constraints
- C-style C++ only: structs and functions, no classes or templates
- Small, focused functions
- Simple rotations (Euler angles / axis-angle) - quaternions deferred for later
- Euler integration for physics
- raylib for 3D visualization
## Project Structure
```
claudes_game/
├── src/
│ ├── main.cpp
│ ├── physics.cpp
│ ├── physics.h
│ ├── bodies.cpp
│ ├── bodies.h
│ ├── renderer.cpp
│ ├── renderer.h
│ ├── config_loader.cpp
│ └── config_loader.h
├── configs/
│ ├── solar_system.txt
│ └── example_binary_star.txt
├── Makefile
└── README.md
```
## Core Data Structures
### Vec3 (physics.h)
```cpp
struct Vec3 {
double x, y, z;
};
```
### CelestialBody (bodies.h)
```cpp
struct CelestialBody {
char name[64];
double mass; // kg
double radius; // meters
Vec3 position; // meters from solar system origin
Vec3 velocity; // m/s
double soi_radius; // sphere of influence radius (meters)
int parent_index; // index of gravitational parent (-1 for Sun)
float color[3]; // RGB for rendering
};
```
### SimulationState (bodies.h)
```cpp
struct SimulationState {
CelestialBody* bodies;
int body_count;
double time; // simulation time (seconds)
double dt; // time step (seconds)
};
```
## Key Components
### 1. Physics Module (physics.c/h)
**Functions:**
- `Vec3 vec3_add(Vec3 a, Vec3 b)` - vector addition
- `Vec3 vec3_sub(Vec3 a, Vec3 b)` - vector subtraction
- `Vec3 vec3_scale(Vec3 v, double s)` - scalar multiplication
- `double vec3_magnitude(Vec3 v)` - vector length
- `double vec3_distance(Vec3 a, Vec3 b)` - distance between points
- `Vec3 vec3_normalize(Vec3 v)` - unit vector
- `Vec3 calculate_gravity_force(CelestialBody* body, CelestialBody* parent)` - 2-body force
- `Vec3 calculate_acceleration(Vec3 force, double mass)` - F = ma
- `void euler_step(CelestialBody* body, Vec3 acceleration, double dt)` - Euler integration
### 2. Bodies Module (bodies.c/h)
**Functions:**
- `SimulationState* create_simulation(int max_bodies)` - allocate simulation
- `void destroy_simulation(SimulationState* sim)` - cleanup
- `void add_body(SimulationState* sim, const char* name, double mass, double radius, Vec3 pos, Vec3 vel, float r, float g, float b)` - add celestial body
- `int find_dominant_body(SimulationState* sim, int body_index)` - determine which body has gravitational dominance
- `void update_soi(CelestialBody* body, CelestialBody* parent)` - calculate sphere of influence radius
- `void update_simulation(SimulationState* sim)` - single time step update
**SOI Calculation:**
Using Hill sphere approximation: `r_soi = a * (m/M)^(2/5)`
where a = semi-major axis, m = body mass, M = parent mass
### 3. Config Loader Module (config_loader.cpp/h)
**Functions:**
- `bool load_system_config(SimulationState* sim, const char* filepath)` - load bodies from config file
- `bool parse_body_line(const char* line, CelestialBody* body)` - parse single body definition
- `void calculate_initial_velocities(SimulationState* sim)` - compute circular orbit velocities from positions
**Config File Format (simple text):**
```
# Comment lines start with #
# Format: name mass(kg) radius(m) x(m) y(m) z(m) parent_index r g b
Sun 1.989e30 6.96e8 0 0 0 -1 1.0 1.0 0.0
Earth 5.972e24 6.371e6 1.496e11 0 0 0 0.0 0.5 1.0
Moon 7.342e22 1.737e6 1.4996e11 0 0 1 0.7 0.7 0.7
# Velocity is calculated automatically for circular orbits
```
**Example configs to provide:**
- `configs/solar_system.txt` - Our solar system with Sun, 8 planets, major moons
- `configs/example_binary_star.txt` - Binary star system with planets
### 4. Renderer Module (renderer.c/h)
**Functions:**
- `void init_renderer(int width, int height, const char* title)` - initialize raylib window
- `void setup_camera(Camera3D* camera)` - configure 3D camera
- `void render_body(CelestialBody* body, double scale)` - draw sphere for body
- `void render_simulation(SimulationState* sim, Camera3D* camera)` - render all bodies
- `void draw_orbit_trail(Vec3* positions, int count, Color color)` - draw orbit path (future enhancement)
- `void close_renderer()` - cleanup raylib
**Rendering approach:**
- Use logarithmic scaling for distances (solar system is huge)
- Use exponential scaling for body sizes (make small bodies visible)
- Simple camera controls (rotate around Sun, zoom in/out)
- Display FPS and simulation time
### 5. Main Loop (main.cpp)
```cpp
1. Parse command line arguments (config file path, default to configs/solar_system.txt)
2. Initialize raylib window and camera
3. Create simulation and load system from config file
4. Main loop:
a. Handle input (camera controls, pause/resume, speed adjustment)
b. Update physics (multiple sub-steps per frame for stability)
c. Check for SOI transitions
d. Render scene
e. Update camera
5. Cleanup and exit
```
## Implementation Steps
### Phase 1: Foundation
1. Create project structure and Makefile
2. Implement Vec3 and basic vector math functions (physics.c/h)
3. Define CelestialBody and SimulationState structs (bodies.h)
4. Create basic simulation functions (create, destroy, add_body)
### Phase 2: Physics Core
5. Implement 2-body gravity calculation
6. Implement Euler integration step
7. Implement SOI detection and parent switching logic
8. Create update_simulation() function
### Phase 3: Config Loading System
9. Implement config file parser (parse_body_line, skip comments/empty lines)
10. Implement load_system_config() to read and populate simulation
11. Create configs/solar_system.txt with our solar system data
12. Implement calculate_initial_velocities() for circular orbits
13. Calculate SOI radii for all bodies after loading
### Phase 4: Visualization
14. Initialize raylib window and 3D camera
15. Implement render_body() with scaling
16. Implement render_simulation() to draw all bodies
17. Add basic camera controls (orbital rotation, zoom)
### Phase 5: Integration and Refinement
18. Integrate rendering with physics loop in main.cpp
19. Add command line argument parsing for config file selection
20. Add time scaling controls (speed up/slow down simulation)
21. Add pause/resume functionality
22. Display simulation info (time, FPS, body count, current config)
23. Create example_binary_star.txt config for testing
24. Test and tune time step for stability
## Technical Notes
### Config File System Benefits
- **Flexibility**: Easily create any star system without recompiling
- **Testing**: Quickly test different scenarios (binary stars, close encounters, etc.)
- **Sharing**: Users can share interesting system configurations
- **Debugging**: Simplified test cases with just 2-3 bodies
- **Education**: Learn about different orbital configurations by experimenting
### Scaling for Visualization
- **Distance scale**: Use logarithmic or power-law scaling (e.g., `display_pos = sign(pos) * pow(abs(pos), 0.3)`)
- **Size scale**: Make minimum visible radius (e.g., max(actual_radius * scale, min_visible_radius))
- Keep Sun at origin for simplicity
### Time Step Considerations
- Solar system scale requires small time steps (try dt = 60 seconds initially)
- May need multiple physics updates per render frame
- Allow user to adjust simulation speed multiplier
### SOI Transition Logic
```
For each body (except Sun):
1. Calculate distance to current parent
2. Calculate distance to all other potential parents
3. Check if body is within SOI of a different parent
4. If yes, switch parent_index and recalculate relative state
```
### Initial Conditions
- Use Keplerian orbital elements or simplified circular orbits
- Ensure velocity is perpendicular to position vector for circular orbits
- Circular orbit velocity: v = sqrt(G * M / r)
## Files to Create
1. `src/physics.h` - Vector math and physics declarations
2. `src/physics.cpp` - Vector math and physics implementations
3. `src/bodies.h` - Body and simulation data structures
4. `src/bodies.cpp` - Body management and simulation update
5. `src/config_loader.h` - Config file parsing declarations
6. `src/config_loader.cpp` - Config file loading implementation
7. `src/renderer.h` - Rendering declarations
8. `src/renderer.cpp` - raylib rendering implementation
9. `src/main.cpp` - Main loop and program entry
10. `configs/solar_system.txt` - Our solar system configuration
11. `configs/example_binary_star.txt` - Example binary star system
12. `Makefile` - Build configuration
13. `README.md` - Project documentation and config format docs
## Future Enhancements (Not in Initial Implementation)
- Quaternion-based rotations
- Orbit trail rendering
- N-body simulation mode
- Patched conic approximation
- More accurate integration (RK4, Verlet)
- Save/load simulation state
- Interactive body selection and info display
- Reference frame switching

1
ext/raylib

@ -0,0 +1 @@
Subproject commit ca89934ed5af9161f781a9b35b808b765dc40f3a

133
src/bodies.cpp

@ -0,0 +1,133 @@
#include "bodies.h"
#include <cstdlib>
#include <cstring>
#include <cmath>
// Create a new simulation
SimulationState* create_simulation(int max_bodies, double time_step) {
SimulationState* sim = (SimulationState*)malloc(sizeof(SimulationState));
sim->bodies = (CelestialBody*)malloc(sizeof(CelestialBody) * max_bodies);
sim->body_count = 0;
sim->max_bodies = max_bodies;
sim->time = 0.0;
sim->dt = time_step;
return sim;
}
// Destroy simulation and free memory
void destroy_simulation(SimulationState* sim) {
if (sim) {
if (sim->bodies) {
free(sim->bodies);
}
free(sim);
}
}
// Add a celestial body to the simulation
void add_body(SimulationState* sim, const char* name, double mass, double radius,
Vec3 pos, Vec3 vel, int parent_index, float r, float g, float b) {
if (sim->body_count >= sim->max_bodies) {
return; // No more space
}
CelestialBody* body = &sim->bodies[sim->body_count];
strncpy(body->name, name, 63);
body->name[63] = '\0';
body->mass = mass;
body->radius = radius;
body->position = pos;
body->velocity = vel;
body->soi_radius = 0.0; // Will be calculated later
body->parent_index = parent_index;
body->color[0] = r;
body->color[1] = g;
body->color[2] = b;
sim->body_count++;
}
// Find which body is gravitationally dominant for the given body
int find_dominant_body(SimulationState* sim, int body_index) {
if (body_index < 0 || body_index >= sim->body_count) {
return -1;
}
CelestialBody* body = &sim->bodies[body_index];
int dominant = body->parent_index;
// Check all other bodies to see if we're within their SOI
for (int i = 0; i < sim->body_count; i++) {
if (i == body_index) continue;
CelestialBody* potential_parent = &sim->bodies[i];
double distance = vec3_distance(body->position, potential_parent->position);
// If we're within this body's SOI and it's not our current parent
if (distance < potential_parent->soi_radius && i != dominant) {
// Check if this body is more dominant (closer or more massive)
if (dominant == -1) {
dominant = i;
} else {
CelestialBody* current_parent = &sim->bodies[dominant];
double dist_to_current = vec3_distance(body->position, current_parent->position);
// Switch if this potential parent is significantly closer
if (distance < dist_to_current * 0.5) {
dominant = i;
}
}
}
}
return dominant;
}
// Update sphere of influence radius using Hill sphere approximation
// r_soi = a * (m/M)^(2/5) where a = semi-major axis, m = body mass, M = parent mass
void update_soi(CelestialBody* body, CelestialBody* parent, double semi_major_axis) {
if (parent == NULL || parent->mass <= 0.0) {
// Root body (like Sun) has infinite SOI, use a large value
body->soi_radius = 1e15; // 1000 AU in meters
return;
}
double mass_ratio = body->mass / parent->mass;
body->soi_radius = semi_major_axis * pow(mass_ratio, 0.4); // 2/5 = 0.4
}
// Update the entire simulation by one time step
void update_simulation(SimulationState* sim) {
// Update each body's physics (except the root body which is stationary)
for (int i = 0; i < sim->body_count; i++) {
CelestialBody* body = &sim->bodies[i];
// Skip if this is a root body (parent_index == -1)
if (body->parent_index == -1) {
continue;
}
// Check if parent has changed (SOI transition)
int new_parent = find_dominant_body(sim, i);
if (new_parent != body->parent_index && new_parent != -1) {
body->parent_index = new_parent;
}
// Get the current parent
if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[body->parent_index];
// Calculate gravitational force from parent
Vec3 force = calculate_gravity_force(body, parent);
// Calculate acceleration
Vec3 acceleration = calculate_acceleration(force, body->mass);
// Perform Euler integration step
euler_step(body, acceleration, sim->dt);
}
}
// Update simulation time
sim->time += sim->dt;
}

38
src/bodies.h

@ -0,0 +1,38 @@
#ifndef BODIES_H
#define BODIES_H
#include "physics.h"
// Celestial body structure
struct CelestialBody {
char name[64];
double mass; // kg
double radius; // meters
Vec3 position; // meters from origin
Vec3 velocity; // m/s
double soi_radius; // sphere of influence radius (meters)
int parent_index; // index of gravitational parent (-1 for root body like Sun)
float color[3]; // RGB color for rendering
};
// Simulation state
struct SimulationState {
CelestialBody* bodies;
int body_count;
int max_bodies;
double time; // simulation time (seconds)
double dt; // time step (seconds)
};
// Simulation management functions
SimulationState* create_simulation(int max_bodies, double time_step);
void destroy_simulation(SimulationState* sim);
void add_body(SimulationState* sim, const char* name, double mass, double radius,
Vec3 pos, Vec3 vel, int parent_index, float r, float g, float b);
// SOI and simulation update functions
int find_dominant_body(SimulationState* sim, int body_index);
void update_soi(CelestialBody* body, CelestialBody* parent, double semi_major_axis);
void update_simulation(SimulationState* sim);
#endif

138
src/config_loader.cpp

@ -0,0 +1,138 @@
#include "config_loader.h"
#include <cstdio>
#include <cstring>
#include <cmath>
// Parse a single body definition line
bool parse_body_line(const char* line, char* name, double* mass, double* radius,
Vec3* pos, int* parent_index, float* r, float* g, float* b) {
// Skip empty lines and comments
if (line[0] == '\0' || line[0] == '#' || line[0] == '\n') {
return false;
}
// Parse: name mass(kg) radius(m) x(m) y(m) z(m) parent_index r g b
int result = sscanf(line, "%63s %lf %lf %lf %lf %lf %d %f %f %f",
name, mass, radius, &pos->x, &pos->y, &pos->z,
parent_index, r, g, b);
return result == 10; // All fields must be present
}
// Load system configuration from file
bool load_system_config(SimulationState* sim, const char* filepath) {
FILE* file = fopen(filepath, "r");
if (!file) {
printf("Error: Could not open config file: %s\n", filepath);
return false;
}
char line[256];
char name[64];
double mass, radius;
Vec3 pos;
int parent_index;
float r, g, b;
while (fgets(line, sizeof(line), file)) {
if (parse_body_line(line, name, &mass, &radius, &pos, &parent_index, &r, &g, &b)) {
Vec3 vel = {0.0, 0.0, 0.0}; // Velocity will be calculated later
add_body(sim, name, mass, radius, pos, vel, parent_index, r, g, b);
}
}
fclose(file);
if (sim->body_count == 0) {
printf("Error: No bodies loaded from config file\n");
return false;
}
// Calculate initial velocities for circular orbits
calculate_initial_velocities(sim);
// Calculate SOI radii
calculate_soi_radii(sim);
printf("Loaded %d bodies from %s\n", sim->body_count, filepath);
return true;
}
// Calculate circular orbit velocity: v = sqrt(G * M / r)
// Velocity is perpendicular to position vector
void calculate_initial_velocities(SimulationState* sim) {
for (int i = 0; i < sim->body_count; i++) {
CelestialBody* body = &sim->bodies[i];
// Skip root body (no parent)
if (body->parent_index == -1) {
body->velocity = {0.0, 0.0, 0.0};
continue;
}
// Get parent body
if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[body->parent_index];
// Calculate relative position
Vec3 r = vec3_sub(body->position, parent->position);
double distance = vec3_magnitude(r);
if (distance < 1.0) {
body->velocity = {0.0, 0.0, 0.0};
continue;
}
// Calculate circular orbit speed
double speed = sqrt(G * parent->mass / distance);
// Create velocity perpendicular to position vector
// If position is mostly in XY plane, make velocity in XY plane
// Cross product of r with z-axis gives perpendicular vector in XY plane
Vec3 z_axis = {0.0, 0.0, 1.0};
// Calculate cross product: r x z_axis
Vec3 vel_dir = {
r.y * z_axis.z - r.z * z_axis.y,
r.z * z_axis.x - r.x * z_axis.z,
r.x * z_axis.y - r.y * z_axis.x
};
// If r is parallel to z-axis, use x-axis instead
double cross_mag = vec3_magnitude(vel_dir);
if (cross_mag < 0.01) {
Vec3 x_axis = {1.0, 0.0, 0.0};
vel_dir.x = r.y * x_axis.z - r.z * x_axis.y;
vel_dir.y = r.z * x_axis.x - r.x * x_axis.z;
vel_dir.z = r.x * x_axis.y - r.y * x_axis.x;
}
// Normalize and scale by orbital speed
vel_dir = vec3_normalize(vel_dir);
body->velocity = vec3_scale(vel_dir, speed);
// Add parent's velocity for absolute reference frame
body->velocity = vec3_add(body->velocity, parent->velocity);
}
}
}
// Calculate SOI radii for all bodies
void calculate_soi_radii(SimulationState* sim) {
for (int i = 0; i < sim->body_count; i++) {
CelestialBody* body = &sim->bodies[i];
if (body->parent_index == -1) {
// Root body has very large SOI
body->soi_radius = 1e15; // ~1000 AU
} else if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[body->parent_index];
// Calculate semi-major axis (distance to parent)
double semi_major_axis = vec3_distance(body->position, parent->position);
// Update SOI using Hill sphere approximation
update_soi(body, parent, semi_major_axis);
}
}
}

19
src/config_loader.h

@ -0,0 +1,19 @@
#ifndef CONFIG_LOADER_H
#define CONFIG_LOADER_H
#include "bodies.h"
// Load a system configuration from a file
bool load_system_config(SimulationState* sim, const char* filepath);
// Parse a single body definition line
bool parse_body_line(const char* line, char* name, double* mass, double* radius,
Vec3* pos, int* parent_index, float* r, float* g, float* b);
// Calculate initial circular orbit velocities for all bodies
void calculate_initial_velocities(SimulationState* sim);
// Calculate SOI radii for all bodies
void calculate_soi_radii(SimulationState* sim);
#endif

90
src/main.cpp

@ -0,0 +1,90 @@
#include "physics.h"
#include "bodies.h"
#include "config_loader.h"
#include "renderer.h"
#include <cstdio>
#include <cstring>
int main(int argc, char** argv) {
// Parse command line arguments
const char* config_file = "configs/solar_system.txt";
if (argc > 1) {
config_file = argv[1];
}
printf("=== Orbital Mechanics Simulation ===\n");
printf("Loading configuration: %s\n", config_file);
// Create simulation with time step of 60 seconds
const int MAX_BODIES = 100;
const double TIME_STEP = 60.0; // 60 seconds per step
SimulationState* sim = create_simulation(MAX_BODIES, TIME_STEP);
// Load system configuration
if (!load_system_config(sim, config_file)) {
printf("Failed to load configuration file\n");
destroy_simulation(sim);
return 1;
}
// Initialize renderer
init_renderer(1280, 720, "Orbital Mechanics Simulation");
// Setup rendering state
RenderState render_state;
setup_camera(&render_state);
// Simulation control variables
bool paused = false;
double speed_multiplier = 1.0;
int physics_steps_per_frame = 100; // Multiple physics steps per frame for stability
printf("\nSimulation started!\n");
printf("Controls:\n");
printf(" Arrow keys: Rotate and zoom camera\n");
printf(" Space: Pause/Resume\n");
printf(" +/-: Speed up/slow down simulation\n");
printf(" I: Toggle info display\n");
printf(" ESC: Quit\n\n");
// Main loop
while (!WindowShouldClose()) {
// Handle input
if (IsKeyPressed(KEY_SPACE)) {
paused = !paused;
printf("Simulation %s\n", paused ? "paused" : "resumed");
}
if (IsKeyPressed(KEY_EQUAL) || IsKeyPressed(KEY_KP_ADD)) {
speed_multiplier *= 2.0;
printf("Speed multiplier: %.1fx\n", speed_multiplier);
}
if (IsKeyPressed(KEY_MINUS) || IsKeyPressed(KEY_KP_SUBTRACT)) {
speed_multiplier /= 2.0;
if (speed_multiplier < 0.125) speed_multiplier = 0.125;
printf("Speed multiplier: %.1fx\n", speed_multiplier);
}
// Update camera
update_camera(&render_state);
// Update physics (multiple steps per frame)
if (!paused) {
int steps = (int)(physics_steps_per_frame * speed_multiplier);
for (int i = 0; i < steps; i++) {
update_simulation(sim);
}
}
// Render
render_simulation(sim, &render_state);
}
// Cleanup
close_renderer();
destroy_simulation(sim);
printf("\nSimulation ended. Final time: %.2f days\n", sim->time / 86400.0);
return 0;
}

71
src/physics.cpp

@ -0,0 +1,71 @@
#include "physics.h"
#include "bodies.h"
#include <cmath>
// Vector addition
Vec3 vec3_add(Vec3 a, Vec3 b) {
return {a.x + b.x, a.y + b.y, a.z + b.z};
}
// Vector subtraction
Vec3 vec3_sub(Vec3 a, Vec3 b) {
return {a.x - b.x, a.y - b.y, a.z - b.z};
}
// Scalar multiplication
Vec3 vec3_scale(Vec3 v, double s) {
return {v.x * s, v.y * s, v.z * s};
}
// Vector magnitude
double vec3_magnitude(Vec3 v) {
return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
// Distance between two points
double vec3_distance(Vec3 a, Vec3 b) {
Vec3 diff = vec3_sub(a, b);
return vec3_magnitude(diff);
}
// Normalize vector to unit length
Vec3 vec3_normalize(Vec3 v) {
double mag = vec3_magnitude(v);
if (mag > 0.0) {
return vec3_scale(v, 1.0 / mag);
}
return {0.0, 0.0, 0.0};
}
// Calculate gravitational force using Newton's law: F = G * m1 * m2 / r^2
Vec3 calculate_gravity_force(CelestialBody* body, CelestialBody* parent) {
Vec3 r = vec3_sub(parent->position, body->position);
double distance = vec3_magnitude(r);
// Avoid division by zero
if (distance < 1.0) {
distance = 1.0;
}
double force_magnitude = G * body->mass * parent->mass / (distance * distance);
Vec3 direction = vec3_normalize(r);
return vec3_scale(direction, force_magnitude);
}
// Calculate acceleration from force: a = F / m
Vec3 calculate_acceleration(Vec3 force, double mass) {
if (mass > 0.0) {
return vec3_scale(force, 1.0 / mass);
}
return {0.0, 0.0, 0.0};
}
// Euler integration step: update position and velocity
void euler_step(CelestialBody* body, Vec3 acceleration, double dt) {
// Update velocity: v = v + a * dt
body->velocity = vec3_add(body->velocity, vec3_scale(acceleration, dt));
// Update position: p = p + v * dt
body->position = vec3_add(body->position, vec3_scale(body->velocity, dt));
}

28
src/physics.h

@ -0,0 +1,28 @@
#ifndef PHYSICS_H
#define PHYSICS_H
// Forward declaration
struct CelestialBody;
// 3D Vector
struct Vec3 {
double x, y, z;
};
// Gravitational constant (m^3 kg^-1 s^-2)
const double G = 6.67430e-11;
// Vector math functions
Vec3 vec3_add(Vec3 a, Vec3 b);
Vec3 vec3_sub(Vec3 a, Vec3 b);
Vec3 vec3_scale(Vec3 v, double s);
double vec3_magnitude(Vec3 v);
double vec3_distance(Vec3 a, Vec3 b);
Vec3 vec3_normalize(Vec3 v);
// Physics functions
Vec3 calculate_gravity_force(CelestialBody* body, CelestialBody* parent);
Vec3 calculate_acceleration(Vec3 force, double mass);
void euler_step(CelestialBody* body, Vec3 acceleration, double dt);
#endif

159
src/renderer.cpp

@ -0,0 +1,159 @@
#include "renderer.h"
#include "raymath.h"
#include <cmath>
#include <cstdio>
// Initialize raylib window
void init_renderer(int width, int height, const char* title) {
InitWindow(width, height, title);
SetTargetFPS(60);
}
// Close raylib
void close_renderer() {
CloseWindow();
}
// Setup the 3D camera
void setup_camera(RenderState* render_state) {
render_state->camera.position = (Vector3){ 0.0f, 50.0f, 100.0f };
render_state->camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
render_state->camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
render_state->camera.fovy = 45.0f;
render_state->camera.projection = CAMERA_PERSPECTIVE;
// Set scaling factors
render_state->distance_scale = 1e-9; // Meters to scaled units (1 unit = 1 billion meters)
render_state->size_scale = 5e-7; // Make bodies visible
render_state->show_info = true;
}
// Update camera with keyboard/mouse controls
void update_camera(RenderState* render_state) {
// Orbital camera rotation with arrow keys
float camera_distance = Vector3Distance(render_state->camera.position, render_state->camera.target);
float angle_speed = 0.02f;
// Rotate around target
if (IsKeyDown(KEY_LEFT)) {
Vector3 pos = render_state->camera.position;
float angle = angle_speed;
float x = pos.x * cosf(angle) - pos.z * sinf(angle);
float z = pos.x * sinf(angle) + pos.z * cosf(angle);
render_state->camera.position.x = x;
render_state->camera.position.z = z;
}
if (IsKeyDown(KEY_RIGHT)) {
Vector3 pos = render_state->camera.position;
float angle = -angle_speed;
float x = pos.x * cosf(angle) - pos.z * sinf(angle);
float z = pos.x * sinf(angle) + pos.z * cosf(angle);
render_state->camera.position.x = x;
render_state->camera.position.z = z;
}
// Zoom in/out with up/down keys
if (IsKeyDown(KEY_UP) && camera_distance > 10.0f) {
Vector3 direction = Vector3Subtract(render_state->camera.target, render_state->camera.position);
direction = Vector3Normalize(direction);
render_state->camera.position = Vector3Add(render_state->camera.position, Vector3Scale(direction, 2.0f));
}
if (IsKeyDown(KEY_DOWN)) {
Vector3 direction = Vector3Subtract(render_state->camera.position, render_state->camera.target);
direction = Vector3Normalize(direction);
render_state->camera.position = Vector3Add(render_state->camera.position, Vector3Scale(direction, 2.0f));
}
// Toggle info display with I key
if (IsKeyPressed(KEY_I)) {
render_state->show_info = !render_state->show_info;
}
}
// Scale a position for rendering
Vector3 scale_position(Vec3 pos, double scale) {
return (Vector3){
(float)(pos.x * scale),
(float)(pos.y * scale),
(float)(pos.z * scale)
};
}
// Scale a radius for rendering (with minimum visible size)
float scale_radius(double radius, double scale) {
float scaled = (float)(radius * scale);
float min_radius = 0.5f; // Minimum visible radius
return (scaled > min_radius) ? scaled : min_radius;
}
// Render a single celestial body
void render_body(CelestialBody* body, RenderState* render_state) {
Vector3 position = scale_position(body->position, render_state->distance_scale);
float radius = scale_radius(body->radius, render_state->size_scale);
Color color = {
(unsigned char)(body->color[0] * 255),
(unsigned char)(body->color[1] * 255),
(unsigned char)(body->color[2] * 255),
255
};
DrawSphere(position, radius, color);
}
// Render the entire simulation
void render_simulation(SimulationState* sim, RenderState* render_state) {
BeginDrawing();
ClearBackground(BLACK);
BeginMode3D(render_state->camera);
// Draw a reference grid
DrawGrid(100, 10.0f);
// Render all bodies
for (int i = 0; i < sim->body_count; i++) {
render_body(&sim->bodies[i], render_state);
}
EndMode3D();
// Render 2D info overlay
if (render_state->show_info) {
render_info(sim, "solar_system.txt");
}
EndDrawing();
}
// Render simulation information overlay
void render_info(SimulationState* sim, const char* config_name) {
DrawText("Orbital Mechanics Simulation", 10, 10, 20, WHITE);
char buffer[256];
// Simulation time (in days)
double days = sim->time / 86400.0; // seconds to days
snprintf(buffer, sizeof(buffer), "Time: %.2f days", days);
DrawText(buffer, 10, 40, 16, LIGHTGRAY);
// Body count
snprintf(buffer, sizeof(buffer), "Bodies: %d", sim->body_count);
DrawText(buffer, 10, 60, 16, LIGHTGRAY);
// Config name
snprintf(buffer, sizeof(buffer), "Config: %s", config_name);
DrawText(buffer, 10, 80, 16, LIGHTGRAY);
// FPS
snprintf(buffer, sizeof(buffer), "FPS: %d", GetFPS());
DrawText(buffer, 10, 100, 16, LIGHTGRAY);
// Controls
DrawText("Controls:", 10, 130, 16, YELLOW);
DrawText(" Arrows: Rotate/Zoom camera", 10, 150, 14, LIGHTGRAY);
DrawText(" Space: Pause/Resume", 10, 170, 14, LIGHTGRAY);
DrawText(" +/-: Speed up/slow down", 10, 190, 14, LIGHTGRAY);
DrawText(" I: Toggle info", 10, 210, 14, LIGHTGRAY);
DrawText(" ESC: Quit", 10, 230, 14, LIGHTGRAY);
}

32
src/renderer.h

@ -0,0 +1,32 @@
#ifndef RENDERER_H
#define RENDERER_H
#include "bodies.h"
#include "raylib.h"
// Rendering state
struct RenderState {
Camera3D camera;
double distance_scale; // Scale factor for distances
double size_scale; // Scale factor for body sizes
bool show_info; // Display simulation info
};
// Renderer initialization and cleanup
void init_renderer(int width, int height, const char* title);
void close_renderer();
// Camera setup and control
void setup_camera(RenderState* render_state);
void update_camera(RenderState* render_state);
// Rendering functions
void render_body(CelestialBody* body, RenderState* render_state);
void render_simulation(SimulationState* sim, RenderState* render_state);
void render_info(SimulationState* sim, const char* config_name);
// Scaling functions
Vector3 scale_position(Vec3 pos, double scale);
float scale_radius(double radius, double scale);
#endif
Loading…
Cancel
Save