From 6de955c67a40ee077f513fa189022fa8cd4bbff7 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Fri, 26 Feb 2021 13:33:02 -0500 Subject: shader gallery --- 00f.glsl | 7 ++ 00v.glsl | 9 +++ 1f.glsl | 7 -- 1v.glsl | 9 --- filesystem-posix.c | 90 +++++++++++++++++++++++++ filesystem-win.c | 89 ++++++++++++++++++++++++ filesystem.h | 41 ++++++++++++ main.c | 193 ++++++++++++++++++++++++++++++++++++++--------------- util.c | 7 ++ 9 files changed, 383 insertions(+), 69 deletions(-) create mode 100644 00f.glsl create mode 100644 00v.glsl delete mode 100644 1f.glsl delete mode 100644 1v.glsl create mode 100644 filesystem-posix.c create mode 100644 filesystem-win.c create mode 100644 filesystem.h diff --git a/00f.glsl b/00f.glsl new file mode 100644 index 0000000..a3e8d18 --- /dev/null +++ b/00f.glsl @@ -0,0 +1,7 @@ +varying vec2 pos; +uniform float u_time; + +void main() { + float t = mod(u_time, 2.0*3.1415926536); + gl_FragColor = vec4(pos.x, 0.5+0.5*sin(5.0*t), pos.y, 1.0); +} diff --git a/00v.glsl b/00v.glsl new file mode 100644 index 0000000..d5286df --- /dev/null +++ b/00v.glsl @@ -0,0 +1,9 @@ +attribute vec2 v_render_pos; +attribute vec2 v_pos; + +varying vec2 pos; + +void main() { + gl_Position = vec4(v_render_pos, 0.0, 1.0); + pos = v_pos; +} diff --git a/1f.glsl b/1f.glsl deleted file mode 100644 index a3e8d18..0000000 --- a/1f.glsl +++ /dev/null @@ -1,7 +0,0 @@ -varying vec2 pos; -uniform float u_time; - -void main() { - float t = mod(u_time, 2.0*3.1415926536); - gl_FragColor = vec4(pos.x, 0.5+0.5*sin(5.0*t), pos.y, 1.0); -} diff --git a/1v.glsl b/1v.glsl deleted file mode 100644 index d5286df..0000000 --- a/1v.glsl +++ /dev/null @@ -1,9 +0,0 @@ -attribute vec2 v_render_pos; -attribute vec2 v_pos; - -varying vec2 pos; - -void main() { - gl_Position = vec4(v_render_pos, 0.0, 1.0); - pos = v_pos; -} diff --git a/filesystem-posix.c b/filesystem-posix.c new file mode 100644 index 0000000..8af0bf2 --- /dev/null +++ b/filesystem-posix.c @@ -0,0 +1,90 @@ +#include "filesystem.h" +#include +#include +#include +#include +#include + +FsType fs_path_type(char const *path) { + struct stat statbuf = {0}; + if (stat(path, &statbuf) != 0) + return FS_NON_EXISTENT; + if (S_ISREG(statbuf.st_mode)) + return FS_FILE; + if (S_ISDIR(statbuf.st_mode)) + return FS_DIRECTORY; + return FS_OTHER; +} + +FsPermission fs_path_permission(char const *path) { + int bits = access(path, R_OK | W_OK); + FsPermission perm = 0; + if (!(bits & R_OK)) perm |= FS_PERMISSION_READ; + if (!(bits & W_OK)) perm |= FS_PERMISSION_WRITE; + return perm; +} + +bool fs_file_exists(char const *path) { + return fs_path_type(path) == FS_FILE; +} + +char **fs_list_directory(char const *dirname) { + char **ret = NULL; + DIR *dir = opendir(dirname); + if (dir) { + struct dirent *ent; + char **filenames = NULL; + size_t nentries = 0; + size_t filename_idx = 0; + + while (readdir(dir)) ++nentries; + rewinddir(dir); + filenames = (char **)calloc(nentries+1, sizeof *filenames); + + while ((ent = readdir(dir))) { + char const *filename = ent->d_name; + size_t len = strlen(filename); + char *filename_copy = (char *)malloc(len+1); + if (!filename_copy) break; + strcpy(filename_copy, filename); + if (filename_idx < nentries) // this could actually fail if someone creates files between calculating nentries and here. + filenames[filename_idx++] = filename_copy; + } + ret = filenames; + closedir(dir); + } + return ret; +} + +int fs_mkdir(char const *path) { + if (mkdir(path, 0755) == 0) { + // directory created successfully + return 1; + } else if (errno == EEXIST) { + struct stat statbuf = {0}; + if (stat(path, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + // already exists, and it's a directory + return 0; + } else { + // already exists, but not a directory + return -1; + } + } else { + return -1; + } + } else { + return -1; + } +} + +int fs_get_cwd(char *buf, size_t buflen) { + assert(buf && buflen); + if (getcwd(buf, buflen)) { + return 1; + } else if (errno == ERANGE) { + return 0; + } else { + return -1; + } +} diff --git a/filesystem-win.c b/filesystem-win.c new file mode 100644 index 0000000..c6c64dd --- /dev/null +++ b/filesystem-win.c @@ -0,0 +1,89 @@ +#include "filesystem.h" +#include +#include +#include + +FsType fs_path_type(char const *path) { + struct _stat statbuf = {0}; + if (_stat(path, &statbuf) != 0) + return FS_NON_EXISTENT; + if (statbuf.st_mode & _S_IFREG) + return FS_FILE; + if (statbuf.st_mode & _S_IFDIR) + return FS_DIRECTORY; + return FS_OTHER; +} + +FsPermission fs_path_permission(char const *path) { + FsPermission permission = 0; + if (_access(path, 04) == 0) permission |= FS_PERMISSION_READ; + if (_access(path, 02) == 0) permission |= FS_PERMISSION_WRITE; + return permission; +} + +bool fs_file_exists(char const *path) { + return fs_path_type(path) == FS_FILE; +} + +char **fs_list_directory(char const *dirname) { + char file_pattern[256] = {0}; + char **ret = NULL; + WIN32_FIND_DATA find_data; + HANDLE fhandle; + assert(*dirname); + sprintf_s(file_pattern, sizeof file_pattern, "%s%s*", dirname, + dirname[strlen(dirname) - 1] == PATH_SEPARATOR ? "" : PATH_SEPARATOR_STR); + fhandle = FindFirstFileA(file_pattern, &find_data); + if (fhandle != INVALID_HANDLE_VALUE) { + // first, figure out number of files + int nfiles = 1, idx = 0; + char **files; + while (FindNextFile(fhandle, &find_data)) { + ++nfiles; + } + FindClose(fhandle); + // now, fill out files array + files = malloc((nfiles + 1) * sizeof *files); + if (files) { + fhandle = FindFirstFileA(file_pattern, &find_data); + if (fhandle != INVALID_HANDLE_VALUE) { + do { + if (idx < nfiles) { + char *dup = _strdup(find_data.cFileName); + if (dup) { + files[idx++] = dup; + } else break; // stop now + } + } while (FindNextFile(fhandle, &find_data)); + files[idx] = NULL; + FindClose(fhandle); + ret = files; + } + } + } + return ret; +} + +int fs_mkdir(char const *path) { + if (CreateDirectoryA(path, NULL)) { + // directory created successfully + return 1; + } else { + if (GetLastError() == ERROR_ALREADY_EXISTS) // directory already exists + return 0; + else + return -1; // some other error + } +} + +int fs_get_cwd(char *buf, size_t buflen) { + assert(buf && buflen); + DWORD pathlen = GetCurrentDirectory((DWORD)buflen, buf); + if (pathlen == 0) { + return -1; + } else if (pathlen < buflen) { // it's confusing, but this is < and not <= + return 1; + } else { + return 0; + } +} diff --git a/filesystem.h b/filesystem.h new file mode 100644 index 0000000..6325bd7 --- /dev/null +++ b/filesystem.h @@ -0,0 +1,41 @@ +#ifndef FILESYSTEM_H_ +#define FILESYSTEM_H_ + +typedef enum { + FS_NON_EXISTENT, + FS_FILE, + FS_DIRECTORY, + FS_OTHER +} FsType; + +enum { + FS_PERMISSION_READ = 0x01, + FS_PERMISSION_WRITE = 0x02, +}; +typedef u8 FsPermission; + +// returns what kind of thing this is. +FsType fs_path_type(char const *path); +FsPermission fs_path_permission(char const *path); +// Does this file exist? Returns false for directories. +bool fs_file_exists(char const *path); +// Returns a NULL-terminated array of the files/directories in this directory, or NULL if the directory does not exist. +// When you're done with the file names, call free on each one, then on the array. +// NOTE: The files aren't returned in any particular order! +char **fs_list_directory(char const *dirname); +// Create the directory specified by `path` +// Returns: +// 1 if the directory was created successfully +// 0 if the directory already exists +// -1 if the path already exists, but it's not a directory, or if there's another error (e.g. don't have permission to create directory). +int fs_mkdir(char const *path); +// Puts the current working directory into buf, including a null-terminator, writing at most buflen bytes. +// Returns: +// 1 if the working directory was inserted into buf successfully +// 0 if buf is too short to hold the cwd +// -1 if we can't get the cwd for whatever reason. +int fs_get_cwd(char *buf, size_t buflen); + + +#endif // FILESYSTEM_H_ + diff --git a/main.c b/main.c index e9835df..705c699 100644 --- a/main.c +++ b/main.c @@ -9,9 +9,15 @@ #include "gl.c" #include "time.c" #include "util.c" +#if _WIN32 +#include "filesystem-win.c" +#else +#include "filesystem-posix.c" +#endif typedef struct { GLuint program; + GLuint vbo, vao; struct timespec last_modified; char vfilename[64], ffilename[64]; } Shader; @@ -43,9 +49,21 @@ static void shader_load(Shader *shader, char const *vfilename, char const *ffile fread(vcode, 1, vsize, vfp); fread(fcode, 1, fsize, ffp); + GLuint program = gl_compile_and_link_shaders(vcode, fcode); - if (program) + if (program) { shader->program = program; + if (shader->vbo) glDeleteBuffers(1, &shader->vbo); + if (shader->vao) glDeleteVertexArrays(1, &shader->vao); + GLuint vbo = 0; + glGenBuffers(1, &vbo); + GLuint vao = 0; + if (gl_version_major >= 3) + glGenVertexArrays(1, &vao); + + shader->vbo = vbo; + shader->vao = vao; + } } else print("Out of memory.\n"); free(vcode); free(fcode); } else { @@ -69,6 +87,54 @@ static void shader_check_for_changes(Shader *shader) { } } +static double start_time; + +static void shader_draw(Shader *shader, Rect where) { + shader_check_for_changes(shader); + float x1, y1, x2, y2; + rect_coords(where, &x1, &y1, &x2, &y2); + + if (shader->vao) + glBindVertexArray(shader->vao); + glBindBuffer(GL_ARRAY_BUFFER, shader->vbo); + v2 buffer_data[] = { + // gl coordinates (v_render_pos) + {x1, y1}, + {x2, y1}, + {x1, y2}, + + {x2, y1}, + {x2, y2}, + {x1, y2}, + + // normalized coordinates (v_pos) + {0, 0}, + {1, 0}, + {0, 1}, + + {1, 0}, + {1, 1}, + {0, 1}, + }; + + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)sizeof buffer_data, buffer_data, GL_STATIC_DRAW); + GLuint v_render_pos = gl_attrib_loc(shader->program, "v_render_pos"); + GLuint v_pos = gl_attrib_loc(shader->program, "v_pos"); + if (v_render_pos != (GLuint)-1) { + glVertexAttribPointer(v_render_pos, 2, GL_FLOAT, 0, sizeof(v2), NULL); + glEnableVertexAttribArray(v_render_pos); + } + if (v_pos != (GLuint)-1) { + glVertexAttribPointer(v_pos, 2, GL_FLOAT, 0, sizeof(v2), (void *)(6 * sizeof(v2))); + glEnableVertexAttribArray(v_pos); + } + glUseProgram(shader->program); + GLint u_time = gl_uniform_loc(shader->program, "u_time"); + if (u_time >= 0) + glUniform1f(u_time, (float)fmod(time_get_seconds() - start_time, 10000)); + glDrawArrays(GL_TRIANGLES, 0, 6); +} + static void die(char const *fmt, ...) { char buf[256] = {0}; @@ -142,63 +208,70 @@ int main(void) { SDL_GL_SetSwapInterval(1); // vsync - Shader shader = {0}; - shader_load(&shader, "1v.glsl", "1f.glsl"); + Shader *shaders = NULL; - GLuint vbo = 0; - glGenBuffers(1, &vbo); - GLuint vao = 0; - if (gl_version_major >= 3) - glGenVertexArrays(1, &vao); - - v2 buffer_data[] = { - // gl coordinates (v_render_pos) - {-1, -1}, - {+1, -1}, - {-1, +1}, - - {+1, -1}, - {+1, +1}, - {-1, +1}, - - // normalized coordinates (v_pos) - {0, 0}, - {1, 0}, - {0, 1}, - - {1, 0}, - {1, 1}, - {0, 1}, - }; + char **files = fs_list_directory("."); + size_t nfiles; for (nfiles = 0; files[nfiles]; ++nfiles); + qsort(files, nfiles, sizeof *files, str_qsort_case_insensitive_cmp); - if (vao) glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)sizeof buffer_data, buffer_data, GL_STATIC_DRAW); - GLuint v_render_pos = gl_attrib_loc(shader.program, "v_render_pos"); - GLuint v_pos = gl_attrib_loc(shader.program, "v_pos"); - if (v_render_pos != (GLuint)-1) { - glVertexAttribPointer(v_render_pos, 2, GL_FLOAT, 0, sizeof(v2), NULL); - glEnableVertexAttribArray(v_render_pos); + u16 nshaders = 0; + for (size_t i = 0; i < nfiles; ++i) { + nshaders += str_is_suffix(files[i], "v.glsl"); } - if (v_pos != (GLuint)-1) { - glVertexAttribPointer(v_pos, 2, GL_FLOAT, 0, sizeof(v2), (void *)(6 * sizeof(v2))); - glEnableVertexAttribArray(v_pos); + + u16 shader_idx = 0; + for (size_t i = 0; i < nfiles; ++i) { + if (str_is_suffix(files[i], "v.glsl")) { + char const *vfilename = files[i]; + char ffilename[64]; + strbuf_cpy(ffilename, vfilename); + ffilename[strlen(ffilename) - 6] = 'f'; + Shader shader = {0}; + shader_load(&shader, vfilename, ffilename); + arr_add(shaders, shader); + ++shader_idx; + } + free(files[i]); } - bool quit = false; - double start = time_get_seconds(); + i32 viewing_shader = -1; + + free(files); + + bool quit = false, fullscreen = false; + start_time = time_get_seconds(); while (!quit) { SDL_Event event = {0}; + int window_width, window_height; + SDL_GetWindowSize(window, &window_width, &window_height); + v2 *mouse_clicks = NULL; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: quit = true; break; + case SDL_MOUSEBUTTONDOWN: { + float x = -1 + 2 * (float)event.button.x / (float)window_width; + float y = +1 - 2 * (float)event.button.y / (float)window_height; + arr_add(mouse_clicks, V2(x, y)); + } break; + case SDL_KEYDOWN: + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + if (viewing_shader == -1) + quit = true; + else + viewing_shader = -1; + break; + case SDLK_F11: + fullscreen = !fullscreen; + SDL_SetWindowFullscreen(window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + break; + } + break; } } - int window_width, window_height; - SDL_GetWindowSize(window, &window_width, &window_height); // set up GL glEnable(GL_BLEND); @@ -207,18 +280,32 @@ int main(void) { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); - shader_check_for_changes(&shader); - - if (shader.program) { - if (vao) glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glUseProgram(shader.program); - GLint u_time = gl_uniform_loc(shader.program, "u_time"); - if (u_time >= 0) - glUniform1f(u_time, (float)fmod(time_get_seconds() - start, 10000)); - glDrawArrays(GL_TRIANGLES, 0, 6); + if (viewing_shader >= 0) { + // one shader + Shader *shader = &shaders[viewing_shader]; + shader_draw(shader, rect4(-1, -1, +1, +1)); + } else { + // shader gallery + u16 idx = 0; + float w = 2 * 1.0f / 6; + float h = 2 * 1.0f / ((nshaders + 5) / 6); + h = minf(h, 1.0f / 3); + arr_foreach_ptr(shaders, Shader, shader) { + float x1 = -1 + (idx % 6) * w; + float y1 = 1 - h - (idx / 6) * h; + Rect r = rect(V2(x1, y1), V2(w, h)); + r = rect_shrink(r, 0.02f); + arr_foreach_ptr(mouse_clicks, v2, click) { + if (rect_contains_point(r, *click)) + viewing_shader = idx; + } + shader_draw(shader, r); + ++idx; + } } + + arr_clear(mouse_clicks); SDL_GL_SwapWindow(window); } diff --git a/util.c b/util.c index aa79ad5..31bc7de 100644 --- a/util.c +++ b/util.c @@ -61,6 +61,13 @@ static bool str_is_prefix(char const *str, char const *prefix) { return strncmp(str, prefix, strlen(prefix)) == 0; } +static bool str_is_suffix(char const *str, char const *suffix) { + size_t str_len = strlen(str); + size_t suf_len = strlen(suffix); + if (str_len < suf_len) return false; + return memcmp(str + str_len - suf_len, suffix, suf_len) == 0; +} + static bool streq(char const *a, char const *b) { return strcmp(a, b) == 0; } -- cgit v1.2.3