diff --git a/src/config.h b/src/config.h index 733585fe..c36a8a10 100644 --- a/src/config.h +++ b/src/config.h @@ -6,7 +6,7 @@ #define RESOURCES_EMBEDDED !(DEVELOPER) #define DEFAULT_CAMERA_WIDTH (6) -#define DEFAULT_CAMERA_ASPECT_RATIO (16.0 / 9.0) +#define DEFAULT_CAMERA_HEIGHT (DEFAULT_CAMERA_WIDTH / (16.0 / 9.0)) #define PIXELS_PER_UNIT 256 diff --git a/src/entity.c b/src/entity.c index c2388ff3..df6963c4 100644 --- a/src/entity.c +++ b/src/entity.c @@ -4,7 +4,7 @@ READONLY struct entity _g_entity_nil = { .rel_xform = XFORM_IDENT, .world_xform = XFORM_IDENT, - .sprite_xform = XFORM_IDENT, + .sprite_quad_xform = XFORM_IDENT, .sprite_tint = COLOR_WHITE }; diff --git a/src/entity.h b/src/entity.h index 0ba06d81..e35f07c8 100644 --- a/src/entity.h +++ b/src/entity.h @@ -65,7 +65,7 @@ struct entity { /* ====================================================================== */ /* Sprite */ - struct xform sprite_xform; + struct xform sprite_quad_xform; struct string sprite_name; struct string sprite_tag_name; u32 sprite_tint; @@ -94,10 +94,11 @@ struct entity { /* ENTITY_PROP_CAMERA */ struct entity_handle camera_follow; - struct xform camera_target_xform; /* Calculated from camera_follow */ - struct v2 camera_size; + struct xform camera_quad_xform; f32 camera_lerp; /* Rate at which camera xform approaches target xform */ + u32 camera_lerp_gen; + struct xform camera_rel_xform_target; /* Calculated from camera_follow */ u32 camera_applied_lerp_gen_plus_one; /* Calculated */ }; @@ -161,7 +162,6 @@ struct entity *entity_from_handle(struct entity_store *store, struct entity_hand 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); #endif diff --git a/src/game.c b/src/game.c index e7abda6b..129e125b 100644 --- a/src/game.c +++ b/src/game.c @@ -73,6 +73,43 @@ INTERNAL void publish_game_tick(void) sys_mutex_unlock(&L.published_tick_mutex); } +INTERNAL void recalculate_world_xform_recurse(struct entity *parent) +{ + struct temp_arena scratch = scratch_begin_no_conflict(); + + struct stack_node { + struct entity *entity; + struct xform parent_xform; + }; + + /* Depth first iteration */ + *arena_push(scratch.arena, struct stack_node) = (struct stack_node) { .entity = parent, .parent_xform = XFORM_IDENT }; + u64 stack_count = 1; + while (stack_count > 0) { + /* Pull from top of stack */ + struct stack_node node = *arena_pop(scratch.arena, struct stack_node); + --stack_count; + + /* Calculate child world xform */ + struct entity *child = node.entity; + struct xform world_xform = xform_mul(node.parent_xform, child->rel_xform); + child->world_xform = world_xform; + + /* Append sub-children to stack */ + struct entity *subchild = entity_from_handle(&L.world.entity_store, child->last); + while (subchild->valid) { + *arena_push(scratch.arena, struct stack_node) = (struct stack_node) { + .entity = subchild, + .parent_xform = world_xform + }; + ++stack_count; + subchild = entity_from_handle(&L.world.entity_store, subchild->prev); + } + } + + scratch_end(scratch); +} + INTERNAL void game_update(void) { __prof; @@ -117,7 +154,7 @@ INTERNAL void game_update(void) sprite_xf = xform_rotate(sprite_xf, sprite_rot); sprite_xf = xform_translate(sprite_xf, v2_neg(sprite_pivot)); sprite_xf = xform_scale(sprite_xf, sprite_size); - e->sprite_xform = sprite_xf; + e->sprite_quad_xform = sprite_xf; e->sprite_name = sprite_name; e->sprite_tag_name = sprite_tag_name; @@ -168,7 +205,7 @@ INTERNAL void game_update(void) sprite_xf = xform_rotate(sprite_xf, sprite_rot); sprite_xf = xform_translate(sprite_xf, v2_neg(sprite_pivot)); sprite_xf = xform_scale(sprite_xf, sprite_size); - e->sprite_xform = sprite_xf; + e->sprite_quad_xform = sprite_xf; e->sprite_name = sprite_name; e->sprite_tag_name = sprite_tag_name; @@ -181,7 +218,6 @@ INTERNAL void game_update(void) entity_enable_prop(e, ENTITY_PROP_ANIMATING); //entity_enable_prop(e, ENTITY_PROP_TEST); - //entity_enable_prop(e, ENTITY_PROP_TEST_FOLLOW_MOUSE); entity_link(&L.world.entity_store, parent, e); @@ -202,13 +238,14 @@ INTERNAL void game_update(void) e->valid = true; e->rel_xform = XFORM_IDENT; + entity_enable_prop(e, ENTITY_PROP_CAMERA); entity_enable_prop(e, ENTITY_PROP_CAMERA_ACTIVE); e->camera_follow = player_ent->handle; f32 width = (f32)DEFAULT_CAMERA_WIDTH; - f32 height = width / (f32)DEFAULT_CAMERA_ASPECT_RATIO; - e->camera_size = V2(width, height); + f32 height = (f32)DEFAULT_CAMERA_HEIGHT; + e->camera_quad_xform = XFORM_TRS(.s = V2(width, height)); } /* Sound ent */ @@ -223,7 +260,7 @@ INTERNAL void game_update(void) f32 meters_height = sheet->frame_size.y / PIXELS_PER_UNIT; struct v2 sprite_size = V2(meters_width, meters_height); - e->sprite_xform = xform_with_scale(XFORM_IDENT, sprite_size); + e->sprite_quad_xform = xform_with_scale(XFORM_IDENT, sprite_size); e->sprite_name = sprite_name; e->sprite_tint = RGBA_F(1, 1, 0, 1); @@ -295,7 +332,7 @@ INTERNAL void game_update(void) if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) { ent->test_initialized = true; ent->test_start_rel_xform = ent->rel_xform; - ent->test_start_sprite_xform = ent->sprite_xform; + ent->test_start_sprite_xform = ent->sprite_quad_xform; } /* ========================== * @@ -409,41 +446,7 @@ INTERNAL void game_update(void) * Calculate xforms * ========================== */ - { - struct temp_arena stack = arena_temp_begin(scratch.arena); - - struct xform_stack_node { - struct entity *entity; - struct xform parent_xform; - }; - - /* Depth first iteration */ - *arena_push(stack.arena, struct xform_stack_node) = (struct xform_stack_node) { .entity = ent, .parent_xform = XFORM_IDENT }; - u64 stack_count = 1; - while (stack_count > 0) { - /* Pull from top of stack */ - struct xform_stack_node node = *arena_pop(stack.arena, struct xform_stack_node); - --stack_count; - - /* Calculate child world xform */ - struct entity *child = node.entity; - struct xform world_xform = xform_mul(node.parent_xform, child->rel_xform); - child->world_xform = world_xform; - - /* Append sub-children to stack */ - struct entity *subchild = entity_from_handle(&L.world.entity_store, child->last); - while (subchild->valid) { - *arena_push(stack.arena, struct xform_stack_node) = (struct xform_stack_node) { - .entity = subchild, - .parent_xform = world_xform - }; - ++stack_count; - subchild = entity_from_handle(&L.world.entity_store, subchild->prev); - } - } - - arena_temp_end(stack); - } + recalculate_world_xform_recurse(ent); } /* ========================== * @@ -461,9 +464,6 @@ INTERNAL void game_update(void) /* Camera follow */ if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { struct entity *follow = entity_from_handle(&L.world.entity_store, ent->camera_follow); - ent->camera_target_xform = follow->world_xform; - ent->camera_target_xform = xform_with_rotation(ent->camera_target_xform, 0); - ent->camera_target_xform = xform_with_scale(ent->camera_target_xform, V2(1, 1)); if (entity_has_prop(follow, ENTITY_PROP_PLAYER_CONTROLLED)) { #if 0 @@ -472,32 +472,40 @@ INTERNAL void game_update(void) //f32 target_dist_y = target_dist_x * (16.0f / 9.0f); f32 target_dist_y = target_dist_x; struct v2 target_dir = v2_mul_v2(v2_norm(follow->player_aim), V2(target_dist_x, target_dist_y)); - struct v2 target_pos = v2_add(ent->camera_target_xform.og, target_dir); - ent->camera_target_xform.og = target_pos; + struct v2 target_pos = v2_add(ent->camera_rel_xform_target.og, target_dir); + ent->camera_rel_xform_target = ent->rel_xform; + ent->camera_rel_xform_target.og = target_pos; #else /* "Look" style camera */ f32 aspect_ratio = 1.0; - if (!v2_eq(ent->camera_size, V2(0, 0))) { - aspect_ratio = ent->camera_size.x / ent->camera_size.y; + { + struct xform quad_xf = xform_mul(ent->world_xform, 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->player_aim, V2(ratio_x, ratio_y)); struct v2 camera_focus_pos = v2_add(follow->world_xform.og, camera_focus_dir); - ent->camera_target_xform.og = camera_focus_pos; + ent->camera_rel_xform_target = ent->rel_xform; + ent->camera_rel_xform_target.og = camera_focus_pos; #endif } /* Lerp camera */ if (ent->camera_applied_lerp_gen_plus_one == ent->camera_lerp_gen + 1) { f32 t = 1 - math_pow(2.f, -20.f * (f32)L.world.dt); - ent->rel_xform = xform_lerp(ent->rel_xform, ent->camera_target_xform, t); + ent->rel_xform = xform_lerp(ent->rel_xform, ent->camera_rel_xform_target, t); } else { /* Skip lerp */ - ent->rel_xform = ent->camera_target_xform; + ent->rel_xform = ent->camera_rel_xform_target; } ent->camera_applied_lerp_gen_plus_one = ent->camera_lerp_gen + 1; - ent->world_xform = ent->rel_xform; + + /* Recalculate xform tree */ + recalculate_world_xform_recurse(ent); } /* ========================== * diff --git a/src/settings.c b/src/settings.c index 0891718c..984a6b10 100644 --- a/src/settings.c +++ b/src/settings.c @@ -98,7 +98,7 @@ struct sys_window_settings settings_default_window_settings(struct sys_window *w struct v2 monitor_size = sys_window_get_monitor_size(window); i32 width = 1280; - i32 height = math_round(width / (f32)DEFAULT_CAMERA_ASPECT_RATIO); + i32 height = math_round(width / (f32)(DEFAULT_CAMERA_WIDTH / DEFAULT_CAMERA_HEIGHT)); i32 x = math_round(monitor_size.x / 2.f - width / 2); i32 y = math_round(monitor_size.y / 2.f - height / 2); diff --git a/src/user.c b/src/user.c index f49acd9f..72293757 100644 --- a/src/user.c +++ b/src/user.c @@ -396,11 +396,11 @@ INTERNAL void user_update(void) e->player_acceleration = math_lerp(e0->player_acceleration, e1->player_acceleration, tick_blend); e->player_aim = v2_lerp(e0->player_aim, e1->player_aim, tick_blend); - e->sprite_xform = xform_lerp(e0->sprite_xform, e1->sprite_xform, tick_blend); + e->sprite_quad_xform = xform_lerp(e0->sprite_quad_xform, e1->sprite_quad_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_size = v2_lerp(e0->camera_size, e1->camera_size, tick_blend); - e->camera_target_xform = xform_lerp(e0->camera_target_xform, e1->camera_target_xform, tick_blend); + e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, tick_blend); + e->camera_rel_xform_target = xform_lerp(e0->camera_rel_xform_target, e1->camera_rel_xform_target, tick_blend); } } #else @@ -541,8 +541,12 @@ INTERNAL void user_update(void) /* Determine viewport size by camera & window dimensions */ f32 aspect_ratio = 1.0; - if (!v2_eq(active_camera->camera_size, V2(0, 0))) { - aspect_ratio = active_camera->camera_size.x / active_camera->camera_size.y; + { + struct xform quad_xf = xform_mul(active_camera->world_xform, active_camera->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 width = L.screen_size.x; f32 height = L.screen_size.y; @@ -569,6 +573,8 @@ INTERNAL void user_update(void) * ========================== */ if (L.debug_camera) { + L.world_view = xform_with_rotation(L.world_view, 0); + /* Pan view */ if (L.bind_states[USER_BIND_KIND_PAN].is_held) { if (!L.debug_camera_panning) { @@ -611,8 +617,12 @@ INTERNAL void user_update(void) /* Scale view into viewport based on camera size */ struct v2 size = L.viewport_size; - if (!v2_eq(active_camera->camera_size, V2(0, 0))) { - size = v2_div_v2(size, active_camera->camera_size); + { + struct xform quad_xf = xform_mul(active_camera->world_xform, active_camera->camera_quad_xform); + struct v2 camera_size = xform_get_scale(quad_xf); + if (!v2_eq(camera_size, V2(0, 0))) { + size = v2_div_v2(size, camera_size); + } } f32 scale = min_f32(size.x, size.y); @@ -701,7 +711,8 @@ INTERNAL void user_update(void) struct entity *ent = &entities_array.entities[entity_index]; if (!ent->valid) continue; - b32 skip_debug_draw = entity_has_prop(ent, ENTITY_PROP_CAMERA); + b32 skip_debug_draw = !L.debug_camera && ent == active_camera; + b32 skip_debug_draw_transform = ent == active_camera; /* Draw sprite */ if (ent->sprite_name.len > 0) { @@ -711,7 +722,7 @@ INTERNAL void user_update(void) struct texture *texture = texture_load_async(tex_name); if (texture) { - struct xform xf = xform_mul(ent->world_xform, ent->sprite_xform); + struct xform xf = xform_mul(ent->world_xform, ent->sprite_quad_xform); struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf); u32 tint = ent->sprite_tint; @@ -806,9 +817,12 @@ INTERNAL void user_update(void) } #endif - debug_draw_xform(ent->world_xform); debug_draw_movement(ent); + if (!skip_debug_draw_transform) { + debug_draw_xform(ent->world_xform); + } + /* Draw hierarchy */ struct entity *parent = entity_from_handle(&L.world.entity_store, ent->parent); if (parent->valid) { @@ -831,6 +845,19 @@ INTERNAL void user_update(void) draw_solid_arrow_ray(L.viewport_canvas, pos, aim_ray, thickness, arrow_height, color); } + /* Draw camera rect */ + if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { + u32 color = ent == active_camera ? RGBA_F(1, 1, 1, 0.5) : RGBA_F(0, 0.75, 0, 0.5); + f32 thickness = 3; + + + struct xform quad_xf = xform_mul(ent->world_xform, ent->camera_quad_xform); + struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, quad_xf); + quad = quad_mul_xform(quad, L.world_view); + + draw_solid_quad_line(L.viewport_canvas, quad, thickness, color); + } + arena_temp_end(temp); } }