fallingCand/gl_utils.c
2025-11-29 14:08:35 -08:00

181 lines
4.9 KiB
C

// gl_utils.c
#include "gl_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
void glfw_error_callback(int error, const char* description) {
fprintf(stderr, "GLFW error %d: %s\n", error, description);
}
char* load_text_file(const char* path) {
FILE* f = fopen(path, "rb");
if (!f) {
fprintf(stderr, "Failed to open '%s': %s\n", path, strerror(errno));
return NULL;
}
if (fseek(f, 0, SEEK_END) != 0) {
fprintf(stderr, "fseek failed on '%s'\n", path);
fclose(f);
return NULL;
}
long size = ftell(f);
if (size < 0) {
fprintf(stderr, "ftell failed on '%s'\n", path);
fclose(f);
return NULL;
}
rewind(f);
char* buffer = malloc((size_t)size + 1);
if (!buffer) {
fprintf(stderr, "Out of memory reading '%s'\n", path);
fclose(f);
return NULL;
}
size_t read = fread(buffer, 1, (size_t)size, f);
fclose(f);
if (read != (size_t)size) {
fprintf(stderr, "Short read on '%s'\n", path);
free(buffer);
return NULL;
}
buffer[size] = '\0';
return buffer;
}
GLuint compile_shader(GLenum type, const char* source, const char* debugName) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
GLint success = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char log[1024];
glGetShaderInfoLog(shader, sizeof(log), NULL, log);
fprintf(stderr, "Shader compile failed (%s):\n%s\n", debugName ? debugName : "unknown", log);
glDeleteShader(shader);
exit(EXIT_FAILURE);
}
return shader;
}
GLuint create_program_from_files(const char* vsPath, const char* fsPath) {
char* vsSource = load_text_file(vsPath);
if (!vsSource) {
fprintf(stderr, "Could not load vertex shader from '%s'\n", vsPath);
exit(EXIT_FAILURE);
}
char* fsSource = load_text_file(fsPath);
if (!fsSource) {
fprintf(stderr, "Could not load fragment shader from '%s'\n", fsPath);
free(vsSource);
exit(EXIT_FAILURE);
}
GLuint vs = compile_shader(GL_VERTEX_SHADER, vsSource, vsPath);
GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fsSource, fsPath);
free(vsSource);
free(fsSource);
GLuint prog = glCreateProgram();
glAttachShader(prog, vs);
glAttachShader(prog, fs);
glLinkProgram(prog);
glDeleteShader(vs);
glDeleteShader(fs);
GLint linked = 0;
glGetProgramiv(prog, GL_LINK_STATUS, &linked);
if (!linked) {
char log[1024];
glGetProgramInfoLog(prog, sizeof(log), NULL, log);
fprintf(stderr, "Program link failed:\n%s\n", log);
glDeleteProgram(prog);
exit(EXIT_FAILURE);
}
return prog;
}
GLuint create_compute_program_from_file(const char* path) {
char* source = load_text_file(path);
if (!source) {
fprintf(stderr, "Could not load compute shader from '%s'\n", path);
exit(EXIT_FAILURE);
}
// Reuse compile_shader; type is GL_COMPUTE_SHADER
GLuint cs = compile_shader(GL_COMPUTE_SHADER, source, path);
free(source);
GLuint prog = glCreateProgram();
glAttachShader(prog, cs);
glLinkProgram(prog);
glDeleteShader(cs);
GLint linked = 0;
glGetProgramiv(prog, GL_LINK_STATUS, &linked);
if (!linked) {
char log[1024];
glGetProgramInfoLog(prog, sizeof(log), NULL, log);
fprintf(stderr, "Compute program link failed (%s):\n%s\n", path, log);
glDeleteProgram(prog);
exit(EXIT_FAILURE);
}
return prog;
}
GLFWwindow* init_glfw_glad(const char* title, int width, int height) {
glfwSetErrorCallback(glfw_error_callback);
// Prefer Wayland if this GLFW build supports it
if (glfwPlatformSupported(GLFW_PLATFORM_WAYLAND)) {
glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_WAYLAND);
fprintf(stderr, "[info] Requesting Wayland platform via glfwInitHint\n");
} else {
fprintf(stderr, "[warn] Wayland platform not supported by this GLFW build, using default\n");
}
if (!glfwInit()) {
fprintf(stderr, "[fatal] glfwInit failed\n");
return NULL;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
if (!window) {
fprintf(stderr, "[fatal] glfwCreateWindow failed\n");
glfwTerminate();
return NULL;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
fprintf(stderr, "[fatal] Failed to initialize GLAD\n");
glfwDestroyWindow(window);
glfwTerminate();
return NULL;
}
glfwSwapInterval(0);
return window;
}