a simple particle physics sandbox in C++
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

508 lines
11 KiB

#include "particles.h"
#include <iostream>
namespace pengine {
Uint8 part_colours[][3] = {
{255, 255, 255}, // BASIC
{252, 250, 148}, // SAND
{70, 90, 255}, // WATER
{200, 220, 255}, // STEAM
{200, 20, 20}, // LAVA
{140, 111, 71}, // ROCK
{200, 20, 20}, // LIQUID QUARTZ
{20, 80, 40}, // QUARTZ
};
std::string part_names[] = {"default", "Sand", "Water",
"Steam", "Lava", "Rock"};
struct particle_data BasicParticle::default_data(ParticleID id) {
struct particle_data datap;
switch (id) {
case ParticleID::WATER:
datap = {.density = 10,
.liquid = true,
.temperature = 20,
.boiling_point = 100,
.gas_state = ParticleID::STEAM};
break;
case ParticleID::LAVA:
datap = {.density = 20,
.liquid = true,
.temperature = 5000,
.boiling_point = 10000,
.freezing_point = 200,
.solid_state = ParticleID::ROCK};
break;
case ParticleID::STEAM:
datap = {.density = 0,
.liquid = false,
.temperature = 150,
.liquid_state = ParticleID::WATER};
break;
case ParticleID::SAND:
datap = {.density = 100, .liquid = false, .temperature = 20};
break;
case ParticleID::ROCK:
datap = {.density = 150,
.liquid = false,
.temperature = 20,
.melting_point = 700,
.liquid_state = ParticleID::LAVA};
break;
default:
datap = {.temperature = 0};
break;
}
return datap;
}
BasicParticle::BasicParticle(ParticleID id) : Particle(id) {
this->id = id;
data = default_data(id);
};
State BasicParticle::get_state() {
switch (id) {
case (ParticleID::STEAM):
return State::GAS;
case (ParticleID::WATER):
case (ParticleID::LAVA):
return State::LIQUID;
case (ParticleID::ROCK):
case (ParticleID::SAND):
return State::SOLID;
default:
return State::SOLID;
}
}
Particle *get_new_particle(ParticleID p) {
switch (p) {
case ParticleID::SAND:
return new Sand();
case ParticleID::WATER:
return new Water();
case ParticleID::BASIC:
return new BasicParticle();
case ParticleID::STEAM:
return new Steam();
case ParticleID::LAVA:
return new Lava();
case ParticleID::ROCK:
return new Rock();
default:
std::cout << "Unknown particle create\n";
throw "unknown particle";
}
}
PixelColour::PixelColour(short r, short g, short b, short a) {
this->r = r;
this->g = g;
this->b = b;
this->a = a;
}
PixelColour::PixelColour(Uint32 c) { pixel = c; }
PixelColour::PixelColour(short r, short g, short b) {
this->r = r;
this->g = g;
this->b = b;
this->a = 255;
}
PixelColour::PixelColour(SDL_Color c) {
this->r = c.r;
this->g = c.g;
this->b = c.b;
this->a = c.a;
}
PixelColour::PixelColour(const Uint8 p[3]) : PixelColour(p[0], p[1], p[2]) {}
SDL_Color PixelColour::get_sdlcol() {
SDL_Color c;
c.r = r;
c.b = b;
c.g = g;
c.a = a;
return c;
}
Uint32 PixelColour::get_pixelcol() { return pixel; }
Grid::Grid(int xs, int ys) {
xsize = xs;
ysize = ys;
grid = new Particle *[xs * ys] {};
pixels = new Uint32[xs * ys]{};
clear();
}
Grid::~Grid() {
for (int i = 0; i < xsize * ysize; i++) {
if (grid[i])
delete grid[i];
}
delete[] grid;
delete[] pixels;
}
Grid &Grid::operator=(Grid o) {
xsize = o.xsize;
ysize = o.ysize;
grid = new Particle *[xsize * ysize] {};
pixels = new Uint32[xsize * ysize]{};
for (int i = 0; i < xsize * ysize; i++) {
grid[i] = o.grid[i];
pixels[i] = o.pixels[i];
}
return *this;
}
void Grid::clear() {
for (int i = 0; i < xsize * ysize; i++) {
grid[i] = 0;
pixels[i] = 0;
}
}
void Grid::update() {
static bool updated = false;
bool done = true;
while (done) {
done = false;
for (int i = 0; i < xsize; i++) {
for (int j = 0; j < ysize; j++) {
int p = j * xsize + i;
if (grid[p]) {
if (grid[p]->updated != updated) {
grid[p]->updated = updated;
grid[p]->update(this, i, j);
done = true;
}
}
}
}
}
updated = !updated;
}
void Grid::erase_particle_at(int x, int y) {
if (x >= xsize || y >= ysize || x < 0 || y < 0) {
std::cout << "invalid position " << x << " " << y << std::endl;
throw "invalid boundaries";
}
grid[x + y * xsize] = 0;
}
void Grid::add_particle_at(int x, int y, ParticleID p) {
if (x >= xsize || y >= ysize || x < 0 || y < 0) {
std::cout << "invalid position " << x << " " << y << std::endl;
throw "invalid boundaries";
}
if (grid[x + y * xsize]) {
return;
}
grid[x + y * xsize] = get_new_particle(p);
}
Particle *Grid::get_particle(int x, int y) {
if (!inside_grid(x, y)) {
return nullptr;
}
return grid[x + xsize * y];
}
void Grid::update_texture() {
PixelColour defaultcol = PixelColour(0, 0, 0, 0);
for (int i = 0; i < xsize; i++) {
for (int j = 0; j < ysize; j++) {
int p = j * xsize + i;
if (grid[p] != nullptr) {
//: w pixels[p] = defaultcoll.pixel;
pixels[p] = grid[p]->get_colour().get_pixelcol();
} else {
pixels[p] = defaultcol.get_pixelcol();
}
}
}
}
bool Grid::inside_grid(int x, int y) {
if (x >= xsize || y >= ysize || x < 0 || y < 0) {
return false;
}
return true;
}
bool Grid::move_particle(int xa, int ya, int xb, int yb) {
if (!inside_grid(xb, yb)) {
return false;
}
if (!inside_grid(xa, ya)) {
return false;
}
if ((get_particle(xa, ya) != nullptr) && (get_particle(xb, yb) == nullptr)) {
grid[xb + yb * xsize] = grid[xa + ya * xsize];
grid[xa + ya * xsize] = nullptr;
return true;
}
return false;
}
int Grid::swap_particle(int xa, int ya, int xb, int yb) {
if (!inside_grid(xb, yb)) {
return 0;
}
if (!inside_grid(xa, ya)) {
return 0;
}
int num = 0;
if (get_particle(xa, ya) != nullptr) {
num++;
}
if (get_particle(xb, yb) != nullptr) {
num++;
}
if (num > 0) {
Particle *temp = grid[xb + yb * xsize];
grid[xb + yb * xsize] = grid[xa + ya * xsize];
grid[xa + ya * xsize] = temp;
return num;
}
return 0;
}
void Grid::replace_particle(int x, int y, Particle *new_particle) {
auto p = get_particle(x, y);
if (!p) {
return;
}
delete p;
grid[x + y * xsize] = new_particle;
}
void Sand::update(Grid *const g, int x, int y) {
if (update_temperature(g, x, y)) {
return;
}
Particle *p;
if (g->move_particle(x, y, x, y + 1)) {
return;
}
if (g->move_particle(x, y, x - 1, y + 1)) {
return;
}
if (g->move_particle(x, y, x + 1, y + 1)) {
return;
}
p = g->get_particle(x, y + 1);
if (p != nullptr) {
if (p->data.density < data.density || p->data.liquid) {
g->swap_particle(x, y, x, y + 1);
return;
}
} else {
if (g->swap_particle(x, y, x, y + 1)) {
return;
}
}
p = g->get_particle(x - 1, y + 1);
if (p != nullptr) {
if (p->data.density < data.density || p->data.liquid) {
g->swap_particle(x, y, x - 1, y + 1);
return;
}
} else {
if (g->swap_particle(x, y, x - 1, y + 1))
return;
}
p = g->get_particle(x + 1, y + 1);
if (p != nullptr) {
if (p->data.density < data.density || p->data.liquid) {
g->swap_particle(x, y, x + 1, y + 1);
return;
}
} else {
if (g->swap_particle(x, y, x + 1, y + 1))
return;
}
}
BasicParticle::BasicParticle() : Particle(ParticleID::BASIC) {}
void BasicParticle::update(Grid *const g, int x, int y) { return; }
PixelColour BasicParticle::get_colour() {
return PixelColour{part_colours[((int)this->id)]};
}
std::string BasicParticle::get_name() { return part_names[((int)this->id)]; }
bool BasicParticle::update_temperature(Grid *const g, int x, int y) {
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
int xa = x + i;
int ya = y + j;
auto p = g->get_particle(xa, ya);
if (p) {
data.temperature += (p->data.temperature - data.temperature) / 5;
}
}
}
/* apply state transition */
if (get_state() != State::GAS && data.temperature > data.boiling_point) {
if (data.gas_state != ParticleID::BASIC) {
auto gas = get_new_particle(data.gas_state);
g->replace_particle(x, y, gas);
return true;
}
}
if (get_state() != State::LIQUID && data.temperature < data.boiling_point &&
data.temperature > data.freezing_point) {
if (data.liquid_state != ParticleID::BASIC) {
auto liquid = get_new_particle(data.liquid_state);
g->replace_particle(x, y, liquid);
return true;
}
}
if (get_state() != State::SOLID && data.temperature < data.freezing_point) {
if (data.solid_state != ParticleID::BASIC) {
auto gas = get_new_particle(data.solid_state);
g->replace_particle(x, y, gas);
return true;
}
}
return false;
}
void Water::update(Grid *const g, int x, int y) {
if (update_temperature(g, x, y)) {
return;
}
auto p = g->get_particle(x, y + 1);
if (p && p->data.liquid && p->id != id &&
this->data.density > p->data.density) {
if (g->swap_particle(x, y, x, y + 1))
return;
}
p = g->get_particle(x - 1, y + 1);
if ((p && p->data.liquid && p->id != id && data.density > p->data.density)) {
if (g->swap_particle(x, y, x - 1, y + 1)) {
return;
}
}
p = g->get_particle(x + 1, y + 1);
if ((p && p->data.liquid && p->id != id && data.density > p->data.density)) {
if (g->swap_particle(x, y, x + 1, y + 1)) {
return;
}
}
if (g->move_particle(x, y, x, y + 1)) {
return;
}
if (g->move_particle(x, y, x - 1, y + 1)) {
return;
}
if (g->move_particle(x, y, x + 1, y + 1)) {
return;
}
/* laksjdlaj */
if (g->move_particle(x, y, x - 1, y)) {
return;
}
if (g->move_particle(x, y, x + 1, y)) {
return;
}
p = g->get_particle(x - 1, y);
if ((p && p->data.liquid)) {
if (g->swap_particle(x, y, x - 1, y)) {
return;
}
}
p = g->get_particle(x + 1, y);
if ((p && p->data.liquid)) {
if (g->swap_particle(x, y, x + 1, y)) {
return;
}
}
}
void Steam::update(Grid *const g, int x, int y) {
Particle *p = g->get_particle(x, y - 1);
if (g->move_particle(x, y, x, y - 1)) {
return;
}
p = g->get_particle(x - 1, y - 1);
if (g->move_particle(x, y, x - 1, y - 1)) {
return;
}
p = g->get_particle(x + 1, y - 1);
if (g->move_particle(x, y, x + 1, y - 1)) {
return;
}
p = g->get_particle(x + 1, y);
if (g->move_particle(x, y, x + 1, y)) {
return;
}
p = g->get_particle(x - 1, y);
if (g->move_particle(x, y, x - 1, y)) {
return;
}
}
Lava::Lava() : Water(ParticleID::LAVA){};
Rock::Rock() : Sand(ParticleID::ROCK){};
void Lava::update(Grid *const g, int x, int y) { Water::update(g, x, y); }
void Rock::update(Grid *const g, int x, int y) { Sand::update(g, x, y); }
} // namespace pengine