From 0f5e6d9a4d8b48e67b8805081444c2362fc43147 Mon Sep 17 00:00:00 2001 From: zlago <104219492+zlago@users.noreply.github.com> Date: Sun, 9 Nov 2025 02:37:17 +0100 Subject: [PATCH] load and display bitmaps --- src/assets.c++ | 255 +++++++++++++++++++++++++++ src/assets.h++ | 5 + src/main.c++ | 75 +++----- src/main.h++ | 32 ++++ src/rect-pack.c | 24 +-- src/{spr.2Dsolid.glsl => spr2D.glsl} | 7 +- 6 files changed, 325 insertions(+), 73 deletions(-) create mode 100644 src/assets.c++ create mode 100644 src/assets.h++ create mode 100644 src/main.h++ rename src/{spr.2Dsolid.glsl => spr2D.glsl} (55%) diff --git a/src/assets.c++ b/src/assets.c++ new file mode 100644 index 0000000..8f82d20 --- /dev/null +++ b/src/assets.c++ @@ -0,0 +1,255 @@ +#include + +#include +#include +#include +#include + +#include "ass.h" +#include "rect-pack.h" +#include "batch.h" + +#include + +#include "main.h++" + +static int read_vlq(unsigned char const *buf, size_t size, size_t &out) { + size_t outsize = buf[0]; + unsigned outsizebytes = 1; + for (unsigned m = 0x80; outsize & m; m = m >> 1) { + outsizebytes++; + outsize &= ~m; + } + if (outsizebytes > size) { + return -outsizebytes; + } + for (unsigned i = 1; i < outsizebytes; i++) { + outsize = (outsize << 8) | buf[i]; + } + out = outsize; + return outsizebytes; +} + +struct { + std::vector filename; + std::vector rect; + std::vector> data; +} pictures = {}; + +static int decode_pic(struct rect const &r, std::vector &data, unsigned char *out, size_t const xstride, size_t const ystride) { + if (data.size() != r.w * r.h) { + return 1; + } + for (size_t y = 0; y < r.h; y++) { + for (size_t x = 0; x < r.w; x++) { + out[y * ystride + x * xstride] = data[y * r.w + x]; + } + } + return 0; +} + +GLuint util_compileShader(char *name, GLsizei size, GLchar const **strings, GLint *lengths, GLenum shaderType) { + GLuint shader = glCreateShader(shaderType); + if (shader == 0) { + return 0; + } + + glShaderSource(shader, size, strings, lengths); + + glCompileShader(shader); + + int success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (success == false) { + char message[512]; + glGetShaderInfoLog(shader, sizeof (message), NULL, message); + fprintf(stderr, "error: %s: %s\n", name, message); + glDeleteShader(shader); + return 0; + } + + return shader; +} + +GLuint util_linkProgram(char *name, GLuint Vshader, GLuint Fshader) { + GLuint program = glCreateProgram(); + if (program == 0) { + return 0; + } + + glAttachShader(program, Vshader); + glAttachShader(program, Fshader); + + glLinkProgram(program); + + int success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (success == false) { + char message[512]; + glGetProgramInfoLog(program, sizeof (message), NULL, message); + fprintf(stderr, "error: %s: %s\n", name, message); + glDeleteProgram(program); + return 0; + } + + return program; +} + +int assets_load(FILE *file, char const *name) { + enum ass_status ret = assread(file, [](char const *filename_, size_t length, void const *data_, size_t size, void *ctx) -> int { + (void) ctx; + std::string filename = std::string(filename_, length); + auto pos = filename.find_last_of('.'); + std::string const name = filename.substr(0, pos); + std::string const extension = pos == filename.npos? "": filename.substr(pos); + unsigned char const *data = (unsigned char const *) data_; + if (extension == ".shader") { + char const *vsrc = (char const *) data; + size_t len = strnlen(vsrc, size); + if (len == size) { + return 1; + } + char const *fsrc = vsrc + len + 1; + len = size - len - 1; + if (strnlen(fsrc, len) != len - 1) { + return 1; + } + + GLuint vshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vshader, 1, &vsrc, NULL); + glCompileShader(vshader); + GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fshader, 1, &fsrc, NULL); + glCompileShader(fshader); + + int vsuccess, fsuccess; + glGetShaderiv(vshader, GL_COMPILE_STATUS, &vsuccess); + glGetShaderiv(fshader, GL_COMPILE_STATUS, &fsuccess); + if (vsuccess == false || fsuccess == false) { + char message[1024]; + if (vsuccess == false) { + glGetShaderInfoLog(vshader, sizeof (message), NULL, message); + fprintf(stderr, "error: %s.vert: %s\n", name.c_str(), message); + } + if (fsuccess == false) { + glGetShaderInfoLog(fshader, sizeof (message), NULL, message); + fprintf(stderr, "error: %s.frag: %s\n", name.c_str(), message); + } + } else { + GLuint program = glCreateProgram(); + glAttachShader(program, vshader); + glAttachShader(program, fshader); + glLinkProgram(program); + + int success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (success == false) { + char message[1024]; + glGetProgramInfoLog(program, sizeof (message), NULL, message); + fprintf(stderr, "error: %s: %s\n", name.c_str(), message); + glDeleteProgram(program); + } else { + programs[name] = program; + } + } + + glDeleteShader(vshader); + glDeleteShader(fshader); + return vsuccess == false || fsuccess == false; + } else if (extension == ".picture") { + if (memcmp(data, "\xdd\xddpic\n\0\0", 8)) { + return 1; + } + data += 8; size -= 8; + size_t value; + struct rect r = {}; + int result = read_vlq(data, size, value); + r.w = value + 1; + if (result < 0 || r.w - 1 != value) { + return 1; + } + data += result; size -= result; + result = read_vlq(data, size, value); + r.h = value + 1; + if (result < 0 || r.h - 1 != value) { + return 1; + } + data += result; size -= result; + std::vector v(data, data + size); + pictures.filename.push_back(name); + pictures.rect.push_back(r); + pictures.data.push_back(v); + } else { + return 1; + } + return 0; + }, NULL); + + if (ret != ASS_OK) { + switch (ret) { + case ASS_OK: break; + case ASS_BAD: fprintf(stderr, "error: %s: malformed asset pack\n", name); break; + case ASS_USER: fprintf(stderr, "error: %s: malformed asset\n", name); break; + case ASS_TRUNC: fprintf(stderr, "error: %s: truncated asset pack\n", name); break; + case ASS_LAZY: fprintf(stderr, "error: %s: this asset pack will not load, sorry\n", name); break; + case ASS_MEM: fprintf(stderr, "error: %s: memory exhausted while trying to load asset pack\n", name); break; + case ASS_UNSUPPORTED: fprintf(stderr, "error: %s: unsupported asset pack version\n", name); break; + } + return 1; + } + + struct pack_result res = guilotine(pictures.rect.data(), pictures.rect.size(), 512, 1); + if (res.bins == 0) { + return 1; + } + + size_t const stride = res.last_bin_size; + unsigned char *tex = new unsigned char [stride * stride]; + memset(tex, 69, stride * stride); + for (size_t i = 0; i < pictures.rect.size(); i++) { + struct rect const &r = pictures.rect[i]; + //fprintf(stderr, "%s: %ux%u +%u+%u @ %u %c\n", pictures.filename[i].c_str(), r.w, r.h, r.x, r.y, r.bin, r.flipped? '+': '-'); + + int ret = decode_pic(r, pictures.data[i], tex + r.y * stride + r.x, r.flipped? stride: 1, r.flipped? 1: stride); + if (ret) { + delete[] tex; + return 1; + } + } + //fprintf(stderr, "info: %u %u %u %u\n", res.bins, res.last_bin_size, res.last_bin_width, res.last_bin_height); + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, stride, stride, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, tex); + delete[] tex; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + #if 1 + unsigned tsize = 65536 / stride; + #else + unsigned tsize = 0; + for (unsigned s = stride; s & 0xffff; s <<= 1, tsize++) + ; + #endif + + for (size_t i = 0; i < pictures.rect.size(); i++) { + struct rect const &r = pictures.rect[i]; + struct texture t = {.texture = texture, .x = r.x, .y = r.y, .size = tsize, .flipped = r.flipped}; + textures[pictures.filename[i]] = t; + } + + pictures.rect.clear(); + pictures.rect.shrink_to_fit(); + pictures.data.clear(); + pictures.data.shrink_to_fit(); + pictures.filename.clear(); + pictures.filename.shrink_to_fit(); + + return 0; +} diff --git a/src/assets.h++ b/src/assets.h++ new file mode 100644 index 0000000..c0c1297 --- /dev/null +++ b/src/assets.h++ @@ -0,0 +1,5 @@ +#pragma once + +#include + +int assets_load(FILE *file, char const *name); diff --git a/src/main.c++ b/src/main.c++ index d5b4404..9618317 100644 --- a/src/main.c++ +++ b/src/main.c++ @@ -9,39 +9,22 @@ #include #include -#include "ass.h" +#include "batch.h" +#include "assets.h++" +#include "main.h++" #define WINDOW_WIDTH 128 #define WINDOW_HEIGHT 128 -#if 0 -// something something string_view faster but actually cryptic error messages so w/e future me problem -#include -struct string_hash { - using is_transparent = void; - [[nodiscard]] size_t operator()(const char *txt) const { - return std::hash{}(txt); - } - [[nodiscard]] size_t operator()(std::string_view txt) const { - return std::hash{}(txt); - } - [[nodiscard]] size_t operator()(const std::string &txt) const { - return std::hash{}(txt); - } -}; - std::unordered_map> programs, textures; -#endif +#define BATCH_SIZE 1024 -struct state { - SDL_Window *window; - SDL_GLContext context; - float color; - std::unordered_map programs, textures; -}; +SDL_Window *window; +SDL_GLContext context; +std::unordered_map programs; +std::unordered_map textures; -SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { +SDL_AppResult SDL_AppInit(void **, int argc, char **argv) { clock_t begin = clock(); - auto state = new (struct state); SDL_Init(SDL_INIT_VIDEO); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -50,10 +33,10 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "60"); - state->window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE*/ | SDL_WINDOW_TRANSPARENT); - SDL_SetWindowMinimumSize(state->window, WINDOW_WIDTH, WINDOW_HEIGHT); + window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE*/ | SDL_WINDOW_TRANSPARENT); + SDL_SetWindowMinimumSize(window, WINDOW_WIDTH, WINDOW_HEIGHT); - state->context = SDL_GL_CreateContext(state->window); + context = SDL_GL_CreateContext(window); int version = gladLoadGLES2((GLADloadfunc) SDL_GL_GetProcAddress); if (version == 0) { @@ -75,35 +58,28 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { perror(path.c_str()); return SDL_APP_FAILURE; } + if (assets_load(file, path.c_str())) { + return SDL_APP_FAILURE; + } } else { file = fopen(argv[1], "rb"); if (file == NULL) { perror(argv[1]); return SDL_APP_FAILURE; } - } - if (assread(file, [](char const *name, size_t length, void const *data, size_t size, /* struct state *state */ void *ctx) -> int { - auto &state = *(struct state *) ctx; - std::string filename = std::string(name, length); - (void) name; - (void) length; - (void) data; - (void) size; - if (filename.ends_with(".glsl")) { - state.programs[filename]; + if (assets_load(file, argv[1])) { + return SDL_APP_FAILURE; } - state.textures[filename]; - return 0; - }, state)) { - return SDL_APP_FAILURE; } + fclose(file); + + batch_init(BATCH_SIZE, programs.at("spr2D")); glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - SDL_ShowWindow(state->window); + SDL_ShowWindow(window); fprintf(stderr, "info: init took %lu ms\n", ((clock() - begin) * 1000) / CLOCKS_PER_SEC); - *appstate = state; return SDL_APP_CONTINUE; } @@ -146,12 +122,15 @@ SDL_AppResult SDL_AppEvent(void *, SDL_Event *evt) { return SDL_APP_CONTINUE; } -SDL_AppResult SDL_AppIterate(void *appstate) { - auto &state = *(struct state *) appstate; +SDL_AppResult SDL_AppIterate(void *) { glClearColor(0.5, 0.5, 0.5, 0.0); glClear(GL_COLOR_BUFFER_BIT); + + struct crop c = {0, 0, 64, 64}; + batch_blit(-32, -32, &textures.at("turret"), &c); + batch_flush(); - SDL_GL_SwapWindow(state.window); + SDL_GL_SwapWindow(window); return SDL_APP_CONTINUE; } diff --git a/src/main.h++ b/src/main.h++ new file mode 100644 index 0000000..dbfd4d0 --- /dev/null +++ b/src/main.h++ @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include +#include + +#include "batch.h" + +#if 0 +// something something string_view faster but actually cryptic error messages so w/e future me problem +#include +struct string_hash { + using is_transparent = void; + [[nodiscard]] size_t operator()(const char *txt) const { + return std::hash{}(txt); + } + [[nodiscard]] size_t operator()(std::string_view txt) const { + return std::hash{}(txt); + } + [[nodiscard]] size_t operator()(const std::string &txt) const { + return std::hash{}(txt); + } +}; + std::unordered_map> programs, textures; +#endif + +extern SDL_Window *window; +extern SDL_GLContext context; +extern std::unordered_map programs; +extern std::unordered_map textures; diff --git a/src/rect-pack.c b/src/rect-pack.c index 7cbdaa1..2f03bb1 100644 --- a/src/rect-pack.c +++ b/src/rect-pack.c @@ -95,7 +95,7 @@ struct pack_result guilotine(struct rect *rects, size_t items, unsigned maxsize, } static struct pack_result guilotine_(struct rect **left_base, size_t left_count, unsigned maxwidth, unsigned maxheight, unsigned bins, unsigned maxbins) { - fprintf(stderr, "%zu %ux%u, %u\n", left_count, maxwidth, maxheight, maxbins - bins); + //fprintf(stderr, "%zu %ux%u, %u\n", left_count, maxwidth, maxheight, maxbins - bins); struct rect **left = left_base; unsigned in_last_bin = 0; size_t slot_count = 1; @@ -233,28 +233,8 @@ static struct pack_result guilotine_(struct rect **left_base, size_t left_count, //free(other_left); free(tighter); - #if 0 - fprintf(stdout, "", (maxsize + 16) * bins - 16, maxsize); - for (unsigned i = 0; i < bins; i++) { - unsigned binx = (maxsize + 16) * i, biny = 0; - fprintf(stdout, "", i, binx, biny, maxsize, maxsize); - } - fputs("", stdout); - for (unsigned i = 0; i < items; i++) { - unsigned w = rects[i].flipped? rects[i].h: rects[i].w; - unsigned h = rects[i].flipped? rects[i].w: rects[i].h; - unsigned binx = (maxsize + 16) * rects[i].bin, biny = 0; - fprintf(stdout, "%u", rects[i].x + binx, rects[i].y + biny, w - 1, h - 1, rects[i].flipped? "flipped": "normal", rects[i].x + w / 2 + binx, rects[i].y + h / 2 + biny, h > 20? 20: h, i); - } - for (unsigned i = 0; i < slot_count; i++) { - unsigned binx = (maxsize + 16) * slots[i].bin, biny = 0; - fprintf(stdout, "", slots[i].x + binx, slots[i].y + biny, slots[i].w - 1, slots[i].h - 1); - } - fputs("", stdout); - #endif - free(left_base); free(slots); - fprintf(stderr, "%u %u %u %u\n", bins, last_bin.last_bin_size, last_bin.last_bin_width, last_bin.last_bin_height); + //fprintf(stderr, "%u %u %u %u\n", bins, last_bin.last_bin_size, last_bin.last_bin_width, last_bin.last_bin_height); return (struct pack_result) {bins, last_bin.last_bin_size, last_bin.last_bin_width, last_bin.last_bin_height}; } diff --git a/src/spr.2Dsolid.glsl b/src/spr2D.glsl similarity index 55% rename from src/spr.2Dsolid.glsl rename to src/spr2D.glsl index 716479b..33dafcd 100644 --- a/src/spr.2Dsolid.glsl +++ b/src/spr2D.glsl @@ -15,7 +15,7 @@ attribute vec2 aTexCoord; void main(void) { vTexCoord = aTexCoord; - gl_Position = vec4(aVertCoord * vec2(1.0 / 256.0), 0.0, 1.0); + gl_Position = vec4(aVertCoord * vec2(1.0 / 64.0), 0.0, 1.0); } ## @@ -23,8 +23,9 @@ void main(void) { uniform sampler2D uTex; void main(void) { - if (texture2D(uTex, vTexCoord).r < 0.5) { + float pixel = texture2D(uTex, vTexCoord).r; + if (pixel < 0.5) { discard; } - gl_FragColor = vec4(1.0); + gl_FragColor = step(vec4(0.75, 0.75, 0.75, 0.5), vec4(pixel)); }