diff --git a/src/common.h b/src/common.h index 8d6952f6..a6d45bfe 100644 --- a/src/common.h +++ b/src/common.h @@ -488,7 +488,6 @@ struct string32 { }; #define UID(hi, lo) (struct uid) { .v = U128((hi), (lo)) } -#define UID0 (struct uid) { 0 } struct uid { union { u128 v; @@ -499,6 +498,7 @@ struct uid { }; }; INLINE b32 uid_eq(struct uid a, struct uid b) { return u128_eq(a.v, b.v); } +INLINE b32 uid_is_zero(struct uid uid) { return u128_eq(uid.v, U128(0, 0)); } struct image_rgba { u32 width; diff --git a/src/sim.c b/src/sim.c index 80c37769..13c400f8 100644 --- a/src/sim.c +++ b/src/sim.c @@ -332,6 +332,8 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_client *client, struct sim_sn root->handle = SIM_ENT_ROOT_HANDLE; root->valid = true; root->is_root = true; + root->mass_unscaled = F32_INFINITY; + root->inertia_unscaled = F32_INFINITY; ++ss->num_ents_allocated; ++ss->num_ents_reserved; } else { diff --git a/src/sim_ent.c b/src/sim_ent.c index 997cb801..de43b447 100644 --- a/src/sim_ent.c +++ b/src/sim_ent.c @@ -99,7 +99,7 @@ void sim_ent_release_raw(struct sim_ent *ent) } /* Release uid */ - sim_ent_set_uid(ent, UID0); + sim_ent_set_uid(ent, UID(0, 0)); /* Release */ ++ent->handle.gen; @@ -174,7 +174,7 @@ void sim_ent_set_uid(struct sim_ent *ent, struct uid uid) struct uid old_uid = ent->uid; /* Release old from lookup */ - if (!uid_eq(old_uid, UID0)) { + if (!uid_is_zero(old_uid)) { u64 hash = hash_from_uid(old_uid); struct sim_ent_bucket *bucket = &buckets[hash % num_uid_buckets]; struct sim_ent *prev = sim_ent_nil(); @@ -203,7 +203,7 @@ void sim_ent_set_uid(struct sim_ent *ent, struct uid uid) } /* Insert new uid into lookup */ - if (!uid_eq(uid, UID0)) { + if (!uid_is_zero(uid)) { u64 hash = hash_from_uid(uid); struct sim_ent_bucket *bucket = &buckets[hash % num_uid_buckets]; struct sim_ent *last = sim_ent_from_handle(ss, bucket->last); @@ -223,7 +223,7 @@ struct sim_ent *sim_ent_from_uid(struct sim_snapshot *ss, struct uid uid) { struct sim_ent *res = sim_ent_nil(); u64 num_buckets = ss->num_uid_buckets; - if (num_buckets > 0 && !uid_eq(uid, UID0)) { + if (num_buckets > 0 && !uid_is_zero(uid)) { u64 hash = hash_from_uid(uid); struct sim_ent_bucket *bucket = &ss->uid_buckets[hash % num_buckets]; for (struct sim_ent *e = sim_ent_from_handle(ss, bucket->first); e->valid; e = sim_ent_from_handle(ss, e->next_in_uid_bucket)) { diff --git a/src/sim_ent.h b/src/sim_ent.h index 81c708e6..a297e21b 100644 --- a/src/sim_ent.h +++ b/src/sim_ent.h @@ -387,6 +387,11 @@ INLINE b32 sim_ent_is_valid_and_active(struct sim_ent *ent) return ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE); } +INLINE b32 sim_ent_should_simulate(struct sim_ent *ent) +{ + return !sim_ent_has_prop(ent, SIM_ENT_PROP_SYNC_DST); +} + /* ========================== * * Ent functions * ========================== */ diff --git a/src/sim_step.c b/src/sim_step.c index 4650f62c..2f954d8d 100644 --- a/src/sim_step.c +++ b/src/sim_step.c @@ -358,124 +358,117 @@ void sim_step(struct sim_step_ctx *ctx) sim_ent_release_all_with_prop(world, SIM_ENT_PROP_RELEASE); sim_accel_reset(world, ctx->accel); - if (ctx->is_master) { - /* ========================== * - * Activate entities - * ========================== */ + /* ========================== * + * Activate entities + * ========================== */ - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!ent->valid) continue; + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!ent->valid) continue; + if (!sim_ent_should_simulate(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE)) { - u64 atick = ent->activation_tick; - if (atick != 0 || world->tick >= atick) { - sim_ent_activate(ent, world->tick); + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE)) { + u64 atick = ent->activation_tick; + if (atick != 0 || world->tick >= atick) { + sim_ent_activate(ent, world->tick); + } + } + } + + /* ========================== * + * Reset triggered entities + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + + if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK)) { + sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK); + sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); + } else if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) { + sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); + } + } + + /* ========================== * + * Process client cmds + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *cmd_ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(cmd_ent)) continue; + + if (sim_ent_has_prop(cmd_ent, SIM_ENT_PROP_CMD_CONTROL)) { + struct sim_ent *client_ent = sim_ent_from_handle(world, cmd_ent->cmd_client); + if (sim_ent_is_valid_and_active(client_ent)) { + if (!ctx->is_master && !uid_eq(client_ent->uid, world->local_client_ent_uid)) { + /* We are not the master and the command is not our own, skip processing */ + continue; } - } - } - /* ========================== * - * Spawn test entities - * ========================== */ + /* Process control cmd for client */ + struct sim_control old_control = client_ent->client_control; + struct sim_control *control = &client_ent->client_control; + *control = cmd_ent->cmd_control; + { + client_ent->client_dbg_drag_start = false; + client_ent->client_dbg_drag_stop = false; - /* TODO: remove this (testing) */ - /* Initialize entities */ - { - static b32 run = 0; - if (!run) { - run = 1; - spawn_test_entities(world, V2(0, 0)); - } - } + if (v2_len_sq(control->move) > 1) { + /* Cap movement vector magnitude at 1 */ + control->move = v2_norm(control->move); + } - /* ========================== * - * Reset triggered entities - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK)) { - sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK); - sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); - } else if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) { - sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); - } - } - - /* ========================== * - * Process client cmds - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *cmd_ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(cmd_ent)) continue; - - if (sim_ent_has_prop(cmd_ent, SIM_ENT_PROP_CMD_CONTROL)) { - struct sim_ent *client_ent = sim_ent_from_handle(world, cmd_ent->cmd_client); - if (sim_ent_is_valid_and_active(client_ent)) { - /* Process control cmd for client */ - struct sim_control old_control = client_ent->client_control; - struct sim_control *control = &client_ent->client_control; - *control = cmd_ent->cmd_control; + /* Determine cursor pos from focus */ { - client_ent->client_dbg_drag_start = false; - client_ent->client_dbg_drag_stop = false; - - if (v2_len_sq(control->move) > 1) { - /* Cap movement vector magnitude at 1 */ - control->move = v2_norm(control->move); + struct sim_ent_handle client_control_ent_handle = client_ent->client_control_ent; + struct sim_ent *client_control_ent = sim_ent_from_handle(world, client_control_ent_handle); + if (client_control_ent->valid || sim_ent_handle_eq(client_control_ent_handle, SIM_ENT_NIL_HANDLE)) { + /* Only update cursor pos if focus ent is valid (or nil) */ + client_ent->client_cursor_pos = v2_add(sim_ent_get_xform(client_control_ent).og, client_ent->client_control.focus); } + } - /* Determine cursor pos from focus */ - { - struct sim_ent_handle client_control_ent_handle = client_ent->client_control_ent; - struct sim_ent *client_control_ent = sim_ent_from_handle(world, client_control_ent_handle); - if (client_control_ent->valid || sim_ent_handle_eq(client_control_ent_handle, SIM_ENT_NIL_HANDLE)) { - /* Only update cursor pos if focus ent is valid (or nil) */ - client_ent->client_cursor_pos = v2_add(sim_ent_get_xform(client_control_ent).og, client_ent->client_control.focus); - } - } + /* Dereference hovered ent */ + client_ent->client_hovered_ent = sim_ent_from_uid(world, cmd_ent->cmd_hovered_ent_uid)->handle; - /* Dereference hovered ent */ - client_ent->client_hovered_ent = sim_ent_from_uid(world, cmd_ent->cmd_hovered_ent_uid)->handle; - - u32 flags = control->flags; - if (flags & SIM_CONTROL_FLAG_DRAG) { - if (!(old_control.flags & SIM_CONTROL_FLAG_DRAG)) { - client_ent->client_dbg_drag_start = true; - } - } else { - if (old_control.flags & SIM_CONTROL_FLAG_DRAG) { - client_ent->client_dbg_drag_stop = true; - } + u32 flags = control->flags; + if (flags & SIM_CONTROL_FLAG_DRAG) { + if (!(old_control.flags & SIM_CONTROL_FLAG_DRAG)) { + client_ent->client_dbg_drag_start = true; } - if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) { - if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) { - test_clear_level(world); - } + } else { + if (old_control.flags & SIM_CONTROL_FLAG_DRAG) { + client_ent->client_dbg_drag_stop = true; } - if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) { - if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN_TEST)) { - logf_info("Spawning (test)"); - u32 count = 1; - f32 spread = 1; - for (u32 j = 0; j < count; ++j) { - spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread)); - } + } + if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) { + if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) { + test_clear_level(world); + } + } + if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) { + if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN_TEST)) { + logf_info("Spawning (test)"); + u32 count = 1; + f32 spread = 1; + for (u32 j = 0; j < count; ++j) { + spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread)); } } } } } } + } - /* ========================== * - * Create client player ents - * ========================== */ + /* ========================== * + * Create client player ents + * ========================== */ + if (ctx->is_master) { for (u64 i = 0; i < world->num_ents_reserved; ++i) { struct sim_ent *ent = &world->ents[i]; if (!sim_ent_is_valid_and_active(ent)) continue; @@ -495,704 +488,691 @@ void sim_step(struct sim_step_ctx *ctx) } } } + } - /* ========================== * - * Update entity control from client control - * ========================== */ + /* ========================== * + * Update entity control from client control + * ========================== */ - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; - if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { - struct sim_ent *client_ent = sim_ent_from_handle(world, ent->controlling_client); - if (client_ent->valid) { - ent->control = client_ent->client_control; - /* TODO: Move this */ - if (ent->control.flags & SIM_CONTROL_FLAG_FIRE) { - sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); - } else { - sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { + struct sim_ent *client_ent = sim_ent_from_handle(world, ent->controlling_client); + if (client_ent->valid) { + ent->control = client_ent->client_control; + /* TODO: Move this */ + if (ent->control.flags & SIM_CONTROL_FLAG_FIRE) { + sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); + } else { + sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); + } + } + } + } + + /* ========================== * + * Update entities from sprite + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (sprite_tag_is_nil(ent->sprite)) continue; + + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); + + /* Update animation */ + { + struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); + if (ent->animation_last_frame_change_time_ns == 0) { + ent->animation_last_frame_change_time_ns = SECONDS_FROM_NS(world->sim_time_ns); + } + + f64 time_in_frame = SECONDS_FROM_NS(world->sim_time_ns - ent->animation_last_frame_change_time_ns); + u64 frame_index = ent->animation_frame; + if (frame_index < span.start || frame_index > span.end) { + frame_index = span.start; + } + + if (span.end > span.start + 1) { + struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, frame_index); + while (time_in_frame > frame.duration) { + time_in_frame -= frame.duration; + ++frame_index; + if (frame_index > span.end) { + /* Loop animation */ + frame_index = span.start; } + frame = sprite_sheet_get_frame(sheet, frame_index); + ent->animation_last_frame_change_time_ns = world->sim_time_ns; } } + + ent->animation_frame = frame_index; } - /* ========================== * - * Update entities from sprite - * ========================== */ + /* Update sprite local xform */ + { + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("pivot"), ent->animation_frame); + struct v2 sprite_size = v2_div(sheet->frame_size, (f32)IMAGE_PIXELS_PER_UNIT); - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (sprite_tag_is_nil(ent->sprite)) continue; + struct v2 dir = v2_mul_v2(sprite_size, slice.dir); + f32 rot = v2_angle(dir) + PI / 2; - struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); - - /* Update animation */ - { - struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); - if (ent->animation_last_frame_change_time_ns == 0) { - ent->animation_last_frame_change_time_ns = SECONDS_FROM_NS(world->sim_time_ns); - } - - f64 time_in_frame = SECONDS_FROM_NS(world->sim_time_ns - ent->animation_last_frame_change_time_ns); - u64 frame_index = ent->animation_frame; - if (frame_index < span.start || frame_index > span.end) { - frame_index = span.start; - } - - if (span.end > span.start + 1) { - struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, frame_index); - while (time_in_frame > frame.duration) { - time_in_frame -= frame.duration; - ++frame_index; - if (frame_index > span.end) { - /* Loop animation */ - frame_index = span.start; - } - frame = sprite_sheet_get_frame(sheet, frame_index); - ent->animation_last_frame_change_time_ns = world->sim_time_ns; - } - } - - ent->animation_frame = frame_index; - } - - /* Update sprite local xform */ - { - struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("pivot"), ent->animation_frame); - struct v2 sprite_size = v2_div(sheet->frame_size, (f32)IMAGE_PIXELS_PER_UNIT); - - struct v2 dir = v2_mul_v2(sprite_size, slice.dir); - f32 rot = v2_angle(dir) + PI / 2; - - struct xform xf = XFORM_IDENT; - xf = xform_rotated(xf, -rot); - xf = xform_scaled(xf, sprite_size); - xf = xform_translated(xf, v2_neg(slice.center)); - ent->sprite_local_xform = xf; - } - - /* Update collider from sprite */ - if (ent->sprite_collider_slice.len > 0) { - struct xform cxf = ent->sprite_local_xform; - - struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, ent->sprite_collider_slice, ent->animation_frame); - ent->local_collider = collider_from_quad(xform_mul_quad(cxf, quad_from_rect(slice.rect))); - } - - /* Test collider */ - #if 0 - if (sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) { - //if ((true)) { - #if 0 - ent->local_collider.points[0] = V2(0, 0); - ent->local_collider.count = 1; - ent->local_collider.radius = 0.5; - #elif 0 - ent->local_collider.points[0] = v2_with_len(V2(0.08f, 0.17f), 0.15f); - ent->local_collider.points[1] = v2_with_len(V2(-0.07f, -0.2f), 0.15f); - ent->local_collider.count = 2; - ent->local_collider.radius = 0.075f; - #elif 1 - #if 0 - /* "Bad" winding order */ - ent->local_collider.points[0] = V2(-0.15, 0.15); - ent->local_collider.points[1] = V2(0.15, 0.15); - ent->local_collider.points[2] = V2(0, -0.15); - #else - ent->local_collider.points[0] = V2(0, -0.15); - ent->local_collider.points[1] = V2(0.15, 0.15); - ent->local_collider.points[2] = V2(-0.15, 0.15); - #endif - ent->local_collider.count = 3; - ent->local_collider.radius = 0.25; - //ent->local_collider.radius = math_fabs(math_sin(ctx->tick.time) / 3); - #else - //ent->local_collider.radius = 0.5; - ent->local_collider.radius = 0.25; - //ent->local_collider.radius = 0.; - #endif - } - #endif + struct xform xf = XFORM_IDENT; + xf = xform_rotated(xf, -rot); + xf = xform_scaled(xf, sprite_size); + xf = xform_translated(xf, v2_neg(slice.center)); + ent->sprite_local_xform = xf; } - /* ========================== * - * Update attachments - * ========================== */ + /* Update collider from sprite */ + if (ent->sprite_collider_slice.len > 0) { + struct xform cxf = ent->sprite_local_xform; - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ATTACHED)) continue; - - struct sim_ent *parent = sim_ent_from_handle(world, ent->parent); - struct sprite_tag parent_sprite = parent->sprite; - struct sprite_sheet *parent_sheet = sprite_sheet_from_tag_await(sprite_frame_scope, parent_sprite); - - struct xform parent_sprite_xf = parent->sprite_local_xform; - - struct sprite_sheet_slice attach_slice = sprite_sheet_get_slice(parent_sheet, ent->attach_slice, parent->animation_frame); - struct v2 attach_pos = xform_mul_v2(parent_sprite_xf, attach_slice.center); - struct v2 attach_dir = xform_basis_mul_v2(parent_sprite_xf, attach_slice.dir); - - struct xform xf = sim_ent_get_local_xform(ent); - xf.og = attach_pos; - xf = xform_basis_with_rotation_world(xf, v2_angle(attach_dir) + PI / 2); - sim_ent_set_local_xform(ent, xf); + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, ent->sprite_collider_slice, ent->animation_frame); + ent->local_collider = collider_from_quad(xform_mul_quad(cxf, quad_from_rect(slice.rect))); } - /* ========================== * - * Test - * ========================== */ - - #if 0 - for (u64 ent_index = 0; ent_index < ss_blended->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &ss_blended->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) continue; - - #if 0 - if (!ent->test_initialized) { - ent->test_initialized = true; - ent->test_start_local_xform = sim_ent_get_local_xform(ent); - ent->test_start_sprite_xform = ent->sprite_local_xform; - } - - f32 t = (f32)time; - struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); - f32 r = t * 3; - struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); - (UNUSED)og; - (UNUSED)r; - (UNUSED)s; - - og = v2_add(og, ent->test_start_local_xform.og); - r += xform_get_rotation(ent->test_start_local_xform); - s = v2_add(s, xform_get_scale(ent->test_start_local_xform)); - - struct xform xf = sim_ent_get_local_xform(ent); - xf.og = og; - xf = xform_rotated_to(xf, r); - xf = xform_scaled_to(xf, s); - sim_ent_set_local_xform(ent, xf); - #else - f32 t = (f32)time; - struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); - f32 rot = t * PI / 3; - struct v2 scale = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); - (UNUSED)og; - (UNUSED)rot; - (UNUSED)scale; - - struct xform xf = sim_ent_get_local_xform(ent); - xf = xform_rotated_to(xf, rot); - xf = xform_scaled_to(xf, scale); - sim_ent_set_local_xform(ent, xf); - #endif - } - #endif - - /* ========================== * - * Trigger equipped - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED)) { - struct sim_ent *eq = sim_ent_from_handle(world, ent->equipped); - if (sim_ent_is_valid_and_active(eq)) { - sim_ent_enable_prop(eq, SIM_ENT_PROP_TRIGGERED_THIS_TICK); - } - } - } - - /* ========================== * - * Process triggered entities - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) continue; - if ((world->sim_time_ns - ent->last_triggered_ns < NS_FROM_SECONDS(ent->trigger_delay)) && ent->last_triggered_ns != 0) continue; - - ent->last_triggered_ns = world->sim_time_ns; - - /* Fire weapon */ - if (sim_ent_has_prop(ent, SIM_ENT_PROP_WEAPON)) { - struct sprite_tag sprite = ent->sprite; - u32 animation_frame = ent->animation_frame; - struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, sprite); - - struct xform sprite_local_xform = ent->sprite_local_xform; - - struct sprite_sheet_slice out_slice = sprite_sheet_get_slice(sheet, LIT("out"), animation_frame); - struct v2 rel_pos = xform_mul_v2(sprite_local_xform, out_slice.center); - struct v2 rel_dir = xform_basis_mul_v2(sprite_local_xform, out_slice.dir); - - /* Spawn bullet */ - struct sim_ent *bullet; - { - bullet = sim_ent_alloc_sync_src(root); - - bullet->bullet_src = ent->handle; - bullet->bullet_src_pos = rel_pos; - bullet->bullet_src_dir = rel_dir; - //bullet->bullet_impulse = 0.75f; - bullet->bullet_impulse = 2.0f; - bullet->bullet_knockback = 10; - bullet->mass_unscaled = 0.04f; - bullet->inertia_unscaled = 0.00001f; - bullet->layer = SIM_LAYER_BULLETS; - - #if 1 - /* Point collider */ - bullet->local_collider.points[0] = V2(0, 0); - bullet->local_collider.count = 1; - #else - bullet->sprite = sprite_tag_from_path(LIT("res/graphics/bullet.ase")); - bullet->sprite_collider_slice = LIT("shape"); - #endif - - sim_ent_enable_prop(bullet, SIM_ENT_PROP_BULLET); - sim_ent_enable_prop(bullet, SIM_ENT_PROP_SENSOR); - } - - /* Spawn tracer */ - { - struct sim_ent *tracer = sim_ent_alloc_sync_src(root); - tracer->tracer_fade_duration = 0.025f; - tracer->layer = SIM_LAYER_TRACERS; - sim_ent_enable_prop(tracer, SIM_ENT_PROP_TRACER); - - bullet->bullet_tracer = tracer->handle; - } - } - } - - /* ========================== * - * Create motor joints from control move - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { - struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->move_joint); - if (!sim_ent_is_valid_and_active(joint_ent)) { - joint_ent = sim_ent_alloc_sync_src(root); - joint_ent->mass_unscaled = F32_INFINITY; - joint_ent->inertia_unscaled = F32_INFINITY; - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - ent->move_joint = joint_ent->handle; - - struct phys_motor_joint_def def = ZI; - def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ - def.e1 = ent->handle; - def.correction_rate = 0; - def.max_force = ent->control_force; - def.max_torque = 0; - joint_ent->motor_joint_data = phys_motor_joint_from_def(def); - } - - sim_ent_set_xform(joint_ent, XFORM_IDENT); /* Reset joint ent position */ - sim_ent_set_linear_velocity(joint_ent, v2_mul(v2_clamp_len(ent->control.move, 1), ent->control_force_max_speed)); - } - } - - /* ========================== * - * Create motor joints from control focus (aim) - * ========================== */ - - #if SIM_PLAYER_AIM - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { - struct xform xf = sim_ent_get_xform(ent); - struct xform sprite_xf = xform_mul(xf, ent->sprite_local_xform); - - /* Retrieve / create aim joint */ - struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->aim_joint); - if (!sim_ent_is_valid_and_active(joint_ent)) { - joint_ent = sim_ent_alloc_sync_src(root); - joint_ent->mass_unscaled = F32_INFINITY; - joint_ent->inertia_unscaled = F32_INFINITY; - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); /* Since we'll be setting velocity manually */ - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - ent->aim_joint = joint_ent->handle; - - struct phys_motor_joint_def def = ZI; - def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ - def.e1 = ent->handle; - def.max_force = 0; - def.max_torque = ent->control_torque; - joint_ent->motor_joint_data = phys_motor_joint_from_def(def); - } - - /* Set correction rate dynamically since motor velocity is only set for one frame */ - joint_ent->motor_joint_data.correction_rate = 10 * sim_dt; - - - /* Solve for final angle using law of sines */ - f32 new_angle; - { - struct v2 ent_pos = xf.og; - struct v2 focus_pos = v2_add(ent_pos, ent->control.focus); - - struct v2 sprite_hold_pos; - struct v2 sprite_hold_dir; - { - struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); - struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("attach.wep"), ent->animation_frame); - sprite_hold_pos = slice.center; - sprite_hold_dir = slice.dir; - } - - struct v2 hold_dir = xform_basis_mul_v2(sprite_xf, sprite_hold_dir); - struct v2 hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); - if (v2_eq(hold_pos, ent_pos)) { - /* If hold pos is same as origin (E.G if pivot is being used as hold pos), then move hold pos forward a tad to avoid issue */ - sprite_hold_pos = v2_add(sprite_hold_pos, V2(0, -1)); - hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); - } - - f32 forward_hold_angle_offset; - { - struct xform xf_unrotated = xform_basis_with_rotation_world(xf, 0); - struct v2 hold_pos_unrotated = xform_mul_v2(xf_unrotated, xform_mul_v2(ent->sprite_local_xform, sprite_hold_pos)); - forward_hold_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_sub(hold_pos_unrotated, xf_unrotated.og)); - } - - 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(hold_ent_dir, 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); - - new_angle = math_unwind_angle(v2_angle_from_dirs(V2(0, -1), v2_sub(focus_pos, ent_pos)) + final_ent_angle_btw_focus_and_hold - forward_hold_angle_offset); - } - - f32 new_vel = 0; - if (!F32_IS_NAN(new_angle)) { - const f32 angle_error_allowed = 0.001f; - struct xform joint_xf = sim_ent_get_xform(joint_ent); - f32 diff = math_unwind_angle(new_angle - xform_get_rotation(joint_xf)); - if (math_fabs(diff) > angle_error_allowed) { - /* Instantly snap joint ent to new angle */ - new_vel = diff / sim_dt; - } - } - sim_ent_set_angular_velocity(joint_ent, new_vel); - } - } - #endif - - /* ========================== * - * Create motor joints from ground friction (gravity) - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_PHYSICAL_DYNAMIC)) continue; - - struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->ground_friction_joint); - - struct phys_motor_joint_def def = ZI; - def.e0 = root->handle; - def.e1 = ent->handle; - def.correction_rate = 0; - def.max_force = ent->linear_ground_friction; - def.max_torque = ent->angular_ground_friction; - if (joint_ent->motor_joint_data.max_force != def.max_force || joint_ent->motor_joint_data.max_torque != def.max_torque) { - if (!sim_ent_is_valid_and_active(joint_ent)) { - joint_ent = sim_ent_alloc_sync_src(root); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - joint_ent->motor_joint_data = phys_motor_joint_from_def(def); - ent->ground_friction_joint = joint_ent->handle; - } - } - } - - /* ========================== * - * Create mouse joints from client debug drag - * ========================== */ - - for (u64 i = 0; i < world->num_ents_reserved; ++i) { - struct sim_ent *client_ent = &world->ents[i]; - if (!sim_ent_is_valid_and_active(client_ent)) continue; - if (!sim_ent_has_prop(client_ent, SIM_ENT_PROP_CLIENT)) continue; - - struct v2 cursor = client_ent->client_cursor_pos; - b32 start_dragging = client_ent->client_dbg_drag_start; - b32 stop_dragging = client_ent->client_dbg_drag_stop; - - struct sim_ent *joint_ent = sim_ent_from_handle(world, client_ent->client_dbg_drag_joint_ent); - struct sim_ent *target_ent = sim_ent_from_handle(world, joint_ent->mouse_joint_data.target); - - if (start_dragging) { + /* Test collider */ #if 0 - struct xform mouse_xf = xform_from_pos(cursor); - struct collider_shape mouse_shape = ZI; - mouse_shape.points[0] = V2(0, 0); - mouse_shape.count = 1; - - for (u64 sim_ent_index = 0; sim_ent_index < world->num_ents_reserved; ++sim_ent_index) { - struct sim_ent *ent = &world->ents[sim_ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_PHYSICAL_DYNAMIC)) continue; - - struct collider_shape ent_collider = ent->local_collider; - if (ent_collider.count > 0) { - struct xform ent_xf = sim_ent_get_xform(ent); - /* TODO: Can just use boolean GJK */ - struct collider_collision_points_result res = collider_collision_points(&ent_collider, &mouse_shape, ent_xf, mouse_xf); - if (res.num_points > 0) { - target_ent = ent; - break; - } - } - } + if (sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) { + //if ((true)) { +#if 0 + ent->local_collider.points[0] = V2(0, 0); + ent->local_collider.count = 1; + ent->local_collider.radius = 0.5; +#elif 0 + ent->local_collider.points[0] = v2_with_len(V2(0.08f, 0.17f), 0.15f); + ent->local_collider.points[1] = v2_with_len(V2(-0.07f, -0.2f), 0.15f); + ent->local_collider.count = 2; + ent->local_collider.radius = 0.075f; +#elif 1 +#if 0 + /* "Bad" winding order */ + ent->local_collider.points[0] = V2(-0.15, 0.15); + ent->local_collider.points[1] = V2(0.15, 0.15); + ent->local_collider.points[2] = V2(0, -0.15); #else - target_ent = sim_ent_from_handle(world, client_ent->client_hovered_ent); + ent->local_collider.points[0] = V2(0, -0.15); + ent->local_collider.points[1] = V2(0.15, 0.15); + ent->local_collider.points[2] = V2(-0.15, 0.15); #endif - } + ent->local_collider.count = 3; + ent->local_collider.radius = 0.25; + //ent->local_collider.radius = math_fabs(math_sin(ctx->tick.time) / 3); +#else + //ent->local_collider.radius = 0.5; + ent->local_collider.radius = 0.25; + //ent->local_collider.radius = 0.; +#endif + } +#endif + } - if (stop_dragging) { - target_ent = sim_ent_nil(); - } + /* ========================== * + * Update attachments + * ========================== */ - if (sim_ent_is_valid_and_active(target_ent)) { - if (!sim_ent_is_valid_and_active(joint_ent)) { - /* FIXME: Joint ent may never release */ - joint_ent = sim_ent_alloc_sync_src(root); - joint_ent->mass_unscaled = F32_INFINITY; - joint_ent->inertia_unscaled = F32_INFINITY; - client_ent->client_dbg_drag_joint_ent = joint_ent->handle; - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOUSE_JOINT); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - } - struct xform xf = sim_ent_get_xform(target_ent); - f32 mass = target_ent->mass_unscaled * math_fabs(xform_get_determinant(xf)); + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ATTACHED)) continue; - struct phys_mouse_joint_def def = ZI; - def.target = target_ent->handle; - if (sim_ent_handle_eq(joint_ent->mouse_joint_data.target, target_ent->handle)) { - def.point_local_start = joint_ent->mouse_joint_data.point_local_start; - } else { - def.point_local_start = xform_invert_mul_v2(xf, cursor); - } - def.point_local_end = xform_invert_mul_v2(xf, cursor); - def.max_force = mass * 1000; - joint_ent->mouse_joint_data = phys_mouse_joint_from_def(def); - } else if (sim_ent_is_valid_and_active(joint_ent)) { - joint_ent->mouse_joint_data.target = target_ent->handle; - } + struct sim_ent *parent = sim_ent_from_handle(world, ent->parent); + struct sprite_tag parent_sprite = parent->sprite; + struct sprite_sheet *parent_sheet = sprite_sheet_from_tag_await(sprite_frame_scope, parent_sprite); + + struct xform parent_sprite_xf = parent->sprite_local_xform; + + struct sprite_sheet_slice attach_slice = sprite_sheet_get_slice(parent_sheet, ent->attach_slice, parent->animation_frame); + struct v2 attach_pos = xform_mul_v2(parent_sprite_xf, attach_slice.center); + struct v2 attach_dir = xform_basis_mul_v2(parent_sprite_xf, attach_slice.dir); + + struct xform xf = sim_ent_get_local_xform(ent); + xf.og = attach_pos; + xf = xform_basis_with_rotation_world(xf, v2_angle(attach_dir) + PI / 2); + sim_ent_set_local_xform(ent, xf); + } + + /* ========================== * + * Test + * ========================== */ + +#if 0 + for (u64 ent_index = 0; ent_index < ss_blended->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &ss_blended->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) continue; + +#if 0 + if (!ent->test_initialized) { + ent->test_initialized = true; + ent->test_start_local_xform = sim_ent_get_local_xform(ent); + ent->test_start_sprite_xform = ent->sprite_local_xform; } - /* ========================== * - * Physics step - * ========================== */ + f32 t = (f32)time; + struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); + f32 r = t * 3; + struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); + (UNUSED)og; + (UNUSED)r; + (UNUSED)s; - { - struct phys_step_ctx phys = ZI; - phys.sim_step_ctx = ctx; - phys.pre_solve_callback = on_collision; - phys_step(&phys, sim_dt); - } + og = v2_add(og, ent->test_start_local_xform.og); + r += xform_get_rotation(ent->test_start_local_xform); + s = v2_add(s, xform_get_scale(ent->test_start_local_xform)); - /* ========================== * - * Update tracers - * ========================== */ + struct xform xf = sim_ent_get_local_xform(ent); + xf.og = og; + xf = xform_rotated_to(xf, r); + xf = xform_scaled_to(xf, s); + sim_ent_set_local_xform(ent, xf); +#else + f32 t = (f32)time; + struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); + f32 rot = t * PI / 3; + struct v2 scale = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); + (UNUSED)og; + (UNUSED)rot; + (UNUSED)scale; - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRACER)) continue; + struct xform xf = sim_ent_get_local_xform(ent); + xf = xform_rotated_to(xf, rot); + xf = xform_scaled_to(xf, scale); + sim_ent_set_local_xform(ent, xf); +#endif + } +#endif - struct v2 end = sim_ent_get_xform(ent).og; + /* ========================== * + * Trigger equipped + * ========================== */ - struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, sim_dt); - struct v2 gradient_start = v2_add(ent->tracer_gradient_start, tick_velocity); - struct v2 gradient_end = v2_add(ent->tracer_gradient_end, tick_velocity); + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; - if (v2_dot(tick_velocity, v2_sub(gradient_start, end)) > 0) { - /* Tracer has disappeared */ - sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE); - } - - ent->tracer_gradient_start = gradient_start; - ent->tracer_gradient_end = gradient_end; - } - - /* ========================== * - * Initialize bullet kinematics from sources - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_BULLET)) continue; - - if (ent->activation_tick == world->tick) { - struct sim_ent *src = sim_ent_from_handle(world, ent->bullet_src); - struct xform src_xf = sim_ent_get_xform(src); - - struct v2 pos = xform_mul_v2(src_xf, ent->bullet_src_pos); - struct v2 impulse = xform_basis_mul_v2(src_xf, ent->bullet_src_dir); - impulse = v2_with_len(impulse, ent->bullet_impulse); - - #if 0 - /* Add shooter velocity to bullet */ - { - /* TODO: Add angular velocity as well? */ - struct sim_ent *top = sim_ent_from_handle(ss_blended, src->top); - impulse = v2_add(impulse, v2_mul(top->linear_velocity, dt)); - } - #endif - - struct xform xf = XFORM_TRS(.t = pos, .r = v2_angle(impulse) + PI / 2); - sim_ent_set_xform(ent, xf); - sim_ent_enable_prop(ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); - - sim_ent_apply_linear_impulse_to_center(ent, impulse); - - /* Initialize tracer */ - struct sim_ent *tracer = sim_ent_from_handle(world, ent->bullet_tracer); - if (sim_ent_is_valid_and_active(tracer)) { - sim_ent_set_xform(tracer, xf); - sim_ent_enable_prop(tracer, SIM_ENT_PROP_PHYSICAL_KINEMATIC); - sim_ent_set_linear_velocity(tracer, ent->linear_velocity); - tracer->tracer_start = pos; - tracer->tracer_start_velocity = ent->linear_velocity; - tracer->tracer_gradient_end = pos; - tracer->tracer_gradient_start = v2_sub(pos, v2_mul(ent->linear_velocity, tracer->tracer_fade_duration)); - } - - /* Spawn quake */ - { - struct sim_ent *quake = sim_ent_alloc_sync_src(root); - sim_ent_set_xform(quake, XFORM_POS(pos)); - quake->quake_intensity = 0.2f; - quake->quake_fade = quake->quake_intensity / 0.1f; - sim_ent_enable_prop(quake, SIM_ENT_PROP_QUAKE); - } + if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED)) { + struct sim_ent *eq = sim_ent_from_handle(world, ent->equipped); + if (sim_ent_is_valid_and_active(eq)) { + sim_ent_enable_prop(eq, SIM_ENT_PROP_TRIGGERED_THIS_TICK); } } + } - /* ========================== * - * Update cameras - * ========================== */ + /* ========================== * + * Process triggered entities + * ========================== */ - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_CAMERA)) continue; + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) continue; + if ((world->sim_time_ns - ent->last_triggered_ns < NS_FROM_SECONDS(ent->trigger_delay)) && ent->last_triggered_ns != 0) continue; + ent->last_triggered_ns = world->sim_time_ns; + + /* Fire weapon */ + if (sim_ent_has_prop(ent, SIM_ENT_PROP_WEAPON)) { + struct sprite_tag sprite = ent->sprite; + u32 animation_frame = ent->animation_frame; + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, sprite); + + struct xform sprite_local_xform = ent->sprite_local_xform; + + struct sprite_sheet_slice out_slice = sprite_sheet_get_slice(sheet, LIT("out"), animation_frame); + struct v2 rel_pos = xform_mul_v2(sprite_local_xform, out_slice.center); + struct v2 rel_dir = xform_basis_mul_v2(sprite_local_xform, out_slice.dir); + + /* Spawn bullet */ + struct sim_ent *bullet; + { + bullet = sim_ent_alloc_sync_src(root); + + bullet->bullet_src = ent->handle; + bullet->bullet_src_pos = rel_pos; + bullet->bullet_src_dir = rel_dir; + //bullet->bullet_impulse = 0.75f; + bullet->bullet_impulse = 2.0f; + bullet->bullet_knockback = 10; + bullet->mass_unscaled = 0.04f; + bullet->inertia_unscaled = 0.00001f; + bullet->layer = SIM_LAYER_BULLETS; + +#if 1 + /* Point collider */ + bullet->local_collider.points[0] = V2(0, 0); + bullet->local_collider.count = 1; +#else + bullet->sprite = sprite_tag_from_path(LIT("res/graphics/bullet.ase")); + bullet->sprite_collider_slice = LIT("shape"); +#endif + + sim_ent_enable_prop(bullet, SIM_ENT_PROP_BULLET); + sim_ent_enable_prop(bullet, SIM_ENT_PROP_SENSOR); + } + + /* Spawn tracer */ + { + struct sim_ent *tracer = sim_ent_alloc_sync_src(root); + tracer->tracer_fade_duration = 0.025f; + tracer->layer = SIM_LAYER_TRACERS; + sim_ent_enable_prop(tracer, SIM_ENT_PROP_TRACER); + + bullet->bullet_tracer = tracer->handle; + } + } + } + + /* ========================== * + * Create motor joints from control move + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { + struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->move_joint); + if (!sim_ent_is_valid_and_active(joint_ent)) { + joint_ent = sim_ent_alloc_local(root); + joint_ent->mass_unscaled = F32_INFINITY; + joint_ent->inertia_unscaled = F32_INFINITY; + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); + ent->move_joint = joint_ent->handle; + + struct phys_motor_joint_def def = ZI; + def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ + def.e1 = ent->handle; + def.correction_rate = 0; + def.max_force = ent->control_force; + def.max_torque = 0; + joint_ent->motor_joint_data = phys_motor_joint_from_def(def); + } + + sim_ent_set_xform(joint_ent, XFORM_IDENT); /* Reset joint ent position */ + sim_ent_set_linear_velocity(joint_ent, v2_mul(v2_clamp_len(ent->control.move, 1), ent->control_force_max_speed)); + } + } + + /* ========================== * + * Create motor joints from control focus (aim) + * ========================== */ + +#if SIM_PLAYER_AIM + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + //if (!sim_ent_should_simulate(ent)) continue; + + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { struct xform xf = sim_ent_get_xform(ent); + struct xform sprite_xf = xform_mul(xf, ent->sprite_local_xform); - /* Camera follow */ + /* Retrieve / create aim joint */ + struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->aim_joint); + if (!sim_ent_is_valid_and_active(joint_ent)) { + joint_ent = sim_ent_alloc_local(root); + joint_ent->mass_unscaled = F32_INFINITY; + joint_ent->inertia_unscaled = F32_INFINITY; + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); /* Since we'll be setting velocity manually */ + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); + ent->aim_joint = joint_ent->handle; + + struct phys_motor_joint_def def = ZI; + def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ + def.e1 = ent->handle; + def.max_force = 0; + def.max_torque = ent->control_torque; + joint_ent->motor_joint_data = phys_motor_joint_from_def(def); + } + + /* Set correction rate dynamically since motor velocity is only set for one frame */ + joint_ent->motor_joint_data.correction_rate = 10 * sim_dt; + + + /* Solve for final angle using law of sines */ + f32 new_angle; { - struct sim_ent *follow = sim_ent_from_handle(world, ent->camera_follow); + struct v2 ent_pos = xf.og; + struct v2 focus_pos = v2_add(ent_pos, ent->control.focus); - f32 aspect_ratio = 1.0; + struct v2 sprite_hold_pos; + struct v2 sprite_hold_dir; { - struct xform quad_xf = xform_mul(sim_ent_get_xform(ent), ent->camera_quad_xform); - struct v2 camera_size = xform_get_scale(quad_xf); - if (!v2_is_zero(camera_size)) { - aspect_ratio = camera_size.x / camera_size.y; - } + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("attach.wep"), ent->animation_frame); + sprite_hold_pos = slice.center; + sprite_hold_dir = slice.dir; } - f32 ratio_y = 0.33f; - f32 ratio_x = ratio_y / aspect_ratio; - struct v2 camera_focus_dir = v2_mul_v2(follow->control.focus, V2(ratio_x, ratio_y)); - struct v2 camera_focus_pos = v2_add(sim_ent_get_xform(follow).og, camera_focus_dir); - ent->camera_xform_target = xf; - ent->camera_xform_target.og = camera_focus_pos; - /* Lerp camera */ - if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) { - f32 t = 1 - math_pow(2.f, -20.f * (f32)sim_dt); - xf = xform_lerp(xf, ent->camera_xform_target, t); - } else { - /* Skip lerp */ - xf = ent->camera_xform_target; + struct v2 hold_dir = xform_basis_mul_v2(sprite_xf, sprite_hold_dir); + struct v2 hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); + if (v2_eq(hold_pos, ent_pos)) { + /* If hold pos is same as origin (E.G if pivot is being used as hold pos), then move hold pos forward a tad to avoid issue */ + sprite_hold_pos = v2_add(sprite_hold_pos, V2(0, -1)); + hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); } - ent->camera_applied_lerp_continuity_gen_plus_one = ent->camera_lerp_continuity_gen + 1; + + f32 forward_hold_angle_offset; + { + struct xform xf_unrotated = xform_basis_with_rotation_world(xf, 0); + struct v2 hold_pos_unrotated = xform_mul_v2(xf_unrotated, xform_mul_v2(ent->sprite_local_xform, sprite_hold_pos)); + forward_hold_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_sub(hold_pos_unrotated, xf_unrotated.og)); + } + + 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(hold_ent_dir, 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); + + new_angle = math_unwind_angle(v2_angle_from_dirs(V2(0, -1), v2_sub(focus_pos, ent_pos)) + final_ent_angle_btw_focus_and_hold - forward_hold_angle_offset); } - /* Camera shake */ + f32 new_vel = 0; + if (!F32_IS_NAN(new_angle)) { + const f32 angle_error_allowed = 0.001f; + struct xform joint_xf = sim_ent_get_xform(joint_ent); + f32 diff = math_unwind_angle(new_angle - xform_get_rotation(joint_xf)); + if (math_fabs(diff) > angle_error_allowed) { + /* Instantly snap joint ent to new angle */ + new_vel = diff / sim_dt; + } + } + sim_ent_set_angular_velocity(joint_ent, new_vel); + } + } +#endif + + /* ========================== * + * Create motor joints from ground friction (gravity) + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_PHYSICAL_DYNAMIC)) continue; + + struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->ground_friction_joint); + + struct phys_motor_joint_def def = ZI; + def.e0 = root->handle; + def.e1 = ent->handle; + def.correction_rate = 0; + def.max_force = ent->linear_ground_friction; + def.max_torque = ent->angular_ground_friction; + if (joint_ent->motor_joint_data.max_force != def.max_force || joint_ent->motor_joint_data.max_torque != def.max_torque) { + if (!sim_ent_is_valid_and_active(joint_ent)) { + joint_ent = sim_ent_alloc_local(root); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); + joint_ent->motor_joint_data = phys_motor_joint_from_def(def); + ent->ground_friction_joint = joint_ent->handle; + } + } + } + + /* ========================== * + * Create mouse joints from client debug drag + * ========================== */ + + for (u64 i = 0; i < world->num_ents_reserved; ++i) { + struct sim_ent *client_ent = &world->ents[i]; + if (!sim_ent_is_valid_and_active(client_ent)) continue; + if (!sim_ent_should_simulate(client_ent)) continue; + if (!sim_ent_has_prop(client_ent, SIM_ENT_PROP_CLIENT)) continue; + + struct v2 cursor = client_ent->client_cursor_pos; + b32 start_dragging = client_ent->client_dbg_drag_start; + b32 stop_dragging = client_ent->client_dbg_drag_stop; + + struct sim_ent *joint_ent = sim_ent_from_handle(world, client_ent->client_dbg_drag_joint_ent); + struct sim_ent *target_ent = sim_ent_from_handle(world, joint_ent->mouse_joint_data.target); + + if (stop_dragging) { + target_ent = sim_ent_nil(); + } else if (start_dragging) { + target_ent = sim_ent_from_handle(world, client_ent->client_hovered_ent); + } + + if (sim_ent_is_valid_and_active(target_ent)) { + if (!sim_ent_is_valid_and_active(joint_ent)) { + /* FIXME: Joint ent may never release */ + joint_ent = sim_ent_alloc_local(root); + joint_ent->mass_unscaled = F32_INFINITY; + joint_ent->inertia_unscaled = F32_INFINITY; + client_ent->client_dbg_drag_joint_ent = joint_ent->handle; + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOUSE_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); + } + struct xform xf = sim_ent_get_xform(target_ent); + f32 mass = target_ent->mass_unscaled * math_fabs(xform_get_determinant(xf)); + + struct phys_mouse_joint_def def = ZI; + def.target = target_ent->handle; + if (sim_ent_handle_eq(joint_ent->mouse_joint_data.target, target_ent->handle)) { + def.point_local_start = joint_ent->mouse_joint_data.point_local_start; + } else { + def.point_local_start = xform_invert_mul_v2(xf, cursor); + } + def.point_local_end = xform_invert_mul_v2(xf, cursor); + def.max_force = mass * 1000; + joint_ent->mouse_joint_data = phys_mouse_joint_from_def(def); + } else if (sim_ent_is_valid_and_active(joint_ent)) { + joint_ent->mouse_joint_data.target = target_ent->handle; + } + } + + /* ========================== * + * Physics step + * ========================== */ + + if (ctx->is_master) { + struct phys_step_ctx phys = ZI; + phys.sim_step_ctx = ctx; + phys.pre_solve_callback = on_collision; + phys_step(&phys, sim_dt); + } + + /* ========================== * + * Update tracers + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRACER)) continue; + + struct v2 end = sim_ent_get_xform(ent).og; + + struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, sim_dt); + struct v2 gradient_start = v2_add(ent->tracer_gradient_start, tick_velocity); + struct v2 gradient_end = v2_add(ent->tracer_gradient_end, tick_velocity); + + if (v2_dot(tick_velocity, v2_sub(gradient_start, end)) > 0) { + /* Tracer has disappeared */ + sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE); + } + + ent->tracer_gradient_start = gradient_start; + ent->tracer_gradient_end = gradient_end; + } + + /* ========================== * + * Initialize bullet kinematics from sources + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_BULLET)) continue; + + if (ent->activation_tick == world->tick) { + struct sim_ent *src = sim_ent_from_handle(world, ent->bullet_src); + struct xform src_xf = sim_ent_get_xform(src); + + struct v2 pos = xform_mul_v2(src_xf, ent->bullet_src_pos); + struct v2 impulse = xform_basis_mul_v2(src_xf, ent->bullet_src_dir); + impulse = v2_with_len(impulse, ent->bullet_impulse); + +#if 0 + /* Add shooter velocity to bullet */ { - /* TODO: Update based on distance to quake */ - ent->shake = 0; - for (u64 quake_ent_index = 0; quake_ent_index < world->num_ents_reserved; ++quake_ent_index) { - struct sim_ent *quake = &world->ents[quake_ent_index]; - if (!sim_ent_is_valid_and_active(quake)) continue; - if (!sim_ent_has_prop(quake, SIM_ENT_PROP_QUAKE)) continue; - ent->shake += quake->quake_intensity; - } + /* TODO: Add angular velocity as well? */ + struct sim_ent *top = sim_ent_from_handle(ss_blended, src->top); + impulse = v2_add(impulse, v2_mul(top->linear_velocity, dt)); } +#endif + struct xform xf = XFORM_TRS(.t = pos, .r = v2_angle(impulse) + PI / 2); sim_ent_set_xform(ent, xf); - } + sim_ent_enable_prop(ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); - /* ========================== * - * Update quakes - * ========================== */ + sim_ent_apply_linear_impulse_to_center(ent, impulse); - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_QUAKE)) continue; + /* Initialize tracer */ + struct sim_ent *tracer = sim_ent_from_handle(world, ent->bullet_tracer); + if (sim_ent_is_valid_and_active(tracer)) { + sim_ent_set_xform(tracer, xf); + sim_ent_enable_prop(tracer, SIM_ENT_PROP_PHYSICAL_KINEMATIC); + sim_ent_set_linear_velocity(tracer, ent->linear_velocity); + tracer->tracer_start = pos; + tracer->tracer_start_velocity = ent->linear_velocity; + tracer->tracer_gradient_end = pos; + tracer->tracer_gradient_start = v2_sub(pos, v2_mul(ent->linear_velocity, tracer->tracer_fade_duration)); + } - ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * sim_dt)); - if (ent->quake_intensity <= 0) { - sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE); + /* Spawn quake */ + { + struct sim_ent *quake = sim_ent_alloc_sync_src(root); + sim_ent_set_xform(quake, XFORM_POS(pos)); + quake->quake_intensity = 0.2f; + quake->quake_fade = quake->quake_intensity / 0.1f; + sim_ent_enable_prop(quake, SIM_ENT_PROP_QUAKE); } } + } - /* ========================== * - * Update relative layers - * ========================== */ + /* ========================== * + * Update cameras + * ========================== */ + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_CAMERA)) continue; + + struct xform xf = sim_ent_get_xform(ent); + + /* Camera follow */ { - struct temp_arena temp = arena_temp_begin(scratch.arena); + struct sim_ent *follow = sim_ent_from_handle(world, ent->camera_follow); - struct sim_ent **stack = arena_push(temp.arena, struct sim_ent *); - u64 stack_count = 1; - *stack = root; - - while (stack_count > 0) { - struct sim_ent *parent; - arena_pop(temp.arena, struct sim_ent *, &parent); - --stack_count; - - i32 parent_layer = parent->final_layer; - for (struct sim_ent *child = sim_ent_from_handle(world, parent->first); child->valid; child = sim_ent_from_handle(world, child->next)) { - if (sim_ent_is_valid_and_active(child)) { - child->final_layer = parent_layer + child->layer; - *arena_push(temp.arena, struct sim_ent *) = child; - ++stack_count; - } + f32 aspect_ratio = 1.0; + { + struct xform quad_xf = xform_mul(sim_ent_get_xform(ent), ent->camera_quad_xform); + struct v2 camera_size = xform_get_scale(quad_xf); + if (!v2_is_zero(camera_size)) { + 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->control.focus, V2(ratio_x, ratio_y)); + struct v2 camera_focus_pos = v2_add(sim_ent_get_xform(follow).og, camera_focus_dir); + ent->camera_xform_target = xf; + ent->camera_xform_target.og = camera_focus_pos; - arena_temp_end(temp); + /* Lerp camera */ + if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) { + f32 t = 1 - math_pow(2.f, -20.f * (f32)sim_dt); + xf = xform_lerp(xf, ent->camera_xform_target, t); + } else { + /* Skip lerp */ + xf = ent->camera_xform_target; + } + ent->camera_applied_lerp_continuity_gen_plus_one = ent->camera_lerp_continuity_gen + 1; } + + /* Camera shake */ + { + /* TODO: Update based on distance to quake */ + ent->shake = 0; + for (u64 quake_ent_index = 0; quake_ent_index < world->num_ents_reserved; ++quake_ent_index) { + struct sim_ent *quake = &world->ents[quake_ent_index]; + if (!sim_ent_is_valid_and_active(quake)) continue; + if (!sim_ent_has_prop(quake, SIM_ENT_PROP_QUAKE)) continue; + ent->shake += quake->quake_intensity; + } + } + + sim_ent_set_xform(ent, xf); + } + + /* ========================== * + * Update quakes + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_should_simulate(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_QUAKE)) continue; + + ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * sim_dt)); + if (ent->quake_intensity <= 0) { + sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE); + } + } + + /* ========================== * + * Update relative layers + * ========================== */ + + { + struct temp_arena temp = arena_temp_begin(scratch.arena); + + struct sim_ent **stack = arena_push(temp.arena, struct sim_ent *); + u64 stack_count = 1; + *stack = root; + + while (stack_count > 0) { + struct sim_ent *parent; + arena_pop(temp.arena, struct sim_ent *, &parent); + --stack_count; + + i32 parent_layer = parent->final_layer; + for (struct sim_ent *child = sim_ent_from_handle(world, parent->first); child->valid; child = sim_ent_from_handle(world, child->next)) { + if (sim_ent_is_valid_and_active(child) && sim_ent_should_simulate(child)) { + child->final_layer = parent_layer + child->layer; + *arena_push(temp.arena, struct sim_ent *) = child; + ++stack_count; + } + } + } + + arena_temp_end(temp); } /* ========================== *