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