diff --git a/src/common.h b/src/common.h index 4555dedf..bb5270bb 100644 --- a/src/common.h +++ b/src/common.h @@ -273,10 +273,11 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t); #define RGB_32_F(r, g, b) RGBA_32_F(r, g, b, 1.f) #define COLOR_WHITE RGB_32(0xFF, 0xFF, 0xFF) -#define COLOR_BLACK RGB_32(0, 0, 0 ) -#define COLOR_RED RGB_32(0xFF, 0, 0 ) -#define COLOR_GREEN RGB_32(0, 0xFF, 0 ) -#define COLOR_BLUE RGB_32(0, 0, 0xFF) +#define COLOR_BLACK RGB_32(0 , 0 , 0 ) +#define COLOR_RED RGB_32(0xFF, 0 , 0 ) +#define COLOR_GREEN RGB_32(0 , 0xFF, 0 ) +#define COLOR_BLUE RGB_32(0 , 0 , 0xFF) +#define COLOR_YELLOW RGB_32(0xFF, 0xFF, 0 ) /* Barrier */ #if COMPILER_MSVC @@ -347,6 +348,9 @@ GLOBAL const f64 *_f64_infinity = (f64 *)&_f64_infinity_u64; #define F32_INFINITY (*_f32_infinity) #define F64_INFINITY (*_f64_infinity) +#define F32_IS_NAN(x) (x != x) +#define F64_IS_NAN(x) (x != x) + #define PI ((f32)3.14159265358979323846) #define TAU ((f32)6.28318530717958647693) diff --git a/src/draw.c b/src/draw.c index 9713c00a..cf0271b4 100644 --- a/src/draw.c +++ b/src/draw.c @@ -196,6 +196,28 @@ void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array, } } +void draw_solid_circle_line(struct renderer_canvas *canvas, struct v2 pos, f32 radius, f32 thickness, u32 color, u32 detail) +{ + struct temp_arena scratch = scratch_begin_no_conflict(); + + struct v2 *points = arena_push_array(scratch.arena, struct v2, detail); + for (u32 i = 0; i < detail; ++i) { + struct v2 p = V2( + radius * math_cos(i * (PI * 2.f) / detail), + radius * math_sin(i * (PI * 2.f) / detail) + ); + points[i] = v2_add(pos, p); + } + + struct v2_array a = { + .points = points, + .count = detail + }; + draw_solid_poly_line(canvas, a, true, thickness, color); + + scratch_end(scratch); +} + void draw_solid_quad_line(struct renderer_canvas *canvas, struct quad quad, f32 thickness, u32 color) { struct v2 points[] = { quad.p1, quad.p2, quad.p3, quad.p4 }; diff --git a/src/draw.h b/src/draw.h index 6f6f07c9..2ed5896c 100644 --- a/src/draw.h +++ b/src/draw.h @@ -33,6 +33,7 @@ void draw_solid_rect(struct renderer_canvas *canvas, struct rect rect, u32 color void draw_solid_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, u32 color); void draw_solid_ray(struct renderer_canvas *canvas, struct v2 start, struct v2 ray, f32 thickness, u32 color); void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array, b32 loop, f32 thickness, u32 color); +void draw_solid_circle_line(struct renderer_canvas *canvas, struct v2 pos, f32 radius, f32 thickness, u32 color, u32 detail); void draw_solid_quad_line(struct renderer_canvas *canvas, struct quad quad, f32 thickness, u32 color); void draw_solid_rect_line(struct renderer_canvas *canvas, struct rect rect, f32 thickness, u32 color); void draw_solid_arrow_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, f32 arrowhead_height, u32 color); diff --git a/src/game.c b/src/game.c index f7c24692..50e5e771 100644 --- a/src/game.c +++ b/src/game.c @@ -179,6 +179,7 @@ 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.one_handed"); e->sprite_span_name = STR("idle.two_handed"); entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); @@ -309,7 +310,7 @@ INTERNAL void game_update(void) struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("pivot"), ent->animation_frame); struct v2 sprite_size = v2_div(sheet->frame_size, (f32)PIXELS_PER_UNIT); - struct v2 pivot_pos = v2_mul_v2(slice.center_norm, sprite_size); + struct v2 pivot_pos = v2_mul_v2(slice.center, sprite_size); ent->sprite_xform = xform_with_scale(XFORM_POS(v2_neg(pivot_pos)), sprite_size); } @@ -349,21 +350,64 @@ INTERNAL void game_update(void) /* Update focus */ ent->focus = G.world.player_aim; +#if 0 struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame); - struct v2 hold_pos = xform_mul_v2(ent->xform_world, xform_mul_v2(ent->sprite_xform, slice.center_norm)); - struct v2 hold_dir = xform_basis_mul_v2(ent->xform_world, xform_basis_mul_v2(ent->sprite_xform, slice.dir_norm)); + struct v2 hold_pos = xform_mul_v2(ent->xform_world, xform_mul_v2(ent->sprite_xform, slice.center)); + struct v2 hold_dir = xform_basis_mul_v2(ent->xform_world, xform_basis_mul_v2(ent->sprite_xform, slice.dir)); struct v2 focus_pos = v2_add(ent->xform_world.og, ent->focus); struct v2 hold_focus_dir = v2_sub(focus_pos, hold_pos); /* Only rotate if rotation to focus around origin is possible */ - f32 ent_hold_len = v2_len(v2_sub(hold_pos, ent->xform_world.og)); + f32 min_hold_radius = 0; + { + struct v2 center = xform_mul_v2(ent->sprite_xform_world, slice.center); + struct v2 center_dir = v2_norm(xform_basis_mul_v2(ent->sprite_xform_world, slice.dir)); + struct v2 minpoint = v2_closest_point_ray(center, center_dir, ent->xform_world.og); + min_hold_radius = v2_len(v2_sub(minpoint, ent->xform_world.og)); + } f32 ent_focus_len = v2_len(v2_sub(focus_pos, ent->xform_world.og)); - if (ent_focus_len > ent_hold_len) { + if (ent_focus_len > min_hold_radius) { ent->xform = xform_rotate(ent->xform, v2_angle_from_dirs(hold_dir, hold_focus_dir)); } +#else + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame); + + struct xform xf = ent->xform; + + struct v2 ent_pos = ent->xform_world.og; + struct v2 focus_pos = v2_add(ent_pos, ent->focus); + struct v2 hold_pos = xform_mul_v2(ent->xform_world, xform_mul_v2(ent->sprite_xform, slice.center)); + + struct v2 hold_dir = xform_basis_mul_v2(ent->xform_world, xform_basis_mul_v2(ent->sprite_xform, slice.dir)); + struct v2 hold_ent_dir = v2_sub(ent_pos, hold_pos); + struct v2 focus_ent_dir = v2_sub(ent_pos, focus_pos); + + f32 hold_ent_len = v2_len(hold_ent_dir); + f32 focus_ent_len = v2_len(focus_ent_dir); + + f32 final_hold_angle_btw_ent_and_focus = v2_angle_from_dirs(v2_norm(hold_ent_dir), v2_norm(hold_dir)); + f32 final_focus_angle_btw_ent_and_hold = math_asin((math_sin(final_hold_angle_btw_ent_and_focus) * hold_ent_len) / focus_ent_len); + f32 final_ent_angle_btw_focus_and_hold = PI - (final_focus_angle_btw_ent_and_hold + final_hold_angle_btw_ent_and_focus); + + f32 final_hold_ent_angle_offset; + { + struct xform ent_world_xform_unrotated = xform_with_rotation(ent->xform_world, 0); + struct v2 hold_pos_unrotated = xform_mul_v2(ent_world_xform_unrotated, xform_mul_v2(ent->sprite_xform, slice.center)); + final_hold_ent_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_norm(v2_sub(hold_pos_unrotated, ent_world_xform_unrotated.og))); + } + + f32 final_ent_angle = v2_angle_from_dirs(V2(0, -1), v2_sub(focus_pos, ent_pos)) + final_ent_angle_btw_focus_and_hold - final_hold_ent_angle_offset; + + if (!F32_IS_NAN(final_ent_angle)) { + xf = xform_with_rotation(xf, final_ent_angle); + } + + ent->xform = xf; +#endif } /* ========================== * diff --git a/src/math.h b/src/math.h index c1e0cfc2..df543993 100644 --- a/src/math.h +++ b/src/math.h @@ -641,6 +641,14 @@ INLINE f32 v2_angle_from_points(struct v2 pt1, struct v2 pt2) return v2_angle(v2_sub(pt2, pt1)); } +INLINE struct v2 v2_closest_point_ray(struct v2 ray_pos, struct v2 ray_dir_norm, struct v2 p) +{ + struct v2 ray_p_dir = v2_sub(p, ray_pos); + f32 dot = v2_dot(ray_dir_norm, ray_p_dir); + struct v2 ray_dir_closest = v2_mul(ray_dir_norm, dot); + return v2_add(ray_pos, ray_dir_closest); +} + /* ========================== * * Mat4x4 * ========================== */ diff --git a/src/sprite.c b/src/sprite.c index 5713e944..0bb6d829 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -518,39 +518,37 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str struct sprite_sheet_slice *slice = &slice_group->frame_slices[(start * slice_group->per_frame_count) + index_in_frame]; slice->original = true; - f32 x1 = ase_slice->x1; - f32 y1 = ase_slice->y1; - f32 x2 = ase_slice->x2; - f32 y2 = ase_slice->y2; + f32 x1_px = ase_slice->x1; + f32 y1_px = ase_slice->y1; + f32 x2_px = ase_slice->x2; + f32 y2_px = ase_slice->y2; + f32 width_px = x2_px - x1_px; + f32 height_px = y2_px - y1_px; + + f32 x1 = (x1_px - frame_center.x) / frame_size.x; + f32 y1 = (y1_px - frame_center.y) / frame_size.y; + f32 x2 = (x2_px - frame_center.x) / frame_size.x; + f32 y2 = (y2_px - frame_center.y) / frame_size.y; f32 width = x2 - x1; f32 height = y2 - y1; - 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_px = RECT(x1_px, y1_px, width_px, height_px); 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_px = V2(x1_px + (width_px * 0.5f), y1_px + (height_px * 0.5f)); 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); + struct v2 dir_px = V2(center_px.x, 0); + struct v2 dir = V2(0, -0.5); + + slice->rect_px = rect_px; + slice->center_px = center_px; + slice->dir_px = dir_px; 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) { @@ -618,14 +616,15 @@ INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, str for (u32 i = 0; i < ase.num_frames; ++i) { /* 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; + struct v2 ray_end = ray_slice->center_px; + struct v2 ray_end_norm = ray_slice->center; /* 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); + point_slice->dir_px = v2_sub(ray_end, point_slice->center_px); + point_slice->dir = v2_sub(ray_end_norm, point_slice->center); + point_slice->has_ray = true; } } @@ -971,10 +970,17 @@ struct sprite_sheet_slice sprite_sheet_get_slice(struct sprite_sheet *sheet, str return group->frame_slices[frame_index * group->per_frame_count]; } } + + /* Return 'pivot' by default */ 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); + if (string_eq(name, STR("pivot"))) { + res.center_px = v2_mul(sheet->frame_size, 0.5f); + res.dir_px = V2(res.center_px.x, 0); + res.dir = V2(0, -0.5); + } else { + res = sprite_sheet_get_slice(sheet, STR("pivot"), frame_index); + } + return res; } diff --git a/src/sprite.h b/src/sprite.h index 135310e7..f659258e 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -93,14 +93,21 @@ 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) */ + /* If true, this slice was not copied over from another frame in the sprite sheet */ b32 original; + + /* If true, the slice has a corresponding '.ray' slice affecting the 'dir' fields */ + b32 has_ray; + + /* Values are in the range -0.5 (top / left edge) -> +0.5 (bottom / right edge) */ struct rect rect; - struct rect rect_norm; struct v2 center; - struct v2 center_norm; struct v2 dir; - struct v2 dir_norm; + + /* '_px' values retain the original sprite pixel dimensions */ + struct rect rect_px; + struct v2 center_px; + struct v2 dir_px; }; struct sprite_sheet_slice_array { diff --git a/src/string.c b/src/string.c index 5eb855a0..94a73242 100644 --- a/src/string.c +++ b/src/string.c @@ -100,7 +100,7 @@ struct string string_from_float(struct arena *arena, f64 f, u32 precision) u8 *final_text = arena_dry_push(arena, u8); u64 final_len = 0; - if (f != f) { + if (F32_IS_NAN(f)) { final_len += string_copy(arena, STR("NaN")).len; } else if (f == F64_INFINITY) { final_len += string_copy(arena, STR("inf")).len; diff --git a/src/user.c b/src/user.c index e631a7a0..63a2005f 100644 --- a/src/user.c +++ b/src/user.c @@ -433,8 +433,9 @@ INTERNAL void user_update(void) world_copy_replace(&G.world, t1); - /* Blend time */ + /* Blend world globals */ G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend); + G.world.player_aim = v2_lerp(t0->player_aim, t1->player_aim, tick_blend); /* Blend entities */ struct entity_array t0_entities = entity_store_as_array(&t0->entity_store); @@ -823,6 +824,17 @@ INTERNAL void user_update(void) debug_draw_xform(ent->xform_world); } + /* Draw aim arrow */ + if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { + struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, ent->sprite); + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame); + struct v2 start = xform_mul_v2(ent->sprite_xform_world, slice.center); + start = xform_mul_v2(G.world_view, start); + struct v2 end = v2_add(ent->xform.og, ent->focus); + end = xform_mul_v2(G.world_view, end); + draw_solid_arrow_line(G.viewport_canvas, start, end, 3, 10, RGBA_32_F(1, 1, 1, 0.5)); + } + /* Draw slices */ if (!sprite_tag_is_nil(ent->sprite)) { struct sprite_tag sprite = ent->sprite; @@ -830,12 +842,29 @@ INTERNAL void user_update(void) for (u64 i = 0; i < sheet->slice_groups_count; ++i) { struct sprite_sheet_slice_group *group = &sheet->slice_groups[i]; + if (string_ends_with(group->name, STR(".ray"))) continue; for (u32 j = 0; j < group->per_frame_count; ++j) { struct sprite_sheet_slice slice = group->frame_slices[(ent->animation_frame * group->per_frame_count) + j]; - struct quad quad = quad_from_rect(slice.rect_norm); - quad = quad_mul_xform(quad, ent->sprite_xform_world); - draw_solid_quad_line(G.viewport_canvas, quad_mul_xform(quad, G.world_view), 3, COLOR_RED); + + struct v2 center = xform_mul_v2(ent->sprite_xform_world, slice.center); + center = xform_mul_v2(G.world_view, center); + + if (!slice.has_ray) { + struct quad quad = quad_from_rect(slice.rect); + quad = quad_mul_xform(quad, ent->sprite_xform_world); + quad = quad_mul_xform(quad, G.world_view); + draw_solid_quad_line(G.viewport_canvas, quad, 2, RGBA_32_F(1, 0, 0.5, 1)); + } + + draw_solid_circle(G.viewport_canvas, center, 3, RGBA_32_F(1, 0, 0, 1), 20); + + if (slice.has_ray) { + struct v2 ray = xform_basis_mul_v2(ent->sprite_xform_world, slice.dir); + ray = xform_basis_mul_v2(G.world_view, ray); + ray = v2_mul(v2_norm(ray), 50); + draw_solid_arrow_ray(G.viewport_canvas, center, ray, 2, 10, RGBA_32_F(1, 0, 0.5, 1)); + } } } } @@ -852,16 +881,6 @@ INTERNAL void user_update(void) draw_solid_arrow_line(G.viewport_canvas, start, end, thickness, arrow_height, color); } - /* Draw aim */ - if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - u32 color = RGBA_32_F(0.75, 0, 0.75, 0.5); - f32 thickness = 3; - f32 arrow_height = 10; - struct v2 pos = xform_mul_v2(G.world_view, ent->xform_world.og); - 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); - } - /* Draw camera rect */ if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { u32 color = ent == active_camera ? RGBA_32_F(1, 1, 1, 0.5) : RGBA_32_F(0, 0.75, 0, 0.5);