xform movement & focus directions by world view basis

This commit is contained in:
jacob 2024-03-13 23:08:18 -05:00
parent 181f8258fe
commit e91a66f5b3
5 changed files with 143 additions and 101 deletions

View File

@ -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
* ========================== */

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}