From 814269ab0c01c766da946fb7adc8128d817a49f4 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Fri, 21 Feb 2025 23:34:01 +0300 Subject: [PATCH] textures working on web, separation of vertex and index buffers (actually matters) --- apps/templates/c/game.c | 10 +- src/rendering/twn_draw_c.h | 23 +++-- src/rendering/twn_gl_15_rendering.c | 143 ++++++++++++++++++--------- src/rendering/twn_gl_any_rendering.c | 43 ++++++-- src/twn_loop.c | 2 +- 5 files changed, 150 insertions(+), 71 deletions(-) diff --git a/apps/templates/c/game.c b/apps/templates/c/game.c index 4b8ba4d..26806d8 100644 --- a/apps/templates/c/game.c +++ b/apps/templates/c/game.c @@ -18,13 +18,9 @@ void game_tick(void) { struct state *state = ctx.udata; ++state->counter; - m_sprite("nothing!", - (Rect) { - .x = 0, - .y = 0, - .w = 32, - .h = 32, - } + m_sprite("twn.png", + (Rect) { .w = 128, .h = 64, }, + m_opt(stretch, true) ); } diff --git a/src/rendering/twn_draw_c.h b/src/rendering/twn_draw_c.h index 1c43074..26264f9 100644 --- a/src/rendering/twn_draw_c.h +++ b/src/rendering/twn_draw_c.h @@ -39,12 +39,18 @@ enum { typedef uint32_t VertexBuffer; +typedef uint32_t IndexBuffer; typedef struct VertexBufferBuilder { size_t size; void *base; } VertexBufferBuilder; +typedef struct IndexBufferBuilder { + size_t size; + void *base; +} IndexBufferBuilder; + typedef struct SpritePrimitive { Rect rect; @@ -268,19 +274,19 @@ void text_cache_reset_arena(TextCache *cache); /* vertex buffer */ VertexBuffer create_vertex_buffer(void); - void restart_scratch_vertex_arrays(void); - VertexBuffer get_scratch_vertex_array(void); - void delete_vertex_buffer(VertexBuffer buffer); - void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes); - VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes); - void finish_vertex_builder(VertexBufferBuilder *builder); +IndexBuffer create_index_buffer(void); +void delete_index_buffer(IndexBuffer buffer); +void specify_index_buffer(IndexBuffer buffer, void const *data, size_t bytes); +IndexBufferBuilder build_index_buffer(IndexBuffer buffer, size_t bytes); +void finish_index_builder(IndexBufferBuilder *builder); + /* 2d */ /* fills two existing arrays with the geometry data of a circle */ @@ -304,9 +310,8 @@ struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len); void render_sprite_batch(const Primitive2D primitives[], struct QuadBatch batch); void render_rect_batch(const Primitive2D primitives[], struct QuadBatch batch); -VertexBuffer get_quad_element_buffer(void); - -VertexBuffer get_circle_element_buffer(void); +IndexBuffer get_quad_element_buffer(void); +IndexBuffer get_circle_element_buffer(void); void render_circle(const CirclePrimitive *circle); diff --git a/src/rendering/twn_gl_15_rendering.c b/src/rendering/twn_gl_15_rendering.c index 42d7d0d..79a70f4 100644 --- a/src/rendering/twn_gl_15_rendering.c +++ b/src/rendering/twn_gl_15_rendering.c @@ -50,8 +50,9 @@ bool render_init(void) { log_info("OpenGL context: %s\n", glGetString(GL_VERSION)); -#ifndef __EMSCRIPTEN__ glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST); + +#ifndef __EMSCRIPTEN__ glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); glHint(GL_FOG_HINT, GL_FASTEST); @@ -196,59 +197,79 @@ static void finally_use_2d_pipeline(void) { pipeline_last_used = PIPELINE_2D; } + +static void setup_ghostly_texture_mode(void) { +#ifndef __EMSCRIPTEN__ + static GLuint list = 0; + if (!list) { + list = glGenLists(1); + glNewList(list, GL_COMPILE); +#endif + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_LESS); + glDepthMask(GL_FALSE); + glDisable(GL_ALPHA_TEST); +#ifndef __EMSCRIPTEN__ + glEndList(); + } + glCallList(list); +#endif +} + + +static void setup_seethrough_texture_mode(void) { +#ifndef __EMSCRIPTEN__ + static GLuint list = 0; + if (!list) { + list = glGenLists(1); + glNewList(list, GL_COMPILE); +#endif + glDisable(GL_BLEND); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 1.0f); +#ifndef __EMSCRIPTEN__ + glEndList(); + } + glCallList(list); +#endif +} + + +static void setup_opaque_texture_mode(void) { +#ifndef __EMSCRIPTEN__ + static GLuint list = 0; + if (!list) { + list = glGenLists(1); + glNewList(list, GL_COMPILE); +#endif + glDisable(GL_BLEND); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + glDisable(GL_ALPHA_TEST); +#ifndef __EMSCRIPTEN__ + glEndList(); + } + glCallList(list); +#endif +} + + /* TODO: ensure we minimize depth func switching to enable Hi-Z (hierarchical depth) optimizations */ static void finally_use_texture_mode(TextureMode mode) { if (texture_mode_last_used == mode) return; -#ifndef __EMSCRIPTEN__ - static GLuint lists = 0; - if (!lists) { - lists = glGenLists(3); - - /* ghostly */ - glNewList(lists + 0, GL_COMPILE); { -#endif - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthFunc(GL_LESS); - glDepthMask(GL_FALSE); - glDisable(GL_ALPHA_TEST); -#ifndef __EMSCRIPTEN__ - } glEndList(); - - /* seethrough */ - glNewList(lists + 1, GL_COMPILE); { -#endif - glDisable(GL_BLEND); - glDepthFunc(GL_LESS); - glDepthMask(GL_TRUE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 1.0f); -#ifndef __EMSCRIPTEN__ - } glEndList(); - - /* opaque */ - glNewList(lists + 2, GL_COMPILE); { -#endif - glDisable(GL_BLEND); - glDepthFunc(GL_LESS); - glDepthMask(GL_TRUE); - glDisable(GL_ALPHA_TEST); -#ifndef __EMSCRIPTEN__ - } glEndList(); - } - if (mode == TEXTURE_MODE_GHOSTLY) { - glCallList(lists + 0); + setup_ghostly_texture_mode(); } else if (mode == TEXTURE_MODE_SEETHROUGH) { - glCallList(lists + 1); + setup_seethrough_texture_mode(); } else { - glCallList(lists + 2); + setup_opaque_texture_mode(); } -#endif - texture_mode_last_used = mode; } @@ -287,6 +308,40 @@ void finish_vertex_builder(VertexBufferBuilder *builder) { } +IndexBufferBuilder build_index_buffer(IndexBuffer buffer, size_t bytes) { + SDL_assert(bytes != 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW); +#ifndef __EMSCRIPTEN__ + void *mapping = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); +#else + void *mapping = SDL_malloc(bytes); +#endif + if (!mapping) + CRY("build_vertex_buffer", "Error mapping a vertex array buffer"); + + return (IndexBufferBuilder) { + .base = mapping, + .size = bytes, + }; +} + + +void finish_index_builder(IndexBufferBuilder *builder) { +#ifndef __EMSCRIPTEN__ + if (!glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER)) + CRY("finish_vertex_builder", "Error unmapping a vertex array buffer"); +#else + glBufferData(GL_ELEMENT_ARRAY_BUFFER, builder->size, builder->base, GL_STREAM_DRAW); + SDL_free(builder->base); +#endif + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + builder->base = 0; + builder->size = 0; +} + + static void load_cubemap_side(const char *path, GLenum target) { SDL_Surface *surface = textures_load_surface(path); /* TODO: sanity check whether all of them have same dimensions? */ diff --git a/src/rendering/twn_gl_any_rendering.c b/src/rendering/twn_gl_any_rendering.c index 21c5ce7..c9914ea 100644 --- a/src/rendering/twn_gl_any_rendering.c +++ b/src/rendering/twn_gl_any_rendering.c @@ -29,6 +29,18 @@ void delete_vertex_buffer(VertexBuffer buffer) { } +IndexBuffer create_index_buffer(void) { + GLuint result; + glGenBuffers(1, &result); + return result; +} + + +void delete_index_buffer(IndexBuffer buffer) { + glDeleteBuffers(1, &buffer); +} + + void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes) { glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW); @@ -36,14 +48,21 @@ void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes) } -VertexBuffer get_quad_element_buffer(void) { - static VertexBuffer buffer = 0; +void specify_index_buffer(IndexBuffer buffer, void const *data, size_t bytes) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bytes, data, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + + +IndexBuffer get_quad_element_buffer(void) { + static IndexBuffer buffer = 0; /* it's only generated once at runtime */ /* TODO: use builder interface, not direct calls (glMapBuffer isn't portable) */ if (buffer == 0) { - buffer = create_vertex_buffer(); - VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); + buffer = create_index_buffer(); + IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { ((GLshort *)builder.base)[i * 6 + 0] = (GLshort)(i * 4 + 0); @@ -54,7 +73,7 @@ VertexBuffer get_quad_element_buffer(void) { ((GLshort *)builder.base)[i * 6 + 5] = (GLshort)(i * 4 + 0); } - finish_vertex_builder(&builder); + finish_index_builder(&builder); } SDL_assert_always(buffer); @@ -63,12 +82,12 @@ VertexBuffer get_quad_element_buffer(void) { } -VertexBuffer get_circle_element_buffer(void) { - static VertexBuffer buffer = 0; +IndexBuffer get_circle_element_buffer(void) { + static IndexBuffer buffer = 0; if (buffer == 0) { - buffer = create_vertex_buffer(); - VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * (CIRCLE_VERTICES_MAX - 2) * 3); + buffer = create_index_buffer(); + IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLshort) * (CIRCLE_VERTICES_MAX - 2) * 3); for (size_t i = 1; i < CIRCLE_VERTICES_MAX - 1; ++i) { /* first one is center point index, always zero */ @@ -79,7 +98,7 @@ VertexBuffer get_circle_element_buffer(void) { ((GLshort *)builder.base)[(i - 1) * 3 + 2] = (GLshort)i + 1; } - finish_vertex_builder(&builder); + finish_index_builder(&builder); } SDL_assert_always(buffer); @@ -128,7 +147,11 @@ GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps, int c SDL_assert(width > 0 && height > 0); SDL_assert(channels > 0 && channels <= 4); +#ifndef __EMSCRIPTEN__ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); +#else + (void)generate_mipmaps; +#endif if (filter == TEXTURE_FILTER_NEAREAST) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); diff --git a/src/twn_loop.c b/src/twn_loop.c index b1ccbf5..6b145a7 100644 --- a/src/twn_loop.c +++ b/src/twn_loop.c @@ -881,7 +881,7 @@ int enter_loop(int argc, char **argv) { profile_end("startup"); #ifdef __EMSCRIPTEN__ - emscripten_set_main_loop(main_loop, ctx.desired_frametime, true); + emscripten_set_main_loop(main_loop, 0, true); #else while (ctx.is_running) main_loop();