diff --git a/.gitignore b/.gitignore index d6c8b50..7e5ac3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -**/raylib_* \ No newline at end of file +**/raylib_* +demo_gen diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a1da26e --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +# used to generate the demos for all languages + +demo_gen: _tools/demo_gen.c + gcc -Wall -Wextra -pedantic -I./_raylib-5.5_linux_amd64/include $^ -o $@ -L./_raylib-5.5_linux_amd64/lib -l:libraylib.a -lm + +.PHONY: run +run: demo_gen + ./demo_gen + +.PHONY: clean +clean: + rm -f ./**/raylib* ./demo_gen diff --git a/_tools/demo_gen.c b/_tools/demo_gen.c new file mode 100644 index 0000000..20176b8 --- /dev/null +++ b/_tools/demo_gen.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 +#define LOGO_HEIGHT 64 +#define LOGO_SPEED 300.0f +#define GIF_FPS 20 + +static unsigned int hash_str(const char *s) { + unsigned int h = 5381; + while (*s) h = h * 33 ^ (unsigned char)*s++; + return h; +} + +static void generate_gif(const char *output_gif, const char **logo_paths, int logo_count, float switch_secs, int duration_secs) { + if (FileExists(output_gif)) { + TraceLog(LOG_INFO, "Skipping %s (already exists)", output_gif); + return; + } + + TraceLog(LOG_INFO, "Generating %s...", output_gif); + + char tmpdir[] = "/tmp/demo_gen_XXXXXX"; + if (!mkdtemp(tmpdir)) { + perror("mkdtemp"); + return; + } + + Texture2D *textures = (Texture2D *)malloc(logo_count * sizeof(Texture2D)); + for (int i = 0; i < logo_count; i++) { + Image img = LoadImage(logo_paths[i]); + ImageResize(&img, img.width * LOGO_HEIGHT / img.height, LOGO_HEIGHT); + textures[i] = LoadTextureFromImage(img); + UnloadImage(img); + } + + RenderTexture2D target = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT); + + SetRandomSeed(hash_str(output_gif)); + + int cur_logo = 0; + float x = GetRandomValue(0, WINDOW_WIDTH - textures[cur_logo].width); + float y = GetRandomValue(0, WINDOW_HEIGHT - textures[cur_logo].height); + float vx = LOGO_SPEED * (GetRandomValue(0, 1) ? 1.0f : -1.0f); + float vy = LOGO_SPEED * (GetRandomValue(0, 1) ? 1.0f : -1.0f); + + int total_frames = duration_secs * GIF_FPS; + float frame_dt = 1.0f / GIF_FPS; + float logo_timer = 0.0f; + + for (int f = 0; f < total_frames; f++) { + Texture2D tex = textures[cur_logo]; + + x += vx * frame_dt; + y += vy * frame_dt; + + if (x < 0 || x + tex.width >= WINDOW_WIDTH) { + vx *= -1.0f; + x += vx * frame_dt; + } + if (y < 0 || y + tex.height >= WINDOW_HEIGHT) { + vy *= -1.0f; + y += vy * frame_dt; + } + + logo_timer += frame_dt; + if (logo_timer >= switch_secs) { + logo_timer = 0.0f; + cur_logo = (cur_logo + 1) % logo_count; + } + + BeginTextureMode(target); + ClearBackground(BLACK); + DrawTextureV(tex, (Vector2){x, y}, WHITE); + EndTextureMode(); + + Image frame_img = LoadImageFromTexture(target.texture); + ImageFlipVertical(&frame_img); + + ExportImage(frame_img, TextFormat("%s/frame_%04d.png", tmpdir, f)); + UnloadImage(frame_img); + + TraceLog(LOG_INFO, " frame %d/%d", f + 1, total_frames); + } + + UnloadRenderTexture(target); + for (int i = 0; i < logo_count; i++) UnloadTexture(textures[i]); + free(textures); + + if (system(TextFormat( + "ffmpeg -y -framerate %d -i '%s/frame_%%04d.png' " + "-vf 'fps=%d,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse' " + "'%s' 2>/dev/null", + GIF_FPS, tmpdir, GIF_FPS, output_gif)) != 0) + TraceLog(LOG_ERROR, "ffmpeg failed for %s", output_gif); + + system(TextFormat("rm -rf '%s'", tmpdir)); +} + +static void extract_dir(const char *logo_path, char *out, int maxlen) { + const char *slash = strchr(logo_path, '/'); + if (slash) { + int len = (int)(slash - logo_path); + if (len >= maxlen) len = maxlen - 1; + strncpy(out, logo_path, len); + out[len] = '\0'; + } else { + strncpy(out, logo_path, maxlen - 1); + out[maxlen - 1] = '\0'; + } +} + +int main(void) { + SetConfigFlags(FLAG_WINDOW_HIDDEN); + InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "demo_gen"); + + glob_t gl; + if (glob("*/*_logo.png", 0, NULL, &gl) != 0) { + TraceLog(LOG_ERROR, "No logos found. Run from project root."); + CloseWindow(); + return 1; + } + + mkdir("docs", 0755); + + // generate docs/demo.gif (cycles all logos) + generate_gif("docs/demo.gif", (const char **)gl.gl_pathv, (int)gl.gl_pathc, 1.0f, (int)gl.gl_pathc); + + // generate languages/**/demo.gif + for (size_t i = 0; i < gl.gl_pathc; i++) { + char dir[256]; + extract_dir(gl.gl_pathv[i], dir, sizeof(dir)); + + const char *path = gl.gl_pathv[i]; + char* output_path = strdup(TextFormat("%s/demo.gif", dir)); + generate_gif(output_path, &path, 1, 9999.0f, 4); + + free(output_path); + } + + CloseWindow(); + globfree(&gl); + return 0; +}