From e91a66f5b34032f30f6a1ccea7246f272ba68a62 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 13 Mar 2024 23:08:18 -0500 Subject: [PATCH] xform movement & focus directions by world view basis --- src/entity.c | 23 +++++- src/entity.h | 11 ++- src/game.c | 2 +- src/math.h | 7 ++ src/user.c | 201 ++++++++++++++++++++++++++------------------------- 5 files changed, 143 insertions(+), 101 deletions(-) diff --git a/src/entity.c b/src/entity.c index 2359ca37..c2388ff3 100644 --- a/src/entity.c +++ b/src/entity.c @@ -88,7 +88,7 @@ struct entity *entity_from_handle(struct entity_store *store, struct entity_hand return entity_nil(); } -struct entity *entity_find_first_with_prop(struct entity_store *store, enum entity_prop prop) +struct entity *entity_find_first_match_one(struct entity_store *store, enum entity_prop prop) { struct entity_array entities_array = entity_store_as_array(store); for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { @@ -100,6 +100,27 @@ struct entity *entity_find_first_with_prop(struct entity_store *store, enum enti return entity_nil(); } +struct entity *entity_find_first_match_all(struct entity_store *store, struct entity_prop_array props) +{ + struct entity_array entities_array = entity_store_as_array(store); + for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { + struct entity *ent = &entities_array.entities[entity_index]; + if (ent->valid) { + b32 all = true; + for (u64 i = 0; i < props.count; ++i) { + if (!entity_has_prop(ent, props.props[i])) { + all = false; + break; + } + } + if (all) { + return ent; + } + } + } + return entity_nil(); +} + /* ========================== * * Tree * ========================== */ diff --git a/src/entity.h b/src/entity.h index d632bd9c..4c837d92 100644 --- a/src/entity.h +++ b/src/entity.h @@ -10,6 +10,7 @@ enum entity_prop { ENTITY_PROP_ANIMATING, ENTITY_PROP_PLAYER_CONTROLLED, ENTITY_PROP_CAMERA, + ENTITY_PROP_CAMERA_ACTIVE, /* Test props */ @@ -92,7 +93,6 @@ struct entity { /* ====================================================================== */ /* ENTITY_PROP_CAMERA */ - b32 camera_active; struct entity_handle camera_follow; f32 camera_zoom; }; @@ -102,6 +102,11 @@ struct entity_array { u64 count; }; +struct entity_prop_array { + enum entity_prop *props; + u64 count; +}; + /* ========================== * * Nil * ========================== */ @@ -149,7 +154,9 @@ void entity_release(struct entity_store *store, struct entity *entity); struct entity_array entity_store_as_array(struct entity_store *store); struct entity *entity_from_handle(struct entity_store *store, struct entity_handle handle); -struct entity *entity_find_first_with_prop(struct entity_store *store, enum entity_prop prop); +struct entity *entity_find_first_match_one(struct entity_store *store, enum entity_prop prop); +struct entity *entity_find_first_match_all(struct entity_store *store, struct entity_prop_array props); + void entity_link(struct entity_store *store, struct entity *parent, struct entity *child); diff --git a/src/game.c b/src/game.c index 359e8d45..8da41076 100644 --- a/src/game.c +++ b/src/game.c @@ -198,7 +198,7 @@ INTERNAL void game_update(void) e->rel_xform = XFORM_IDENT; entity_enable_prop(e, ENTITY_PROP_CAMERA); - e->camera_active = true; + entity_enable_prop(e, ENTITY_PROP_CAMERA_ACTIVE); e->camera_follow = player_ent->handle; e->camera_zoom = 1; diff --git a/src/math.h b/src/math.h index 8f0f8911..21f94622 100644 --- a/src/math.h +++ b/src/math.h @@ -629,6 +629,13 @@ INLINE struct v2 xform_mul_v2(struct xform xf, struct v2 v) return res; } +INLINE struct v2 xform_basis_invert_mul_v2(struct xform xf, struct v2 v) +{ + struct xform inv = xform_invert(xf); + struct v2 res = xform_basis_mul_v2(inv, v); + return res; +} + INLINE struct v2 xform_invert_mul_v2(struct xform xf, struct v2 v) { struct xform inv = xform_invert(xf); diff --git a/src/user.c b/src/user.c index 9c0833d5..c61ab22d 100644 --- a/src/user.c +++ b/src/user.c @@ -325,11 +325,22 @@ INTERNAL struct v2 view_xform_v2(struct view view, struct v2 p) return xform_mul_v2(mtx, p); } +INTERNAL struct v2 view_xform_basis_v2(struct view view, struct v2 p) +{ + struct xform mtx = view_get_xform(view); + return xform_basis_mul_v2(mtx, p); +} + INTERNAL struct v2 view_inverse_xform_v2(struct view view, struct v2 p) { return xform_invert_mul_v2(view_get_xform(view), p); } +INTERNAL struct v2 view_inverse_xform_basis_v2(struct view view, struct v2 p) +{ + return xform_basis_invert_mul_v2(view_get_xform(view), p); +} + /* ========================== * * Update * ========================== */ @@ -394,13 +405,77 @@ INTERNAL void user_update(void) ); L.screen_center = v2_mul(L.screen_size, 0.5); - /* Read input */ - struct sys_event_array events = pop_sys_events(scratch.arena); + /* ========================== * + * Produce interpolated tick + * ========================== */ + + b32 tick_is_first_frame = false; + { + __profscope(produce_interpolated_tick); + + f64 blend_time_offset = (1.0 / GAME_FPS) * USER_INTERP_OFFSET_TICK_RATIO; + f64 blend_time = L.time > blend_time_offset ? L.time - blend_time_offset : 0; + + /* Pull ticks */ + struct interp_ticks interp_ticks = pull_ticks(blend_time); + struct world *t0 = interp_ticks.from_tick; + struct world *t1 = interp_ticks.to_tick; + + tick_is_first_frame = (t0->tick_id == 0 || t1->tick_id == 0); + + f32 tick_blend = 0; + { + f64 t0_time = sys_timestamp_seconds(t0->tick_ts); + f64 t1_time = sys_timestamp_seconds(t1->tick_ts); + if (t1_time > t0_time) { + tick_blend = (f32)((blend_time - t0_time) / (t1_time - t0_time)); + } + tick_blend = clamp_f32(tick_blend, 0.0f, 1.0f); + } + + world_copy_replace(&L.world, t1); +#if 1 + /* Blend time */ + L.world.time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend); + + /* Blend entities */ + struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); + struct entity_array t1_entities = entity_store_as_array(&t1->entity_store); + struct entity_array world_entities = entity_store_as_array(&L.world.entity_store); + + u64 num_entities = min_u64(t0_entities.count, t1_entities.count); + for (u64 i = 0; i < num_entities; ++i) { + struct entity *e0 = &t0_entities.entities[i]; + 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->rel_xform = xform_lerp(e0->rel_xform, e1->rel_xform, tick_blend); + e->world_xform = xform_lerp(e0->world_xform, e1->world_xform, tick_blend); + + e->acceleration = v2_lerp(e0->acceleration, e1->acceleration, tick_blend); + e->velocity = v2_lerp(e0->velocity, e1->velocity, tick_blend); + e->player_acceleration = math_lerp_f32(e0->player_acceleration, e1->player_acceleration, tick_blend); + e->player_focus_dir = v2_lerp(e0->player_focus_dir, e1->player_focus_dir, tick_blend); + + e->sprite_xform = xform_lerp(e0->sprite_xform, e1->sprite_xform, tick_blend); + e->animation_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); + + e->camera_zoom = math_lerp_f32(e0->camera_zoom, e1->camera_zoom, tick_blend); + } + } +#else + (UNUSED)tick_blend; +#endif + } + + struct entity_array entities_array = entity_store_as_array(&L.world.entity_store); /* ========================== * * Read sys events * ========================== */ + struct sys_event_array events = pop_sys_events(scratch.arena); + /* Reset bind states "was_pressed" */ for (u32 i = 0; i < ARRAY_COUNT(L.bind_states); ++i) { L.bind_states[i] = (struct bind_state) { @@ -447,7 +522,6 @@ INTERNAL void user_update(void) /* Update mouse pos */ if (event->kind == SYS_EVENT_KIND_MOUSE_MOVE) { L.screen_mouse = event->mouse_position; - //L.screen_mouse_delta = v2_add(L.screen_mouse_delta, event->mouse_delta); } /* Update mouse delta */ @@ -551,10 +625,26 @@ INTERNAL void user_update(void) /* Keep cursor invisible and in screen */ sys_window_cursor_hide(L.window); sys_window_cursor_enable_keep_in_window(L.window); + + enum entity_prop props[] = { ENTITY_PROP_CAMERA, ENTITY_PROP_CAMERA_ACTIVE }; + struct entity_prop_array props_array = { + .props = props, + .count = ARRAY_COUNT(props) + }; + struct entity *ent = entity_find_first_match_all(&L.world.entity_store, props_array); + + struct v2 center = ent->world_xform.og; + f32 rot = xform_get_rotation(ent->world_xform); + f32 zoom = ent->camera_zoom; + zoom = zoom > 0 ? zoom : 1; + L.world_view.center = center; + L.world_view.rot = rot; + L.world_view.zoom = zoom; + L.world_mouse = view_inverse_xform_v2(L.world_view, L.screen_mouse); } /* ========================== * - * Process movement input + * Construct movement input * ========================== */ /* Movement */ @@ -588,102 +678,22 @@ INTERNAL void user_update(void) default: break; } } + + player_move_dir = view_inverse_xform_basis_v2(L.world_view, player_move_dir); /* Make move dir relative to world view */ + player_move_dir = v2_norm(player_move_dir); + struct game_cmd cmd = (struct game_cmd) { .kind = GAME_CMD_KIND_PLAYER_MOVE, - .move_dir = v2_norm(player_move_dir) + .move_dir = player_move_dir }; if (!L.debug_camera) { - struct v2 focus_move_dir = v2_div(L.screen_mouse_delta, PIXELS_PER_UNIT * L.world_view.zoom); + struct v2 focus_move_dir = view_inverse_xform_basis_v2(L.world_view, L.screen_mouse_delta); /* Make focus relative to world view direction */ focus_move_dir = v2_mul(focus_move_dir, MOUSE_SENSITIVITY); cmd.focus_move_dir = focus_move_dir; } queue_game_cmd(&cmd_list, cmd); } - /* ========================== * - * Produce interpolated tick - * ========================== */ - - /* Pull ticks */ - - f64 blend_time_offset = (1.0 / GAME_FPS) * USER_INTERP_OFFSET_TICK_RATIO; - f64 blend_time = L.time > blend_time_offset ? L.time - blend_time_offset : 0; - - struct interp_ticks interp_ticks = pull_ticks(blend_time); - struct world *t0 = interp_ticks.from_tick; - struct world *t1 = interp_ticks.to_tick; - - /* Produce interpolated tick */ - { - __profscope(produce_interpolated_tick); - - f64 t0_time = sys_timestamp_seconds(t0->tick_ts); - f64 t1_time = sys_timestamp_seconds(t1->tick_ts); - - f32 tick_blend = 0; - if (t1_time > t0_time) { - tick_blend = (f32)((blend_time - t0_time) / (t1_time - t0_time)); - } - tick_blend = clamp_f32(tick_blend, 0.0f, 1.0f); - - world_copy_replace(&L.world, t1); - -#if 1 - /* Blend time */ - L.world.time = math_lerp_f64(t0->time, t1->time, (f64)tick_blend); - - /* Blend entities */ - struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); - struct entity_array t1_entities = entity_store_as_array(&t1->entity_store); - struct entity_array world_entities = entity_store_as_array(&L.world.entity_store); - - u64 num_entities = min_u64(t0_entities.count, t1_entities.count); - for (u64 i = 0; i < num_entities; ++i) { - struct entity *e0 = &t0_entities.entities[i]; - 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->rel_xform = xform_lerp(e0->rel_xform, e1->rel_xform, tick_blend); - e->world_xform = xform_lerp(e0->world_xform, e1->world_xform, tick_blend); - - e->acceleration = v2_lerp(e0->acceleration, e1->acceleration, tick_blend); - e->velocity = v2_lerp(e0->velocity, e1->velocity, tick_blend); - e->player_acceleration = math_lerp_f32(e0->player_acceleration, e1->player_acceleration, tick_blend); - e->player_focus_dir = v2_lerp(e0->player_focus_dir, e1->player_focus_dir, tick_blend); - - e->sprite_xform = xform_lerp(e0->sprite_xform, e1->sprite_xform, tick_blend); - e->animation_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); - - e->camera_zoom = math_lerp_f32(e0->camera_zoom, e1->camera_zoom, tick_blend); - } - } -#else - (UNUSED)tick_blend; -#endif - } - - struct entity_array entities_array = entity_store_as_array(&L.world.entity_store); - - /* ========================== * - * Update view from game camera - * ========================== */ - - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; - if (!ent->valid) continue; - - if (entity_has_prop(ent, ENTITY_PROP_CAMERA) && ent->camera_active && !L.debug_camera) { - struct v2 center = ent->world_xform.og; - f32 rot = xform_get_rotation(ent->world_xform); - f32 zoom = ent->camera_zoom; - zoom = zoom > 0 ? zoom : 1; - L.world_view.center = center; - L.world_view.rot = rot; - L.world_view.zoom = zoom; - L.world_mouse = view_inverse_xform_v2(L.world_view, L.screen_mouse); - } - } - /* ========================== * * Draw test grid * ========================== */ @@ -877,12 +887,9 @@ INTERNAL void user_update(void) u32 color = RGBA_F(0.75, 0, 0.75, 0.5); f32 thickness = 3; f32 arrow_height = 10; - - struct v2 focus_pos = v2_add(ent->world_xform.og, ent->player_focus_dir); - - struct v2 start = view_xform_v2(L.world_view, ent->world_xform.og); - struct v2 end = view_xform_v2(L.world_view, focus_pos); - draw_solid_arrow_line(L.screen_canvas, start, end, thickness, arrow_height, color); + struct v2 pos = view_xform_v2(L.world_view, ent->world_xform.og); + struct v2 focus_ray = view_xform_basis_v2(L.world_view, ent->player_focus_dir); + draw_solid_arrow_ray(L.screen_canvas, pos, focus_ray, thickness, arrow_height, color); } arena_temp_end(temp); @@ -955,7 +962,7 @@ INTERNAL void user_update(void) u64 canvases_count = 0; { /* Only render world if not on first frame */ - if (t0->tick_id > 0 && t1->tick_id > 0) { + if (!tick_is_first_frame) { *arena_push(scratch.arena, struct renderer_canvas *) = L.world_canvas; ++canvases_count; }