#ifdef __cplusplus
#include <string>
std::string shader_physics = R"("
#version 460

// execute 1 shader per particle. 128 executions per group
layout (local_size_x = 128, local_size_y = 1, local_size_z = 1) in;

// due to byte alignment, storing pos and dir as vec4 was required anyways.
struct particle_t {
    vec4 pos;
    vec4 dir;
};

const int offset_size = 8192;
const float SPEED = 1.0f;
const float SPEED_FACTOR = 25.0f;
const float BOUNCE_FACTOR = 0.75f;
const float SPREAD = 4.5f;
const float particle_lifetime = 25.0f;
const vec3 inital_pos = vec3(0.0f, 1.0f, 0.0f);
const vec4 inital_dir = vec4(0.0f, 1.0f, 0.0f, 0.0f);

const vec2 p_min = vec2(-50, -50);
const vec2 p_max = vec2(50, 50);
const vec4 GRAVITY = vec4(0.0, -9.8, 0.0, 0.0);


layout (std430, binding=0) buffer particle_buffer {
    particle_t particles[];
};

layout (std430, binding=1) buffer offset_buffer {
    vec4 offsets[offset_size];
};

uniform float deltaSeconds;

void resetParticle(uint i, float w) {
    particles[i].dir = inital_dir * SPEED_FACTOR + offsets[i % offset_size] * SPREAD;
    particles[i].pos = vec4(inital_pos, w);
}

bool checkBounds(vec2 pos) {
    return pos.x > p_min.x && pos.y > p_min.y && pos.x < p_max.x && pos.y < p_max.y;
}

void main() {
    uint i = gl_GlobalInvocationID.x;

    vec4 pos = particles[i].pos;
    vec4 dir = particles[i].dir;

    dir.w += deltaSeconds;

    if (dir.w > particle_lifetime) {
        resetParticle(i, pos.w);
        return;
    }

    pos += vec4(dir.xyz * SPEED * deltaSeconds, 0.0);
    dir += vec4(GRAVITY.xyz * deltaSeconds, 0.0);

    if (pos.y < 0 && checkBounds(pos.xz)) {
        dir.y = -dir.y * BOUNCE_FACTOR;
        pos.y = 0;
    }

    particles[i].dir = dir;
    particles[i].pos = pos;

    if (pos.y < -50)
        resetParticle(i, pos.w);

}

")";
#endif