From 21476a3b95da091bd1af4a56094ba5c864eedd6f Mon Sep 17 00:00:00 2001 From: Peaceultime Date: Mon, 27 Apr 2026 13:41:50 +0200 Subject: [PATCH] Makefile working with imgui --- .gitignore | 5 +- CLAUDE.md | 63 ++++++++++++++++++ build.sh | 17 ----- debug.sh | 18 ------ fetch_libs.sh | 50 +++++++++++++++ makefile | 45 +++++++------ src/api.h | 17 ++--- src/generated/{sprite.wgsl.h => sprite.h} | 4 +- src/main.c | 78 ++++++++--------------- src/shaders/sprite.wgsl | 2 +- src/sprite.h | 19 ++---- 11 files changed, 187 insertions(+), 131 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 build.sh delete mode 100644 debug.sh create mode 100644 fetch_libs.sh rename src/generated/{sprite.wgsl.h => sprite.h} (99%) diff --git a/.gitignore b/.gitignore index 6a44bee..c3b3f9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ app.* -.vscode/* \ No newline at end of file +.vscode/* +lib/ +.vscode +.claude \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f837ff8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,63 @@ +# CLAUDE.md + +**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment. + +## 1. Think Before Coding + +**Don't assume. Don't hide confusion. Surface tradeoffs.** + +Before implementing: +- State your assumptions explicitly. If uncertain, ask. +- If multiple interpretations exist, present them - don't pick silently. +- If a simpler approach exists, say so. Push back when warranted. +- If something is unclear, stop. Name what's confusing. Ask. + +## 2. Simplicity First + +**Minimum code that solves the problem. Nothing speculative.** + +- No features beyond what was asked. +- No abstractions for single-use code. +- No "flexibility" or "configurability" that wasn't requested. +- No error handling for impossible scenarios. +- If you write 200 lines and it could be 50, rewrite it. + +Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. + +## 3. Surgical Changes + +**Touch only what you must. Clean up only your own mess.** + +When editing existing code: +- Don't "improve" adjacent code, comments, or formatting. +- Don't refactor things that aren't broken. +- Match existing style, even if you'd do it differently. +- If you notice unrelated dead code, mention it - don't delete it. + +When your changes create orphans: +- Remove imports/variables/functions that YOUR changes made unused. +- Don't remove pre-existing dead code unless asked. + +The test: Every changed line should trace directly to the user's request. + +## 4. Goal-Driven Execution + +**Define success criteria. Loop until verified.** + +Transform tasks into verifiable goals: +- "Add validation" → "Write tests for invalid inputs, then make them pass" +- "Fix the bug" → "Write a test that reproduces it, then make it pass" +- "Refactor X" → "Ensure tests pass before and after" + +For multi-step tasks, state a brief plan: +``` +1. [Step] → verify: [check] +2. [Step] → verify: [check] +3. [Step] → verify: [check] +``` + +Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. + +--- + +**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes. \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100644 index 3706b8e..0000000 --- a/build.sh +++ /dev/null @@ -1,17 +0,0 @@ -emcc -O3 src/main.c \ - ../cimgui/cimgui.cpp \ - ../cimgui/imgui.cpp \ - ../cimgui/imgui_draw.cpp \ - ../cimgui/imgui_tables.cpp \ - ../cimgui/imgui_widgets.cpp \ - -o app.html \ - -sUSE_WEBGPU \ - -sWASM_BIGINT \ - -sALLOW_MEMORY_GROWTH \ - -I../sokol \ - -I../cimgui \ - -msimd128 \ - -flto \ - --llvm-lto 1 \ - --shell-file=shell.html \ - -sFILESYSTEM=0 \ No newline at end of file diff --git a/debug.sh b/debug.sh deleted file mode 100644 index 0f6d425..0000000 --- a/debug.sh +++ /dev/null @@ -1,18 +0,0 @@ -xxd -i src/shaders/sprite.wgsl src/generated/sprite.h - -emcc src/main.c \ - ../cimgui/cimgui.cpp \ - ../cimgui/imgui.cpp \ - ../cimgui/imgui_draw.cpp \ - ../cimgui/imgui_tables.cpp \ - ../cimgui/imgui_widgets.cpp \ - -o app.html \ - -sUSE_WEBGPU \ - -sASSERTIONS \ - -sWASM_BIGINT \ - -sALLOW_MEMORY_GROWTH \ - -I../sokol \ - -I../cimgui \ - -msimd128 \ - --shell-file=shell.html \ - -sFILESYSTEM=0 \ No newline at end of file diff --git a/fetch_libs.sh b/fetch_libs.sh new file mode 100644 index 0000000..bf50ac9 --- /dev/null +++ b/fetch_libs.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +LIB_DIR="lib" + +echo "=== Fetching library dependencies ===" + +# 1. Sokol (single-file headers) +mkdir -p "$LIB_DIR/sokol" +mkdir -p "$LIB_DIR/imgui" +mkdir -p "$LIB_DIR/util" + +if [ ! -f "$LIB_DIR/sokol/sokol_gfx.h" ]; then + echo " > Fetching sokol (pinned to emdawnwebgpu-compatible version)..." + git clone --depth 500 https://github.com/floooh/sokol.git "$LIB_DIR/sokol_tmp" + git -C "$LIB_DIR/sokol_tmp" checkout 36debc7 + cp "$LIB_DIR/sokol_tmp"/*.h "$LIB_DIR/sokol/" + cp "$LIB_DIR/sokol_tmp"/util/*.h "$LIB_DIR/util/" 2>/dev/null || true + rm -rf "$LIB_DIR/sokol_tmp" +else + echo " > Sokol already present" +fi + +# 2. Dear ImGui +if [ ! -f "$LIB_DIR/imgui/imgui/imgui.cpp" ]; then + echo " > Fetching Dear ImGui..." + mkdir -p "$LIB_DIR/imgui/imgui" + git clone --depth 1 --branch v1.92.7-docking https://github.com/ocornut/imgui.git "$LIB_DIR/imgui_src" + cp "$LIB_DIR/imgui_src"/*.cpp "$LIB_DIR/imgui/imgui/" + cp "$LIB_DIR/imgui_src"/*.h "$LIB_DIR/imgui/imgui/" + rm -rf "$LIB_DIR/imgui_src" +else + echo " > Dear ImGui already present" +fi + +# 3. cimgui +if [ ! -f "$LIB_DIR/imgui/cimgui.cpp" ]; then + echo " > Fetching cimgui..." + git clone --depth 1 https://github.com/cimgui/cimgui.git "$LIB_DIR/cimgui_tmp" + cp "$LIB_DIR/cimgui_tmp/cimgui.h" "$LIB_DIR/imgui/" + cp "$LIB_DIR/cimgui_tmp/cimgui.cpp" "$LIB_DIR/imgui/" + cp "$LIB_DIR/cimgui_tmp/cimconfig.h" "$LIB_DIR/imgui/" + cp "$LIB_DIR/cimgui_tmp/cimgui_impl.h" "$LIB_DIR/imgui/" + cp "$LIB_DIR/cimgui_tmp/cimgui_impl.cpp" "$LIB_DIR/imgui/" + rm -rf "$LIB_DIR/cimgui_tmp" +else + echo " > cimgui already present" +fi + +echo "=== Done ===" diff --git a/makefile b/makefile index ea54402..d5ba8d1 100644 --- a/makefile +++ b/makefile @@ -1,52 +1,56 @@ # Compiler and tools CC = emcc XXD = xxd +FETCH = ./fetch_libs.sh # Directories SRC_DIR = src +LIB_DIR = lib SHADER_DIR = $(SRC_DIR)/shaders GENERATED_DIR = $(SRC_DIR)/generated -SOKOL_DIR = ../sokol -NUKLEAR_DIR = ../nuklear # Output TARGET = app.html # Source files C_SOURCES = $(SRC_DIR)/main.c +IMGUI_SOURCES = $(LIB_DIR)/imgui/imgui/imgui.cpp \ + $(LIB_DIR)/imgui/imgui/imgui_draw.cpp \ + $(LIB_DIR)/imgui/imgui/imgui_tables.cpp \ + $(LIB_DIR)/imgui/imgui/imgui_widgets.cpp \ + $(LIB_DIR)/imgui/cimgui.cpp # Dynamic shader processing -SHADER_FILES = $(wildcard $(SHADER_DIR)/*) -SHADER_HEADERS = $(patsubst $(SHADER_DIR)/%,$(GENERATED_DIR)/%.h,$(SHADER_FILES)) +SHADER_FILES = $(wildcard $(SHADER_DIR)/*.wgsl) +SHADER_HEADERS = $(patsubst $(SHADER_DIR)/%.wgsl,$(GENERATED_DIR)/%.h,$(SHADER_FILES)) # Compiler flags -EMCC_FLAGS = -sUSE_WEBGPU \ - -sASSERTIONS \ +EMCC_FLAGS = --use-port=emdawnwebgpu \ -sWASM_BIGINT \ -sALLOW_MEMORY_GROWTH \ -msimd128 \ -sFILESYSTEM=0 -# Include directories -INCLUDES = -I$(SOKOL_DIR) -I$(NUKLEAR_DIR) - # Shell template SHELL_FILE = shell.html # Default target -all: $(TARGET) +all: $(FETCH) $(TARGET) # Main build target -$(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(SHELL_FILE) - $(CC) $(C_SOURCES) \ +$(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE) + $(CC) $(C_SOURCES) $(IMGUI_SOURCES) \ -o $(TARGET) \ $(EMCC_FLAGS) \ -O3 \ - $(INCLUDES) \ + -I$(LIB_DIR)/sokol \ + -I$(LIB_DIR)/imgui \ + -I$(LIB_DIR)/imgui/imgui \ + -I$(LIB_DIR)/util \ --shell-file=$(SHELL_FILE) -# Pattern rule for generating shader headers -$(GENERATED_DIR)/%.h: $(SHADER_DIR)/% | $(GENERATED_DIR) +# Shader header generation +$(GENERATED_DIR)/%.h: $(SHADER_DIR)/%.wgsl | $(GENERATED_DIR) @echo "Generating header for $<" $(XXD) -i $< $@ @@ -54,12 +58,15 @@ $(GENERATED_DIR)/%.h: $(SHADER_DIR)/% | $(GENERATED_DIR) $(GENERATED_DIR): mkdir -p $(GENERATED_DIR) -debug: $(SHADER_HEADERS) $(C_SOURCES) $(SHELL_FILE) - $(CC) $(C_SOURCES) \ +debug: $(FETCH) $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE) + $(CC) $(C_SOURCES) $(IMGUI_SOURCES) \ -o $(TARGET) \ $(EMCC_FLAGS) \ -g -gsource-map=inline \ - $(INCLUDES) \ + -I$(LIB_DIR)/sokol \ + -I$(LIB_DIR)/imgui \ + -I$(LIB_DIR)/imgui/imgui \ + -I$(LIB_DIR)/util \ --shell-file=$(SHELL_FILE) # Clean build artifacts @@ -79,4 +86,4 @@ list-shaders: @echo "$(SHADER_HEADERS)" # Phony targets -.PHONY: all clean rebuild list-shaders \ No newline at end of file +.PHONY: all clean rebuild list-shaders $(FETCH) \ No newline at end of file diff --git a/src/api.h b/src/api.h index f4aaaf0..e91c7b0 100644 --- a/src/api.h +++ b/src/api.h @@ -1,29 +1,26 @@ #ifndef API_DEFINITION #define API_DEFINITION +#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS + #define SOKOL_IMPL #define SOKOL_WGPU - -#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT -#define NK_INCLUDE_FONT_BAKING -#define NK_INCLUDE_DEFAULT_ALLOCATOR -#define NK_INCLUDE_DEFAULT_FONT -#define NK_IMPLEMENTATION +#define SOKOL_IMGUI_IMPL #include "sokol_gfx.h" #include "sokol_app.h" #include "sokol_glue.h" #include "sokol_log.h" -#include "nuklear.h" -#include "util/sokol_memtrack.h" -#include "util/sokol_nuklear.h" +#include "cimgui.h" +#include "sokol_imgui.h" +#include "sokol_memtrack.h" #include "math.h" #include "rand.h" #include "util.h" #include "sprite.h" -#include "generated/sprite.wgsl.h" +#include "generated/sprite.h" #include #include diff --git a/src/generated/sprite.wgsl.h b/src/generated/sprite.h similarity index 99% rename from src/generated/sprite.wgsl.h rename to src/generated/sprite.h index ae760b9..d7ac77d 100644 --- a/src/generated/sprite.wgsl.h +++ b/src/generated/sprite.h @@ -140,6 +140,6 @@ unsigned char src_shaders_sprite_wgsl[] = { 0x6d, 0x70, 0x2c, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x75, 0x76, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, - 0x0d, 0x0a, 0x7d, 0x0d, 0x0a + 0x0d, 0x0a, 0x7d }; -unsigned int src_shaders_sprite_wgsl_len = 1697; +unsigned int src_shaders_sprite_wgsl_len = 1695; diff --git a/src/main.c b/src/main.c index e8fca8e..86c326c 100644 --- a/src/main.c +++ b/src/main.c @@ -54,23 +54,6 @@ void compute_mvp(userdata_t *userdata) static void frame(void* _userdata) { userdata_t* userdata = (userdata_t*) _userdata; - - struct nk_context *nk_ctx = snk_new_frame(); - - if(nk_begin(nk_ctx, "Menu", nk_rect(20, 20, 600, 200), NK_WINDOW_BORDER | NK_WINDOW_BACKGROUND | NK_WINDOW_MOVABLE | NK_WINDOW_CLOSABLE)) - { - nk_layout_row_dynamic(nk_ctx, 0, 1); - nk_label(nk_ctx, format("Frame duration: %.4fms", sapp_frame_duration() * 1000.0), NK_TEXT_ALIGN_CENTERED); - - nk_layout_row_dynamic(nk_ctx, 0, 1); - nk_label(nk_ctx, format("Zoom: %.3f", userdata->zoom), NK_TEXT_ALIGN_CENTERED); - - nk_layout_row_dynamic(nk_ctx, 0, 1); - nk_label(nk_ctx, format("Pan: %.3f/%.3f", userdata->pan.x, userdata->pan.y), NK_TEXT_ALIGN_CENTERED); - - - } - nk_end(nk_ctx); sg_begin_pass(&(sg_pass){ .action = userdata->renderer.clear_pass, @@ -89,7 +72,8 @@ static void frame(void* _userdata) if(texture->dirty) { const sg_range range = vector_range(&texture->sprites); - sg_update_buffer(texture->binding.storage_buffers[0], &range); + sg_buffer buf = sg_query_view_buffer(texture->binding.views[1]); + sg_update_buffer(buf, &range); texture->dirty = false; } @@ -100,8 +84,6 @@ static void frame(void* _userdata) } } - snk_render(userdata->width, userdata->height); - sg_end_pass(); sg_commit(); } @@ -128,6 +110,12 @@ static void init(void* _userdata) {2.0f, -2.0f}, // top right {-2.0f, -2.0f}, // top left }; + const vec2f uv[4] = { + {0.0f, 1.0f}, // bottom left + {1.0f, 1.0f}, // bottom right + {1.0f, 0.0f}, // top right + {0.0f, 0.0f}, // top left + }; const uint16_t indices[] = { 0, 1, 2, 0, 2, 3, }; @@ -146,12 +134,21 @@ static void init(void* _userdata) .source = (const char*) src_shaders_sprite_wgsl, .entry = "fs_main", }, - .images = { + .views = { [0] = { - .stage = SG_SHADERSTAGE_FRAGMENT, - .image_type = SG_IMAGETYPE_2D, - .wgsl_group1_binding_n = 0, - .sample_type = SG_IMAGESAMPLETYPE_FLOAT, + .texture = { + .stage = SG_SHADERSTAGE_FRAGMENT, + .image_type = SG_IMAGETYPE_2D, + .wgsl_group1_binding_n = 0, + .sample_type = SG_IMAGESAMPLETYPE_FLOAT, + }, + }, + [1] = { + .storage_buffer = { + .stage = SG_SHADERSTAGE_VERTEX, + .readonly = true, + .wgsl_group1_binding_n = 2, + }, }, }, .samplers = { @@ -161,20 +158,13 @@ static void init(void* _userdata) .wgsl_group1_binding_n = 1, } }, - .image_sampler_pairs = { + .texture_sampler_pairs = { [0] = { .stage = SG_SHADERSTAGE_FRAGMENT, - .image_slot = 0, + .view_slot = 0, .sampler_slot = 0, } }, - .storage_buffers = { - [0] = { - .wgsl_group1_binding_n = 2, - .stage = SG_SHADERSTAGE_VERTEX, - .readonly = true, - }, - }, .uniform_blocks = { [0] = { .size = sizeof(uniform_t), @@ -190,13 +180,14 @@ static void init(void* _userdata) userdata->renderer = (renderer_t) { .clear_pass = (sg_pass_action) { - .colors[0] = { .clear_value = { 1.0f, 1.0f, 1.0f, 1.0f }, .load_action = SG_LOADACTION_CLEAR } + .colors[0] = { .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }, .load_action = SG_LOADACTION_CLEAR } }, .pipeline = sg_make_pipeline(&(sg_pipeline_desc) { .shader = sprite_shader, .index_type = SG_INDEXTYPE_UINT16, .layout.attrs = { [0].format = SG_VERTEXFORMAT_FLOAT2, + [1].format = SG_VERTEXFORMAT_FLOAT2, }, .label = "Sprite pipeline", }), @@ -213,15 +204,6 @@ static void init(void* _userdata) gs_init(&userdata->manager); compute_mvp(userdata); - - snk_setup(&(snk_desc_t) { - .enable_set_mouse_cursor = true, - .logger.func = slog_func, - .allocator = { - .alloc_fn = smemtrack_alloc, - .free_fn = smemtrack_free, - }, - }); } static void cleanup(void* _userdata) @@ -232,7 +214,6 @@ static void cleanup(void* _userdata) FREE(userdata); - snk_shutdown(); sg_shutdown(); } @@ -240,9 +221,6 @@ static void event(const sapp_event* event, void* _userdata) { userdata_t* userdata = (userdata_t*) _userdata; - if(snk_handle_event(event)) - return; - switch(event->type) { case SAPP_EVENTTYPE_FILES_DROPPED: @@ -264,9 +242,7 @@ static void event(const sapp_event* event, void* _userdata) .buffer = (sapp_range) { .ptr = buffer, .size = size }, .dropped_file_index = i, .callback = gs_import_file, - .user_data = &(import_t) { - .userdata = userdata, - }, + .user_data = userdata }); } diff --git a/src/shaders/sprite.wgsl b/src/shaders/sprite.wgsl index 6795211..af29a8d 100644 --- a/src/shaders/sprite.wgsl +++ b/src/shaders/sprite.wgsl @@ -53,4 +53,4 @@ struct FsO { //Fragment shader output output.color = textureSample(tex, samp, input.uv); return output; -} +} \ No newline at end of file diff --git a/src/sprite.h b/src/sprite.h index cebcf2e..7f4bf9e 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -23,24 +23,19 @@ typedef struct manager_t { vector_t textures; } manager_t; -typedef struct import_t { - userdata_t *userdata; - -} import_t; - typedef struct loader_t { uint8_t buffer[MAX_FILE_SIZE]; } loader_t; -typedef void (*ForEachTexture)(texture_t *texture, userdata_t *userdata); -typedef void (*ForEachSprite)(sprite_t *sprite, userdata_t *userdata); +typedef void (*ForEachTexture)(texture_t *texture, void *userdata); +typedef void (*ForEachSprite)(sprite_t *sprite, void *userdata); void gs_init(manager_t *manager); void gs_shutdown(manager_t *manager); void gs_import_file(const sapp_html5_fetch_response *response); -void gs_each_textures(manager_t *manager, ForEachTexture callback, userdata_t *userdata); -void gs_each_sprites(manager_t *manager, ForEachSprite callback, userdata_t *userdata); +void gs_each_textures(manager_t *manager, ForEachTexture callback, void *userdata); +void gs_each_sprites(manager_t *manager, ForEachSprite callback, void *userdata); void gs_init(manager_t *manager) { @@ -51,7 +46,7 @@ void gs_shutdown(manager_t *manager) vector_free(&manager->textures); } -void fs_import_file(const sapp_html5_fetch_response *response) +void gs_import_file(const sapp_html5_fetch_response *response) { const char* filename = sapp_get_dropped_file_path(response->file_index); @@ -64,12 +59,12 @@ void fs_import_file(const sapp_html5_fetch_response *response) //Toast error } } -void gs_each_textures(manager_t *manager, ForEachTexture callback, userdata_t *userdata) +void gs_each_textures(manager_t *manager, ForEachTexture callback, void *userdata) { for(uint32_t i = 0; i < manager->textures.size; i++) callback((texture_t*) manager->textures.data[i], userdata); } -void gs_each_sprites(manager_t *manager, ForEachSprite callback, userdata_t *userdata) +void gs_each_sprites(manager_t *manager, ForEachSprite callback, void *userdata) { for(uint32_t i = 0; i < manager->textures.size; i++) {