diff --git a/src/entity.c b/src/entity.c index 28648af8..b4202045 100644 --- a/src/entity.c +++ b/src/entity.c @@ -14,8 +14,16 @@ READONLY struct entity_store _g_entity_store_nil = { READONLY struct entity _g_entity_nil = { .local_xform = XFORM_IDENT_NOCAST, .cached_global_xform = XFORM_IDENT_NOCAST, - .cached_global_xform_clean = true, - .sprite_xform = XFORM_IDENT_NOCAST, + .sprite_local_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 }; @@ -32,9 +40,10 @@ struct entity_store *entity_store_alloc(void) ASSERT((u64)store->entities - (u64)store == STORE_ENTITIES_OFFSET); /* Offset must be correct */ struct entity *root = entity_alloc_unlinked(store); - root->root = true; - root->cached_global_xform = root->local_xform; - root->cached_global_xform_clean = true; + root->is_root = true; + root->local_xform = XFORM_IDENT; + root->cached_global_xform = XFORM_IDENT; + root->cached_global_xform_dirty = false; store->root = root->handle; return store; @@ -74,10 +83,8 @@ struct entity *entity_alloc_unlinked(struct entity_store *store) entity = arena_push(&store->arena, struct entity); handle = (struct entity_handle) { .gen = 1, .idx = store->count++ }; } - *entity = *entity_nil(); + *entity = g_entity_default; entity->handle = handle; - entity->valid = true; - entity->cached_global_xform_clean = false; return entity; } @@ -85,7 +92,7 @@ struct entity *entity_alloc_top(struct entity_store *store) { struct entity *e = entity_alloc_unlinked(store); struct entity *root = entity_from_handle(store, store->root); - entity_link(root, e); + entity_link_parent_child(root, 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 *e = entity_alloc_unlinked(store); - entity_link(parent, e); + entity_link_parent_child(parent, 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 */ - if (entity->first.gen) { - for (struct entity *child = entity_from_handle(store, entity->first); child->valid; child = entity_from_handle(store, child->next)) { + if (ent->first.gen) { + for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) { 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 * ========================== */ -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)) { - if (child->cached_global_xform_clean) { - entity_mark_xform_dirty(store, ent); - } else { + if (child->cached_global_xform_dirty) { 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) { - struct entity_handle parent_handle = ent->parent; - struct xform xf; - if (entity_handle_eq(parent_handle, store->root)) { + if (ent->is_top) { xf = ent->local_xform; - } else if (ent->cached_global_xform_clean) { + } else if (!ent->cached_global_xform_dirty) { xf = ent->cached_global_xform; } else { + struct entity_handle parent_handle = ent->parent; struct entity *parent = entity_from_handle(store, parent_handle); xf = entity_get_xform_w_store(store, parent); xf = xform_mul(xf, ent->local_xform); ent->cached_global_xform = xf; - ent->cached_global_xform_clean = true; + ent->cached_global_xform_dirty = false; } - return xf; } struct xform entity_get_xform(struct entity *ent) { - struct entity_store *store = entity_get_store(ent); - struct xform xf = entity_get_xform_w_store(store, 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 *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; } @@ -224,31 +243,34 @@ struct xform entity_get_local_xform(struct entity *ent) 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 *parent = entity_from_handle(store, ent->parent); + if (ent->is_top) { + ent->local_xform = xf; + } else { + /* Update child local xform */ + struct entity *parent = entity_from_handle(store, ent->parent); + struct xform parent_global = entity_get_xform_w_store(store, parent); + ent->local_xform = xform_mul(xform_invert(parent_global), xf); - struct xform child_global = xf; - - 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 = child_local; - ent->cached_global_xform = child_global; - ent->cached_global_xform_clean = true; + /* Set child global xform */ + ent->cached_global_xform = xf; + ent->cached_global_xform_dirty = false; + } + entity_mark_child_xforms_dirty(store, ent); } void entity_set_local_xform(struct entity *ent, struct 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 * ========================== */ -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); @@ -268,6 +290,8 @@ void entity_link(struct entity *parent, struct entity *child) last_child->next = child_handle; } parent->last = child_handle; + + child->is_top = parent->is_root; } void entity_unlink(struct entity *ent) @@ -275,7 +299,9 @@ void entity_unlink(struct entity *ent) struct entity_store *store = entity_get_store(ent); 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 *next = entity_from_handle(store, ent->next); @@ -290,12 +316,29 @@ void entity_unlink(struct entity *ent) } else { parent->last = prev->handle; } + ent->is_top = false; + ent->is_root = false; /* Link children to grandparent */ - if (ent->first.gen) { - struct entity_handle parent_handle = parent->handle; - for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) { + struct entity_handle child_first_handle = ent->first; + if (child_first_handle.gen) { + 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->is_top = parent_is_root; } } } diff --git a/src/entity.h b/src/entity.h index 1cb67963..867f15e4 100644 --- a/src/entity.h +++ b/src/entity.h @@ -45,7 +45,10 @@ struct entity { u64 store_offset; /* Is this the root entity */ - b32 root; + b32 is_root; + + /* Is the parent the root entity */ + b32 is_top; /* Tree */ 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 cached_global_xform; /* Calculated from entity tree */ - b32 cached_global_xform_clean; + b32 cached_global_xform_dirty; /* ====================================================================== */ /* Physics */ @@ -78,8 +81,8 @@ struct entity { struct string sprite_span_name; u32 sprite_tint; - struct xform sprite_xform; /* Sprite transform in relation to xform_world */ - struct xform sprite_xform_world; /* Calculated from world xform */ + struct xform sprite_local_xform; /* Sprite transform in relation to xform_world */ + struct xform sprite_global_xform; /* ====================================================================== */ /* 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); /* 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); #endif diff --git a/src/game.c b/src/game.c index cb10de11..0c49d3fd 100644 --- a/src/game.c +++ b/src/game.c @@ -130,6 +130,28 @@ INTERNAL void spawn_test_entities(void) //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 */ { struct entity *e = entity_alloc_top(G.world.entity_store); @@ -207,9 +229,14 @@ INTERNAL void recalculate_world_xforms_recurse(struct entity *parent) #else 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 xform sprite_xform_world = xform_mul(entity_get_xform(ent), ent->sprite_xform); - ent->sprite_xform_world = sprite_xform_world; + struct xform sprite_global_xform = xform_mul(xform_global, ent->sprite_local_xform); + 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)) { recalculate_world_xforms_recurse(child); } @@ -269,7 +296,7 @@ INTERNAL void game_update(void) logf_info("Clearing level"); for (u64 i = 0; i < G.world.entity_store->count; ++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); } } @@ -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); /* ========================== * @@ -303,7 +329,7 @@ INTERNAL void game_update(void) /* Recalculate world xforms */ for (u64 entity_index = 0; entity_index < entities_array.count; ++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); } } @@ -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 v2 sprite_size = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT); 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 */ for (u64 entity_index = 0; entity_index < entities_array.count; ++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); } } @@ -378,7 +404,7 @@ INTERNAL void game_update(void) if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) { ent->test_initialized = true; 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; { 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)); } struct v2 ent_pos = xf.og; 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 focus_ent_dir = v2_sub(ent_pos, focus_pos); @@ -461,7 +487,7 @@ INTERNAL void game_update(void) /* Recalculate world xforms */ for (u64 entity_index = 0; entity_index < entities_array.count; ++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); } } @@ -508,7 +534,7 @@ INTERNAL void game_update(void) /* Recalculate world xforms */ for (u64 entity_index = 0; entity_index < entities_array.count; ++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); } } @@ -587,7 +613,7 @@ INTERNAL void game_update(void) /* Recalculate world xforms */ for (u64 entity_index = 0; entity_index < entities_array.count; ++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); } } diff --git a/src/user.c b/src/user.c index 5e065aa2..4bfdfc3c 100644 --- a/src/user.c +++ b/src/user.c @@ -461,8 +461,8 @@ INTERNAL void user_update(void) e->player_acceleration = math_lerp(e0->player_acceleration, e1->player_acceleration, 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_xform_world = xform_lerp(e0->sprite_xform_world, e1->sprite_xform_world, tick_blend); + e->sprite_local_xform = xform_lerp(e0->sprite_local_xform, e1->sprite_local_xform, 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_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]; 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); @@ -795,7 +795,7 @@ INTERNAL void user_update(void) /* TODO: Fade in placeholder if texture isn't loaded */ if (sheet->loaded) { 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); 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)) { 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 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); struct v2 end = v2_add(xf.og, ent->focus); 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) { 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); if (!slice.has_ray) { 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); 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); 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 = 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)); @@ -894,7 +894,7 @@ INTERNAL void user_update(void) } /* 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); f32 thickness = 5; f32 arrow_height = 15;