From 2453ceb23988c3483adb1fb01147a5e37ac310d4 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 27 Aug 2024 18:35:43 -0500 Subject: [PATCH] fixed skew & child entity interpolation (still drifting skew bug due to accumulated sin & atan2 imprecision) --- src/game.c | 109 +++++++++++++++++++++++++---------------------------- src/math.h | 44 ++++++++++----------- src/user.c | 11 ++++-- 3 files changed, 81 insertions(+), 83 deletions(-) diff --git a/src/game.c b/src/game.c index 2bec7231..2525538a 100644 --- a/src/game.c +++ b/src/game.c @@ -124,7 +124,7 @@ INTERNAL void spawn_test_entities(void) struct v2 pos = V2(-1, -1); struct v2 size = V2(1, 1); f32 r = 0; - f32 skew = 0; + f32 skew = PI / 4; struct entity *e = entity_alloc(root); @@ -353,6 +353,9 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) struct entity *root = entity_from_handle(store, store->root); struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); + (UNUSED)dt; + (UNUSED)time; + /* ========================== * * Spawn test entities * ========================== */ @@ -568,6 +571,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; if (!entity_has_prop(ent, ENTITY_PROP_TEST)) continue; +#if 0 if (!ent->test_initialized) { ent->test_initialized = true; ent->test_start_local_xform = entity_get_local_xform(ent); @@ -578,6 +582,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); f32 r = t * 3; struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); + f32 skew = t * PI / 10; (UNUSED)og; (UNUSED)r; (UNUSED)s; @@ -585,12 +590,31 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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)); + skew += xform_get_skew(ent->test_start_local_xform); struct xform xf = entity_get_local_xform(ent); xf.og = og; xf = xform_rotated_to(xf, r); xf = xform_scaled_to(xf, s); + xf = xform_skewed_to(xf, skew); entity_set_local_xform(ent, xf); +#else + f32 t = (f32)time; + struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); + f32 rot = t * PI / 3; + struct v2 scale = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); + f32 skew = t * PI / 10; + (UNUSED)og; + (UNUSED)rot; + (UNUSED)scale; + (UNUSED)skew; + + struct xform xf = entity_get_local_xform(ent); + //xf = xform_rotated_to(xf, rot); + //xf = xform_scaled_to(xf, scale); + xf = xform_skewed_to(xf, skew); + entity_set_local_xform(ent, xf); +#endif } /* ========================== * @@ -704,7 +728,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } /* ========================== * - * Integrate forces & impulses + * Integrate forces * ========================== */ for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { @@ -735,6 +759,19 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) entity_set_xform(ent, xf); } + /* ========================== * + * Release forces + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + + if (entity_has_prop(ent, ENTITY_PROP_FORCE) || entity_has_prop(ent, ENTITY_PROP_IMPULSE)) { + entity_enable_prop(ent, ENTITY_PROP_RELEASE); + } + } + /* ========================== * * Collision * ========================== */ @@ -890,22 +927,20 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) 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_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; - } + f32 aspect_ratio = 1.0; + { + 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; } - f32 ratio_y = 0.33f; - f32 ratio_x = ratio_y / aspect_ratio; - struct v2 camera_focus_dir = v2_mul_v2(follow->control.focus, V2(ratio_x, ratio_y)); - 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; } + f32 ratio_y = 0.33f; + f32 ratio_x = ratio_y / aspect_ratio; + struct v2 camera_focus_dir = v2_mul_v2(follow->control.focus, V2(ratio_x, ratio_y)); + 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) { @@ -985,48 +1020,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) * Publish tick * ========================== */ - /* Update cached global xforms to ensure they're accurate in published tick */ - { - struct temp_arena temp = arena_temp_begin(scratch.arena); - - struct stack_node { - struct entity *entity; - struct xform parent_global_xform; - }; - - /* Depth first iteration */ - *arena_push(temp.arena, struct stack_node) = (struct stack_node) { .entity = root, .parent_global_xform = XFORM_IDENT }; - u64 stack_count = 1; - while (stack_count > 0) { - /* Pull from top of stack */ - struct stack_node node; - arena_pop(temp.arena, struct stack_node, &node); - --stack_count; - - struct entity *child = node.entity; - - /* Calculate global xform */ - struct xform xf; - if (child->cached_global_xform_dirty) { - xf = xform_mul(node.parent_global_xform, child->local_xform); - child->cached_global_xform = xf; - child->cached_global_xform_dirty = false; - } else { - xf = child->cached_global_xform; - } - - /* Append sub-children to stack */ - struct entity *subchild = entity_from_handle(store, child->last); - while (subchild->valid) { - *arena_push(temp.arena, struct stack_node) = (struct stack_node) { .entity = subchild, .parent_global_xform = xf }; - ++stack_count; - subchild = entity_from_handle(store, subchild->prev); - } - } - - arena_temp_end(temp); - } - /* Publish tick */ publish_game_tick(); __profframe("Game"); diff --git a/src/math.h b/src/math.h index 716a7adb..20f8a888 100644 --- a/src/math.h +++ b/src/math.h @@ -627,7 +627,8 @@ INLINE struct v2 v2_rotated(struct v2 v, f32 a) INLINE struct v2 v2_from_angle(f32 a) { - return V2(math_cos(a), math_sin(a)); + /* TODO: More precise trig functions to avoid unnecessary normalization */ + return v2_norm(V2(math_cos(a), math_sin(a))); } INLINE f32 v2_angle(struct v2 v) @@ -743,10 +744,11 @@ INLINE f32 xform_get_determinant(struct xform xf); INLINE struct v2 xform_get_scale(struct xform xf); INLINE f32 xform_get_skew(struct xform xf); INLINE struct xform xform_skewed_to(struct xform xf, f32 s); +INLINE f32 xform_get_rotation(struct xform xf); INLINE b32 xform_eq(struct xform xf1, struct xform xf2) { - return v2_eq(xf1.bx, xf2.bx) && v2_eq(xf1.by, xf2.by) && v2_eq(xf1.og, xf2.og); + return v2_eq(xf1.og, xf2.og) && v2_eq(xf1.bx, xf2.bx) && v2_eq(xf1.by, xf2.by); } INLINE struct xform xform_from_pos(struct v2 v) @@ -814,19 +816,6 @@ INLINE struct xform xform_scaled_to(struct xform xf, struct v2 s) return xf; } -INLINE struct xform xform_skewed_to(struct xform xf, f32 s) -{ - - i32 det_sign = math_fsign(xform_get_determinant(xf)); - struct v2 by = xf.by; - f32 len = v2_len(by); - /* TODO: Is norm & mul by len really necessary? */ - by = v2_mul(v2_norm(v2_rotated(xf.bx, PI * 0.5f + s)), len); - by = v2_mul(by, det_sign); - xf.by = by; - return xf; -} - INLINE struct xform xform_trs(struct xform xf, struct trs trs) { xf = xform_translated(xf, trs.t); @@ -855,10 +844,16 @@ INLINE struct xform xform_trs_pivot_rs(struct xform xf, struct trs trs, struct v INLINE struct xform xform_lerp(struct xform a, struct xform b, f32 t) { - struct xform res; - res.og = v2_lerp(a.og, b.og, t); - res.bx = v2_lerp(a.bx, b.bx, t); - res.by = v2_lerp(a.by, b.by, t); + struct v2 og = v2_lerp(a.og, b.og, t); + f32 rot = math_lerp_angle(xform_get_rotation(a), xform_get_rotation(b), t); + struct v2 scale = v2_lerp(xform_get_scale(a), xform_get_scale(b), t); + f32 skew = math_lerp_angle(xform_get_skew(a), xform_get_skew(b), t); + + struct xform res = xform_from_pos(og); + res = xform_rotated(res, rot); + res = xform_scaled(res, scale); + res = xform_skewed_to(res, skew); + return res; } @@ -937,9 +932,14 @@ INLINE f32 xform_get_determinant(struct xform xf) INLINE f32 xform_get_skew(struct xform xf) { - i32 det_sign = math_fsign(xform_get_determinant(xf)); - f32 dot = v2_dot(v2_norm(xf.bx), v2_norm(xf.by)) * det_sign; - return math_acos(dot) - (PI / 2.0f); + return v2_angle_from_dirs(v2_norm(xf.bx), v2_norm(xf.by)) - PI / 2; +} + +INLINE struct xform xform_skewed_to(struct xform xf, f32 s) +{ + f32 angle = v2_angle(xf.bx) + s + (PI / 2); + xf.by = v2_mul(v2_from_angle(angle), v2_len(xf.by)); + return xf; } INLINE struct v2 xform_get_right(struct xform xf) diff --git a/src/user.c b/src/user.c index 93291c39..a35d486c 100644 --- a/src/user.c +++ b/src/user.c @@ -360,7 +360,7 @@ INTERNAL void debug_draw_xform(struct xform xf) struct v2 pos = xform_mul_v2(G.world_view, xf.og); struct v2 x_ray = xform_basis_mul_v2(G.world_view, xform_get_right(xf)); - struct v2 y_ray = xform_basis_mul_v2(G.world_view, xform_get_up(xf)); + struct v2 y_ray = xform_basis_mul_v2(G.world_view, xform_get_down(xf)); f32 ray_scale = 1; x_ray = v2_mul(x_ray, ray_scale); @@ -451,14 +451,19 @@ INTERNAL void user_update(void) struct entity *e = &store->entities[i]; struct entity *e0 = &t0->entity_store->entities[i]; struct entity *e1 = &t1->entity_store->entities[i]; - ASSERT(!e->valid || e->cached_global_xform_dirty == false); /* Game thread should have cached all global xforms before publishing */ if (e0->valid && e1->valid && entity_has_prop(e0, ENTITY_PROP_ACTIVE) && entity_has_prop(e1, ENTITY_PROP_ACTIVE) && e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) { 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); + + if (e->is_top) { + /* TODO: Cache parent & child xforms in game thread */ + struct xform e0_xf = entity_get_xform(e0); + struct xform e1_xf = entity_get_xform(e1); + entity_set_xform(e, xform_lerp(e0_xf, e1_xf, tick_blend)); + } e->verlet_xform = xform_lerp(e0->verlet_xform, e1->verlet_xform, tick_blend);