add sdl, fix vec3, fix threading
This commit is contained in:
parent
8151edeebf
commit
ffc8cdbdeb
3
Makefile
3
Makefile
|
@ -6,6 +6,7 @@ STD ?= c++23
|
|||
|
||||
CXXFLAGS := -std=$(STD) -Wall -Wextra -O2 -Iinclude
|
||||
LDFLAGS :=
|
||||
LDLIBS := -lSDL2
|
||||
|
||||
# Source files and object files
|
||||
SRCS := $(shell find $(SRC_DIR) -name '*.cpp')
|
||||
|
@ -17,7 +18,7 @@ all: $(TARGET)
|
|||
# Link final executable
|
||||
$(TARGET): $(OBJS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CXX) $(OBJS) -o $@ $(LDFLAGS)
|
||||
$(CXX) $(OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
# Compile source files
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef CANVAS_H
|
||||
#define CANVAS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
struct Color {
|
||||
uint8_t r, g, b;
|
||||
};
|
||||
|
||||
struct Canvas {
|
||||
std::vector<Color> pixels;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
Canvas(int w, int h) : pixels(w * h), width(w), height(h) {}
|
||||
|
||||
void set_pixel(int x, int y, Color color) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
pixels[y * width + x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
Color get_pixel(int x, int y) const {
|
||||
return pixels[y * width + x];
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
47
src/main.cpp
47
src/main.cpp
|
@ -1,19 +1,46 @@
|
|||
#include "canvas.h"
|
||||
#include "simulation.h"
|
||||
#include <thread>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
int main() {
|
||||
auto state = std::make_shared<SimulationState>();
|
||||
state->positions.push_back(Vec3(1.0, 2.0, 3.0));
|
||||
Canvas canvas(800, 600);
|
||||
RingBuffer<SimulationState> buffer(64);
|
||||
|
||||
std::thread sim(simulation_loop, state);
|
||||
std::thread render(render_loop);
|
||||
std::thread sim(simulation_thread, std::ref(buffer));
|
||||
std::thread render(render_thread, std::ref(buffer), std::ref(canvas));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
running = false;
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_Window *window =
|
||||
SDL_CreateWindow("Solar System", SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, canvas.width, canvas.height, 0);
|
||||
SDL_Renderer *renderer =
|
||||
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
canvas.width, canvas.height);
|
||||
|
||||
sim.join();
|
||||
render.join();
|
||||
while (running) {
|
||||
void *pixels;
|
||||
int pitch;
|
||||
SDL_LockTexture(texture, nullptr, &pixels, &pitch);
|
||||
memcpy(pixels, canvas.pixels.data(), canvas.pixels.size() * sizeof(Color));
|
||||
SDL_UnlockTexture(texture);
|
||||
|
||||
return 0;
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_QUIT)
|
||||
running = false;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // ~60fps
|
||||
}
|
||||
|
||||
sim.join();
|
||||
render.join();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef RAY_H
|
||||
#define RAY_H
|
||||
|
||||
#include "vec3.h"
|
||||
|
||||
class Ray {
|
||||
public:
|
||||
Ray() {}
|
||||
|
||||
Ray(const Point3& origin, const Vec3& direction) : orig(origin), dir(direction) {}
|
||||
|
||||
const Point3& origin() const { return orig; }
|
||||
const Vec3& direction() const { return dir; }
|
||||
|
||||
Point3 at(double t) const {
|
||||
return orig + t*dir;
|
||||
}
|
||||
|
||||
private:
|
||||
Point3 orig;
|
||||
Vec3 dir;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -41,6 +41,8 @@ public:
|
|||
T item = buffer[head];
|
||||
full = false;
|
||||
head = (head + 1) % capacity;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
|
@ -48,7 +50,7 @@ public:
|
|||
return empty_unlocked();
|
||||
}
|
||||
|
||||
bool full_() const {
|
||||
bool _full() const {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
return full;
|
||||
}
|
||||
|
|
|
@ -1,34 +1,165 @@
|
|||
#include "simulation.h"
|
||||
#include "canvas.h"
|
||||
#include "vec3.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
std::atomic<bool> running(true);
|
||||
RingBuffer<Vec3> snapshot_buffer(128);
|
||||
|
||||
void simulation_loop(std::shared_ptr<SimulationState> state) {
|
||||
Vec3 velocity(0.01, 0.02, 0.03);
|
||||
/**
|
||||
* Initialize solar system.
|
||||
*/
|
||||
SimulationState initialize_solar_system() {
|
||||
SimulationState state;
|
||||
state.bodies.push_back(Body{"Sol", Vec3(0.0, 0.0, 0.0), Vec3(0.0, 0.0, 0.0),
|
||||
1.989e30}); // a star similar to the sun
|
||||
state.bodies.push_back(Body{"Mercury", Vec3(57.909e9, 0.0, 0.0),
|
||||
Vec3(0.0, 47.36e3, 0.0),
|
||||
0.33011e24}); // a planet similar to mercury
|
||||
state.bodies.push_back(Body{"Venus", Vec3(108.209e9, 0.0, 0.0),
|
||||
Vec3(0.0, 35.02e3, 0.0),
|
||||
4.8675e24}); // a planet similar to venus
|
||||
state.bodies.push_back(Body{"Earth", Vec3(149.596e9, 0.0, 0.0),
|
||||
Vec3(0.0, 29.78e3, 0.0),
|
||||
5.9724e24}); // a planet similar to earth
|
||||
state.bodies.push_back(Body{"Mars", Vec3(227.923e9, 0.0, 0.0),
|
||||
Vec3(0.0, 24.07e3, 0.0),
|
||||
0.64171e24}); // a planet similar to mars
|
||||
state.bodies.push_back(Body{"Jupiter", Vec3(778.570e9, 0.0, 0.0),
|
||||
Vec3(0.0, 13e3, 0.0),
|
||||
1898.19e24}); // a planet similar to jupiter
|
||||
state.bodies.push_back(Body{"Saturn", Vec3(1433.529e9, 0.0, 0.0),
|
||||
Vec3(0.0, 9.68e3, 0.0),
|
||||
568.34e24}); // a planet similar to saturn
|
||||
state.bodies.push_back(Body{"Uranus", Vec3(2872.463e9, 0.0, 0.0),
|
||||
Vec3(0.0, 6.80e3, 0.0),
|
||||
86.813e24}); // a planet similar to uranus
|
||||
state.bodies.push_back(Body{"Neptune", Vec3(4495.060e9, 0.0, 0.0),
|
||||
Vec3(0.0, 5.43e3, 0.0),
|
||||
102.413e24}); // a planet similar to neptune
|
||||
return state;
|
||||
}
|
||||
|
||||
while (running) {
|
||||
for (auto &p : state->positions) {
|
||||
p += velocity;
|
||||
/**
|
||||
* Step .
|
||||
*/
|
||||
void simulate_step(SimulationState state) {
|
||||
double t_0 = 0;
|
||||
double t = t_0;
|
||||
double dt = 86400;
|
||||
double BIG_G = 6.67e-11; // gravitational constant
|
||||
|
||||
size_t n_bodies = state.bodies.size();
|
||||
for (size_t m1_idx = 0; m1_idx < n_bodies; m1_idx++) {
|
||||
Vec3 a_g = {0, 0, 0};
|
||||
|
||||
for (size_t m2_idx = 0; m2_idx < n_bodies; m2_idx++) {
|
||||
if (m2_idx != m1_idx) {
|
||||
Vec3 r_vector;
|
||||
|
||||
r_vector.x() = state.bodies[m1_idx].position.x() -
|
||||
state.bodies[m2_idx].position.x();
|
||||
r_vector.y() = state.bodies[m1_idx].position.y() -
|
||||
state.bodies[m2_idx].position.y();
|
||||
r_vector.z() = state.bodies[m1_idx].position.z() -
|
||||
state.bodies[m2_idx].position.z();
|
||||
|
||||
double r_mag =
|
||||
sqrt(r_vector.x() * r_vector.x() + r_vector.y() * r_vector.y() +
|
||||
r_vector.z() * r_vector.z());
|
||||
|
||||
double acceleration =
|
||||
-1.0 * BIG_G * (state.bodies[m2_idx].mass) / pow(r_mag, 2.0);
|
||||
|
||||
Vec3 r_unit_vector = {r_vector.x() / r_mag, r_vector.y() / r_mag,
|
||||
r_vector.z() / r_mag};
|
||||
|
||||
a_g.x() += acceleration * r_unit_vector.x();
|
||||
a_g.y() += acceleration * r_unit_vector.y();
|
||||
a_g.z() += acceleration * r_unit_vector.z();
|
||||
}
|
||||
}
|
||||
|
||||
while (snapshot_buffer.push(state->positions[0])) {
|
||||
std::this_thread::yield();
|
||||
state.bodies[m1_idx].velocity.x() += a_g.x() * dt;
|
||||
state.bodies[m1_idx].velocity.y() += a_g.y() * dt;
|
||||
state.bodies[m1_idx].velocity.z() += a_g.z() * dt;
|
||||
}
|
||||
|
||||
for (size_t entity_idx = 0; entity_idx < n_bodies; entity_idx++) {
|
||||
state.bodies[entity_idx].position.x() +=
|
||||
state.bodies[entity_idx].velocity.x() * dt;
|
||||
state.bodies[entity_idx].position.y() +=
|
||||
state.bodies[entity_idx].velocity.y() * dt;
|
||||
state.bodies[entity_idx].position.z() +=
|
||||
state.bodies[entity_idx].velocity.z() * dt;
|
||||
}
|
||||
|
||||
t += dt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulation thread.
|
||||
*/
|
||||
void simulation_thread(RingBuffer<SimulationState> &buffer) {
|
||||
SimulationState state = initialize_solar_system();
|
||||
double dt = 0.01; // if we didnt do this it would run too fast for render
|
||||
std::chrono::duration<double> elapsed;
|
||||
|
||||
auto last = std::chrono::high_resolution_clock::now();
|
||||
|
||||
while (running) {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
elapsed = now - last;
|
||||
|
||||
if (elapsed.count() >= dt) {
|
||||
simulate_step(state);
|
||||
|
||||
// Try to push it into the buffer
|
||||
while (!buffer.push(state)) {
|
||||
// Buffer full, drop or wait (e.g. yield or sleep)
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
state.tick++;
|
||||
last = now;
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void render_loop() {
|
||||
while (running) {
|
||||
/**
|
||||
* Render raytracing.
|
||||
*/
|
||||
void render(SimulationState state, Canvas &canvas) {
|
||||
for (int j = 0; j < canvas.height; j++) {
|
||||
for (int i = 0; i < canvas.width; i++) {
|
||||
auto r = double(i) / (canvas.width - 1);
|
||||
auto g = double(j) / (canvas.height - 1);
|
||||
auto b = 0.0;
|
||||
|
||||
Vec3 latest;
|
||||
while (latest = snapshot_buffer.pop()) {
|
||||
std::cout << "[Render] ";
|
||||
latest.print();
|
||||
uint8_t ir = uint8_t(255.999 * r);
|
||||
uint8_t ig = uint8_t(255.999 * g);
|
||||
uint8_t ib = uint8_t(255.999 * b);
|
||||
|
||||
canvas.set_pixel(i, j, Color(ir, ig, ib));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render thread.
|
||||
*/
|
||||
void render_thread(RingBuffer<SimulationState> &buffer, Canvas &canvas) {
|
||||
while (running) {
|
||||
auto maybe_state = buffer.pop();
|
||||
if (maybe_state.has_value()) {
|
||||
const SimulationState &state = maybe_state.value();
|
||||
render(state, canvas); // Pure function: write pixel colors into canvas
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
// Optional: add very short pause to avoid spinning too fast
|
||||
std::this_thread::yield(); // cooperative multitasking
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,24 @@
|
|||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include "vec3.h"
|
||||
#include "canvas.h"
|
||||
#include "ring_buffer.h"
|
||||
|
||||
struct Body {
|
||||
std::string name;
|
||||
Vec3 position;
|
||||
Vec3 velocity;
|
||||
double mass;
|
||||
};
|
||||
|
||||
struct SimulationState {
|
||||
std::vector<Vec3> positions;
|
||||
std::vector<Body> bodies;
|
||||
uint64_t tick; // global simulation tick
|
||||
};
|
||||
|
||||
extern std::atomic<bool> running;
|
||||
extern RingBuffer<Vec3> snapshot_buffer;
|
||||
|
||||
void simulation_loop(std::shared_ptr<SimulationState> state);
|
||||
void render_loop();
|
||||
void simulation_thread(RingBuffer<SimulationState> &buffer);
|
||||
void render_thread(RingBuffer<SimulationState> &buffer, Canvas& canvas);
|
||||
|
||||
#endif
|
||||
|
|
118
src/vec3.h
118
src/vec3.h
|
@ -1,45 +1,101 @@
|
|||
#ifndef VEC3_H
|
||||
#define VEC3_H
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <ostream>
|
||||
|
||||
class Vec3 {
|
||||
std::array<double, 3> data;
|
||||
std::array<double, 3> e{};
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
Vec3() = default;
|
||||
Vec3(double x, double y, double z) : data{x, y, z} {}
|
||||
constexpr Vec3() = default;
|
||||
constexpr Vec3(double x, double y, double z) noexcept : e{x, y, z} {}
|
||||
|
||||
// Named accessors (mutable)
|
||||
double& x() { return data[0]; }
|
||||
double& y() { return data[1]; }
|
||||
double& z() { return data[2]; }
|
||||
constexpr double x() const noexcept { return e[0]; }
|
||||
constexpr double y() const noexcept { return e[1]; }
|
||||
constexpr double z() const noexcept { return e[2]; }
|
||||
|
||||
// Named accessors (const)
|
||||
double x() const { return data[0]; }
|
||||
double y() const { return data[1]; }
|
||||
double z() const { return data[2]; }
|
||||
constexpr double &x() { return e[0]; }
|
||||
constexpr double &y() { return e[1]; }
|
||||
constexpr double &z() { return e[2]; }
|
||||
|
||||
// Arithmetic operators
|
||||
friend Vec3 operator+(const Vec3& a, const Vec3& b) {
|
||||
return Vec3(a.x() + b.x(),
|
||||
a.y() + b.y(),
|
||||
a.z() + b.z());
|
||||
}
|
||||
constexpr double operator[](int i) const noexcept { return e[i]; }
|
||||
constexpr double &operator[](int i) noexcept { return e[i]; }
|
||||
|
||||
Vec3& operator+=(const Vec3& other) {
|
||||
x() += other.x();
|
||||
y() += other.y();
|
||||
z() += other.z();
|
||||
return *this;
|
||||
}
|
||||
constexpr Vec3 operator-() const noexcept {
|
||||
return Vec3(-e[0], -e[1], -e[2]);
|
||||
}
|
||||
|
||||
// Debug print
|
||||
void print() const {
|
||||
std::cout << "(" << x() << ", " << y() << ", " << z() << ")\n";
|
||||
}
|
||||
constexpr Vec3 &operator+=(const Vec3 &v) noexcept {
|
||||
e[0] += v.e[0];
|
||||
e[1] += v.e[1];
|
||||
e[2] += v.e[2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Vec3 &operator*=(double t) noexcept {
|
||||
e[0] *= t;
|
||||
e[1] *= t;
|
||||
e[2] *= t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Vec3 &operator/=(double t) noexcept { return *this *= 1.0 / t; }
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr double length_squared() const noexcept {
|
||||
return e[0] * e[0] + e[1] * e[1] + e[2] * e[2];
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
double length() const noexcept {
|
||||
return std::sqrt(length_squared());
|
||||
}
|
||||
|
||||
friend constexpr Vec3 operator+(Vec3 u, const Vec3 &v) noexcept {
|
||||
return u += v;
|
||||
}
|
||||
|
||||
friend constexpr Vec3 operator-(Vec3 u, const Vec3 &v) noexcept {
|
||||
u.e[0] -= v.e[0];
|
||||
u.e[1] -= v.e[1];
|
||||
u.e[2] -= v.e[2];
|
||||
return u;
|
||||
}
|
||||
|
||||
friend constexpr Vec3 operator*(Vec3 u, const Vec3 &v) noexcept {
|
||||
u.e[0] *= v.e[0];
|
||||
u.e[1] *= v.e[1];
|
||||
u.e[2] *= v.e[2];
|
||||
return u;
|
||||
}
|
||||
|
||||
friend constexpr Vec3 operator*(double t, Vec3 v) noexcept { return v *= t; }
|
||||
|
||||
friend constexpr Vec3 operator*(Vec3 v, double t) noexcept { return v *= t; }
|
||||
|
||||
friend constexpr Vec3 operator/(Vec3 v, double t) noexcept { return v /= t; }
|
||||
|
||||
friend constexpr double dot(const Vec3 &u, const Vec3 &v) noexcept {
|
||||
return u.e[0] * v.e[0] + u.e[1] * v.e[1] + u.e[2] * v.e[2];
|
||||
}
|
||||
|
||||
friend constexpr Vec3 cross(const Vec3 &u, const Vec3 &v) noexcept {
|
||||
return Vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1],
|
||||
u.e[2] * v.e[0] - u.e[0] * v.e[2],
|
||||
u.e[0] * v.e[1] - u.e[1] * v.e[0]);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend Vec3 unit_vector(Vec3 v) noexcept {
|
||||
return v / v.length();
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &out, const Vec3 &v) {
|
||||
return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
|
||||
}
|
||||
};
|
||||
|
||||
#endif // VEC3_H
|
||||
using Point3 = Vec3;
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue