xform movement & focus directions by world view basis
This commit is contained in:
parent
181f8258fe
commit
e91a66f5b3
23
src/entity.c
23
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
|
||||
* ========================== */
|
||||
|
||||
11
src/entity.h
11
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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
201
src/user.c
201
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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user