diff --git a/data/scene2.glb b/data/scene2.glb new file mode 100644 index 0000000..89dc2f1 Binary files /dev/null and b/data/scene2.glb differ diff --git a/src/computer.c b/src/computer.c new file mode 100644 index 0000000..8f333da --- /dev/null +++ b/src/computer.c @@ -0,0 +1,103 @@ +#include "game.h" + +#include + +#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) { +} diff --git a/src/game.c b/src/game.c index 4a50a9a..d77ec5f 100644 --- a/src/game.c +++ b/src/game.c @@ -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); diff --git a/src/game.h b/src/game.h index 0413945..a928cfb 100644 --- a/src/game.h +++ b/src/game.h @@ -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); diff --git a/src/gfx.c b/src/gfx.c index aa4271f..8847c5c 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -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; diff --git a/src/gfx.h b/src/gfx.h index 76aae07..fb78d21 100644 --- a/src/gfx.h +++ b/src/gfx.h @@ -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); + ///////////////////////////////////////////////////// diff --git a/src/main.c b/src/main.c index 6a10528..54dfbd1 100644 --- a/src/main.c +++ b/src/main.c @@ -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; - - sg_begin_pass(&(sg_pass){ .action = pass_action, .swapchain = sglue_swapchain() }); - { - 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); - } + 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(); + { - // SHIT ASS CODE PLEASE DON'T LOOK - static float dt_avg = 0.f; - static u32 dt_count = 0; + 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; + } - dt_avg += dt; - dt_count++; + int cur = ms_tail; + for (int i = 0; i < ms_count; ++i) { + ms += ms_avg[cur++]; + if (cur >= arrlen(ms_avg)) { + cur = 0; + } + } + + ms_count++; + if (ms_count > arrlen(ms_avg)) { + ms_count = arrlen(ms_avg); + } - float delta_avg = dt_avg / (float)dt_count; + ms /= ms_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 diff --git a/src/obj.c b/src/obj.c index ab36be0..68a799c 100644 --- a/src/obj.c +++ b/src/obj.c @@ -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; + image = obj__load_texture(scratch, texture_view.texture); + } - // diffuse texture - cgltf_texture_view texture_view = mat->pbr_metallic_roughness.base_color_texture; - sg_image image = obj__load_texture(scratch, texture_view.texture); if (image.id == 0) { image = missing_img; } diff --git a/src/obj.h b/src/obj.h new file mode 100644 index 0000000..05c3b43 --- /dev/null +++ b/src/obj.h @@ -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); diff --git a/src/player.c b/src/player.c index b0de3e1..fa61450 100644 --- a/src/player.c +++ b/src/player.c @@ -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) { +} diff --git a/src/scene.c b/src/scene.c new file mode 100644 index 0000000..7baf5e4 --- /dev/null +++ b/src/scene.c @@ -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); + } + } +}