child xform fixes

This commit is contained in:
jacob 2024-08-07 14:13:06 -05:00
parent f5e28a04af
commit 0f113f640f
4 changed files with 146 additions and 74 deletions

View File

@ -14,8 +14,16 @@ READONLY struct entity_store _g_entity_store_nil = {
READONLY struct entity _g_entity_nil = { READONLY struct entity _g_entity_nil = {
.local_xform = XFORM_IDENT_NOCAST, .local_xform = XFORM_IDENT_NOCAST,
.cached_global_xform = XFORM_IDENT_NOCAST, .cached_global_xform = XFORM_IDENT_NOCAST,
.cached_global_xform_clean = true, .sprite_local_xform = XFORM_IDENT_NOCAST,
.sprite_xform = XFORM_IDENT_NOCAST, .sprite_tint = COLOR_WHITE
};
GLOBAL READONLY struct entity g_entity_default = {
.valid = true,
.local_xform = XFORM_IDENT_NOCAST,
.cached_global_xform = XFORM_IDENT_NOCAST,
.cached_global_xform_dirty = true,
.sprite_local_xform = XFORM_IDENT_NOCAST,
.sprite_tint = COLOR_WHITE .sprite_tint = COLOR_WHITE
}; };
@ -32,9 +40,10 @@ struct entity_store *entity_store_alloc(void)
ASSERT((u64)store->entities - (u64)store == STORE_ENTITIES_OFFSET); /* Offset must be correct */ ASSERT((u64)store->entities - (u64)store == STORE_ENTITIES_OFFSET); /* Offset must be correct */
struct entity *root = entity_alloc_unlinked(store); struct entity *root = entity_alloc_unlinked(store);
root->root = true; root->is_root = true;
root->cached_global_xform = root->local_xform; root->local_xform = XFORM_IDENT;
root->cached_global_xform_clean = true; root->cached_global_xform = XFORM_IDENT;
root->cached_global_xform_dirty = false;
store->root = root->handle; store->root = root->handle;
return store; return store;
@ -74,10 +83,8 @@ struct entity *entity_alloc_unlinked(struct entity_store *store)
entity = arena_push(&store->arena, struct entity); entity = arena_push(&store->arena, struct entity);
handle = (struct entity_handle) { .gen = 1, .idx = store->count++ }; handle = (struct entity_handle) { .gen = 1, .idx = store->count++ };
} }
*entity = *entity_nil(); *entity = g_entity_default;
entity->handle = handle; entity->handle = handle;
entity->valid = true;
entity->cached_global_xform_clean = false;
return entity; return entity;
} }
@ -85,7 +92,7 @@ struct entity *entity_alloc_top(struct entity_store *store)
{ {
struct entity *e = entity_alloc_unlinked(store); struct entity *e = entity_alloc_unlinked(store);
struct entity *root = entity_from_handle(store, store->root); struct entity *root = entity_from_handle(store, store->root);
entity_link(root, e); entity_link_parent_child(root, e);
return e; return e;
} }
@ -93,22 +100,25 @@ struct entity *entity_alloc_child(struct entity *parent)
{ {
struct entity_store *store = entity_get_store(parent); struct entity_store *store = entity_get_store(parent);
struct entity *e = entity_alloc_unlinked(store); struct entity *e = entity_alloc_unlinked(store);
entity_link(parent, e); entity_link_parent_child(parent, e);
return e; return e;
} }
void entity_release(struct entity_store *store, struct entity *entity) void entity_release(struct entity_store *store, struct entity *ent)
{ {
++entity->handle.gen;
entity->valid = false;
entity->next_free = store->first_free;
store->first_free = entity->handle;
/* Release children */ /* Release children */
if (entity->first.gen) { if (ent->first.gen) {
for (struct entity *child = entity_from_handle(store, entity->first); child->valid; child = entity_from_handle(store, child->next)) { for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) {
entity_release(store, child); entity_release(store, child);
} }
} }
/* Unlink */
entity_unlink(ent);
/* Release */
++ent->handle.gen;
ent->valid = false;
ent->next_free = store->first_free;
store->first_free = ent->handle;
} }
/* ========================== * /* ========================== *
@ -178,42 +188,51 @@ struct entity *entity_find_first_match_all(struct entity_store *store, struct en
* Xform * Xform
* ========================== */ * ========================== */
INTERNAL void entity_mark_xform_dirty(struct entity_store *store, struct entity *ent) INTERNAL void entity_mark_child_xforms_dirty(struct entity_store *store, struct entity *ent)
{ {
ent->cached_global_xform_clean = false;
for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) { for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) {
if (child->cached_global_xform_clean) { if (child->cached_global_xform_dirty) {
entity_mark_xform_dirty(store, ent);
} else {
break; break;
} else {
child->cached_global_xform_dirty = true;
entity_mark_child_xforms_dirty(store, child);
} }
} }
} }
INTERNAL struct xform entity_get_xform_w_store(struct entity_store *store, struct entity *ent) INTERNAL struct xform entity_get_xform_w_store(struct entity_store *store, struct entity *ent)
{ {
struct entity_handle parent_handle = ent->parent;
struct xform xf; struct xform xf;
if (entity_handle_eq(parent_handle, store->root)) { if (ent->is_top) {
xf = ent->local_xform; xf = ent->local_xform;
} else if (ent->cached_global_xform_clean) { } else if (!ent->cached_global_xform_dirty) {
xf = ent->cached_global_xform; xf = ent->cached_global_xform;
} else { } else {
struct entity_handle parent_handle = ent->parent;
struct entity *parent = entity_from_handle(store, parent_handle); struct entity *parent = entity_from_handle(store, parent_handle);
xf = entity_get_xform_w_store(store, parent); xf = entity_get_xform_w_store(store, parent);
xf = xform_mul(xf, ent->local_xform); xf = xform_mul(xf, ent->local_xform);
ent->cached_global_xform = xf; ent->cached_global_xform = xf;
ent->cached_global_xform_clean = true; ent->cached_global_xform_dirty = false;
} }
return xf; return xf;
} }
struct xform entity_get_xform(struct entity *ent) struct xform entity_get_xform(struct entity *ent)
{ {
struct xform xf;
if (ent->is_top) {
xf = ent->local_xform;
} else if (!ent->cached_global_xform_dirty) {
xf = ent->cached_global_xform;
} else {
struct entity_store *store = entity_get_store(ent); struct entity_store *store = entity_get_store(ent);
struct xform xf = entity_get_xform_w_store(store, ent); struct entity *parent = entity_from_handle(store, ent->parent);
xf = entity_get_xform_w_store(store, parent);
xf = xform_mul(xf, ent->local_xform);
ent->cached_global_xform = xf;
ent->cached_global_xform_dirty = false;
}
return xf; return xf;
} }
@ -224,31 +243,34 @@ struct xform entity_get_local_xform(struct entity *ent)
void entity_set_xform(struct entity *ent, struct xform xf) void entity_set_xform(struct entity *ent, struct xform xf)
{ {
/* TODO: Instead of calculating & storing derived local xform, maybe just cache global xform and update local if retrieved? */
struct entity_store *store = entity_get_store(ent); struct entity_store *store = entity_get_store(ent);
if (ent->is_top) {
ent->local_xform = xf;
} else {
/* Update child local xform */
struct entity *parent = entity_from_handle(store, ent->parent); struct entity *parent = entity_from_handle(store, ent->parent);
struct xform child_global = xf;
struct xform parent_global = entity_get_xform_w_store(store, parent); struct xform parent_global = entity_get_xform_w_store(store, parent);
struct xform child_local = xform_mul(xform_invert(parent_global), child_global); ent->local_xform = xform_mul(xform_invert(parent_global), xf);
ent->local_xform = child_local; /* Set child global xform */
ent->cached_global_xform = child_global; ent->cached_global_xform = xf;
ent->cached_global_xform_clean = true; ent->cached_global_xform_dirty = false;
}
entity_mark_child_xforms_dirty(store, ent);
} }
void entity_set_local_xform(struct entity *ent, struct xform xf) void entity_set_local_xform(struct entity *ent, struct xform xf)
{ {
ent->local_xform = xf; ent->local_xform = xf;
entity_mark_xform_dirty(entity_get_store(ent), ent); ent->cached_global_xform_dirty = true;
entity_mark_child_xforms_dirty(entity_get_store(ent), ent);
} }
/* ========================== * /* ========================== *
* Tree * Tree
* ========================== */ * ========================== */
void entity_link(struct entity *parent, struct entity *child) void entity_link_parent_child(struct entity *parent, struct entity *child)
{ {
struct entity_store *store = entity_get_store(parent); struct entity_store *store = entity_get_store(parent);
@ -268,6 +290,8 @@ void entity_link(struct entity *parent, struct entity *child)
last_child->next = child_handle; last_child->next = child_handle;
} }
parent->last = child_handle; parent->last = child_handle;
child->is_top = parent->is_root;
} }
void entity_unlink(struct entity *ent) void entity_unlink(struct entity *ent)
@ -275,7 +299,9 @@ void entity_unlink(struct entity *ent)
struct entity_store *store = entity_get_store(ent); struct entity_store *store = entity_get_store(ent);
ASSERT(!entity_handle_eq(ent->handle, store->root)); ASSERT(!entity_handle_eq(ent->handle, store->root));
struct entity *parent = entity_from_handle(store, ent->parent); struct entity_handle parent_handle = ent->parent;
struct entity *parent = entity_from_handle(store, parent_handle);
struct entity *prev = entity_from_handle(store, ent->prev); struct entity *prev = entity_from_handle(store, ent->prev);
struct entity *next = entity_from_handle(store, ent->next); struct entity *next = entity_from_handle(store, ent->next);
@ -290,12 +316,29 @@ void entity_unlink(struct entity *ent)
} else { } else {
parent->last = prev->handle; parent->last = prev->handle;
} }
ent->is_top = false;
ent->is_root = false;
/* Link children to grandparent */ /* Link children to grandparent */
if (ent->first.gen) { struct entity_handle child_first_handle = ent->first;
struct entity_handle parent_handle = parent->handle; if (child_first_handle.gen) {
for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) { struct entity_handle child_last_handle = ent->last;
struct entity_handle parent_last_handle = parent->last;
struct entity *parent_last = entity_from_handle(store, parent_last_handle);
if (parent_last->valid) {
parent_last->next = child_first_handle;
} else {
parent->first = child_first_handle;
}
parent->last = child_last_handle;
/* Set children parent & is_top */
b32 parent_is_root = parent->is_root;
for (struct entity *child = entity_from_handle(store, child_first_handle); child->valid; child = entity_from_handle(store, child->next)) {
child->parent = parent_handle; child->parent = parent_handle;
child->is_top = parent_is_root;
} }
} }
} }

View File

@ -45,7 +45,10 @@ struct entity {
u64 store_offset; u64 store_offset;
/* Is this the root entity */ /* Is this the root entity */
b32 root; b32 is_root;
/* Is the parent the root entity */
b32 is_top;
/* Tree */ /* Tree */
struct entity_handle parent; struct entity_handle parent;
@ -58,7 +61,7 @@ struct entity {
struct xform local_xform; /* Transform in relation to parent entity (or the world if entity has no parent) */ struct xform local_xform; /* Transform in relation to parent entity (or the world if entity has no parent) */
struct xform cached_global_xform; /* Calculated from entity tree */ struct xform cached_global_xform; /* Calculated from entity tree */
b32 cached_global_xform_clean; b32 cached_global_xform_dirty;
/* ====================================================================== */ /* ====================================================================== */
/* Physics */ /* Physics */
@ -78,8 +81,8 @@ struct entity {
struct string sprite_span_name; struct string sprite_span_name;
u32 sprite_tint; u32 sprite_tint;
struct xform sprite_xform; /* Sprite transform in relation to xform_world */ struct xform sprite_local_xform; /* Sprite transform in relation to xform_world */
struct xform sprite_xform_world; /* Calculated from world xform */ struct xform sprite_global_xform;
/* ====================================================================== */ /* ====================================================================== */
/* Animation */ /* Animation */
@ -208,7 +211,7 @@ 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 *entity_find_first_match_all(struct entity_store *store, struct entity_prop_array props);
/* Tree */ /* Tree */
void entity_link(struct entity *parent, struct entity *child); void entity_link_parent_child(struct entity *parent, struct entity *child);
void entity_unlink(struct entity *ent); void entity_unlink(struct entity *ent);
#endif #endif

View File

@ -130,6 +130,28 @@ INTERNAL void spawn_test_entities(void)
//entity_enable_prop(e, ENTITY_PROP_TEST); //entity_enable_prop(e, ENTITY_PROP_TEST);
} }
/* Child ent */
{
struct v2 pos = V2(0, 0);
struct v2 size = V2(1, 1);
f32 r = 0;
struct xform xf = XFORM_TRS(.t = pos, .r = r, .s = size);
struct entity *e = entity_alloc_child(player_ent);
entity_set_xform(e, xf);
e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase"));
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_tint = RGBA_32_F(1, 0, 0, 0.5);
entity_enable_prop(e, ENTITY_PROP_ANIMATING);
//entity_enable_prop(e, ENTITY_PROP_TEST);
}
/* Camera ent */ /* Camera ent */
{ {
struct entity *e = entity_alloc_top(G.world.entity_store); struct entity *e = entity_alloc_top(G.world.entity_store);
@ -207,9 +229,14 @@ INTERNAL void recalculate_world_xforms_recurse(struct entity *parent)
#else #else
INTERNAL void recalculate_world_xforms_recurse(struct entity *ent) INTERNAL void recalculate_world_xforms_recurse(struct entity *ent)
{ {
struct xform xform_global = entity_get_xform(ent);
//struct xform xform_local = entity_get_local_xform(ent);
(UNUSED)xform_global;
//(UNUSED)xform_local;
struct entity_store *store = G.world.entity_store; struct entity_store *store = G.world.entity_store;
struct xform sprite_xform_world = xform_mul(entity_get_xform(ent), ent->sprite_xform); struct xform sprite_global_xform = xform_mul(xform_global, ent->sprite_local_xform);
ent->sprite_xform_world = sprite_xform_world; ent->sprite_global_xform = sprite_global_xform;
for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) { for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) {
recalculate_world_xforms_recurse(child); recalculate_world_xforms_recurse(child);
} }
@ -269,7 +296,7 @@ INTERNAL void game_update(void)
logf_info("Clearing level"); logf_info("Clearing level");
for (u64 i = 0; i < G.world.entity_store->count; ++i) { for (u64 i = 0; i < G.world.entity_store->count; ++i) {
struct entity *ent = &G.world.entity_store->entities[i]; struct entity *ent = &G.world.entity_store->entities[i];
if (ent->valid && !ent->root) { if (ent->valid && !ent->is_root) {
entity_release(G.world.entity_store, ent); entity_release(G.world.entity_store, ent);
} }
} }
@ -293,7 +320,6 @@ INTERNAL void game_update(void)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
struct entity_handle root_handle = G.world.entity_store->root;
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);
/* ========================== * /* ========================== *
@ -303,7 +329,7 @@ INTERNAL void game_update(void)
/* Recalculate world xforms */ /* Recalculate world xforms */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &entities_array.entities[entity_index];
if (ent->valid && entity_handle_eq(ent->parent, root_handle)) { if (ent->valid && ent->is_top) {
recalculate_world_xforms_recurse(ent); recalculate_world_xforms_recurse(ent);
} }
} }
@ -350,7 +376,7 @@ INTERNAL void game_update(void)
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("pivot"), ent->animation_frame); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("pivot"), ent->animation_frame);
struct v2 sprite_size = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT); struct v2 sprite_size = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT);
struct v2 pivot_pos = v2_mul_v2(slice.center, sprite_size); struct v2 pivot_pos = v2_mul_v2(slice.center, sprite_size);
ent->sprite_xform = xform_with_scale(XFORM_POS(v2_neg(pivot_pos)), sprite_size); ent->sprite_local_xform = xform_with_scale(XFORM_POS(v2_neg(pivot_pos)), sprite_size);
} }
@ -361,7 +387,7 @@ INTERNAL void game_update(void)
/* Recalculate world xforms */ /* Recalculate world xforms */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &entities_array.entities[entity_index];
if (ent->valid && entity_handle_eq(ent->parent, root_handle)) { if (ent->valid && ent->is_top) {
recalculate_world_xforms_recurse(ent); recalculate_world_xforms_recurse(ent);
} }
} }
@ -378,7 +404,7 @@ INTERNAL void game_update(void)
if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) { if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) {
ent->test_initialized = true; ent->test_initialized = true;
ent->test_start_local_xform = entity_get_local_xform(ent); ent->test_start_local_xform = entity_get_local_xform(ent);
ent->test_start_sprite_xform = ent->sprite_xform; ent->test_start_sprite_xform = ent->sprite_local_xform;
} }
/* ========================== * /* ========================== *
@ -400,15 +426,15 @@ INTERNAL void game_update(void)
f32 forward_hold_angle_offset; f32 forward_hold_angle_offset;
{ {
struct xform xf_unrotated = xform_with_rotation(xf, 0); struct xform xf_unrotated = xform_with_rotation(xf, 0);
struct v2 hold_pos_unrotated = xform_mul_v2(xf_unrotated, xform_mul_v2(ent->sprite_xform, slice.center)); struct v2 hold_pos_unrotated = xform_mul_v2(xf_unrotated, xform_mul_v2(ent->sprite_local_xform, slice.center));
forward_hold_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_sub(hold_pos_unrotated, xf_unrotated.og)); forward_hold_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_sub(hold_pos_unrotated, xf_unrotated.og));
} }
struct v2 ent_pos = xf.og; struct v2 ent_pos = xf.og;
struct v2 focus_pos = v2_add(ent_pos, ent->focus); struct v2 focus_pos = v2_add(ent_pos, ent->focus);
struct v2 hold_pos = xform_mul_v2(xf, xform_mul_v2(ent->sprite_xform, slice.center)); struct v2 hold_pos = xform_mul_v2(xf, xform_mul_v2(ent->sprite_local_xform, slice.center));
struct v2 hold_dir = xform_basis_mul_v2(xf, xform_basis_mul_v2(ent->sprite_xform, slice.dir)); struct v2 hold_dir = xform_basis_mul_v2(xf, xform_basis_mul_v2(ent->sprite_local_xform, slice.dir));
struct v2 hold_ent_dir = v2_sub(ent_pos, hold_pos); struct v2 hold_ent_dir = v2_sub(ent_pos, hold_pos);
struct v2 focus_ent_dir = v2_sub(ent_pos, focus_pos); struct v2 focus_ent_dir = v2_sub(ent_pos, focus_pos);
@ -461,7 +487,7 @@ INTERNAL void game_update(void)
/* Recalculate world xforms */ /* Recalculate world xforms */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &entities_array.entities[entity_index];
if (ent->valid && entity_handle_eq(ent->parent, root_handle)) { if (ent->valid && ent->is_top) {
recalculate_world_xforms_recurse(ent); recalculate_world_xforms_recurse(ent);
} }
} }
@ -508,7 +534,7 @@ INTERNAL void game_update(void)
/* Recalculate world xforms */ /* Recalculate world xforms */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &entities_array.entities[entity_index];
if (ent->valid && entity_handle_eq(ent->parent, root_handle)) { if (ent->valid && ent->is_top) {
recalculate_world_xforms_recurse(ent); recalculate_world_xforms_recurse(ent);
} }
} }
@ -587,7 +613,7 @@ INTERNAL void game_update(void)
/* Recalculate world xforms */ /* Recalculate world xforms */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &entities_array.entities[entity_index];
if (ent->valid && entity_handle_eq(ent->parent, root_handle)) { if (ent->valid && ent->is_top) {
recalculate_world_xforms_recurse(ent); recalculate_world_xforms_recurse(ent);
} }
} }

View File

@ -461,8 +461,8 @@ INTERNAL void user_update(void)
e->player_acceleration = math_lerp(e0->player_acceleration, e1->player_acceleration, tick_blend); e->player_acceleration = math_lerp(e0->player_acceleration, e1->player_acceleration, tick_blend);
e->focus = v2_lerp(e0->focus, e1->focus, tick_blend); e->focus = v2_lerp(e0->focus, e1->focus, tick_blend);
e->sprite_xform = xform_lerp(e0->sprite_xform, e1->sprite_xform, tick_blend); e->sprite_local_xform = xform_lerp(e0->sprite_local_xform, e1->sprite_local_xform, tick_blend);
e->sprite_xform_world = xform_lerp(e0->sprite_xform_world, e1->sprite_xform_world, tick_blend); e->sprite_global_xform = xform_lerp(e0->sprite_global_xform, e1->sprite_global_xform, tick_blend);
e->animation_time_in_frame = math_lerp64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); e->animation_time_in_frame = math_lerp64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend);
e->animation_frame = (u32)math_round_to_int(math_lerp(e0->animation_frame, e1->animation_frame, tick_blend)); e->animation_frame = (u32)math_round_to_int(math_lerp(e0->animation_frame, e1->animation_frame, tick_blend));
@ -773,7 +773,7 @@ INTERNAL void user_update(void)
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &entities_array.entities[entity_index];
if (!ent->valid) continue; if (!ent->valid) continue;
if (ent->root) continue; if (ent->is_root) continue;
struct entity *parent = entity_from_handle(G.world.entity_store, ent->parent); struct entity *parent = entity_from_handle(G.world.entity_store, ent->parent);
@ -795,7 +795,7 @@ INTERNAL void user_update(void)
/* TODO: Fade in placeholder if texture isn't loaded */ /* TODO: Fade in placeholder if texture isn't loaded */
if (sheet->loaded) { if (sheet->loaded) {
struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, ent->animation_frame); struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, ent->animation_frame);
struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, ent->sprite_xform_world); struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, ent->sprite_global_xform);
struct draw_sprite_params params = DRAW_SPRITE_PARAMS(.sprite = sprite, .tint = ent->sprite_tint, .clip = frame.clip); struct draw_sprite_params params = DRAW_SPRITE_PARAMS(.sprite = sprite, .tint = ent->sprite_tint, .clip = frame.clip);
draw_sprite_quad(G.world_canvas, params, quad); draw_sprite_quad(G.world_canvas, params, quad);
} }
@ -852,7 +852,7 @@ INTERNAL void user_update(void)
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, ent->sprite); struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame);
struct v2 start = xform_mul_v2(ent->sprite_xform_world, slice.center); struct v2 start = xform_mul_v2(ent->sprite_global_xform, slice.center);
start = xform_mul_v2(G.world_view, start); start = xform_mul_v2(G.world_view, start);
struct v2 end = v2_add(xf.og, ent->focus); struct v2 end = v2_add(xf.og, ent->focus);
end = xform_mul_v2(G.world_view, end); end = xform_mul_v2(G.world_view, end);
@ -871,12 +871,12 @@ INTERNAL void user_update(void)
for (u32 j = 0; j < group->per_frame_count; ++j) { for (u32 j = 0; j < group->per_frame_count; ++j) {
struct sprite_sheet_slice slice = group->frame_slices[(ent->animation_frame * group->per_frame_count) + j]; struct sprite_sheet_slice slice = group->frame_slices[(ent->animation_frame * group->per_frame_count) + j];
struct v2 center = xform_mul_v2(ent->sprite_xform_world, slice.center); struct v2 center = xform_mul_v2(ent->sprite_global_xform, slice.center);
center = xform_mul_v2(G.world_view, center); center = xform_mul_v2(G.world_view, center);
if (!slice.has_ray) { if (!slice.has_ray) {
struct quad quad = quad_from_rect(slice.rect); struct quad quad = quad_from_rect(slice.rect);
quad = quad_mul_xform(quad, ent->sprite_xform_world); quad = quad_mul_xform(quad, ent->sprite_global_xform);
quad = quad_mul_xform(quad, G.world_view); quad = quad_mul_xform(quad, G.world_view);
draw_solid_quad_line(G.viewport_canvas, quad, 2, RGBA_32_F(1, 0, 0.5, 1)); draw_solid_quad_line(G.viewport_canvas, quad, 2, RGBA_32_F(1, 0, 0.5, 1));
} }
@ -884,7 +884,7 @@ INTERNAL void user_update(void)
draw_solid_circle(G.viewport_canvas, center, 3, RGBA_32_F(1, 0, 0, 1), 20); draw_solid_circle(G.viewport_canvas, center, 3, RGBA_32_F(1, 0, 0, 1), 20);
if (slice.has_ray) { if (slice.has_ray) {
struct v2 ray = xform_basis_mul_v2(ent->sprite_xform_world, slice.dir); struct v2 ray = xform_basis_mul_v2(ent->sprite_global_xform, slice.dir);
ray = xform_basis_mul_v2(G.world_view, ray); ray = xform_basis_mul_v2(G.world_view, ray);
ray = v2_mul(v2_norm(ray), 25); ray = v2_mul(v2_norm(ray), 25);
draw_solid_arrow_ray(G.viewport_canvas, center, ray, 2, 10, RGBA_32_F(1, 0, 0.5, 1)); draw_solid_arrow_ray(G.viewport_canvas, center, ray, 2, 10, RGBA_32_F(1, 0, 0.5, 1));
@ -894,7 +894,7 @@ INTERNAL void user_update(void)
} }
/* Draw hierarchy */ /* Draw hierarchy */
if (parent->valid && !parent->root) { if (parent->valid && !parent->is_root) {
u32 color = RGBA_32_F(0.6, 0.6, 1, 0.75); u32 color = RGBA_32_F(0.6, 0.6, 1, 0.75);
f32 thickness = 5; f32 thickness = 5;
f32 arrow_height = 15; f32 arrow_height = 15;