// main.c #include #include #include "gl_utils.h" #include "sand_sim.h" #define GRID_W 2048 #define GRID_H 2048 static int g_fbWidth = 800; static int g_fbHeight = 800; static double g_cursorX = 0.0; static double g_cursorY = 0.0; SandSim sim; static void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) { (void)window; g_cursorX = xpos; g_cursorY = ypos; } static void render_sand(SandSim* sim, GLuint program, GLuint vao, int fbW, int fbH, float brushX, float brushY, float brushRadius) { glUseProgram(program); GLint uResLoc = glGetUniformLocation(program, "u_resolution"); GLint uGridLoc = glGetUniformLocation(program, "u_gridSize"); GLint uStateLoc = glGetUniformLocation(program, "u_state"); GLint uBrushPosLoc = glGetUniformLocation(program, "u_brushPos"); GLint uBrushRadLoc = glGetUniformLocation(program, "u_brushRadius"); GLint uShowBrushLoc = glGetUniformLocation(program, "u_showBrush"); if (uResLoc >= 0) { glUniform2f(uResLoc, (float)fbW, (float)fbH); } if (uGridLoc >= 0) { glUniform2i(uGridLoc, sim->gridW, sim->gridH); } if (uStateLoc >= 0) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, sim->tex_curr); glUniform1i(uStateLoc, 0); } if (uBrushPosLoc >= 0) { glUniform2f(uBrushPosLoc, brushX, brushY); } if (uBrushRadLoc >= 0) { glUniform1f(uBrushRadLoc, brushRadius); } if (uShowBrushLoc >= 0) { glUniform1i(uShowBrushLoc, 1); // always show for now } glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES, 0, 3); } int main(void) { GLFWwindow* window = init_glfw_glad("Falling Sand - Fullscreen Quad", g_fbWidth, g_fbHeight); if (!window) { return EXIT_FAILURE; } glfwSetCursorPosCallback(window, cursor_pos_callback); glfwGetFramebufferSize(window, &g_fbWidth, &g_fbHeight); glViewport(0, 0, g_fbWidth, g_fbHeight); printf("Renderer: %s\n", (const char*)glGetString(GL_RENDERER)); printf("OpenGL: %s\n", (const char*)glGetString(GL_VERSION)); printf("GLSL: %s\n", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); // --- Create fullscreen triangle geometry (VAO + VBO) --- float vertices[] = { // x, y -1.0f, -1.0f, 3.0f, -1.0f, -1.0f, 3.0f}; GLuint vao = 0, vbo = 0; glGenVertexArrays(1, &vao); glGenBuffers(1, &vbo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // layout(location = 0) in vec2 aPos; glEnableVertexAttribArray(0); glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glBindVertexArray(0); if (!sand_init(&sim, GRID_W, GRID_H, "shaders/sand_step.comp")) { fprintf(stderr, "Failed to initialize sand sim\n"); return EXIT_FAILURE; } // --- Create shader program --- GLuint program = create_program_from_files( "shaders/fullscreen.vert", "shaders/sand_display.frag"); glUseProgram(program); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); double sim_dt = 1.0 / 360.0; double currentTime = glfwGetTime(); double accumulator = 0.0; float brushX = 0.0f; float brushY = 0.0f; // --- Main loop --- while (!glfwWindowShouldClose(window)) { double newTime = glfwGetTime(); double frameTime = newTime - currentTime; currentTime = newTime; if (frameTime > 0.25) frameTime = 0.25; accumulator += frameTime; while (accumulator >= sim_dt) { sand_step_gpu(&sim); // one discrete CA step on the GPU sand_relax_gpu(&sim); accumulator -= sim_dt; } int paintMode = 0; glfwPollEvents(); if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { paintMode = 1; } else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) { paintMode = -1; } // Re-query framebuffer size each frame (cheap and simple) int fbw, fbh; glfwGetFramebufferSize(window, &fbw, &fbh); if (fbw != g_fbWidth || fbh != g_fbHeight) { g_fbWidth = fbw; g_fbHeight = fbh; glViewport(0, 0, g_fbWidth, g_fbHeight); } if (g_fbWidth > 0 && g_fbHeight > 0) { float uvx = (float)g_cursorX / (float)g_fbWidth; float uvy = (float)g_cursorY / (float)g_fbHeight; brushX = uvx * (float)sim.gridW; brushY = uvy * (float)sim.gridH; } float brushRadius = 60.0f; sand_paint_gpu(&sim, brushX, brushY, brushRadius, paintMode); glClear(GL_COLOR_BUFFER_BIT); render_sand(&sim, program, vao, g_fbWidth, g_fbHeight, brushX, brushY, brushRadius); glfwSwapBuffers(window); } // --- Cleanup --- glDeleteProgram(program); glDeleteBuffers(1, &vbo); glDeleteVertexArrays(1, &vao); glfwDestroyWindow(window); glfwTerminate(); return EXIT_SUCCESS; }