begin entity xform tree refactor
This commit is contained in:
parent
bda472d15e
commit
c7ee34037a
BIN
res/graphics/tim.ase
(Stored with Git LFS)
BIN
res/graphics/tim.ase
(Stored with Git LFS)
Binary file not shown.
136
src/entity.c
136
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() */
|
||||
|
||||
25
src/entity.h
25
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
|
||||
|
||||
46
src/game.c
46
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;
|
||||
}
|
||||
|
||||
10
src/user.c
10
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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user