fixed skew & child entity interpolation (still drifting skew bug due to accumulated sin & atan2 imprecision)

This commit is contained in:
jacob 2024-08-27 18:35:43 -05:00
parent 1e8b6973c1
commit 2453ceb239
3 changed files with 81 additions and 83 deletions

View File

@ -124,7 +124,7 @@ INTERNAL void spawn_test_entities(void)
struct v2 pos = V2(-1, -1); struct v2 pos = V2(-1, -1);
struct v2 size = V2(1, 1); struct v2 size = V2(1, 1);
f32 r = 0; f32 r = 0;
f32 skew = 0; f32 skew = PI / 4;
struct entity *e = entity_alloc(root); 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 entity *root = entity_from_handle(store, store->root);
struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
(UNUSED)dt;
(UNUSED)time;
/* ========================== * /* ========================== *
* Spawn test entities * 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 (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue;
if (!entity_has_prop(ent, ENTITY_PROP_TEST)) continue; if (!entity_has_prop(ent, ENTITY_PROP_TEST)) continue;
#if 0
if (!ent->test_initialized) { if (!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);
@ -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); struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3);
f32 r = t * 3; f32 r = t * 3;
struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1);
f32 skew = t * PI / 10;
(UNUSED)og; (UNUSED)og;
(UNUSED)r; (UNUSED)r;
(UNUSED)s; (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); og = v2_add(og, ent->test_start_local_xform.og);
r += xform_get_rotation(ent->test_start_local_xform); r += xform_get_rotation(ent->test_start_local_xform);
s = v2_add(s, xform_get_scale(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); struct xform xf = entity_get_local_xform(ent);
xf.og = og; xf.og = og;
xf = xform_rotated_to(xf, r); xf = xform_rotated_to(xf, r);
xf = xform_scaled_to(xf, s); xf = xform_scaled_to(xf, s);
xf = xform_skewed_to(xf, skew);
entity_set_local_xform(ent, xf); 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) { 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); 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 * Collision
* ========================== */ * ========================== */
@ -890,7 +927,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
struct xform xf = entity_get_xform(ent); struct xform xf = entity_get_xform(ent);
if (entity_has_prop(follow, ENTITY_PROP_PLAYER_CONTROLLED)) {
f32 aspect_ratio = 1.0; f32 aspect_ratio = 1.0;
{ {
struct xform quad_xf = xform_mul(entity_get_xform(ent), ent->camera_quad_xform); struct xform quad_xf = xform_mul(entity_get_xform(ent), ent->camera_quad_xform);
@ -905,7 +941,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
struct v2 camera_focus_pos = v2_add(entity_get_xform(follow).og, camera_focus_dir); struct v2 camera_focus_pos = v2_add(entity_get_xform(follow).og, camera_focus_dir);
ent->camera_xform_target = xf; ent->camera_xform_target = xf;
ent->camera_xform_target.og = camera_focus_pos; ent->camera_xform_target.og = camera_focus_pos;
}
/* Lerp camera */ /* Lerp camera */
if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) { 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 * 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 tick */
publish_game_tick(); publish_game_tick();
__profframe("Game"); __profframe("Game");

View File

@ -627,7 +627,8 @@ INLINE struct v2 v2_rotated(struct v2 v, f32 a)
INLINE struct v2 v2_from_angle(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) 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 struct v2 xform_get_scale(struct xform xf);
INLINE f32 xform_get_skew(struct xform xf); INLINE f32 xform_get_skew(struct xform xf);
INLINE struct xform xform_skewed_to(struct xform xf, f32 s); 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) 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) 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; 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) INLINE struct xform xform_trs(struct xform xf, struct trs trs)
{ {
xf = xform_translated(xf, trs.t); 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) INLINE struct xform xform_lerp(struct xform a, struct xform b, f32 t)
{ {
struct xform res; struct v2 og = v2_lerp(a.og, b.og, t);
res.og = v2_lerp(a.og, b.og, t); f32 rot = math_lerp_angle(xform_get_rotation(a), xform_get_rotation(b), t);
res.bx = v2_lerp(a.bx, b.bx, t); struct v2 scale = v2_lerp(xform_get_scale(a), xform_get_scale(b), t);
res.by = v2_lerp(a.by, b.by, 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; return res;
} }
@ -937,9 +932,14 @@ INLINE f32 xform_get_determinant(struct xform xf)
INLINE f32 xform_get_skew(struct xform xf) INLINE f32 xform_get_skew(struct xform xf)
{ {
i32 det_sign = math_fsign(xform_get_determinant(xf)); return v2_angle_from_dirs(v2_norm(xf.bx), v2_norm(xf.by)) - PI / 2;
f32 dot = v2_dot(v2_norm(xf.bx), v2_norm(xf.by)) * det_sign; }
return math_acos(dot) - (PI / 2.0f);
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) INLINE struct v2 xform_get_right(struct xform xf)

View File

@ -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 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 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; f32 ray_scale = 1;
x_ray = v2_mul(x_ray, ray_scale); 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 *e = &store->entities[i];
struct entity *e0 = &t0->entity_store->entities[i]; struct entity *e0 = &t0->entity_store->entities[i];
struct entity *e1 = &t1->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 if (e0->valid && e1->valid
&& entity_has_prop(e0, ENTITY_PROP_ACTIVE) && entity_has_prop(e1, ENTITY_PROP_ACTIVE) && entity_has_prop(e0, ENTITY_PROP_ACTIVE) && entity_has_prop(e1, ENTITY_PROP_ACTIVE)
&& e0->handle.gen == e1->handle.gen && e0->handle.gen == e1->handle.gen
&& e0->continuity_gen == e1->continuity_gen) { && e0->continuity_gen == e1->continuity_gen) {
e->local_xform = xform_lerp(e0->local_xform, e1->local_xform, 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);
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); e->verlet_xform = xform_lerp(e0->verlet_xform, e1->verlet_xform, tick_blend);