#include #include #include #include #include #include #include #include #include #include #include "dynamic-array.h" #define ZOOM_SPEED 1 #define FONT_COLOR WHITE #define FONT_SIZE 20 #define TEXT_SPACING 5 #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 #else #define GLSL_VERSION 100 #endif #define STRINGIFY(X) #X #define TO_STRING(X) STRINGIFY(X) #define DIRECTORY_SHADERS "./res/shaders/glsl" TO_STRING (GLSL_VERSION) #define INOTIFY_EVENT_BUFFER_SIZE 4096 #define MOD(A, B) ((((A) % (B)) + (B)) % (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) struct shader_info_da { struct shader_info { char *name; Shader shader; } *items; uint32_t count; uint32_t capacity; }; int main (void) { SetTraceLogLevel (LOG_ALL); char inotify_event_buffer[INOTIFY_EVENT_BUFFER_SIZE] __attribute__ ((aligned (__alignof__ (struct inotify_event)))); int inotify_fd = inotify_init (); if (inotify_fd < 0) { perror ("inotify_init"); exit (EXIT_FAILURE); } int inotify_wd = inotify_add_watch (inotify_fd, DIRECTORY_SHADERS, IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO); if (inotify_wd == -1) { perror ("inotify_add_watch"); exit (EXIT_FAILURE); } TraceLog (LOG_TRACE, "Watching "DIRECTORY_SHADERS" for changes"); const int screen_width = 800; const int screen_height = 450; SetConfigFlags (FLAG_WINDOW_RESIZABLE); InitWindow (screen_width, screen_height, "shadertoy"); SetTargetFPS (60); Font font = GetFontDefault (); struct shader_info_da shader_info_da = {0}; DIR *shaders_dp = opendir (DIRECTORY_SHADERS); if (shaders_dp == NULL) { perror ("opendir"); exit (EXIT_FAILURE); } struct dirent *entry; while ((entry = readdir (shaders_dp)) != NULL) { if (entry->d_type == DT_REG && entry->d_name[0] != '.' && strlen (entry->d_name) > 3 && !strcmp (entry->d_name + strlen (entry->d_name) - 3, ".fs")) { TraceLog (LOG_INFO, "Found shader %s", entry->d_name); char *name = malloc (strlen (entry->d_name) - 2); memcpy (name, entry->d_name, strlen (entry->d_name) - 3); name[strlen (entry->d_name) - 3] = '\0'; Shader shader = LoadShader (NULL, TextFormat (DIRECTORY_SHADERS"/%s", entry->d_name)); DYNAMIC_ARRAY_APPEND (shader_info_da, ((struct shader_info) { .name = name, .shader = shader, })); } } closedir (shaders_dp); int32_t shader_idx = 0; if (shader_info_da.count) { SetWindowTitle (TextFormat ("shadertoy - %s", shader_info_da.items[shader_idx].name)); } RenderTexture2D target = LoadRenderTexture (GetScreenWidth (), GetScreenHeight ()); float zoom_target = 1; float zoom = 1; while (!WindowShouldClose ()) { fd_set read_fds; FD_ZERO (&read_fds); FD_SET (inotify_fd, &read_fds); struct timeval notime = {0}; int activity = select (FD_SETSIZE, &read_fds, NULL, NULL, ¬ime); if (activity < 0) { perror ("select"); exit (EXIT_FAILURE); } if (FD_ISSET (inotify_fd, &read_fds)) { int length = read (inotify_fd, inotify_event_buffer, INOTIFY_EVENT_BUFFER_SIZE); if (length < 0) { perror ("read"); exit (EXIT_FAILURE); } for (int i = 0; i + sizeof (struct inotify_event) < length; ) { struct inotify_event *event = (struct inotify_event *) &inotify_event_buffer[i]; if (event->name[0] != '.' && !strcmp (&event->name[strlen (event->name) - 3], ".fs")) { if (event->mask & IN_MODIFY) { TraceLog (LOG_INFO, "Reloading shader %s", event->name); DYNAMIC_ARRAY_FOREACH (shader_info, shader_info_da) { if (!strncmp (event->name, shader_info->name, strlen (shader_info->name))) { UnloadShader (shader_info->shader); shader_info->shader = LoadShader (NULL, TextFormat (DIRECTORY_SHADERS"/%s.fs", shader_info->name)); break; } } } else if (event->mask & (IN_DELETE | IN_MOVED_FROM)) { TraceLog (LOG_INFO, "Unloading shader %s", event->name); DYNAMIC_ARRAY_FOREACH (shader_info, shader_info_da) { if (!strncmp (event->name, shader_info->name, strlen (shader_info->name))) { free (shader_info->name); UnloadShader (shader_info->shader); int32_t i = shader_info - shader_info_da.items; memmove (&shader_info_da.items[i], &shader_info_da.items[i + 1], (shader_info_da.count - i)*sizeof (struct shader_info)); shader_info_da.count -= 1; if (shader_info_da.count) { shader_idx = MIN (shader_idx, shader_info_da.count - 1); SetWindowTitle (TextFormat ("shadertoy - %s", shader_info_da.items[shader_idx].name)); } SetWindowTitle ("shadertoy"); break; } } } else if (event->mask & (IN_CREATE | IN_MOVED_TO)) { TraceLog (LOG_INFO, "Loading shader %s", event->name); char *name = malloc (strlen (event->name) - 2); memcpy (name, event->name, strlen (event->name) - 3); name[strlen (event->name) - 3] = '\0'; Shader shader = LoadShader (NULL, TextFormat (DIRECTORY_SHADERS"/%s", event->name)); DYNAMIC_ARRAY_APPEND (shader_info_da, ((struct shader_info) { .name = name, .shader = shader, })); if (shader_info_da.count == 1) SetWindowTitle (TextFormat ("shadertoy - %s", name)); } } i += sizeof (struct inotify_event) + event->len; } } if (shader_info_da.count && (IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_RIGHT))) { int32_t c = shader_info_da.count; if (IsKeyPressed(KEY_LEFT )) shader_idx = MOD (shader_idx - 1, c); if (IsKeyPressed(KEY_RIGHT)) shader_idx = MOD (shader_idx + 1, c); SetWindowTitle (TextFormat ("shadertoy - %s", shader_info_da.items[shader_idx].name)); } zoom_target += GetMouseWheelMove()*ZOOM_SPEED; zoom += Clamp (zoom_target - zoom, -10*GetFrameTime (), +10*GetFrameTime ()); if (shader_info_da.count) { if (shader_info_da.items[shader_idx].shader.id != rlGetShaderIdDefault ()) { Shader active_shader = shader_info_da.items[shader_idx].shader; float time = GetTime (); int shader_location_time = GetShaderLocation(active_shader, "time"); SetShaderValue (active_shader, shader_location_time, &time, SHADER_UNIFORM_FLOAT); Vector2 resolution = { GetScreenWidth (), GetScreenHeight () }; int shader_location_resolution = GetShaderLocation(active_shader, "resolution"); SetShaderValue (active_shader, shader_location_resolution, &resolution, SHADER_UNIFORM_VEC2); Vector2 mouse = GetMousePosition (); mouse.y = GetScreenHeight () - mouse.y; int shader_location_mouse = GetShaderLocation (active_shader, "mouse"); SetShaderValue (active_shader, shader_location_mouse, &mouse, SHADER_UNIFORM_VEC2); int shader_location_zoom = GetShaderLocation (active_shader, "zoom"); SetShaderValue (active_shader, shader_location_zoom, &zoom, SHADER_UNIFORM_FLOAT); if (GetScreenWidth () != target.texture.width || GetScreenHeight () != target.texture.height) { UnloadRenderTexture (target); target = LoadRenderTexture (GetScreenWidth (), GetScreenHeight ()); } BeginTextureMode (target); { ClearBackground (BLANK); // NOTE(cmmm): plugin code here } EndTextureMode (); BeginDrawing (); { BeginShaderMode (active_shader); { DrawTextureV(target.texture, Vector2Zero(), BLANK); // NOTE(cmmm): plugin code maybe here } EndShaderMode(); } EndDrawing(); } else { BeginDrawing (); { ClearBackground (BLACK); const char *note = "Unable to compile shader"; Vector2 note_size = MeasureTextEx (font, note, FONT_SIZE, TEXT_SPACING); Vector2 note_pos = Vector2Scale ( Vector2Subtract ((Vector2) { GetScreenWidth (), GetScreenHeight () }, note_size), 0.5); DrawTextEx (font, note, note_pos, FONT_SIZE, TEXT_SPACING, FONT_COLOR); } EndDrawing (); } } else { BeginDrawing (); { ClearBackground (BLACK); const char *note = "There are no shaders in "DIRECTORY_SHADERS; Vector2 note_size = MeasureTextEx (font, note, FONT_SIZE, TEXT_SPACING); Vector2 note_pos = Vector2Scale ( Vector2Subtract ((Vector2) { GetScreenWidth (), GetScreenHeight () }, note_size), 0.5); DrawTextEx (font, note, note_pos, FONT_SIZE, TEXT_SPACING, FONT_COLOR); } EndDrawing (); } } inotify_rm_watch (inotify_fd, inotify_wd); close (inotify_fd); DYNAMIC_ARRAY_FOREACH(shader_info, shader_info_da) { UnloadShader (shader_info->shader); free (shader_info->name); } DYNAMIC_ARRAY_FREE(shader_info_da); UnloadRenderTexture (target); CloseWindow (); return EXIT_SUCCESS; }