From adce14bbf3df451a1389ad7c79133c553afe6d16 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 8 Mar 2024 19:56:34 -0600 Subject: [PATCH] rework tick -> world, move related logic from game into world api --- src/arena.c | 13 ++++ src/arena.h | 1 + src/ase.c | 8 +-- src/common.h | 15 +---- src/config.h | 1 - src/entity.c | 6 +- src/entity.h | 11 +--- src/game.c | 166 ++++++++++++++++----------------------------------- src/game.h | 4 +- src/log.c | 2 +- src/sheet.c | 2 +- src/tick.c | 10 ---- src/tick.h | 22 ------- src/user.c | 88 ++++++++++++++------------- src/world.c | 98 ++++++++++++++++++++++++++++++ src/world.h | 39 ++++++++++++ 16 files changed, 262 insertions(+), 224 deletions(-) delete mode 100644 src/tick.c delete mode 100644 src/tick.h create mode 100644 src/world.c create mode 100644 src/world.h diff --git a/src/arena.c b/src/arena.c index 1ddaaebe..72813879 100644 --- a/src/arena.c +++ b/src/arena.c @@ -90,6 +90,19 @@ void *_arena_push_bytes(struct arena *arena, u64 size, u64 align) return start; } +/* Copies the memory from the source arena into the destination arena, + * replacing old contents. Destination arena will be expanded if necessary contents. */ +void arena_copy_replace(struct arena *dest, struct arena *src) +{ + arena_reset(dest); + if (dest->committed < src->committed) { + u64 diff = src->committed - dest->committed; + _arena_push_bytes(dest, diff, 1); + } + MEMCPY(dest->base, src->base, src->committed); + +} + void arena_decommit_unused_blocks(struct arena *arena) { __prof; diff --git a/src/arena.h b/src/arena.h index 89dbd147..8e77ec25 100644 --- a/src/arena.h +++ b/src/arena.h @@ -29,6 +29,7 @@ struct temp_arena { struct arena arena_alloc(u64 reserve); void arena_release(struct arena *arena); void *_arena_push_bytes(struct arena *arena, u64 size, u64 align); +void arena_copy_replace(struct arena *dest, struct arena *src); void arena_decommit_unused_blocks(struct arena *arena); INLINE void *_arena_push_bytes_zero(struct arena *arena, u64 size, u64 align) diff --git a/src/ase.c b/src/ase.c index 2f0134cd..d34829b9 100644 --- a/src/ase.c +++ b/src/ase.c @@ -71,11 +71,11 @@ struct huffman { struct huffman_entry *entries; }; -GLOBAL const u32 g_hclen_order[] = { +GLOBAL READONLY const u32 g_hclen_order[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; -GLOBAL const struct huffman_entry g_length_table[] = { +GLOBAL READONLY const struct huffman_entry g_length_table[] = { {3, 0}, /* 257 */ {4, 0}, /* 258 */ {5, 0}, /* 259 */ @@ -107,7 +107,7 @@ GLOBAL const struct huffman_entry g_length_table[] = { {258, 0}, /* 285 */ }; -GLOBAL const struct huffman_entry g_dist_table[] = { +GLOBAL READONLY const struct huffman_entry g_dist_table[] = { {1, 0}, /* 0 */ {2, 0}, /* 1 */ {3, 0}, /* 2 */ @@ -140,7 +140,7 @@ GLOBAL const struct huffman_entry g_dist_table[] = { {24577, 13}, /* 29 */ }; -GLOBAL const u32 g_fixed_bl_counts[][2] = { +GLOBAL READONLY const u32 g_fixed_bl_counts[][2] = { {143, 8}, {255, 9}, {279, 7}, diff --git a/src/common.h b/src/common.h index ceef65ec..7a1011a4 100644 --- a/src/common.h +++ b/src/common.h @@ -163,7 +163,7 @@ extern "C" { /* Storage specifiers */ #pragma section(".roglob", read) -#define READ_ONLY __declspec(allocate(".roglob")) +#define READONLY __declspec(allocate(".roglob")) /* Markup */ #define UNUSED void @@ -231,19 +231,6 @@ extern "C" { #define CAT1(a, b) a ## b #define CAT(a, b) CAT1(a, b) -/* ========================== * - * Nil structs - * ========================== */ - -/* Declare */ -#define DECLARE_NIL_STRUCT(type, function_name) \ - extern READ_ONLY type CAT(__nil_struct__, function_name); \ - INLINE READ_ONLY type *function_name(void) { return &CAT(__nil_struct__, function_name); } - -/* Expects struct body after invocation. E.g. DEFINE_NIL_STRUCT(struct a, a_nil) { 0 } */ -#define DEFINE_NIL_STRUCT(type, function_name) \ - type READ_ONLY CAT(__nil_struct__, function_name) = - #if 0 /* ========================== * * Bit utils diff --git a/src/config.h b/src/config.h index 03abbfc2..82ba958b 100644 --- a/src/config.h +++ b/src/config.h @@ -6,7 +6,6 @@ #define RESOURCES_EMBEDDED !(DEVELOPER) #define PIXELS_PER_UNIT 256 -#define MAX_ENTITIES 4096 #define AUDIO_ENABLED 0 #define VSYNC_ENABLED 0 diff --git a/src/entity.c b/src/entity.c index 7cfb4048..cc79bc30 100644 --- a/src/entity.c +++ b/src/entity.c @@ -1,10 +1,6 @@ #include "entity.h" -DEFINE_NIL_STRUCT(struct entity, entity_nil) { 0 }; - -/* ========================== * - * Prop - * ========================== */ +READONLY struct entity _g_entity_nil = { 0 }; void entity_enable_prop(struct entity *entity, enum entity_prop prop) { diff --git a/src/entity.h b/src/entity.h index 0bf1cc7a..c5990d32 100644 --- a/src/entity.h +++ b/src/entity.h @@ -4,10 +4,6 @@ #include "sheet.h" #include "mixer.h" -/* ========================== * - * Entity - * ========================== */ - struct entity_handle { u64 idx; u64 gen; @@ -35,7 +31,7 @@ struct entity { u64 continuity_gen; u64 props[(ENTITY_PROP_COUNT + 63) / 64]; - struct entity *next_free; + struct entity_handle next_free; /* Tree */ struct entity_handle parent; @@ -96,9 +92,8 @@ struct entity { f32 camera_zoom; }; -DECLARE_NIL_STRUCT(struct entity, entity_nil); - -/* Prop */ +extern READONLY struct entity _g_entity_nil; +INLINE READONLY struct entity *entity_nil(void) { return &_g_entity_nil; } void entity_enable_prop(struct entity *entity, enum entity_prop prop); void entity_disable_prop(struct entity *entity, enum entity_prop prop); diff --git a/src/game.c b/src/game.c index ab960523..914e1920 100644 --- a/src/game.c +++ b/src/game.c @@ -2,9 +2,8 @@ #include "app.h" #include "sys.h" #include "util.h" -#include "entity.h" +#include "world.h" #include "sheet.h" -#include "tick.h" #include "sound.h" #include "mixer.h" #include "math.h" @@ -17,8 +16,7 @@ GLOBAL struct { struct sys_thread game_thread; f64 timescale; - struct entity *free_entity_head; - struct tick tick; + struct world world; /* Game thread input */ struct sys_mutex game_cmds_mutex; @@ -26,78 +24,9 @@ GLOBAL struct { /* Game thread output */ struct sys_mutex published_tick_mutex; - struct tick published_tick; + struct world published_tick; } L = { 0 }, DEBUG_LVAR(L_game); -/* ========================== * - * Entity allocation - * ========================== */ - -INTERNAL struct entity *entity_alloc(void) -{ - struct entity *entity = NULL; - if (L.free_entity_head) { - /* Reuse from free list */ - entity = L.free_entity_head; - L.free_entity_head = entity->next_free; - *entity = (struct entity) { - .handle = entity->handle - }; - } else { - /* Make new */ - if (L.tick.entities_count >= MAX_ENTITIES) { - sys_panic(STR("MAX_ENTITIES reached")); - } - u64 idx = L.tick.entities_count++; - entity = &L.tick.entities[idx]; - *entity = (struct entity) { - .handle = { .gen = 1, .idx = idx } - }; - } - return entity; -} - -#if 0 -INTERNAL void entity_release(struct entity *entity) -{ - entity->next_free = L.free_entity_head; - L.free_entity_head = entity; - *entity = (struct entity) { - .gen = entity->gen + 1 - }; -} -#endif - -/* Returns a valid entity or nil entity. Always safe to read result, need to check to write. */ -INTERNAL struct entity *entity_from_handle(struct entity_handle eh) -{ - if (eh.idx < L.tick.entities_count) { - struct entity *entity = &L.tick.entities[eh.idx]; - if (entity->handle.gen == eh.gen) { - return entity; - } - } - return entity_nil(); -} - -INTERNAL void entity_tree_attach(struct entity *parent, struct entity *child) -{ - struct entity *first_child = entity_from_handle(parent->first); - struct entity *last_child = entity_from_handle(parent->last); - - child->prev = last_child->handle; - child->parent = parent->handle; - - if (last_child->valid) { - last_child->next = child->handle; - } - parent->last = child->handle; - - if (!first_child->valid) { - parent->first = child->handle; - } -} - /* ========================== * * Game cmd * ========================== */ @@ -140,8 +69,8 @@ INTERNAL void publish_game_tick(void) { __prof; sys_mutex_lock(&L.published_tick_mutex); - L.tick.published_ts = sys_timestamp(); - tick_copy(&L.published_tick, &L.tick); + L.world.tick_published_ts = sys_timestamp(); + world_copy_replace(&L.published_tick, &L.world); sys_mutex_unlock(&L.published_tick_mutex); } @@ -151,16 +80,16 @@ INTERNAL void game_update(void) struct temp_arena scratch = scratch_begin_no_conflict(); - ++L.tick.id; - L.tick.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale); - L.tick.time += L.tick.dt; + ++L.world.tick_id; + L.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * L.timescale); + L.world.time += L.world.dt; /* TODO: remove this (testing) */ /* Initialize entities */ static b32 run = 0; if (!run) { run = 1; - (UNUSED)entity_tree_attach; + (UNUSED)world_link_entities; /* Player ent */ struct entity *player_ent; @@ -169,7 +98,7 @@ INTERNAL void game_update(void) struct v2 size = V2(1, 1); f32 r = 0; - struct entity *e = entity_alloc(); + struct entity *e = world_alloc_entity(&L.world); e->valid = true; e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size); @@ -219,7 +148,7 @@ INTERNAL void game_update(void) struct v2 size = V2(1, 1); f32 r = 0; - struct entity *e = entity_alloc(); + struct entity *e = world_alloc_entity(&L.world); e->valid = true; e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size); @@ -260,12 +189,12 @@ INTERNAL void game_update(void) //entity_enable_prop(e, ENTITY_PROP_TEST); //entity_enable_prop(e, ENTITY_PROP_TEST_FOLLOW_MOUSE); - entity_tree_attach(player_ent, e); + world_link_entities(&L.world, player_ent, e); } /* Camera ent */ { - struct entity *e = entity_alloc(); + struct entity *e = world_alloc_entity(&L.world); e->valid = true; e->rel_xform = XFORM_IDENT; @@ -281,7 +210,7 @@ INTERNAL void game_update(void) * Process game cmds * ========================== */ - L.tick.player_move_dir = V2(0, 0); + L.world.player_move_dir = V2(0, 0); struct game_cmd_array game_cmds = pop_cmds(scratch.arena); for (u64 i = 0; i < game_cmds.count; ++i) { @@ -292,21 +221,21 @@ INTERNAL void game_update(void) case GAME_CMD_KIND_PLAYER_MOVE: { struct v2 dir = cmd.dir; - L.tick.player_move_dir = v2_add(L.tick.player_move_dir, dir); + L.world.player_move_dir = v2_add(L.world.player_move_dir, dir); } break; /* Focus */ case GAME_CMD_KIND_PLAYER_FOCUS: { - L.tick.player_focus = cmd.pos; + L.world.player_focus = cmd.pos; } break; default: break; }; } - if (v2_len(L.tick.player_move_dir) > 1.f) { + if (v2_len(L.world.player_move_dir) > 1.f) { /* Clamp movement magnitude */ - L.tick.player_move_dir = v2_norm(L.tick.player_move_dir); + L.world.player_move_dir = v2_norm(L.world.player_move_dir); } /* ---------------------------------------------------------------------- */ @@ -316,8 +245,8 @@ INTERNAL void game_update(void) * Update entities pre-physics * ========================== */ - for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) { - struct entity *ent = &L.tick.entities[entity_index]; + for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) { + struct entity *ent = &L.world.entities[entity_index]; if (!ent->valid) continue; /* ========================== * @@ -333,7 +262,7 @@ INTERNAL void game_update(void) /* ENTITY_PROP_ANIMATING */ if (entity_has_prop(ent, ENTITY_PROP_ANIMATING) && ent->animation_start_time == 0) { - ent->animation_start_time = L.tick.time; + ent->animation_start_time = L.world.time; } /* ========================== * @@ -343,7 +272,7 @@ INTERNAL void game_update(void) if (ent->animation_start_time > 0) { /* Stop animation if past duration and not looping */ if (!ent->animation_looping) { - f64 time_in_anim = L.tick.time - ent->animation_start_time; + f64 time_in_anim = L.world.time - ent->animation_start_time; struct sheet *sheet = sheet_load(ent->sprite_name); if (sheet) { @@ -374,7 +303,7 @@ INTERNAL void game_update(void) /* ENTITY_PROP_TEST */ if (entity_has_prop(ent, ENTITY_PROP_TEST)) { - f32 t = ((f32)L.tick.time); + f32 t = ((f32)L.world.time); f32 r = t * 2.f; f32 s = 1 + (math_fabs(math_sin(t * 5)) * 3); (UNUSED)r; @@ -390,8 +319,8 @@ INTERNAL void game_update(void) * Update entity physics * ========================== */ - for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) { - struct entity *ent = &L.tick.entities[entity_index]; + for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) { + struct entity *ent = &L.world.entities[entity_index]; if (!ent->valid) continue; if (ent->parent.gen) continue; /* Only update parent entities */ @@ -403,7 +332,7 @@ INTERNAL void game_update(void) f32 max_speed = ent->player_max_speed; f32 acceleration_rate = ent->player_acceleration; acceleration_rate = clamp_f32(acceleration_rate, 0, GAME_FPS); /* Can't integrate acceleration rate higher than FPS */ - struct v2 target_velocity = v2_mul(L.tick.player_move_dir, max_speed); + struct v2 target_velocity = v2_mul(L.world.player_move_dir, max_speed); struct v2 target_acceleration = v2_sub(target_velocity, ent->velocity); ent->acceleration = v2_mul(target_acceleration, acceleration_rate); } @@ -413,7 +342,7 @@ INTERNAL void game_update(void) * ========================== */ { - f32 dt = (f32)L.tick.dt; + f32 dt = (f32)L.world.dt; /* Apply acceleration to velocity */ struct v2 a = v2_mul(ent->acceleration, dt); @@ -429,7 +358,7 @@ INTERNAL void game_update(void) if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { struct v2 ent_pos = ent->rel_xform.og; - struct v2 look_pos = L.tick.player_focus; + struct v2 look_pos = L.world.player_focus; f32 r = v2_angle_to_point(ent_pos, look_pos) + PI / 2; ent->rel_xform = xform_with_rotation(ent->rel_xform, r); } @@ -440,8 +369,8 @@ INTERNAL void game_update(void) /* ENTITY_PROP_TEST_FOLLOW_MOUSE */ if (entity_has_prop(ent, ENTITY_PROP_TEST_FOLLOW_MOUSE)) { - ent->rel_xform.og = L.tick.player_focus; - ent->test_start_rel_xform.og = L.tick.player_focus; + ent->rel_xform.og = L.world.player_focus; + ent->test_start_rel_xform.og = L.world.player_focus; } /* ========================== * @@ -450,25 +379,27 @@ INTERNAL void game_update(void) ent->world_xform = ent->rel_xform; - struct entity *child = entity_from_handle(ent->first); + struct entity *child = world_entity_from_handle(&L.world, ent->first); struct xform parent_xform = ent->world_xform; while (child->valid) { + + /* Calculate child world xform */ child->world_xform = xform_mul(parent_xform, child->rel_xform); /* Depth first iteration */ if (child->first.gen) { /* Next child */ parent_xform = child->world_xform; - child = entity_from_handle(child->first); + child = world_entity_from_handle(&L.world, child->first); } else if (child->next.gen) { /* Next sibling */ - child = entity_from_handle(child->next); - } else if (child->parent.gen && entity_from_handle(child->parent)->next.gen) { + child = world_entity_from_handle(&L.world, child->next); + } else if (child->parent.gen && world_entity_from_handle(&L.world, child->parent)->next.gen) { /* Next parent sibling */ - struct entity *parent = entity_from_handle(child->parent); - struct entity *grandparent = entity_from_handle(parent->parent); + struct entity *parent = world_entity_from_handle(&L.world, child->parent); + struct entity *grandparent = world_entity_from_handle(&L.world, parent->parent); parent_xform = grandparent->world_xform; - child = entity_from_handle(parent->next); + child = world_entity_from_handle(&L.world, parent->next); } else { child = entity_nil(); } @@ -479,8 +410,8 @@ INTERNAL void game_update(void) * Update entities post-physics * ========================== */ - for (u64 entity_index = 0; entity_index < ARRAY_COUNT(L.tick.entities); ++entity_index) { - struct entity *ent = &L.tick.entities[entity_index]; + for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) { + struct entity *ent = &L.world.entities[entity_index]; if (!ent->valid) continue; /* ========================== * @@ -488,7 +419,7 @@ INTERNAL void game_update(void) * ========================== */ if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { - struct entity *follow = entity_from_handle(ent->camera_follow); + struct entity *follow = world_entity_from_handle(&L.world, ent->camera_follow); ent->world_xform = follow->world_xform; ent->world_xform = xform_with_rotation(ent->world_xform, 0); @@ -548,9 +479,14 @@ void game_startup(void) L.game_cmds_mutex = sys_mutex_alloc(); L.game_cmds_arena = arena_alloc(GIGABYTE(64)); - /* Initialize tick storage */ + /* Initialize world */ + world_alloc(&L.world); + + /* Initialize tick transmission */ + world_alloc(&L.published_tick); L.published_tick_mutex = sys_mutex_alloc(); + L.timescale = 1.0; L.game_thread = sys_thread_init(&game_thread_entry_point, NULL, STR("[P2] Game thread")); } @@ -565,16 +501,16 @@ void game_shutdown(void) * Interface * ========================== */ -void game_get_latest_tick(struct tick *dest) +void game_get_latest_tick(struct world *dest) { sys_mutex_lock(&L.published_tick_mutex); - tick_copy(dest, &L.published_tick); + world_copy_replace(dest, &L.published_tick); sys_mutex_unlock(&L.published_tick_mutex); } u64 game_get_latest_tick_id(void) { - return L.published_tick.id; + return L.published_tick.tick_id; } void game_push_cmds(struct game_cmd_array cmd_array) diff --git a/src/game.h b/src/game.h index c5817c10..d506fe91 100644 --- a/src/game.h +++ b/src/game.h @@ -1,7 +1,7 @@ #ifndef GAME_H #define GAME_H -struct tick; +struct world; enum game_cmd_kind { GAME_CMD_KIND_NONE, @@ -31,7 +31,7 @@ struct game_cmd_array { void game_startup(void); void game_shutdown(void); -void game_get_latest_tick(struct tick *dest); +void game_get_latest_tick(struct world *dest); u64 game_get_latest_tick_id(void); void game_push_cmds(struct game_cmd_array cmd_array); diff --git a/src/log.c b/src/log.c index 212b94d3..d3c17305 100644 --- a/src/log.c +++ b/src/log.c @@ -21,7 +21,7 @@ GLOBAL struct { b32 file_valid; } L = { 0 }, DEBUG_LVAR(L_log); -GLOBAL const struct log_level_settings g_log_level_settings[LOG_LEVEL_COUNT] = { +GLOBAL READONLY const struct log_level_settings g_log_level_settings[LOG_LEVEL_COUNT] = { [LOG_LEVEL_CRITICAL] = { STR_NOCAST("CRITICAL"), 0xFFFF00FF diff --git a/src/sheet.c b/src/sheet.c index d85045ef..e87aabde 100644 --- a/src/sheet.c +++ b/src/sheet.c @@ -267,7 +267,7 @@ struct sheet *sheet_load(struct string path) * Sheet data * ========================== */ -GLOBAL struct sheet_tag g_default_tag = { 0 }; +GLOBAL READONLY struct sheet_tag g_default_tag = { 0 }; struct sheet_tag sheet_get_tag(struct sheet *sheet, struct string name) { diff --git a/src/tick.c b/src/tick.c deleted file mode 100644 index 1376ffb1..00000000 --- a/src/tick.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "tick.h" - -void tick_copy(struct tick *dst, struct tick *src) -{ - __prof; - /* Copy non-entity fields */ - MEMCPY(dst, src, FIELD_OFFSETOF(struct tick, entities)); - /* Copy entities */ - MEMCPY(&dst->entities, &src->entities, sizeof(struct entity) * src->entities_count); -} diff --git a/src/tick.h b/src/tick.h deleted file mode 100644 index ed8d8608..00000000 --- a/src/tick.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TICK_H -#define TICK_H - -#include "entity.h" - -struct tick { - u64 id; /* Starts at 1 */ - sys_timestamp_t published_ts; - - f64 dt; - f64 time; - - struct v2 player_move_dir; /* Player movement direction */ - struct v2 player_focus; /* Mouse cursor pos in world coordinates */ - - u64 entities_count; /* Includes 'released' & non-active entities */ - struct entity entities[MAX_ENTITIES]; -}; - -void tick_copy(struct tick *dst, struct tick *src); - -#endif diff --git a/src/user.c b/src/user.c index 8786b590..51fb18a9 100644 --- a/src/user.c +++ b/src/user.c @@ -12,7 +12,8 @@ #include "math.h" #include "console.h" #include "sys.h" -#include "tick.h" +#include "world.h" +#include "entity.h" /* FIXME: remove this (testing) */ #include "sound.h" @@ -29,6 +30,11 @@ struct view { f32 rot; }; +struct bind_state { + b32 pressed; /* Is this bind held down this frame */ + u32 num_presses; /* How many times was this bind pressed since last frame */ +}; + GLOBAL struct { b32 shutdown; struct sys_thread user_thread; @@ -37,7 +43,11 @@ GLOBAL struct { struct renderer_canvas *world_canvas; struct renderer_canvas *ui_canvas; struct view world_view; - struct tick blend_ticks[2]; + + struct world blend_ticks[2]; + struct world world; + + struct bind_state bind_states[USER_BIND_KIND_COUNT]; b32 debug_camera; b32 debug_camera_panning; @@ -62,16 +72,9 @@ GLOBAL struct { * Bind state * ========================== */ -struct bind_state { - b32 pressed; /* Is this bind held down this frame */ - u32 num_presses; /* How many times was this bind pressed since last frame */ -}; - -GLOBAL struct bind_state g_bind_states[USER_BIND_KIND_COUNT] = { 0 }; - /* TODO: Remove this */ -GLOBAL enum user_bind_kind g_binds[SYS_BTN_COUNT] = { +GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = { [SYS_BTN_W] = USER_BIND_KIND_MOVE_UP, [SYS_BTN_S] = USER_BIND_KIND_MOVE_DOWN, [SYS_BTN_A] = USER_BIND_KIND_MOVE_LEFT, @@ -122,8 +125,8 @@ INTERNAL struct sys_event_array pull_sys_events(struct arena *arena) * ========================== */ struct interp_ticks { - struct tick *from_tick; - struct tick *to_tick; + struct world *from_tick; + struct world *to_tick; }; INTERNAL struct interp_ticks pull_interp_ticks(void) @@ -132,9 +135,9 @@ INTERNAL struct interp_ticks pull_interp_ticks(void) ASSERT(ARRAY_COUNT(L.blend_ticks) == 2); /* Determine from & to tick from existing ticks */ - struct tick *from_tick = NULL; - struct tick *to_tick = NULL; - if (L.blend_ticks[1].id > L.blend_ticks[0].id) { + struct world *from_tick = NULL; + struct world *to_tick = NULL; + if (L.blend_ticks[1].tick_id > L.blend_ticks[0].tick_id) { from_tick = &L.blend_ticks[0]; to_tick = &L.blend_ticks[1]; } else { @@ -144,9 +147,9 @@ INTERNAL struct interp_ticks pull_interp_ticks(void) /* Check for new tick from game thread */ u64 latest_tick_id = game_get_latest_tick_id(); - if (latest_tick_id > to_tick->id) { + if (latest_tick_id > to_tick->tick_id) { /* Swap pointers */ - struct tick *temp = from_tick; + struct world *temp = from_tick; from_tick = to_tick; to_tick = temp; /* Pull game tick */ @@ -314,9 +317,9 @@ INTERNAL void user_update(void) * ========================== */ /* Reset bind states "was_pressed" */ - for (u32 i = 0; i < ARRAY_COUNT(g_bind_states); ++i) { - g_bind_states[i] = (struct bind_state) { - .pressed = g_bind_states[i].pressed + for (u32 i = 0; i < ARRAY_COUNT(L.bind_states); ++i) { + L.bind_states[i] = (struct bind_state) { + .pressed = L.bind_states[i].pressed }; } @@ -371,9 +374,9 @@ INTERNAL void user_update(void) enum user_bind_kind bind = g_binds[button]; if (bind) { b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN; - g_bind_states[bind].pressed = pressed; + L.bind_states[bind].pressed = pressed; if (pressed) { - ++g_bind_states[bind].num_presses; + ++L.bind_states[bind].num_presses; } } } @@ -386,8 +389,8 @@ INTERNAL void user_update(void) /* Movement */ { struct v2 input_move_dir = { 0 }; - for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(g_bind_states); ++bind) { - struct bind_state state = g_bind_states[bind]; + for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(L.bind_states); ++bind) { + struct bind_state state = L.bind_states[bind]; if (!state.pressed && state.num_presses <= 0) { continue; @@ -424,11 +427,11 @@ INTERNAL void user_update(void) * Update view from debug camera * ========================== */ - if (g_bind_states[USER_BIND_KIND_DEBUG_DRAW].num_presses > 0) { + if (L.bind_states[USER_BIND_KIND_DEBUG_DRAW].num_presses > 0) { L.debug_draw = !L.debug_draw; } - if (g_bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) { + if (L.bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) { L.debug_camera = !L.debug_camera; } @@ -444,7 +447,7 @@ INTERNAL void user_update(void) } /* Zoom view */ - i32 input_zooms = g_bind_states[USER_BIND_KIND_ZOOM_IN].num_presses - g_bind_states[USER_BIND_KIND_ZOOM_OUT].num_presses; + i32 input_zooms = L.bind_states[USER_BIND_KIND_ZOOM_IN].num_presses - L.bind_states[USER_BIND_KIND_ZOOM_OUT].num_presses; if (input_zooms != 0) { i32 dir = input_zooms >= 0 ? 1 : -1; u32 zooms_abs = input_zooms >= 0 ? input_zooms : -input_zooms; @@ -479,29 +482,28 @@ INTERNAL void user_update(void) /* Pull ticks */ struct interp_ticks interp_ticks = pull_interp_ticks(); - struct tick *t0 = interp_ticks.from_tick; - struct tick *t1 = interp_ticks.to_tick; + struct world *t0 = interp_ticks.from_tick; + struct world *t1 = interp_ticks.to_tick; /* Produce interpolated tick */ - struct tick *tick = arena_push_zero(scratch.arena, struct tick); { __profscope(produce_interpolated_tick); - sys_timestamp_t tick_publish_delta = t1->published_ts - t0->published_ts; - f32 tick_blend = (f32)((sys_timestamp() - t1->published_ts) / (f64)tick_publish_delta); + sys_timestamp_t tick_publish_delta = t1->tick_published_ts - t0->tick_published_ts; + f32 tick_blend = (f32)((sys_timestamp() - t1->tick_published_ts) / (f64)tick_publish_delta); - tick_copy(tick, t1); + world_copy_replace(&L.world, t1); #if 1 /* Blend time */ - tick->time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend); + L.world.time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend); /* Blend entities */ u64 num_entities = min_u64(t0->entities_count, t1->entities_count); for (u64 i = 0; i < num_entities; ++i) { - struct entity *e = &tick->entities[i]; struct entity *e0 = &t0->entities[i]; struct entity *e1 = &t1->entities[i]; + struct entity *e = &L.world.entities[i]; if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) { e->rel_xform = xform_lerp(e0->rel_xform, e1->rel_xform, tick_blend); e->world_xform = xform_lerp(e0->world_xform, e1->world_xform, tick_blend); @@ -525,8 +527,8 @@ INTERNAL void user_update(void) * ========================== */ /* Find camera */ - for (u64 entity_index = 0; entity_index < tick->entities_count; ++entity_index) { - struct entity *ent = &tick->entities[entity_index]; + for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) { + struct entity *ent = &L.world.entities[entity_index]; if (!ent->valid) continue; if (entity_has_prop(ent, ENTITY_PROP_CAMERA) && ent->camera_active && !L.debug_camera) { @@ -586,8 +588,8 @@ INTERNAL void user_update(void) * ========================== */ /* Iterate entities */ - for (u64 entity_index = 0; entity_index < tick->entities_count; ++entity_index) { - struct entity *ent = &tick->entities[entity_index]; + for (u64 entity_index = 0; entity_index < L.world.entities_count; ++entity_index) { + struct entity *ent = &L.world.entities[entity_index]; if (!ent->valid) continue; b32 is_camera = entity_has_prop(ent, ENTITY_PROP_CAMERA); @@ -613,7 +615,7 @@ INTERNAL void user_update(void) struct sheet_frame frame = sheet_get_frame(sheet, tag.start); if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) { b32 looping = ent->animation_looping; - f64 time_in_anim = tick->time - ent->animation_start_time; + f64 time_in_anim = L.world.time - ent->animation_start_time; u64 frame_index = tag.start; while (time_in_anim > 0) { @@ -769,7 +771,7 @@ INTERNAL void user_update(void) u64 canvases_count = 0; { /* Only render world if not on first frame */ - if (t0->id > 0 && t1->id > 0) { + if (t0->tick_id > 0 && t1->tick_id > 0) { *arena_push(scratch.arena, struct renderer_canvas *) = L.world_canvas; ++canvases_count; } @@ -810,6 +812,10 @@ void user_startup(struct sys_window *window) L.sys_events_mutex = sys_mutex_alloc(); L.sys_events_arena = arena_alloc(GIGABYTE(64)); + world_alloc(&L.blend_ticks[0]); + world_alloc(&L.blend_ticks[1]); + world_alloc(&L.world); + L.world_canvas = renderer_canvas_alloc(); L.world_view = (struct view) { .px_per_unit = PIXELS_PER_UNIT, diff --git a/src/world.c b/src/world.c new file mode 100644 index 00000000..a877e5a5 --- /dev/null +++ b/src/world.c @@ -0,0 +1,98 @@ +#include "world.h" +#include "entity.h" + +/* ========================== * + * World allocation + * ========================== */ + +void world_alloc(struct world *world) +{ + MEMZERO_STRUCT(world); + world->entities_arena = arena_alloc(GIGABYTE(64)); + world->entities = (struct entity *)world->entities_arena.base; +} + +void world_copy_replace(struct world *dest, struct world *src) +{ + __prof; + + /* Copy non-arena fields */ + MEMCPY(dest, src, FIELD_OFFSETOF(struct world, _copy_barrier)); + + /* Copy arena contents */ + arena_copy_replace(&dest->entities_arena, &src->entities_arena); +} + +/* ========================== * + * Entity allocation + * ========================== */ + +struct entity *world_alloc_entity(struct world *world) +{ + struct entity *entity = NULL; + if (world->first_free_entity.gen) { + /* Reuse from free list */ + entity = world_entity_from_handle(world, world->first_free_entity); + world->first_free_entity = entity->next_free; + *entity = (struct entity) { + .handle = entity->handle + }; + } else { + /* Make new */ + u64 idx = world->entities_count++; + entity = &world->entities[idx]; + *entity = (struct entity) { + .handle = { .gen = 1, .idx = idx } + }; + } + return entity; +} + +void world_release_entity(struct world *world, struct entity *entity) +{ + struct entity_handle next_free = world->first_free_entity; + world->first_free_entity = entity->handle; + *entity = (struct entity) { + .handle.gen = entity->handle.gen + 1, + .next_free = next_free + }; +} + +/* ========================== * + * Queries + * ========================== */ + +/* Returns a valid entity or nil entity. Always safe to read result, need to check to write. */ +struct entity *world_entity_from_handle(struct world *world, struct entity_handle handle) +{ + if (handle.idx < world->entities_count) { + struct entity *entities = world->entities; + struct entity *entity = &entities[handle.idx]; + if (entity->handle.gen == handle.gen) { + return entity; + } + } + return entity_nil(); +} + +/* ========================== * + * Tree + * ========================== */ + +void world_link_entities(struct world *world, struct entity *parent, struct entity *child) +{ + struct entity *first_child = world_entity_from_handle(world, parent->first); + struct entity *last_child = world_entity_from_handle(world, parent->last); + + child->prev = last_child->handle; + child->parent = parent->handle; + + if (last_child->valid) { + last_child->next = child->handle; + } + parent->last = child->handle; + + if (!first_child->valid) { + parent->first = child->handle; + } +} diff --git a/src/world.h b/src/world.h new file mode 100644 index 00000000..5256a215 --- /dev/null +++ b/src/world.h @@ -0,0 +1,39 @@ +#ifndef WORLD_H +#define WORLD_H + +#include "entity.h" + +struct world { + u64 tick_id; /* Starts at 1 */ + sys_timestamp_t tick_published_ts; + + f64 dt; + f64 time; + + struct v2 player_move_dir; /* Player movement direction */ + struct v2 player_focus; /* Mouse cursor pos in world coordinates */ + + u64 entities_count; /* Includes 'released' & inactive entities */ + struct entity_handle first_free_entity; + + + /* ====================================================================== */ + /* Everything after this field is not automatically coppied + * in world_copy_replace */ + u8 _copy_barrier; + + /* Arenas */ + struct arena entities_arena; + struct entity *entities; +}; + +void world_alloc(struct world *world); +void world_copy_replace(struct world *dest, struct world *src); + +struct entity *world_alloc_entity(struct world *world); +void world_release_entity(struct world *world, struct entity *entity); +struct entity *world_entity_from_handle(struct world *world, struct entity_handle handle); + +void world_link_entities(struct world *world, struct entity *parent, struct entity *child); + +#endif