Browse Source
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
commit
e8a56c93c1
17 changed files with 1215 additions and 0 deletions
@ -0,0 +1,14 @@
|
||||
# Build artifacts |
||||
build/ |
||||
orbit_sim |
||||
|
||||
# Editor/IDE files |
||||
.vscode/ |
||||
.idea/ |
||||
*.swp |
||||
*.swo |
||||
*~ |
||||
|
||||
# OS files |
||||
.DS_Store |
||||
Thumbs.db |
||||
@ -0,0 +1,3 @@
|
||||
[submodule "ext/raylib"] |
||||
path = ext/raylib |
||||
url = https://github.com/raysan5/raylib.git |
||||
@ -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 |
||||
@ -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. |
||||
@ -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 |
||||
@ -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 |
||||
@ -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 |
||||
@ -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; |
||||
} |
||||
@ -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 |
||||
@ -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); |
||||
} |
||||
} |
||||
} |
||||
@ -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 |
||||
@ -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; |
||||
} |
||||
@ -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)); |
||||
} |
||||
@ -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 |
||||
@ -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); |
||||
} |
||||
@ -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…
Reference in new issue