fixed skew & child entity interpolation (still drifting skew bug due to accumulated sin & atan2 imprecision)
This commit is contained in:
parent
1e8b6973c1
commit
2453ceb239
109
src/game.c
109
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");
|
||||
|
||||
44
src/math.h
44
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)
|
||||
|
||||
11
src/user.c
11
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);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user