diff --git a/src/entity.c b/src/entity.c index 1dde5560..28648af8 100644 --- a/src/entity.c +++ b/src/entity.c @@ -12,8 +12,9 @@ READONLY struct entity_store _g_entity_store_nil = { /* 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, + .local_xform = XFORM_IDENT_NOCAST, + .cached_global_xform = XFORM_IDENT_NOCAST, + .cached_global_xform_clean = true, .sprite_xform = XFORM_IDENT_NOCAST, .sprite_tint = COLOR_WHITE }; @@ -32,6 +33,8 @@ struct entity_store *entity_store_alloc(void) struct entity *root = entity_alloc_unlinked(store); root->root = true; + root->cached_global_xform = root->local_xform; + root->cached_global_xform_clean = true; store->root = root->handle; return store; @@ -74,6 +77,7 @@ struct entity *entity_alloc_unlinked(struct entity_store *store) *entity = *entity_nil(); entity->handle = handle; entity->valid = true; + entity->cached_global_xform_clean = false; return entity; } @@ -174,36 +178,70 @@ 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) +{ + 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 { + break; + } + } +} + +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)) { + xf = ent->local_xform; + } else if (ent->cached_global_xform_clean) { + xf = ent->cached_global_xform; + } else { + 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; + } + + 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); + return xf; +} + struct xform entity_get_local_xform(struct entity *ent) { - return ent->xform; + return ent->local_xform; +} + +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); + + 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; } 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; + ent->local_xform = xf; + entity_mark_xform_dirty(entity_get_store(ent), ent); } /* ========================== * diff --git a/src/entity.h b/src/entity.h index 3ac87069..1cb67963 100644 --- a/src/entity.h +++ b/src/entity.h @@ -56,8 +56,9 @@ struct entity { /* ====================================================================== */ - struct xform xform; /* Transform in relation to parent entity (or the world if entity has no parent) */ - struct xform xform_world; /* Calculated from entity tree */ + 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; /* ====================================================================== */ /* Physics */ @@ -92,7 +93,7 @@ struct entity { /* ENTITY_PROP_TEST */ b32 test_initialized; - struct xform test_start_rel_xform; + struct xform test_start_local_xform; struct xform test_start_sprite_xform; /* ENTITY_PROP_TEST_SOUND_EMITTER */ @@ -108,7 +109,7 @@ struct entity { f32 camera_lerp; /* Rate at which camera xform approaches target xform */ u32 camera_lerp_continuity_gen; - struct xform camera_rel_xform_target; /* Calculated from camera_follow */ + struct xform camera_xform_target; /* Calculated from camera_follow */ u32 camera_applied_lerp_continuity_gen_plus_one; /* Calculated */ }; @@ -195,10 +196,10 @@ struct entity *entity_alloc_child(struct entity *parent); void entity_release(struct entity_store *store, struct entity *entity); /* Xform */ +struct xform entity_get_xform(struct entity *ent); struct xform entity_get_local_xform(struct entity *ent); +void entity_set_xform(struct entity *ent, struct xform xf); 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_store *entity_get_store(struct entity *ent); diff --git a/src/game.c b/src/game.c index d85bcfab..cb10de11 100644 --- a/src/game.c +++ b/src/game.c @@ -111,7 +111,7 @@ INTERNAL void spawn_test_entities(void) f32 r = 0; struct entity *e = entity_alloc_top(G.world.entity_store); - e->xform = XFORM_TRS(.t = pos, .r = r, .s = size); + entity_set_xform(e, 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"); @@ -133,7 +133,7 @@ INTERNAL void spawn_test_entities(void) /* Camera ent */ { struct entity *e = entity_alloc_top(G.world.entity_store); - e->xform = XFORM_IDENT; + entity_set_xform(e, XFORM_IDENT); entity_enable_prop(e, ENTITY_PROP_CAMERA); entity_enable_prop(e, ENTITY_PROP_CAMERA_ACTIVE); @@ -158,6 +158,13 @@ INTERNAL void publish_game_tick(void) sys_mutex_unlock(&lock); } + + + + + +/* TODO: Remove this(?) */ +#if 0 INTERNAL void recalculate_world_xforms_recurse(struct entity *parent) { struct temp_arena scratch = scratch_begin_no_conflict(); @@ -179,8 +186,8 @@ INTERNAL void recalculate_world_xforms_recurse(struct entity *parent) struct entity *child = node.entity; /* Calculate world xform */ - struct xform xform_world = xform_mul(node.parent_xform_world, child->xform); - child->xform_world = xform_world; + struct xform xform_world = xform_mul(node.parent_xform_world, child->_xform); + child->_xform_world = xform_world; /* Calculate sprite world xform */ struct xform sprite_xform_world = xform_mul(xform_world, child->sprite_xform); @@ -197,6 +204,22 @@ INTERNAL void recalculate_world_xforms_recurse(struct entity *parent) scratch_end(scratch); } +#else +INTERNAL void recalculate_world_xforms_recurse(struct entity *ent) +{ + 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; + for (struct entity *child = entity_from_handle(store, ent->first); child->valid; child = entity_from_handle(store, child->next)) { + recalculate_world_xforms_recurse(child); + } +} +#endif + + + + + INTERNAL void game_update(void) { @@ -354,7 +377,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 = entity_get_local_xform(ent); + ent->test_start_local_xform = entity_get_local_xform(ent); ent->test_start_sprite_xform = ent->sprite_xform; } @@ -366,7 +389,7 @@ INTERNAL void game_update(void) /* Update focus */ ent->focus = G.world.player_aim; - struct xform xf = entity_get_global_xform(ent); + struct xform xf = entity_get_xform(ent); /* Solve for final angle using law of sines */ f32 final_xf_angle; @@ -400,7 +423,7 @@ INTERNAL void game_update(void) } if (!F32_IS_NAN(final_xf_angle)) { - entity_set_global_xform(ent, xform_with_rotation(ent->xform_world, final_xf_angle)); + entity_set_xform(ent, xform_with_rotation(xf, final_xf_angle)); } } @@ -418,15 +441,15 @@ INTERNAL void game_update(void) (UNUSED)r; (UNUSED)s; - og = v2_add(og, ent->test_start_rel_xform.og); - r += xform_get_rotation(ent->test_start_rel_xform); - s = v2_add(s, xform_get_scale(ent->test_start_rel_xform)); + og = v2_add(og, ent->test_start_local_xform.og); + r += xform_get_rotation(ent->test_start_local_xform); + s = v2_add(s, xform_get_scale(ent->test_start_local_xform)); struct xform xf = entity_get_local_xform(ent); xf.og = og; xf= xform_with_rotation(xf, r); - xf= xform_with_scale(ent->xform, s); + xf= xform_with_scale(xf, s); entity_set_local_xform(ent, xf); } } @@ -472,9 +495,9 @@ INTERNAL void game_update(void) ent->velocity = v2_add(ent->velocity, a); /* Apply velocity to position */ - struct xform xf = entity_get_global_xform(ent); + struct xform xf = entity_get_xform(ent); xf.og = v2_add(xf.og, v2_mul(ent->velocity, dt)); - entity_set_global_xform(ent, xf); + entity_set_xform(ent, xf); } } @@ -502,10 +525,12 @@ INTERNAL void game_update(void) if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { struct entity *follow = entity_from_handle(G.world.entity_store, ent->camera_follow); + struct xform xf = entity_get_xform(ent); + if (entity_has_prop(follow, ENTITY_PROP_PLAYER_CONTROLLED)) { f32 aspect_ratio = 1.0; { - struct xform quad_xf = xform_mul(entity_get_global_xform(ent), ent->camera_quad_xform); + struct xform quad_xf = xform_mul(entity_get_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; @@ -514,20 +539,21 @@ 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(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; + struct v2 camera_focus_pos = v2_add(entity_get_xform(follow).og, camera_focus_dir); + ent->camera_xform_target = xf; + ent->camera_xform_target.og = camera_focus_pos; } /* Lerp camera */ if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) { f32 t = 1 - math_pow(2.f, -20.f * (f32)G.world.dt); - ent->xform = xform_lerp(ent->xform, ent->camera_rel_xform_target, t); + xf = xform_lerp(xf, ent->camera_xform_target, t); } else { /* Skip lerp */ - ent->xform = ent->camera_rel_xform_target; + xf = ent->camera_xform_target; } ent->camera_applied_lerp_continuity_gen_plus_one = ent->camera_lerp_continuity_gen + 1; + entity_set_xform(ent, xf); } /* ========================== * @@ -538,7 +564,7 @@ INTERNAL void game_update(void) struct mixer_desc desc = ent->sound_desc; desc.speed = G.world.timescale; - desc.pos = ent->xform_world.og; + desc.pos = entity_get_xform(ent).og; struct sound *sound = sound_load_async(ent->sound_name, 0); b32 played = ent->sound_handle.gen != 0; if (sound) { diff --git a/src/user.c b/src/user.c index 585cc512..5e065aa2 100644 --- a/src/user.c +++ b/src/user.c @@ -376,7 +376,9 @@ INTERNAL void debug_draw_movement(struct entity *ent) u32 color_vel = RGBA_32_F(1, 0.5, 0, 1); u32 color_acc = RGBA_32_F(1, 1, 0.5, 1); - struct v2 pos = xform_mul_v2(G.world_view, ent->xform_world.og); + struct xform xf = entity_get_xform(ent); + + struct v2 pos = xform_mul_v2(G.world_view, xf.og); struct v2 vel_ray = xform_basis_mul_v2(G.world_view, ent->velocity); struct v2 acc_ray = xform_basis_mul_v2(G.world_view, ent->acceleration); @@ -451,8 +453,8 @@ INTERNAL void user_update(void) struct entity *e1 = &t1_entities.entities[i]; struct entity *e = &world_entities.entities[i]; if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) { - e->xform = xform_lerp(e0->xform, e1->xform, tick_blend); - e->xform_world = xform_lerp(e0->xform_world, e1->xform_world, tick_blend); + e->local_xform = xform_lerp(e0->local_xform, e1->local_xform, tick_blend); + e->cached_global_xform = xform_lerp(e0->cached_global_xform, e1->cached_global_xform, tick_blend); e->acceleration = v2_lerp(e0->acceleration, e1->acceleration, tick_blend); e->velocity = v2_lerp(e0->velocity, e1->velocity, tick_blend); @@ -465,7 +467,7 @@ INTERNAL void user_update(void) e->animation_frame = (u32)math_round_to_int(math_lerp(e0->animation_frame, e1->animation_frame, tick_blend)); e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, tick_blend); - e->camera_rel_xform_target = xform_lerp(e0->camera_rel_xform_target, e1->camera_rel_xform_target, tick_blend); + e->camera_xform_target = xform_lerp(e0->camera_xform_target, e1->camera_xform_target, tick_blend); } } } @@ -613,7 +615,7 @@ INTERNAL void user_update(void) /* Determine viewport size by camera & window dimensions */ f32 aspect_ratio = 1.0; { - struct xform quad_xf = xform_mul(active_camera->xform_world, active_camera->camera_quad_xform); + struct xform quad_xf = xform_mul(entity_get_xform(active_camera), active_camera->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; @@ -671,13 +673,15 @@ INTERNAL void user_update(void) G.world_view = xform_translate(G.world_view, v2_neg(world_cursor)); } } else { - struct v2 center = active_camera->xform_world.og; - f32 rot = xform_get_rotation(active_camera->xform_world); + struct xform xf = entity_get_xform(active_camera); + + struct v2 center = xf.og; + f32 rot = xform_get_rotation(xf); /* Scale view into viewport based on camera size */ struct v2 size = G.viewport_size; { - struct xform quad_xf = xform_mul(active_camera->xform_world, active_camera->camera_quad_xform); + struct xform quad_xf = xform_mul(xf, active_camera->camera_quad_xform); struct v2 camera_size = xform_get_scale(quad_xf); if (!v2_eq(camera_size, V2(0, 0))) { size = v2_div_v2(size, camera_size); @@ -771,6 +775,11 @@ INTERNAL void user_update(void) if (!ent->valid) continue; if (ent->root) continue; + struct entity *parent = entity_from_handle(G.world.entity_store, ent->parent); + + struct xform xf = entity_get_xform(ent); + struct xform parent_xf = entity_get_xform(parent); + b32 skip_debug_draw = !G.debug_camera && ent == active_camera; b32 skip_debug_draw_transform = ent == active_camera; @@ -836,7 +845,7 @@ INTERNAL void user_update(void) debug_draw_movement(ent); if (!skip_debug_draw_transform) { - debug_draw_xform(ent->xform_world); + debug_draw_xform(xf); } /* Draw aim arrow */ @@ -845,7 +854,7 @@ INTERNAL void user_update(void) 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); start = xform_mul_v2(G.world_view, start); - struct v2 end = v2_add(ent->xform.og, ent->focus); + struct v2 end = v2_add(xf.og, ent->focus); end = xform_mul_v2(G.world_view, end); draw_solid_arrow_line(G.viewport_canvas, start, end, 3, 10, RGBA_32_F(1, 1, 1, 0.5)); } @@ -885,14 +894,13 @@ INTERNAL void user_update(void) } /* Draw hierarchy */ - struct entity *parent = entity_from_handle(G.world.entity_store, ent->parent); if (parent->valid && !parent->root) { u32 color = RGBA_32_F(0.6, 0.6, 1, 0.75); f32 thickness = 5; f32 arrow_height = 15; - struct v2 start = xform_mul_v2(G.world_view, ent->xform_world.og); - struct v2 end = xform_mul_v2(G.world_view, parent->xform_world.og); + struct v2 start = xform_mul_v2(G.world_view, xf.og); + struct v2 end = xform_mul_v2(G.world_view, parent_xf.og); draw_solid_arrow_line(G.viewport_canvas, start, end, thickness, arrow_height, color); } @@ -902,7 +910,7 @@ INTERNAL void user_update(void) f32 thickness = 3; - struct xform quad_xf = xform_mul(ent->xform_world, ent->camera_quad_xform); + struct xform quad_xf = xform_mul(xf, ent->camera_quad_xform); struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, quad_xf); quad = quad_mul_xform(quad, G.world_view); @@ -985,7 +993,7 @@ INTERNAL void user_update(void) /* Queue aim cmd */ if (!G.debug_camera) { - struct v2 input_aim = v2_sub(G.world_cursor, player->xform_world.og); + struct v2 input_aim = v2_sub(G.world_cursor, entity_get_xform(player).og); queue_game_cmd(&cmd_list, (struct game_cmd) { .kind = GAME_CMD_KIND_PLAYER_AIM, .aim = input_aim,