From c7ee34037aad3d9b47d84fe9556f845f4a909955 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 6 Aug 2024 13:44:24 -0500 Subject: [PATCH] begin entity xform tree refactor --- res/graphics/tim.ase | 4 +- src/entity.c | 136 +++++++++++++++++++++++++++++++++---------- src/entity.h | 25 +++++++- src/game.c | 46 ++++++++------- src/user.c | 10 ++-- src/world.c | 2 +- src/world.h | 2 +- 7 files changed, 159 insertions(+), 66 deletions(-) diff --git a/res/graphics/tim.ase b/res/graphics/tim.ase index 983f9a13..38449ab2 100644 --- a/res/graphics/tim.ase +++ b/res/graphics/tim.ase @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32478c7862c244341a3311a16b862da428e750260af2510f08251779d743b023 -size 4451 +oid sha256:9535c14cdb801d25a07247d344f15582fa1b873c2353218dfc7dc3e65bc9220d +size 4411 diff --git a/src/entity.c b/src/entity.c index 613af4b4..fcc39e1a 100644 --- a/src/entity.c +++ b/src/entity.c @@ -1,7 +1,13 @@ #include "entity.h" #include "math.h" +/* Accessed via entity_store_nil() */ +READONLY struct entity_store _g_entity_store_nil = { + 0 +}; + /* Accessed via entity_nil() */ +/* TODO: Allocate nil entity in nil store */ READONLY struct entity _g_entity_nil = { .xform = XFORM_IDENT_NOCAST, .xform_world = XFORM_IDENT_NOCAST, @@ -13,10 +19,19 @@ READONLY struct entity _g_entity_nil = { * Store allocation * ========================== */ -struct entity_store entity_store_alloc(void) +struct entity_store *entity_store_alloc(void) { - struct entity_store store = { 0 }; - store.arena = arena_alloc(GIGABYTE(64)); + struct arena arena = arena_alloc(GIGABYTE(64)); + struct entity_store *store = arena_push_zero(&arena, struct entity_store); + store->arena = arena; + store->entities = arena_dry_push(&arena, struct entity); + + /* Special entity for keeping track of store offset */ + struct entity *offset_entity = entity_alloc(store); + offset_entity->valid = false; + offset_entity->store_offset = (u64)store->entities - (u64)store; + ASSERT(offset_entity->handle.idx == 0); /* Must be first entity in array */ + return store; } @@ -27,10 +42,14 @@ void entity_store_release(struct entity_store *store) void entity_store_copy_replace(struct entity_store *dest, struct entity_store *src) { - struct arena orig_arena = dest->arena; + struct arena dest_arena = dest->arena; + struct entity *dest_entities = dest->entities; + u64 dest_store_offset = dest_entities[0].store_offset; MEMCPY_STRUCT(dest, src); - dest->arena = orig_arena; - arena_copy_replace(&dest->arena, &src->arena); + arena_copy_replace(&dest_arena, &src->arena); + dest->arena = dest_arena; + dest->entities = dest_entities; + dest->entities[0].store_offset = dest_store_offset; } /* ========================== * @@ -40,48 +59,41 @@ void entity_store_copy_replace(struct entity_store *dest, struct entity_store *s struct entity *entity_alloc(struct entity_store *store) { struct entity *entity = NULL; + struct entity_handle handle = { 0 }; if (store->first_free.gen) { /* Reuse from free list */ entity = entity_from_handle(store, store->first_free); + handle = entity->handle; store->first_free = entity->next_free; - entity->next_free = (struct entity_handle) { 0 }; } else { /* Make new */ - u64 idx = store->count++; entity = arena_push(&store->arena, struct entity); - *entity = *entity_nil(); - entity->handle = (struct entity_handle) { .gen = 1, .idx = idx }; + handle = (struct entity_handle) { .gen = 1, .idx = store->count++ }; } + *entity = *entity_nil(); + entity->handle = handle; + entity->valid = true; return entity; } void entity_release(struct entity_store *store, struct entity *entity) { - struct entity_handle handle = (struct entity_handle) { .gen = entity->handle.gen, .idx = entity->handle.idx }; - *entity = *entity_nil(); - entity->handle = handle; + /* FIXME: Unlink entity */ + ++entity->handle.gen; + entity->valid = false; entity->next_free = store->first_free; - store->first_free = handle; + store->first_free = entity->handle; } /* ========================== * * Query - * ========================== */ + * ========================== */ -struct entity_array entity_store_as_array(struct entity_store *store) -{ - return (struct entity_array) { - .entities = (struct entity *)store->arena.base, - .count = store->count - }; -} - -/* Returns a valid entity or nil entity. Always safe to read result, need to check to write. */ +/* Returns a valid entity or nil entity. Always safe to read result, need to check `valid` to write. */ struct entity *entity_from_handle(struct entity_store *store, struct entity_handle handle) { if (handle.idx < store->count) { - struct entity *entities = (struct entity *)store->arena.base; - struct entity *entity = &entities[handle.idx]; + struct entity *entity = &store->entities[handle.idx]; if (entity->handle.gen == handle.gen) { return entity; } @@ -91,9 +103,10 @@ struct entity *entity_from_handle(struct entity_store *store, struct entity_hand struct entity *entity_find_first_match_one(struct entity_store *store, enum entity_prop prop) { - struct entity_array entities_array = entity_store_as_array(store); - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; + u64 count = store->count; + struct entity *entities = store->entities; + for (u64 entity_index = 0; entity_index < count; ++entity_index) { + struct entity *ent = &entities[entity_index]; if (ent->valid && entity_has_prop(ent, prop)) { return ent; } @@ -103,9 +116,10 @@ struct entity *entity_find_first_match_one(struct entity_store *store, enum enti struct entity *entity_find_first_match_all(struct entity_store *store, struct entity_prop_array props) { - struct entity_array entities_array = entity_store_as_array(store); - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; + u64 count = store->count; + struct entity *entities = store->entities; + for (u64 entity_index = 0; entity_index < count; ++entity_index) { + struct entity *ent = &entities[entity_index]; if (ent->valid) { b32 all = true; for (u64 i = 0; i < props.count; ++i) { @@ -122,6 +136,62 @@ struct entity *entity_find_first_match_all(struct entity_store *store, struct en return entity_nil(); } +/* ========================== * + * Xform + * ========================== */ + + + + + +/* TODO: Move this */ +INTERNAL struct entity_store *entity_get_store(struct entity *ent) +{ + struct entity_store *store = entity_store_nil(); + if (ent->valid) { + struct entity *first_entity = ent - ent->handle.idx; + u64 store_offset = first_entity->store_offset; + store = (struct entity_store *)((u64)first_entity - store_offset); + ASSERT(store->entities == first_entity); + } + return store; +} + + + + +struct xform entity_get_local_xform(struct entity *ent) +{ + return ent->xform; +} + +void entity_set_local_xform(struct entity *ent, struct xform xf) +{ + ent->xform = xf; +} + +struct xform entity_get_global_xform(struct entity *ent) +{ + /* TODO: Remove this */ + return ent->xform_world; +} + +void entity_set_global_xform(struct entity *ent, struct xform xf) +{ + /* TODO: Remove this */ + struct entity_store *store = entity_get_store(ent); + + struct entity *parent = entity_from_handle(store, ent->parent); + + struct xform parent_global = parent->xform_world; + struct xform child_global = xf; + + struct xform child_local = xform_mul(xform_invert(parent_global), child_global); + + ent->xform = child_local; + ent->xform_world = child_global; +} + /* ========================== * * Tree * ========================== */ @@ -143,3 +213,5 @@ void entity_link(struct entity_store *store, struct entity *parent, struct entit parent->first = child->handle; } } + +/* TODO: entity_unlink() */ diff --git a/src/entity.h b/src/entity.h index 84e2b1f8..e495e731 100644 --- a/src/entity.h +++ b/src/entity.h @@ -29,16 +29,20 @@ struct entity_store { struct arena arena; u64 count; struct entity_handle first_free; + struct entity *entities; }; struct entity { + /* Metadata */ b32 valid; struct entity_handle handle; u64 continuity_gen; u64 props[(ENTITY_PROP_COUNT + 63) / 64]; - struct entity_handle next_free; + /* Special value stored in first entity in store array */ + u64 store_offset; + /* Tree */ struct entity_handle parent; struct entity_handle next; @@ -124,6 +128,12 @@ INLINE struct entity *entity_nil(void) return &_g_entity_nil; } +INLINE struct entity_store *entity_store_nil(void) +{ + extern READONLY struct entity_store _g_entity_store_nil; + return &_g_entity_store_nil; +} + /* ========================== * * Property helpers * ========================== */ @@ -153,8 +163,10 @@ INLINE b32 entity_has_prop(struct entity *entity, enum entity_prop prop) * Entity functions * ========================== */ +INLINE struct entity_array entity_store_as_array(struct entity_store *store) { return (struct entity_array) { .count = store->count, .entities = store->entities }; } + /* Entity store */ -struct entity_store entity_store_alloc(void); +struct entity_store *entity_store_alloc(void); void entity_store_release(struct entity_store *store); void entity_store_copy_replace(struct entity_store *dest, struct entity_store *src); @@ -162,11 +174,18 @@ void entity_store_copy_replace(struct entity_store *dest, struct entity_store *s struct entity *entity_alloc(struct entity_store *store); void entity_release(struct entity_store *store, struct entity *entity); -struct entity_array entity_store_as_array(struct entity_store *store); +/* Xform */ +struct xform entity_get_local_xform(struct entity *ent); +void entity_set_local_xform(struct entity *ent, struct xform xf); +struct xform entity_get_global_xform(struct entity *ent); +void entity_set_global_xform(struct entity *ent, struct xform xf); + +/* Query */ struct entity *entity_from_handle(struct entity_store *store, struct entity_handle handle); struct entity *entity_find_first_match_one(struct entity_store *store, enum entity_prop prop); struct entity *entity_find_first_match_all(struct entity_store *store, struct entity_prop_array props); +/* Tree */ void entity_link(struct entity_store *store, struct entity *parent, struct entity *child); #endif diff --git a/src/game.c b/src/game.c index f4073ef1..1e6f2c9a 100644 --- a/src/game.c +++ b/src/game.c @@ -137,11 +137,11 @@ INTERNAL void recalculate_world_xforms_recurse(struct entity *parent) child->sprite_xform_world = sprite_xform_world; /* Append sub-children to stack */ - struct entity *subchild = entity_from_handle(&G.world.entity_store, child->last); + struct entity *subchild = entity_from_handle(G.world.entity_store, child->last); while (subchild->valid) { *arena_push(scratch.arena, struct stack_node) = (struct stack_node) { .entity = subchild, .parent_xform_world = xform_world }; ++stack_count; - subchild = entity_from_handle(&G.world.entity_store, subchild->prev); + subchild = entity_from_handle(G.world.entity_store, subchild->prev); } } @@ -173,14 +173,13 @@ INTERNAL void game_update(void) struct v2 size = V2(1, 1); f32 r = 0; - struct entity *e = entity_alloc(&G.world.entity_store); - e->valid = true; + struct entity *e = entity_alloc(G.world.entity_store); e->xform = XFORM_TRS(.t = pos, .r = r, .s = size); e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase")); - //e->sprite_span_name = STR("idle.unarmed"); + e->sprite_span_name = STR("idle.unarmed"); //e->sprite_span_name = STR("idle.one_handed"); - e->sprite_span_name = STR("idle.two_handed"); + //e->sprite_span_name = STR("idle.two_handed"); entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); e->player_max_speed = 4.f; @@ -196,8 +195,7 @@ INTERNAL void game_update(void) /* Camera ent */ { - struct entity *e = entity_alloc(&G.world.entity_store); - e->valid = true; + struct entity *e = entity_alloc(G.world.entity_store); e->xform = XFORM_IDENT; entity_enable_prop(e, ENTITY_PROP_CAMERA); @@ -214,7 +212,7 @@ INTERNAL void game_update(void) G.world.tick_ts = sys_timestamp(); G.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * G.world.timescale); G.world.time += G.world.dt; - struct entity_array entities_array = entity_store_as_array(&G.world.entity_store); + struct entity_array entities_array = entity_store_as_array(G.world.entity_store); /* ========================== * * Process game cmds @@ -240,7 +238,7 @@ INTERNAL void game_update(void) for (u64 i = 0; i < entities_array.count; ++i) { struct entity *ent = &entities_array.entities[i]; if (ent->valid) { - entity_release(&G.world.entity_store, ent); + entity_release(G.world.entity_store, ent); } } } break; @@ -338,7 +336,7 @@ INTERNAL void game_update(void) /* ENTITY_PROP_TEST */ if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) { ent->test_initialized = true; - ent->test_start_rel_xform = ent->xform; + ent->test_start_rel_xform = entity_get_local_xform(ent); ent->test_start_sprite_xform = ent->sprite_xform; } @@ -350,14 +348,14 @@ INTERNAL void game_update(void) /* Update focus */ ent->focus = G.world.player_aim; - /* Solve for missing angle using law of sines */ + struct xform xf = entity_get_global_xform(ent); + + /* Solve for final angle using law of sines */ f32 final_xf_angle; { struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame); - struct xform xf = ent->xform_world; - f32 forward_hold_angle_offset; { struct xform xf_unrotated = xform_with_rotation(xf, 0); @@ -384,7 +382,7 @@ INTERNAL void game_update(void) } if (!F32_IS_NAN(final_xf_angle)) { - ent->xform = xform_with_rotation(ent->xform_world, final_xf_angle); + entity_set_global_xform(ent, xform_with_rotation(ent->xform_world, final_xf_angle)); } } @@ -407,9 +405,11 @@ INTERNAL void game_update(void) s = v2_add(s, xform_get_scale(ent->test_start_rel_xform)); - ent->xform.og = og; - ent->xform = xform_with_rotation(ent->xform, r); - ent->xform = xform_with_scale(ent->xform, s); + struct xform xf = entity_get_local_xform(ent); + xf.og = og; + xf= xform_with_rotation(xf, r); + xf= xform_with_scale(ent->xform, s); + entity_set_local_xform(ent, xf); } } @@ -454,7 +454,9 @@ INTERNAL void game_update(void) ent->velocity = v2_add(ent->velocity, a); /* Apply velocity to position */ - ent->xform.og = v2_add(ent->xform.og, v2_mul(ent->velocity, dt)); + struct xform xf = entity_get_global_xform(ent); + xf.og = v2_add(xf.og, v2_mul(ent->velocity, dt)); + entity_set_global_xform(ent, xf); } } @@ -480,12 +482,12 @@ INTERNAL void game_update(void) /* Camera follow */ if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { - struct entity *follow = entity_from_handle(&G.world.entity_store, ent->camera_follow); + struct entity *follow = entity_from_handle(G.world.entity_store, ent->camera_follow); if (entity_has_prop(follow, ENTITY_PROP_PLAYER_CONTROLLED)) { f32 aspect_ratio = 1.0; { - struct xform quad_xf = xform_mul(ent->xform_world, ent->camera_quad_xform); + struct xform quad_xf = xform_mul(entity_get_global_xform(ent), ent->camera_quad_xform); struct v2 camera_size = xform_get_scale(quad_xf); if (!v2_eq(camera_size, V2(0, 0))) { aspect_ratio = camera_size.x / camera_size.y; @@ -494,7 +496,7 @@ INTERNAL void game_update(void) f32 ratio_y = 0.33f; f32 ratio_x = ratio_y / aspect_ratio; struct v2 camera_focus_dir = v2_mul_v2(follow->focus, V2(ratio_x, ratio_y)); - struct v2 camera_focus_pos = v2_add(follow->xform_world.og, camera_focus_dir); + struct v2 camera_focus_pos = v2_add(entity_get_global_xform(follow).og, camera_focus_dir); ent->camera_rel_xform_target = ent->xform; ent->camera_rel_xform_target.og = camera_focus_pos; } diff --git a/src/user.c b/src/user.c index 6acbabab..67ffce5a 100644 --- a/src/user.c +++ b/src/user.c @@ -438,9 +438,9 @@ INTERNAL void user_update(void) G.world.player_aim = v2_lerp(t0->player_aim, t1->player_aim, tick_blend); /* Blend entities */ - struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); - struct entity_array t1_entities = entity_store_as_array(&t1->entity_store); - struct entity_array world_entities = entity_store_as_array(&G.world.entity_store); + struct entity_array t0_entities = entity_store_as_array(t0->entity_store); + struct entity_array t1_entities = entity_store_as_array(t1->entity_store); + struct entity_array world_entities = entity_store_as_array(G.world.entity_store); u64 num_entities = min_u64(t0_entities.count, t1_entities.count); for (u64 i = 0; i < num_entities; ++i) { @@ -471,7 +471,7 @@ INTERNAL void user_update(void) tick_is_first_frame = G.world.tick_id == 0; #endif } - struct entity_array entities_array = entity_store_as_array(&G.world.entity_store); + struct entity_array entities_array = entity_store_as_array(G.world.entity_store); /* ========================== * * Find important entities @@ -870,7 +870,7 @@ INTERNAL void user_update(void) } /* Draw hierarchy */ - struct entity *parent = entity_from_handle(&G.world.entity_store, ent->parent); + struct entity *parent = entity_from_handle(G.world.entity_store, ent->parent); if (parent->valid) { u32 color = RGBA_32_F(0.6, 0.6, 1, 0.75); f32 thickness = 5; diff --git a/src/world.c b/src/world.c index 95d7f0eb..6f2b6b9d 100644 --- a/src/world.c +++ b/src/world.c @@ -18,7 +18,7 @@ void world_copy_replace(struct world *dest, struct world *src) MEMCPY_STRUCT(dest, src); dest->entity_store = old->entity_store; - entity_store_copy_replace(&dest->entity_store, &src->entity_store); + entity_store_copy_replace(dest->entity_store, src->entity_store); scratch_end(scratch); } diff --git a/src/world.h b/src/world.h index f96c7a5a..6d6ede55 100644 --- a/src/world.h +++ b/src/world.h @@ -15,7 +15,7 @@ struct world { struct v2 player_move_dir; struct v2 player_aim; - struct entity_store entity_store; + struct entity_store *entity_store; }; void world_alloc(struct world *world);