begin entity xform tree refactor

This commit is contained in:
jacob 2024-08-06 13:44:24 -05:00
parent bda472d15e
commit c7ee34037a
7 changed files with 159 additions and 66 deletions

BIN
res/graphics/tim.ase (Stored with Git LFS)

Binary file not shown.

View File

@ -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() */

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);