This commit is contained in:
alessandro bason 2026-03-06 17:43:59 +01:00
parent 1c13514a4d
commit 5d1fa53405
11 changed files with 396 additions and 121 deletions

BIN
data/scene2.glb Normal file

Binary file not shown.

103
src/computer.c Normal file
View file

@ -0,0 +1,103 @@
#include "game.h"
#include <string.h>
#include "libs/cgltf.h"
#include "utils.h"
#include "gfx.h"
#define PC_SCREEN_WIDTH 200
#define PC_SCREEN_HEIGHT 150
entity_t *computer_init(void) {
entity_t *e = game_new_entity(ENTITY_COMPUTER);
e->offscreen.resolve = sg_make_image(&(sg_image_desc){
.usage.resolve_attachment = true,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.width = PC_SCREEN_WIDTH,
.height = PC_SCREEN_HEIGHT,
});
e->offscreen.colors = sg_make_image(&(sg_image_desc) {
.usage.color_attachment = true,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.width = PC_SCREEN_WIDTH,
.height = PC_SCREEN_HEIGHT,
.sample_count = 2,
});
e->offscreen.attachments = (sg_attachments) {
.colors[0] = sg_make_view(&(sg_view_desc){
.color_attachment.image = e->offscreen.colors,
}),
.resolves[0] = sg_make_view(&(sg_view_desc){
.resolve_attachment.image = e->offscreen.resolve,
}),
};
obj_t *screen = NULL;
entity_t *scene = game_get_entity_of_type(ENTITY_SCENE);
for (int i = 0; i < scene->model.object_count; ++i) {
obj_t *obj = &scene->model.objects[i];
if (strcmp(obj->name, "screen") == 0) {
screen = obj;
break;
}
}
if (screen) {
screen->bindings.views[0] = sg_make_view(&(sg_view_desc){
.texture = e->offscreen.resolve
});
}
return e;
}
void computer__update(entity_t *e, float dt) {
strview_t string = strv("hello world :)");
static int pos = 0;
static float timer = 0;
static float frame = 0.5f;
timer += dt;
while (timer >= frame) {
timer -= frame;
if (++pos > string.len) {
pos = 0;
}
}
// screen pass
sg_begin_pass(&(sg_pass){
.action = {
.colors[0] = {
.load_action = SG_LOADACTION_CLEAR,
.store_action = SG_STOREACTION_DONTCARE,
.clear_value = { 0, 0, 0, 1},
},
},
.attachments = e->offscreen.attachments,
});
batcher.render_data.one_over_window_size = v2(
1.f / PC_SCREEN_WIDTH,
1.f / PC_SCREEN_HEIGHT
);
gfx_batcher_puts(&(gfx_print_desc_t){
.str = strv_sub(string, 0, pos),
.col = v4(1, 1, 1, 1),
.pos = v2(1, 1),
});
gfx_batcher_present();
sg_end_pass();
}
void computer_frame(arena_t frame_arena, entity_t *players, float dt) {
for_each (e, players) {
computer__update(e, dt);
}
}
void computer_draw(arena_t frame_arena, entity_t *players) {
}

View file

@ -6,6 +6,10 @@
ENTITY_KIND(FRAME_X)
#undef FRAME_X
#define DRAW_X(_, name) extern void name##_draw(arena_t scratch, entity_t *entities);
ENTITY_KIND(DRAW_X)
#undef DRAW_X
entity_update_fn game_frame_fn[ENTITY__COUNT] = {
NULL,
#define FN_X(_, name) name##_frame,
@ -13,6 +17,13 @@ ENTITY_KIND(FN_X)
#undef FN_X
};
entity_draw_fn game_draw_fn[ENTITY__COUNT] = {
NULL,
#define FN_X(_, name) name##_draw,
ENTITY_KIND(FN_X)
#undef FN_X
};
game_t game = {0};
void game_init() {
@ -32,6 +43,13 @@ void game_frame(float dt) {
#undef FRAME_X
}
void game_draw(void) {
#define DRAW_X(enumval, name) \
name##_draw(game.frame_arena, game.active_by_type[ENTITY_##enumval]);
ENTITY_KIND(DRAW_X);
#undef DRAW_X
}
entity_t *game_new_entity(entity_kind_e kind) {
entity_t *e = game.freelist;
list_pop(game.freelist);
@ -47,6 +65,10 @@ entity_t *game_new_entity(entity_kind_e kind) {
return e;
}
entity_t *game_get_entity_of_type(entity_kind_e kind) {
return game.active_by_type[kind];
}
void game_entity_destroy(entity_t *e) {
dlist_pop(game.active_by_type[e->kind], e);
dlist_push(game.freelist, e);

View file

@ -1,10 +1,15 @@
#pragma once
#include "colla/colla.h"
#include "libs/sokol_gfx.h"
#include "libs/cgltf.h"
#include "camera.h"
#include "obj.h"
#define ENTITY_KIND(X) \
X(PLAYER, player) \
X(SCENE, scene) \
X(COMPUTER, computer) \
typedef enum {
ENTITY_NONE,
@ -19,14 +24,23 @@ typedef enum {
typedef struct entity_t entity_t;
typedef void (*entity_update_fn)(arena_t scratch, entity_t *entites, float dt);
typedef void (*entity_draw_fn)(arena_t scratch, entity_t *entites);
struct entity_t {
entity_kind_e kind;
entity_t *next;
entity_t *prev;
camera_t camera;
vec3 position;
camera_t camera;
scene_t model;
struct {
sg_image colors;
sg_image resolve;
sg_attachments attachments;
} offscreen;
};
typedef struct game_t game_t;
@ -42,6 +56,8 @@ void game_init(void);
void game_cleanup(void);
void game_frame(float dt);
void game_draw(void);
entity_t *game_new_entity(entity_kind_e kind);
entity_t *game_get_entity_of_type(entity_kind_e kind);
void game_entity_destroy(entity_t *e);

View file

@ -8,35 +8,6 @@
// 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) {
@ -62,7 +33,7 @@ bool gfx_init_batcher(arena_t *arena, uint max_instances) {
.label = "batcher",
});
sg_update_buffer(batcher.render_buffer, SG_RANGE_REF(batcher.render_data));
// sg_update_buffer(batcher.render_buffer, SG_RANGE_REF(batcher.render_data));
batcher.instance_buffer = sg_make_buffer(&(sg_buffer_desc){
.usage = {
@ -83,6 +54,12 @@ bool gfx_init_batcher(arena_t *arena, uint max_instances) {
[ATTR_batcher_colour].format = SG_VERTEXFORMAT_FLOAT4,
},
},
.sample_count = 2,
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
.depth = {
.pixel_format = SG_PIXELFORMAT_NONE,
.write_enabled = false,
},
});
u32 empty_image[] = {
@ -221,13 +198,6 @@ void gfx_batcher_push_arr(gfx_batch_t *arr, uint count) {
}
}
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;

View file

@ -20,7 +20,43 @@ struct gfx_batch_t {
vec4 colour;
};
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;
};
typedef struct gfx_batcher_t gfx_batcher_t;
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;
};
typedef struct gfx_print_desc_t gfx_print_desc_t;
struct gfx_print_desc_t {
strview_t str;
vec2 pos;
vec4 col;
};
extern gfx_batcher_t batcher;
bool gfx_init_batcher(arena_t *arena, uint max_instances);
@ -34,4 +70,6 @@ void gfx_batcher_set_texture(sg_image texture);
void gfx_batcher_push(gfx_batch_t *data);
void gfx_batcher_push_arr(gfx_batch_t *arr, uint count);
void gfx_batcher_puts(gfx_print_desc_t *desc);
/////////////////////////////////////////////////////

View file

@ -8,6 +8,9 @@
#error "unsupported os"
#endif
#define SCREEN_WIDTH 200
#define SCREEN_HEIGHT 150
#include "libs/sokol_app.h"
#include "libs/sokol_gfx.h"
#include "libs/sokol_time.h"
@ -22,6 +25,8 @@
#include "camera.c"
#include "game.c"
#include "player.c"
#include "scene.c"
#include "computer.c"
////////////////////////////////
@ -33,7 +38,12 @@ scene_t scene = {0};
u64 time_last = 0;
// gfx_batcher_t *batch = NULL;
obj_vs_params_t vs_params = {0};
struct {
sg_image colors;
sg_image resolve;
sg_pass_action pass_action;
sg_attachments attachments;
} screen = {0};
////////////////////////////////
@ -62,6 +72,23 @@ void sokol_log_cb(
);
}
bool scene_material_cb(arena_t scratch, cgltf_material *mat, sg_image *out, void *ud) {
if (strcmp(mat->name, "screen") != 0) {
return false;
}
screen.resolve = sg_make_image(&(sg_image_desc){
.pixel_format = SG_PIXELFORMAT_RGBA8,
.width = SCREEN_WIDTH,
.height = SCREEN_HEIGHT,
.usage.resolve_attachment = true,
});
*out = screen.resolve;
return true;
}
void app_init(void) {
sg_setup(&(sg_desc){
.environment = sglue_environment(),
@ -77,30 +104,18 @@ void app_init(void) {
gfx_init_batcher(&game.arena, 128);
player_init();
scene_init();
computer_init();
arena_t scratch = game.arena;
scene = obj_load_gltf(scratch, strv("data/scene.glb"));
sg_shader shd = sg_make_shader(obj_shader_desc(sg_query_backend()));
pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = shd,
.index_type = SG_INDEXTYPE_UINT16,
// .cull_mode = SG_CULLMODE_BACK,
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
#if 0
screen.pass_action = (sg_pass_action){
.colors[0] = {
.load_action = SG_LOADACTION_CLEAR,
.store_action = SG_STOREACTION_DONTCARE,
.clear_value = { 0, 0, 0, 1},
},
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT3,
[1].format = SG_VERTEXFORMAT_FLOAT3,
[2].format = SG_VERTEXFORMAT_FLOAT2,
[3].format = SG_VERTEXFORMAT_FLOAT2,
},
},
});
};
#endif
pass_action = (sg_pass_action){
.colors[0] = {
@ -118,50 +133,59 @@ void app_frame(void) {
u64 dt_ticks = stm_laptime(&time_last);
float dt = (float)stm_sec(dt_ticks);
utils_update();
game_frame(dt);
entity_t *player = &game.active_by_type[ENTITY_PLAYER][0];
camera_t *cam = &player->camera;
float aspect_ratio = sapp_widthf() / sapp_heightf();
mat4 view = cam_view(cam);
mat4 proj = cam_proj(cam);
mat4 model = HMM_Translate(v3(0, 0, -10));
vs_params.mvp = hmm_mul(proj, view);
vs_params.cam_pos = cam->pos;
if (is_key_pressed(SAPP_KEYCODE_TAB)) {
sapp_lock_mouse(!sapp_mouse_locked());
}
// main pass
sg_begin_pass(&(sg_pass){ .action = pass_action, .swapchain = sglue_swapchain() });
game_draw();
{
sg_apply_pipeline(pip);
sg_apply_uniforms(0, SG_RANGE_REF(vs_params));
for (int i = 0; i < scene.object_count; ++i) {
sg_apply_bindings(&scene.objects[i].bindings);
sg_draw(0, scene.objects[i].draw_count, 1);
static float ms_avg[100] = {0};
static u32 ms_count = 0;
static int ms_tail = 0;
float ms = stm_ms(dt_ticks);
ms_avg[ms_tail++] = ms;
if (ms_tail >= arrlen(ms_avg)) {
ms_tail = 0;
}
int cur = ms_tail;
for (int i = 0; i < ms_count; ++i) {
ms += ms_avg[cur++];
if (cur >= arrlen(ms_avg)) {
cur = 0;
}
}
{
// SHIT ASS CODE PLEASE DON'T LOOK
static float dt_avg = 0.f;
static u32 dt_count = 0;
ms_count++;
if (ms_count > arrlen(ms_avg)) {
ms_count = arrlen(ms_avg);
}
dt_avg += dt;
dt_count++;
ms /= ms_count;
float delta_avg = dt_avg / (float)dt_count;
// batcher.zoom = 2.f;
str_t fps = str_fmt(&game.frame_arena, "dt: %.5f // fps: %.2f", delta_avg, 1.f / delta_avg);
gfx_batcher_puts(&(gfx_print_desc_t){
.str = strv(fps),
.col = v4(0.93, 0.6, 0.26, 1),
});
gfx_batcher_present();
// str_t fps = str_fmt(
// &game.frame_arena,
// "ms: %.2f // fps: %.2f",
// ms, 1000.f / ms
// );
// gfx_batcher_puts(&(gfx_print_desc_t){
// .str = strv(fps),
// .col = v4(0.93, 0.6, 0.26, 1),
// });
// gfx_batcher_present();
}
sg_end_pass();
sg_commit();
utils_update();
}
void app_event(const sapp_event *e) {
@ -204,6 +228,9 @@ sapp_desc sokol_main(int argc, char* argv[]) {
};
}
#define CGLTF_IMPLEMENTATION
#include "libs/cgltf.h"
#define SOKOL_IMPL
#define STB_IMAGE_IMPLEMENTATION

View file

@ -1,26 +1,13 @@
#include "obj.h"
#include "colla/colla.h"
#include "libs/sokol_gfx.h"
#include "libs/stb_image.h"
#define CGLTF_IMPLEMENTATION
#include "libs/cgltf.h"
#include "libs/handmademath.h"
typedef struct vertex_t vertex_t;
struct vertex_t {
vec3 pos;
vec3 norm;
vec2 tex;
vec2 lightmap;
};
typedef struct obj_t obj_t;
struct obj_t {
sg_bindings bindings;
u16 draw_count;
};
cgltf_result obj__cgltf_file_read_cb(
const cgltf_memory_options *m,
const cgltf_file_options *opt,
@ -51,14 +38,6 @@ void obj__cgltf_file_release_callback(
// no-op
}
#define MAX_SCENE_OBJECTS 64
typedef struct scene_t scene_t;
struct scene_t {
obj_t objects[MAX_SCENE_OBJECTS];
int object_count;
};
#define CACHE_SIZE (64)
#define MAX_CACHE_NAME_LEN (64)
@ -130,8 +109,12 @@ sg_image obj__load_texture(arena_t scratch, cgltf_texture *texture) {
return out;
}
scene_t obj_load_gltf(arena_t scratch, strview_t filename) {
scene_t obj_load_gltf(arena_t scratch, strview_t filename, obj_desc_t *desc) {
scene_t scene = {0};
obj_desc_t default_settings = {0};
if (!desc) {
desc = &default_settings;
}
str_t fname = str(&scratch, filename);
buffer_t buf = os_file_read_all(&scratch, filename);
@ -208,10 +191,21 @@ scene_t obj_load_gltf(arena_t scratch, strview_t filename) {
}
cgltf_material *mat = prim->material;
sg_image image = {0};
strview_t material_name = strv(mat->name);
usize to_copy = MIN(material_name.len, arrlen(obj->name) - 1);
memmove(obj->name, material_name.buf, to_copy);
if (!
(desc->material_cb &&
desc->material_cb(scratch, mat, &image, desc->userdata))
) {
// diffuse texture
cgltf_texture_view texture_view = mat->pbr_metallic_roughness.base_color_texture;
sg_image image = obj__load_texture(scratch, texture_view.texture);
image = obj__load_texture(scratch, texture_view.texture);
}
if (image.id == 0) {
image = missing_img;
}

39
src/obj.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "libs/sokol_gfx.h"
#include "libs/handmademath.h"
#include "libs/cgltf.h"
#include "colla/colla.h"
#define MAX_SCENE_OBJECTS 64
typedef struct vertex_t vertex_t;
struct vertex_t {
vec3 pos;
vec3 norm;
vec2 tex;
vec2 lightmap;
};
typedef struct obj_t obj_t;
struct obj_t {
char name[16];
sg_bindings bindings;
u16 draw_count;
};
typedef struct scene_t scene_t;
struct scene_t {
obj_t objects[MAX_SCENE_OBJECTS];
int object_count;
};
typedef bool (*obj_material_cb)(arena_t scratch, cgltf_material *material, sg_image *out, void *userdata);
typedef struct obj_desc_t obj_desc_t;
struct obj_desc_t {
obj_material_cb material_cb;
void *userdata;
};
scene_t obj_load_gltf(arena_t scratch, strview_t filename, obj_desc_t *desc);

View file

@ -22,3 +22,6 @@ void player_frame(arena_t frame_arena, entity_t *players, float dt) {
player__update(e, dt);
}
}
void player_draw(arena_t frame_arena, entity_t *players) {
}

63
src/scene.c Normal file
View file

@ -0,0 +1,63 @@
#include "game.h"
#include "gen/obj.h"
sg_pipeline scene_pipeline = {0};
obj_vs_params_t scene_vs_params = {0};
entity_t *scene_init(void) {
entity_t *e = game_new_entity(ENTITY_SCENE);
e->model = obj_load_gltf(game.frame_arena, strv("data/scene2.glb"), NULL);
e->position = v3(-10, 0, 0);
if (sg_query_pipeline_state(scene_pipeline) != SG_RESOURCESTATE_VALID) {
sg_shader shd = sg_make_shader(obj_shader_desc(sg_query_backend()));
scene_pipeline = sg_make_pipeline(&(sg_pipeline_desc){
.shader = shd,
.index_type = SG_INDEXTYPE_UINT16,
.cull_mode = SG_CULLMODE_FRONT,
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true,
},
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT3,
[1].format = SG_VERTEXFORMAT_FLOAT3,
[2].format = SG_VERTEXFORMAT_FLOAT2,
[3].format = SG_VERTEXFORMAT_FLOAT2,
},
},
});
}
return e;
}
void scene_frame(arena_t frame_arena, entity_t *scenes, float dt) {
entity_t *player = game_get_entity_of_type(ENTITY_PLAYER);
camera_t *cam = &player->camera;
mat4 view = cam_view(cam);
mat4 proj = cam_proj(cam);
// for_each (s, scenes) {
mat4 model = HMM_Translate(v3(-5, 0, 0));
// }
scene_vs_params.mvp = hmm_mul(proj, hmm_mul(view, model));
scene_vs_params.cam_pos = cam->pos;
}
void scene_draw(arena_t frame_arena, entity_t *players) {
sg_apply_pipeline(scene_pipeline);
sg_apply_uniforms(UB_obj_vs_params, SG_RANGE_REF(scene_vs_params));
for_each (e, players) {
for (int i = 0; i < e->model.object_count; ++i) {
sg_apply_bindings(&e->model.objects[i].bindings);
sg_draw(0, e->model.objects[i].draw_count, 1);
}
}
}