engine/src/gfx.c
alessandro bason 1c13514a4d .
2026-03-05 16:45:29 +01:00

268 lines
6.9 KiB
C

#include "gfx.h"
#include "libs/sokol_app.h"
#include "libs/stb_image.h"
#include "gen/batcher.h"
#include "utils.h"
#include "utils.h"
// batcher stuff ////////////////////////////////////
typedef struct gfx_batch_render_data_t gfx_batch_render_data_t;
struct gfx_batch_render_data_t {
vec2 one_over_window_size;
// float2 one_over_texture_size;
float zoom;
float padding;
};
struct gfx_batcher_t {
sg_buffer render_buffer;
sg_buffer instance_buffer;
sg_pipeline pipeline;
sg_image empty_texture;
sg_sampler sampler;
sg_image texture;
gfx_batch_render_data_t render_data;
gfx_batch_t *batch_data;
uint instance_count;
uint max_instances;
float zoom;
sg_image font;
int font_width;
int font_height;
};
gfx_batcher_t batcher = {0};
bool gfx_init_batcher(arena_t *arena, uint max_instances) {
batcher.zoom = 1.f;
batcher.max_instances = max_instances;
batcher.batch_data = alloc(arena, gfx_batch_t, max_instances);
batcher.render_data = (gfx_batch_render_data_t){
.one_over_window_size = {
1.f / sapp_widthf(),
1.f / sapp_heightf(),
},
.zoom = batcher.zoom
};
batcher.render_buffer = sg_make_buffer(&(sg_buffer_desc){
.usage = {
.storage_buffer = true,
.dynamic_update = true,
},
.size = sizeof(batcher.render_data),
.label = "batcher",
});
sg_update_buffer(batcher.render_buffer, SG_RANGE_REF(batcher.render_data));
batcher.instance_buffer = sg_make_buffer(&(sg_buffer_desc){
.usage = {
.vertex_buffer = true,
.stream_update = true,
},
.size = sizeof(gfx_batch_t) * batcher.max_instances,
});
sg_shader shd = sg_make_shader(batcher_shader_desc(sg_query_backend()));
batcher.pipeline = sg_make_pipeline(&(sg_pipeline_desc){
.shader = shd,
.layout = {
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE,
.attrs = {
[ATTR_batcher_quad].format = SG_VERTEXFORMAT_FLOAT4,
[ATTR_batcher_tex_quad].format = SG_VERTEXFORMAT_FLOAT4,
[ATTR_batcher_colour].format = SG_VERTEXFORMAT_FLOAT4,
},
},
});
u32 empty_image[] = {
0xffffffff,
};
batcher.empty_texture = sg_make_image(&(sg_image_desc){
.data = SG_RANGE(empty_image),
.width = 1,
.height = 1,
.usage = {
.immutable = true,
},
.pixel_format = SG_PIXELFORMAT_RGBA8,
.label = "empty batcher texture",
});
batcher.sampler = sg_make_sampler(&(sg_sampler_desc){0});
int fw, fh;
u8 *data = stbi_load("data/font.png", &fw, &fh, NULL, 4);
batcher.font = sg_make_image(&(sg_image_desc){
.data = { data, fw * fh * 4 },
.width = fw,
.height = fh,
.usage = {
.immutable = true,
},
.pixel_format = SG_PIXELFORMAT_RGBA8,
.label = "xterm font"
});
batcher.font_width = fw;
batcher.font_height = fh;
stbi_image_free(data);
return true;
}
void gfx_batcher_cleanup(void) {
sg_dealloc_buffer(batcher.render_buffer);
sg_dealloc_buffer(batcher.instance_buffer);
sg_dealloc_pipeline(batcher.pipeline);
sg_dealloc_image(batcher.empty_texture);
}
void gfx_batcher_present(void) {
gfx_batcher_flush();
}
void gfx_batcher_flush(void) {
if (batcher.instance_count == 0) {
return;
}
if (win_has_resized() || batcher.render_data.zoom != batcher.zoom) {
batcher.render_data = (gfx_batch_render_data_t) {
.one_over_window_size = {
1.f / sapp_widthf(),
1.f / sapp_heightf(),
},
.zoom = batcher.zoom,
};
sg_update_buffer(batcher.render_buffer, SG_RANGE_REF(batcher.render_data));
}
if (batcher.texture.id == 0) {
batcher.texture = batcher.empty_texture;
}
buffer_t instance_data = (buffer_t){
(u8*)batcher.batch_data,
sizeof(gfx_batch_t) * batcher.instance_count,
};
sg_update_buffer(batcher.instance_buffer, &(sg_range){
.ptr = batcher.batch_data,
.size = sizeof(gfx_batch_t) * batcher.instance_count,
});
sg_view img_view = sg_make_view(&(sg_view_desc){
.texture.image = batcher.texture,
});
sg_apply_pipeline(batcher.pipeline);
sg_apply_uniforms(UB_render_data, SG_RANGE_REF(batcher.render_data));
sg_apply_bindings(&(sg_bindings){
.vertex_buffers[0] = batcher.instance_buffer,
.samplers[SMP_smp] = batcher.sampler,
.views[VIEW_tex] = img_view,
});
sg_draw(0, 6, batcher.instance_count);
sg_destroy_view(img_view);
batcher.instance_count = 0;
}
void gfx_batcher_set_texture(sg_image texture) {
if (texture.id != batcher.texture.id) {
gfx_batcher_flush();
}
batcher.texture = texture;
}
void gfx_batcher_push(gfx_batch_t *data) {
if (batcher.instance_count >= batcher.max_instances) {
gfx_batcher_flush();
}
memmove(&batcher.batch_data[batcher.instance_count++], data, sizeof(gfx_batch_t));
}
void gfx_batcher_push_arr(gfx_batch_t *arr, uint count) {
if (count == 0) {
return;
}
uint copy_off = 0;
while (count > 0) {
if (batcher.instance_count >= batcher.max_instances) {
gfx_batcher_flush();
}
uint remaining_in_batch = batcher.max_instances - batcher.instance_count;
uint to_copy = MIN(remaining_in_batch, count);
memmove(&batcher.batch_data[batcher.instance_count], &arr[copy_off], sizeof(gfx_batch_t) * to_copy);
count -= to_copy;
copy_off += to_copy;
batcher.instance_count += to_copy;
}
}
typedef struct gfx_print_desc_t gfx_print_desc_t;
struct gfx_print_desc_t {
strview_t str;
vec2 pos;
vec4 col;
};
void gfx_batcher_puts(gfx_print_desc_t *desc) {
float atlas_1ow = 1.f / (float)batcher.font_width;
float atlas_1oh = 1.f / (float)batcher.font_height;
float char_width = 6.f;
float char_height = 13.f;
float char_scaled_width = char_width * atlas_1ow;
float char_scaled_height = char_height * atlas_1oh;
gfx_batcher_set_texture(batcher.font);
vec2 pos = desc->pos;
strview_t s = desc->str;
for (usize i = 0; i < s.len; ++i, pos.x += char_width) {
char c = s.buf[i];
int index = c - ' ';
if (index == 0) {
continue;
}
int cx = index % 16;
int cy = index / 16;
vec4 tex_quad = v4(
(float)cx * char_scaled_width,
(float)cy * char_scaled_height,
char_scaled_width,
char_scaled_height
);
gfx_batcher_push(&(gfx_batch_t){
.tex_quad = tex_quad,
.colour = desc->col,
.quad = v4(pos.x, pos.y, char_width, char_height),
});
}
}