#include "sim_ent.h" #include "sim.h" #include "math.h" #include "bitbuff.h" #include "uid.h" /* Id magic number constants (to be used in conjunction with ent ids in deterministic id combinations) */ #define SIM_ENT_CONTACT_BASIS_ID (UID(0x6a2a5d2dbecf534f, 0x0a8ca7c372a015af)) INTERNAL u32 index_from_ent(struct sim_snapshot *ss, struct sim_ent *ent) { return ent - ss->ents; } INTERNAL struct sim_ent *ent_from_index(struct sim_snapshot *ss, u32 index) { if (index > 0 && index < ss->num_ents_reserved) { return &ss->ents[index]; } else { return sim_ent_nil(); } } /* ========================== * * Ent allocation * ========================== */ /* Allocates an entity with no parent & no id (these must be set immediately after by the caller) */ struct sim_ent *sim_ent_alloc_raw(struct sim_snapshot *ss) { struct sim_ent *ent; if (ss->first_free_ent > 0 && ss->first_free_ent < ss->num_ents_reserved) { /* Reuse from free list */; ent = &ss->ents[ss->first_free_ent]; ss->first_free_ent = ent->next_free; } else { /* Make new */ ent = arena_push(&ss->ents_arena, struct sim_ent); ++ss->num_ents_reserved; } *ent = *sim_ent_nil(); ent->ss = ss; ent->valid = true; ent->owner = ss->client->ent_id; ent->_is_xform_dirty = true; ++ss->num_ents_allocated; return ent; } /* Allocates a new entity that will not sync */ struct sim_ent *sim_ent_alloc_local(struct sim_ent *parent) { ASSERT(parent->valid); struct sim_snapshot *ss = parent->ss; struct sim_ent *e = sim_ent_alloc_raw(ss); sim_ent_set_id(e, sim_ent_random_id()); sim_ent_link_parent(e, parent); return e; } struct sim_ent *sim_ent_alloc_local_with_id(struct sim_ent *parent, struct sim_ent_id id) { ASSERT(parent->valid); struct sim_snapshot *ss = parent->ss; struct sim_ent *e = sim_ent_alloc_raw(ss); sim_ent_set_id(e, id); sim_ent_link_parent(e, parent); return e; } /* Allocates a new entity with a random uid to be synced to clients */ struct sim_ent *sim_ent_alloc_sync_src(struct sim_ent *parent) { struct sim_snapshot *ss = parent->ss; struct sim_ent *e = sim_ent_alloc_raw(ss); sim_ent_set_id(e, sim_ent_random_id()); sim_ent_enable_prop(e, SIM_ENT_PROP_SYNC_SRC); e->owner = ss->local_client_ent; sim_ent_link_parent(e, parent); return e; } /* Allocates a new entity that will sync with incoming net src ents containing id, and coming from the specified owner */ struct sim_ent *sim_ent_alloc_sync_dst(struct sim_ent *parent, struct sim_ent_id ent_id, struct sim_ent_id owner_client_ent_id) { struct sim_snapshot *ss = parent->ss; struct sim_ent *e = sim_ent_alloc_raw(ss); sim_ent_set_id(e, ent_id); sim_ent_link_parent(e, parent); sim_ent_enable_prop(e, SIM_ENT_PROP_SYNC_DST); e->owner = owner_client_ent_id; return e; } void sim_ent_release_raw(struct sim_ent *ent) { struct sim_snapshot *ss = ent->ss; /* Release children */ struct sim_ent *child = sim_ent_from_id(ss, ent->first); while (child->valid) { struct sim_ent *next = sim_ent_from_id(ss, child->next); sim_ent_release_raw(child); child = next; } /* Release uid */ sim_ent_set_id(ent, SIM_ENT_NIL_ID); /* Release */ ent->valid = false; ent->next_free = ss->first_free_ent; ss->first_free_ent = index_from_ent(ss, ent); --ss->num_ents_allocated; } void sim_ent_release(struct sim_ent *ent) { struct sim_snapshot *ss = ent->ss; struct sim_ent *parent = sim_ent_from_id(ss, ent->parent); if (parent->valid) { sim_ent_unlink_from_parent(ent); } sim_ent_release_raw(ent); } void sim_ent_release_all_with_prop(struct sim_snapshot *ss, enum sim_ent_prop prop) { struct temp_arena scratch = scratch_begin_no_conflict(); struct sim_ent **ents_to_release = arena_dry_push(scratch.arena, struct sim_ent *); u64 ents_to_release_count = 0; for (u64 ent_index = 0; ent_index < ss->num_ents_reserved; ++ent_index) { struct sim_ent *ent = &ss->ents[ent_index]; if (ent->valid && sim_ent_has_prop(ent, prop)) { *arena_push(scratch.arena, struct sim_ent *) = ent; ++ents_to_release_count; } } /* Release from snapshot */ /* TODO: Breadth first iteration to only release parent entities (since * child entities will be released along with parent anyway) */ for (u64 i = 0; i < ents_to_release_count; ++i) { struct sim_ent *ent = ents_to_release[i]; if (ent->valid && !ent->is_root && !sim_ent_has_prop(ent, SIM_ENT_PROP_CMD_CONTROL) && !sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) { sim_ent_release(ent); } } scratch_end(scratch); } /* ========================== * * Activate * ========================== */ void sim_ent_activate(struct sim_ent *ent, u64 current_tick) { sim_ent_enable_prop(ent, SIM_ENT_PROP_ACTIVE); ent->activation_tick = current_tick; ++ent->continuity_gen; } /* ========================== * * Ent id * ========================== */ INTERNAL struct sim_ent_bin *bin_from_id(struct sim_snapshot *ss, struct sim_ent_id id) { return &ss->id_bins[id.uid.lo % ss->num_id_bins]; } /* NOTE: This should only really happen during ent allocation (it doesn't make sense for an allocated ent's id to change) */ void sim_ent_set_id(struct sim_ent *ent, struct sim_ent_id id) { struct sim_snapshot *ss = ent->ss; struct sim_ent_id old_id = ent->id; if (!sim_ent_id_eq(old_id, id)) { /* Release old from lookup */ if (!sim_ent_id_eq(old_id, SIM_ENT_NIL_ID)) { struct sim_ent_bin *bin = bin_from_id(ss, old_id); u32 prev_index = 0; u32 next_index = 0; u32 search_index = bin->first; struct sim_ent *prev = sim_ent_nil(); struct sim_ent *next = sim_ent_nil(); struct sim_ent *search = ent_from_index(ss, search_index); while (search->valid) { next_index = search->next_in_id_bin; next = ent_from_index(ss, next_index); if (sim_ent_id_eq(search->id, old_id)) { break; } prev_index = search_index; prev = search; search_index = next_index; search = next; } /* Old id not in bin, this should be impossible. */ ASSERT(search->valid); if (prev->valid) { prev->next_in_id_bin = next_index; } else { bin->first = next_index; } if (next->valid) { next->prev_in_id_bin = prev_index; } else { bin->last = prev_index; } } /* Insert new id into lookup */ if (!sim_ent_id_eq(id, SIM_ENT_NIL_ID)) { #if RTC { struct sim_ent *existing = sim_ent_from_id(ss, id); /* Collision should be extremely unlikely under normal circumstances, there's probably a logic error somewhere. */ ASSERT(!existing->valid); } #endif struct sim_ent_bin *bin = bin_from_id(ss, id); u32 ent_index = index_from_ent(ss, ent); struct sim_ent *last = ent_from_index(ss, bin->last); if (last->valid) { last->next_in_id_bin = ent_index; ent->prev_in_id_bin = bin->last; } else { bin->first = ent_index; ent->prev_in_id_bin = 0; } bin->last = ent_index; } ent->id = id; } } struct sim_ent *sim_ent_from_id(struct sim_snapshot *ss, struct sim_ent_id id) { struct sim_ent *res = sim_ent_nil(); if (!sim_ent_id_eq(id, SIM_ENT_NIL_ID) && ss->valid) { struct sim_ent_bin *bin = bin_from_id(ss, id); for (struct sim_ent *e = ent_from_index(ss, bin->first); e->valid; e = ent_from_index(ss, e->next_in_id_bin)) { if (sim_ent_id_eq(e->id, id)) { res = e; break; } } } return res; } struct sim_ent_id sim_ent_random_id(void) { struct sim_ent_id res = ZI; res.uid = uid_true_rand(); return res; } /* Returns the deterministic id of the contact constraint ent id that should be produced from e0 & e1 colliding */ struct sim_ent_id sim_ent_contact_constraint_id_from_contacting_ids(struct sim_ent_id id0, struct sim_ent_id id1) { struct sim_ent_id res = ZI; res.uid = uid_combine(uid_combine(SIM_ENT_CONTACT_BASIS_ID, id0.uid), id1.uid); return res; } /* ========================== * * Ent query * ========================== */ struct sim_ent *sim_ent_find_first_match_one(struct sim_snapshot *ss, enum sim_ent_prop prop) { u64 count = ss->num_ents_reserved; struct sim_ent *entities = ss->ents; for (u64 ent_index = 0; ent_index < count; ++ent_index) { struct sim_ent *ent = &entities[ent_index]; if (ent->valid && sim_ent_has_prop(ent, prop)) { return ent; } } return sim_ent_nil(); } struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim_ent_prop_array props) { u64 count = ss->num_ents_reserved; struct sim_ent *entities = ss->ents; for (u64 ent_index = 0; ent_index < count; ++ent_index) { struct sim_ent *ent = &entities[ent_index]; if (ent->valid) { b32 all = true; for (u64 i = 0; i < props.count; ++i) { if (!sim_ent_has_prop(ent, props.props[i])) { all = false; break; } } if (all) { return ent; } } } return sim_ent_nil(); } /* ========================== * * Ent tree * ========================== */ void sim_ent_link_parent(struct sim_ent *ent, struct sim_ent *parent) { struct sim_snapshot *ss = ent->ss; struct sim_ent *old_parent = sim_ent_from_id(ss, ent->parent); if (old_parent->valid) { /* Unlink from current parent */ sim_ent_unlink_from_parent(ent); } struct sim_ent_id ent_id = ent->id; struct sim_ent_id last_child_id = parent->last; struct sim_ent *last_child = sim_ent_from_id(ss, last_child_id); if (last_child->valid) { ent->prev = last_child_id; last_child->next = ent_id; } else { parent->first = ent_id; } parent->last = ent_id; if (parent->is_root) { ent->is_top = true; ent->top = ent_id; } else { ent->top = parent->top; } ent->parent = parent->id; } /* NOTE: Entity will be dangling after calling this, should re-link to root ent. */ void sim_ent_unlink_from_parent(struct sim_ent *ent) { struct sim_snapshot *ss = ent->ss; struct sim_ent_id parent_id = ent->parent; struct sim_ent *parent = sim_ent_from_id(ss, parent_id); struct sim_ent *prev = sim_ent_from_id(ss, ent->prev); struct sim_ent *next = sim_ent_from_id(ss, ent->next); /* Unlink from parent & siblings */ if (prev->valid) { prev->next = next->id; } else { parent->first = next->id; } if (next->valid) { next->prev = prev->id; } else { parent->last = prev->id; } ent->prev = SIM_ENT_NIL_ID; ent->next = SIM_ENT_NIL_ID; } /* ========================== * * Ent xform * ========================== */ INTERNAL void sim_ent_mark_child_xforms_dirty(struct sim_snapshot *ss, struct sim_ent *ent) { for (struct sim_ent *child = sim_ent_from_id(ss, ent->first); child->valid; child = sim_ent_from_id(ss, child->next)) { if (child->_is_xform_dirty) { break; } else { child->_is_xform_dirty = true; sim_ent_mark_child_xforms_dirty(ss, child); } } } INTERNAL struct xform sim_ent_get_xform_internal(struct sim_snapshot *ss, struct sim_ent *ent) { struct xform xf; if (ent->_is_xform_dirty) { if (ent->is_top) { xf = ent->_local_xform; } else { struct sim_ent *parent = sim_ent_from_id(ss, ent->parent); xf = sim_ent_get_xform_internal(ss, parent); xf = xform_mul(xf, ent->_local_xform); ent->_xform = xf; ent->_is_xform_dirty = false; } ent->_xform = xf; ent->_is_xform_dirty = false; } else { xf = ent->_xform; } return xf; } struct xform sim_ent_get_xform(struct sim_ent *ent) { struct xform xf; if (ent->_is_xform_dirty) { if (ent->is_top) { xf = ent->_local_xform; } else { struct sim_snapshot *ss = ent->ss; struct sim_ent *parent = sim_ent_from_id(ss, ent->parent); xf = sim_ent_get_xform_internal(ss, parent); xf = xform_mul(xf, ent->_local_xform); ent->_xform = xf; ent->_is_xform_dirty = false; } ent->_xform = xf; ent->_is_xform_dirty = false; } else { xf = ent->_xform; } return xf; } struct xform sim_ent_get_local_xform(struct sim_ent *ent) { return ent->_local_xform; } void sim_ent_set_xform(struct sim_ent *ent, struct xform xf) { if (!xform_eq(xf, ent->_xform)) { struct sim_snapshot *ss = ent->ss; /* Update local xform */ if (ent->is_top) { ent->_local_xform = xf; } else { struct sim_ent *parent = sim_ent_from_id(ss, ent->parent); struct xform parent_global = sim_ent_get_xform_internal(ss, parent); ent->_local_xform = xform_mul(xform_invert(parent_global), xf); } ent->_xform = xf; ent->_is_xform_dirty = false; sim_ent_mark_child_xforms_dirty(ss, ent); } } void sim_ent_set_local_xform(struct sim_ent *ent, struct xform xf) { if (!xform_eq(xf, ent->_local_xform)) { ent->_local_xform = xf; ent->_is_xform_dirty = true; sim_ent_mark_child_xforms_dirty(ent->ss, ent); } } /* ========================== * * Ent movement * ========================== */ void sim_ent_apply_linear_impulse(struct sim_ent *ent, struct v2 impulse, struct v2 point) { struct xform xf = sim_ent_get_xform(ent); struct v2 center = xf.og; f32 scale = math_fabs(xform_get_determinant(xf)); f32 inv_mass = 1.f / (ent->mass_unscaled * scale); f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale); struct v2 vcp = v2_sub(point, center); sim_ent_set_linear_velocity(ent, v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass))); sim_ent_set_angular_velocity(ent, v2_wedge(vcp, impulse) * inv_inertia); } void sim_ent_apply_linear_impulse_to_center(struct sim_ent *ent, struct v2 impulse) { struct xform xf = sim_ent_get_xform(ent); f32 scale = math_fabs(xform_get_determinant(xf)); f32 inv_mass = 1.f / (ent->mass_unscaled * scale); sim_ent_set_linear_velocity(ent, v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass))); } void sim_ent_apply_force_to_center(struct sim_ent *ent, struct v2 force) { ent->force = v2_add(ent->force, force); } void sim_ent_apply_angular_impulse(struct sim_ent *ent, f32 impulse) { struct xform xf = sim_ent_get_xform(ent); f32 scale = math_fabs(xform_get_determinant(xf)); f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale); sim_ent_set_angular_velocity(ent, ent->angular_velocity + impulse * inv_inertia); } void sim_ent_apply_torque(struct sim_ent *ent, f32 torque) { ent->torque += torque; } /* ========================== * * Ent lerp * ========================== */ void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend) { if (sim_ent_is_valid_and_active(e0) && sim_ent_is_valid_and_active(e1) && sim_ent_id_eq(e0->id, e1->id) && e0->continuity_gen == e1->continuity_gen) { e->_local_xform = xform_lerp(e0->_local_xform, e1->_local_xform, blend); if (e->is_top) { /* TODO: Cache parent & child xforms in sim */ struct xform e0_xf = sim_ent_get_xform(e0); struct xform e1_xf = sim_ent_get_xform(e1); sim_ent_set_xform(e, xform_lerp(e0_xf, e1_xf, blend)); } e->control_force = math_lerp_f32(e0->control_force, e1->control_force, blend); e->control_torque = math_lerp_f32(e0->control_torque, e1->control_torque, blend); e->linear_velocity = v2_lerp(e0->linear_velocity, e1->linear_velocity, blend); e->angular_velocity = math_lerp_angle(e0->angular_velocity, e1->angular_velocity, blend); e->control.move = v2_lerp(e0->control.move, e1->control.move, blend); e->control.focus = v2_lerp(e0->control.focus, e1->control.focus, blend); e->sprite_local_xform = xform_lerp(e0->sprite_local_xform, e1->sprite_local_xform, blend); e->animation_last_frame_change_time_ns = math_lerp_i64(e0->animation_last_frame_change_time_ns, e1->animation_last_frame_change_time_ns, (f64)blend); e->animation_frame = (u32)math_round_to_int(math_lerp_f32(e0->animation_frame, e1->animation_frame, blend)); e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, blend); e->camera_xform_target = xform_lerp(e0->camera_xform_target, e1->camera_xform_target, blend); e->shake = math_lerp_f32(e0->shake, e1->shake, blend); e->tracer_gradient_start = v2_lerp(e0->tracer_gradient_start, e1->tracer_gradient_start, blend); e->tracer_gradient_end = v2_lerp(e0->tracer_gradient_end, e1->tracer_gradient_end, blend); } } /* ========================== * * Ent sync * ========================== */ /* Walks a local & remote ent tree and allocates any missing net dst ents from remote src ents */ void sim_ent_sync_alloc_tree(struct sim_ent *local_parent, struct sim_ent *remote, struct sim_ent_id remote_client_ent) { __prof; if (sim_ent_has_prop(remote, SIM_ENT_PROP_SYNC_SRC)) { struct sim_snapshot *local_ss = local_parent->ss; struct sim_snapshot *remote_ss = remote->ss; struct sim_ent_id id = remote->id; struct sim_ent *local_ent = sim_ent_from_id(local_ss, id); if (!local_ent->valid) { local_ent = sim_ent_alloc_sync_dst(local_parent, id, remote_client_ent); } for (struct sim_ent *remote_child = sim_ent_from_id(remote_ss, remote->first); remote_child->valid; remote_child = sim_ent_from_id(remote_ss, remote_child->next)) { sim_ent_sync_alloc_tree(local_ent, remote_child, remote_client_ent); } } } /* Copies data between two synced entities */ void sim_ent_sync(struct sim_ent *local, struct sim_ent *remote) { struct sim_ent old = *local; MEMCPY_STRUCT(local, remote); if (!MEMEQ_STRUCT(&old._xform, &remote->_xform)) { DEBUGBREAKABLE; } /* Why would 2 ents w/ different uids ever be synced? */ ASSERT(sim_ent_id_eq(local->id, old.id)); local->ss = old.ss; /* Keep local tree */ local->parent = old.parent; local->prev = old.prev; local->next = old.next; local->first = old.first; local->last = old.last; local->top = old.top; local->owner = old.owner; /* Keep indices */ local->next_in_id_bin = old.next_in_id_bin; local->prev_in_id_bin = old.prev_in_id_bin; local->next_free = old.next_free; sim_ent_disable_prop(local, SIM_ENT_PROP_SYNC_SRC); sim_ent_enable_prop(local, SIM_ENT_PROP_SYNC_DST); } #if 1 /* ========================== * * Ent encode * ========================== */ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1) { struct sim_snapshot *ss = e1->ss; /* FIXME: Things like xforms need to be retreived manually rather than memcopied. * This will also be true for things like ent handles once uids are implemented. */ /* TODO: Granular delta encoding */ u64 pos = 0; e1->ss = e0->ss; while (pos < sizeof(*e1)) { u64 chunk_size = min_u64(pos + 8, sizeof(*e1)) - pos; u8 *chunk0 = (u8 *)e0 + pos; u8 *chunk1 = (u8 *)e1 + pos; if (bw_write_bit(bw, !MEMEQ(chunk0, chunk1, chunk_size))) { u64 bits = 0; MEMCPY(&bits, chunk1, chunk_size); bw_write_ubits(bw, bits, 64); } pos += 8; } e1->ss = ss; } /* ========================== * * Ent decode * ========================== */ void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e) { struct sim_snapshot *old_ss = e->ss; { u64 pos = 0; while (pos < sizeof(*e)) { u8 *chunk = (u8 *)e + pos; if (br_read_bit(br)) { u64 chunk_size = min_u64(pos + 8, sizeof(*e)) - pos; u64 bits = br_read_ubits(br, 64); MEMCPY(chunk, &bits, chunk_size); } pos += 8; } } e->ss = old_ss; } #else /* ========================== * * Ent encode * ========================== */ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1) { struct sim_snapshot *ss = e1->ss; /* FIXME: Things like xforms need to be retreived manually rather than memcopied. * This will also be true for things like ent handles once uids are implemented. */ /* TODO: Granular delta encoding */ u64 pos = 0; e1->ss = e0->ss; while (pos < sizeof(*e1)) { u64 chunk_size = min_u64(pos + 8, sizeof(*e1)) - pos; u8 *chunk0 = (u8 *)e0 + pos; u8 *chunk1 = (u8 *)e1 + pos; if (MEMEQ(chunk0, chunk1, chunk_size)) { bw_write_bit(bw, 0); } else { bw_write_bit(bw, 1); u64 bits = 0; MEMCPY(&bits, chunk1, chunk_size); bw_write_ubits(bw, bits, 64); } pos += 8; } e1->ss = ss; } /* ========================== * * Ent decode * ========================== */ void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e) { struct sim_ent decoded = *e; { u64 pos = 0; while (pos < sizeof(decoded)) { u8 *chunk = (u8 *)&decoded + pos; if (br_read_bit(br)) { u64 chunk_size = min_u64(pos + 8, sizeof(decoded)) - pos; u64 bits = br_read_ubits(br, 64); MEMCPY(chunk, &bits, chunk_size); } pos += 8; } } decoded.ss = e->ss; struct sim_ent_id old_id = e->id; struct sim_ent_id new_id = decoded.id; MEMCPY_STRUCT(e, &decoded); e->id = old_id; if (!sim_ent_id_eq(old_id, new_id)) { sim_ent_set_id(e, new_id); } } #endif