fallingCand/sand_sim.c
2025-11-29 13:59:31 -08:00

135 lines
4.1 KiB
C

// sand_sim.c
#include "sand_sim.h"
#include "gl_utils.h" // for create_compute_program_from_file
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static void init_textures(SandSim* sim) {
// tex_curr
glGenTextures(1, &sim->tex_curr);
glBindTexture(GL_TEXTURE_2D, sim->tex_curr);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8UI, sim->gridW, sim->gridH);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// tex_next
glGenTextures(1, &sim->tex_next);
glBindTexture(GL_TEXTURE_2D, sim->tex_next);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8UI, sim->gridW, sim->gridH);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Unbind
glBindTexture(GL_TEXTURE_2D, 0);
}
static void upload_initial_state(SandSim* sim) {
size_t count = (size_t)sim->gridW * (size_t)sim->gridH;
uint8_t* data = calloc(count, 1);
if (!data) {
fprintf(stderr, "[sand_init] Out of memory for initial grid\n");
exit(EXIT_FAILURE);
}
// Seed RNG once (safe enough for now)
static bool seeded = false;
if (!seeded) {
seeded = true;
srand((unsigned int)time(NULL));
}
// Fill top 1/3 of grid with random sand vs air
for (int y = 0; y < sim->gridH / 3; ++y) {
for (int x = 0; x < sim->gridW; ++x) {
data[(size_t)y * sim->gridW + x] = (rand() & 1) ? 1 : 0; // 0=air,1=sand
}
}
// Upload into tex_curr and tex_next
glBindTexture(GL_TEXTURE_2D, sim->tex_curr);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sim->gridW, sim->gridH,
GL_RED_INTEGER, GL_UNSIGNED_BYTE, data);
glBindTexture(GL_TEXTURE_2D, sim->tex_next);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sim->gridW, sim->gridH,
GL_RED_INTEGER, GL_UNSIGNED_BYTE, data);
glBindTexture(GL_TEXTURE_2D, 0);
free(data);
}
bool sand_init(SandSim* sim, int gridW, int gridH, const char* computeShaderPath) {
if (!sim || gridW <= 0 || gridH <= 0 || !computeShaderPath) {
fprintf(stderr, "[sand_init] Invalid arguments\n");
return false;
}
sim->gridW = gridW;
sim->gridH = gridH;
sim->tex_curr = 0;
sim->tex_next = 0;
sim->prog_sim = 0;
init_textures(sim);
upload_initial_state(sim);
sim->prog_sim = create_compute_program_from_file(computeShaderPath);
if (!sim->prog_sim) {
fprintf(stderr, "[sand_init] Failed to create compute program\n");
return false;
}
return true;
}
void sand_step_gpu(SandSim* sim) {
if (!sim || !sim->prog_sim) return;
glUseProgram(sim->prog_sim);
// Set grid size uniform
GLint uGridLoc = glGetUniformLocation(sim->prog_sim, "u_gridSize");
if (uGridLoc >= 0) {
glUniform2i(uGridLoc, sim->gridW, sim->gridH);
}
// Bind images (must match bindings in sand_step.comp)
glBindImageTexture(0, sim->tex_curr, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8UI);
glBindImageTexture(1, sim->tex_next, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8UI);
GLuint groupsX = (sim->gridW + 15) / 16;
GLuint groupsY = (sim->gridH + 15) / 16;
glDispatchCompute(groupsX, groupsY, 1);
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
// Ping-pong
GLuint tmp = sim->tex_curr;
sim->tex_curr = sim->tex_next;
sim->tex_next = tmp;
}
void sand_destroy(SandSim* sim) {
if (!sim) return;
if (sim->tex_curr) {
glDeleteTextures(1, &sim->tex_curr);
sim->tex_curr = 0;
}
if (sim->tex_next) {
glDeleteTextures(1, &sim->tex_next);
sim->tex_next = 0;
}
if (sim->prog_sim) {
glDeleteProgram(sim->prog_sim);
sim->prog_sim = 0;
}
}