diff --git a/src/config.h b/src/config.h index 9ce0703f..871621cb 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,7 @@ /* Project-wide configurable constants */ #define WRITE_DIR "power_play" -#define SETTINGS_FILENAME "settings.json" +#define SETTINGS_FILENAME "settings.txt" /* Window title */ #if RTC diff --git a/src/entity.h b/src/entity.h index 916cda72..15cbb7c6 100644 --- a/src/entity.h +++ b/src/entity.h @@ -56,11 +56,11 @@ struct entity { struct v2 acceleration; struct v2 velocity; + struct v2 focus; /* Focus is a vector relative to the entity */ /* ENTITY_PROP_PLAYER_CONTROLLED */ f32 player_max_speed; f32 player_acceleration; - struct v2 player_aim; /* ====================================================================== */ /* Sprite */ diff --git a/src/game.c b/src/game.c index c86c5486..9e378a4d 100644 --- a/src/game.c +++ b/src/game.c @@ -134,10 +134,7 @@ INTERNAL void recalculate_world_xform_recurse(struct entity *parent) /* Append sub-children to stack */ struct entity *subchild = entity_from_handle(&G.world.entity_store, child->last); while (subchild->valid) { - *arena_push(scratch.arena, struct stack_node) = (struct stack_node) { - .entity = subchild, - .parent_xform = world_xform - }; + *arena_push(scratch.arena, struct stack_node) = (struct stack_node) { .entity = subchild, .parent_xform = world_xform }; ++stack_count; subchild = entity_from_handle(&G.world.entity_store, subchild->prev); } @@ -146,21 +143,6 @@ INTERNAL void recalculate_world_xform_recurse(struct entity *parent) scratch_end(scratch); } -#if 0 -INTERNAL struct v2 sprite_sheet_size_meters(struct sprite_tag s) -{ - struct v2 size = { 0 }; - struct sprite_scope *scope = sprite_scope_begin(); - { - struct sheet *sheet = sprite_sheet_from_tag_await(scope, s); - size.x = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; - size.y = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; - } - sprite_scope_end(scope); - return size; -} -#endif - INTERNAL void game_update(void) { __prof; @@ -193,11 +175,12 @@ INTERNAL void game_update(void) e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase")); e->sprite_span_name = STR("idle.unarmed"); //e->sprite_span_name = STR("idle.two_handed"); + e->sprite_quad_xform = XFORM_TRS(.s = size); entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); e->player_max_speed = 4.f; e->player_acceleration = 20.0f; - e->player_aim = V2(0, -1); + e->focus = V2(0, -1); entity_enable_prop(e, ENTITY_PROP_ANIMATING); @@ -340,6 +323,39 @@ INTERNAL void game_update(void) ent->rel_xform = xform_with_rotation(ent->rel_xform, r); ent->rel_xform = xform_with_scale(ent->rel_xform, s); } + + /* ========================== * + * Calculate sprite xform + * ========================== */ + + if (ent->sprite.hash != 0) { + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); + struct v2 sprite_size = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT); + + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("pivot"), ent->animation_frame); + struct v2 pivot_pos = v2_mul_v2(slice.center_norm, sprite_size); + + /* Apply Pivot */ + struct xform sprite_xf = XFORM_POS(v2_neg(pivot_pos)); + sprite_xf = xform_scale(sprite_xf, sprite_size); + + ent->sprite_quad_xform = sprite_xf; + } + + /* ========================== * + * Calculate player aim angle + * ========================== */ + + if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { + /* Update focus */ + ent->focus = G.world.player_aim; + + /* Update view angle */ + struct v2 ent_pos = ent->rel_xform.og; + struct v2 look_pos = v2_add(ent_pos, ent->focus); + f32 r = v2_angle_to_point(ent_pos, look_pos) + PI / 2; + ent->rel_xform = xform_with_rotation(ent->rel_xform, r); + } } /* ========================== * @@ -379,21 +395,6 @@ INTERNAL void game_update(void) ent->rel_xform.og = v2_add(ent->rel_xform.og, v2_mul(ent->velocity, dt)); } - /* ========================== * - * Player aim - * ========================== */ - - if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - /* Update aim */ - ent->player_aim = G.world.player_aim; - - /* Update view angle */ - struct v2 ent_pos = ent->rel_xform.og; - struct v2 look_pos = v2_add(ent_pos, ent->player_aim); - f32 r = v2_angle_to_point(ent_pos, look_pos) + PI / 2; - ent->rel_xform = xform_with_rotation(ent->rel_xform, r); - } - /* ========================== * * Calculate xforms * ========================== */ @@ -428,7 +429,7 @@ INTERNAL void game_update(void) } 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_dir = v2_mul_v2(follow->focus, V2(ratio_x, ratio_y)); struct v2 camera_focus_pos = v2_add(follow->world_xform.og, camera_focus_dir); ent->camera_rel_xform_target = ent->rel_xform; ent->camera_rel_xform_target.og = camera_focus_pos; diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index f0f94732..ede613b3 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -878,41 +878,47 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou struct dx11_shader *shader = cmd->shader; struct dx11_buffer *buffer = &canvas->buffers[shader->kind]; + b32 texture_loaded; struct renderer_handle texture_handle; if (handle_is_nil(cmd->texture_handle)) { - texture_handle = sprite_texture_from_tag_async(sprite_scope, cmd->sprite)->renderer_handle; + struct sprite_texture *sprite_texture = sprite_texture_from_tag_async(sprite_scope, cmd->sprite); + texture_loaded = sprite_texture->loaded; + texture_handle = sprite_texture->renderer_handle; } else { + texture_loaded = true; texture_handle = cmd->texture_handle; } - /* Activate shader */ - if (shader != last_shader) { - ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0); - ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0); - ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout); - last_shader = shader; + if (texture_loaded) { + /* Activate shader */ + if (shader != last_shader) { + ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0); + ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0); + ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout); + last_shader = shader; + } + + /* FIXME: what if texture_srv is 0? will this unset it correctly? */ + /* Activate texture */ + if (!handle_eq(texture_handle, last_texture_handle)) { + ID3D11ShaderResourceView *texture_srv = handle_data(texture_handle); + ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, &texture_srv); + last_texture_handle = texture_handle; + } + + u32 vertex_offset = cmd->vertex_offset; + u32 index_offset = cmd->index_offset; + u32 index_count = cmd->index_count; + + /* Activate buffer */ + u32 zero = 0; + UINT vertex_stride = shader->vertex_size; + ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &buffer->gpu_vertex_buffer, &vertex_stride, &zero); + ID3D11DeviceContext_IASetIndexBuffer(G.devcon, buffer->gpu_index_buffer, DXGI_FORMAT_R32_UINT, zero); + + /* Draw */ + ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset); } - - /* FIXME: what if texture_srv is 0? will this unset it correctly? */ - /* Activate texture */ - if (!handle_eq(texture_handle, last_texture_handle)) { - ID3D11ShaderResourceView *texture_srv = handle_data(texture_handle); - ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, &texture_srv); - last_texture_handle = texture_handle; - } - - u32 vertex_offset = cmd->vertex_offset; - u32 index_offset = cmd->index_offset; - u32 index_count = cmd->index_count; - - /* Activate buffer */ - u32 zero = 0; - UINT vertex_stride = shader->vertex_size; - ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &buffer->gpu_vertex_buffer, &vertex_stride, &zero); - ID3D11DeviceContext_IASetIndexBuffer(G.devcon, buffer->gpu_index_buffer, DXGI_FORMAT_R32_UINT, zero); - - /* Draw */ - ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset); } } diff --git a/src/sprite.c b/src/sprite.c index aafa14e9..f8814193 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -232,10 +232,16 @@ struct sprite_startup_receipt sprite_startup(struct renderer_startup_receipt *re G.nil_texture->renderer_handle = renderer_texture_alloc(purple_black_image); scratch_end(scratch); } + /* Init loading sheet */ G.loading_sheet = arena_push_zero(&G.perm_arena, struct sprite_sheet); + G.loading_sheet->image_size = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT); + G.loading_sheet->frame_size = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT); + /* Init nil sheet */ G.nil_sheet = arena_push_zero(&G.perm_arena, struct sprite_sheet); + G.nil_sheet->image_size = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT); + G.nil_sheet->frame_size = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT); G.nil_sheet->loaded = true; } arena_set_readonly(&G.perm_arena); @@ -394,6 +400,9 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str ASSERT(ase.num_frames >= 1); + struct v2 frame_size = ase.frame_size; + struct v2 frame_center = v2_mul(ase.frame_size, 0.5f); + /* Init frames */ { __profscope(init_frames); @@ -513,10 +522,35 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str f32 y1 = ase_slice->y1; f32 x2 = ase_slice->x2; f32 y2 = ase_slice->y2; + f32 width = x2 - x1; + f32 height = y2 - y1; - slice->rect = RECT(x1, y1, x2 - x1, y2 - y1); - slice->center = V2(x1 + ((x2 - x1) / 2.f), y1 + ((y2 - y1) / 2.f)); - slice->dir = V2(0, -1); + f32 x1_norm = (x1 - frame_center.x) / frame_size.x; + f32 y1_norm = (y1 - frame_center.y) / frame_size.y; + f32 x2_norm = (x2 - frame_center.x) / frame_size.x; + f32 y2_norm = (y2 - frame_center.y) / frame_size.y; + f32 width_norm = x2_norm - x1_norm; + f32 height_norm = y2_norm - y1_norm; + + /* Rect */ + struct rect rect = RECT(x1, y1, width, height); + /* Rect norm */ + struct rect rect_norm = RECT(x1_norm, y1_norm, width_norm, height_norm); + /* Center */ + struct v2 center = V2(x1 + (width * 0.5f), y1 + (height * 0.5f)); + /* Center norm */ + struct v2 center_norm = V2(x1_norm + (width_norm * 0.5f), y1_norm + (height_norm * 0.5f)); + /* Dir */ + struct v2 dir = V2(center.x, 0); + /* Dir norm */ + struct v2 dir_norm = V2(0, -0.5); + + slice->rect = rect; + slice->rect_norm = rect_norm; + slice->center = center; + slice->center_norm = center_norm; + slice->dir = dir; + slice->dir_norm = dir_norm; node->index_in_frame = index_in_frame; if (start < node->earliest_frame) { @@ -567,7 +601,7 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str } } - /* Calculate rays */ + /* Calculate dirs */ for (struct temp_slice_group_node *temp_slice_group_node = temp_slice_group_head; temp_slice_group_node; temp_slice_group_node = temp_slice_group_node->next) { struct string ray_suffix = STR(".ray"); @@ -585,11 +619,13 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str /* Use ray slice in ray group */ struct sprite_sheet_slice *ray_slice = &ray_slice_group->frame_slices[i * point_slices_per_frame]; struct v2 ray_end = ray_slice->center; + struct v2 ray_end_norm = ray_slice->center_norm; /* Apply to each point slice in point group */ for (u32 j = 0; j < point_slices_per_frame; ++j) { struct sprite_sheet_slice *point_slice = &point_slice_group->frame_slices[(i * point_slices_per_frame) + j]; point_slice->dir = v2_sub(ray_end, point_slice->center); + point_slice->dir_norm = v2_sub(ray_end_norm, point_slice->center_norm); } } @@ -905,17 +941,14 @@ struct sprite_sheet *sprite_sheet_from_tag_async(struct sprite_scope *scope, str struct sprite_sheet_frame sprite_sheet_get_frame(struct sprite_sheet *sheet, u32 index) { __prof; - if (sheet->frames_count > 0) { - index = min_u32(sheet->frames_count - 1, index); + if (index < sheet->frames_count ) { return sheet->frames[index]; - } else { - return (struct sprite_sheet_frame) - { - .index = 0, - .duration = 0.1, - .clip = CLIP_ALL - }; } + struct sprite_sheet_frame res = { 0 }; + res.index = 0; + res.duration = 0.1; + res.clip = CLIP_ALL; + return res; } struct sprite_sheet_span sprite_sheet_get_span(struct sprite_sheet *sheet, struct string name) @@ -933,13 +966,16 @@ struct sprite_sheet_span sprite_sheet_get_span(struct sprite_sheet *sheet, struc struct sprite_sheet_slice sprite_sheet_get_slice(struct sprite_sheet *sheet, struct string name, u32 frame_index) { - struct sprite_sheet_slice res = { 0 }; if (sheet->slice_groups_count > 0) { struct sprite_sheet_slice_group *group = fixed_dict_get(&sheet->slice_groups_dict, name); if (group) { - res = group->frame_slices[frame_index * group->per_frame_count]; + return group->frame_slices[frame_index * group->per_frame_count]; } } + struct sprite_sheet_slice res = { 0 }; + res.center = v2_mul(sheet->frame_size, 0.5f); + res.dir = V2(res.center.x, 0); + res.dir_norm = V2(0, -0.5); return res; } diff --git a/src/sprite.h b/src/sprite.h index 254f8765..135310e7 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -93,10 +93,14 @@ struct sprite_sheet_span { }; struct sprite_sheet_slice { + /* Values suffixed with '_norm' are in the range -0.5 (top / left edge) -> +0.5 (bottom / right edge) */ b32 original; struct rect rect; + struct rect rect_norm; struct v2 center; + struct v2 center_norm; struct v2 dir; + struct v2 dir_norm; }; struct sprite_sheet_slice_array { diff --git a/src/user.c b/src/user.c index 23808599..7a52f721 100644 --- a/src/user.c +++ b/src/user.c @@ -453,7 +453,7 @@ INTERNAL void user_update(void) 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(e0->player_acceleration, e1->player_acceleration, tick_blend); - e->player_aim = v2_lerp(e0->player_aim, e1->player_aim, tick_blend); + e->focus = v2_lerp(e0->focus, e1->focus, tick_blend); e->sprite_quad_xform = xform_lerp(e0->sprite_quad_xform, e1->sprite_quad_xform, tick_blend); e->animation_time_in_frame = math_lerp64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend); @@ -785,41 +785,7 @@ INTERNAL void user_update(void) } } - - struct quad quad; - { - struct xform sprite_xf = XFORM_IDENT; - { - struct v2 sprite_size_meters = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT); - - /* TODO: Cache these in slice */ - struct v2 pivot_pos; - f32 pivot_rot; - { - struct sprite_sheet_slice pivot_slice = sprite_sheet_get_slice(sheet, STR("pivot"), frame.index); - - /* Pivot pos */ - struct v2 pivot_pos_norm = pivot_slice.center; - pivot_pos_norm = v2_mul(pivot_pos_norm, 2); - pivot_pos_norm = v2_sub(pivot_pos_norm, sheet->frame_size); - pivot_pos_norm = v2_div_v2(pivot_pos_norm, sheet->frame_size); - pivot_pos = v2_mul_v2(pivot_pos_norm, v2_mul(sprite_size_meters, 0.5f)); - - /* Pivot rot */ - pivot_rot = -v2_angle(pivot_slice.dir) - (TAU / 4.0f); - } - - /* Apply entity sprite xform */ - sprite_xf = xform_mul(sprite_xf, ent->sprite_quad_xform); - - /* Apply Pivot */ - sprite_xf = xform_rotate(sprite_xf, pivot_rot); - sprite_xf = xform_translate(sprite_xf, v2_neg(pivot_pos)); - sprite_xf = xform_scale(sprite_xf, sprite_size_meters); - } - - quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xform_mul(ent->world_xform, sprite_xf)); - } + struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xform_mul(ent->world_xform, ent->sprite_quad_xform)); /* TODO: Fade in placeholder if texture isn't loaded */ struct draw_sprite_params params = DRAW_SPRITE_PARAMS(.sprite = sprite, .tint = ent->sprite_tint, .clip = frame.clip); @@ -908,7 +874,7 @@ INTERNAL void user_update(void) f32 thickness = 3; f32 arrow_height = 10; struct v2 pos = xform_mul_v2(G.world_view, ent->world_xform.og); - struct v2 aim_ray = xform_basis_mul_v2(G.world_view, ent->player_aim); + struct v2 aim_ray = xform_basis_mul_v2(G.world_view, ent->focus); draw_solid_arrow_ray(G.viewport_canvas, pos, aim_ray, thickness, arrow_height, color); }