re-add lerping via snapshot interpolation
This commit is contained in:
parent
cd38ab4fbf
commit
f04a81fbb0
BIN
res/graphics/box.ase
(Stored with Git LFS)
BIN
res/graphics/box.ase
(Stored with Git LFS)
Binary file not shown.
@ -6,6 +6,7 @@
|
|||||||
#include "work.h"
|
#include "work.h"
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
#include "sim.h"
|
#include "sim.h"
|
||||||
|
#include "sim_snapshot.h"
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
@ -224,7 +225,10 @@ void app_entry_point(struct string args_str)
|
|||||||
struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
||||||
struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr);
|
struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr);
|
||||||
struct phys_startup_receipt phys_sr = phys_startup();
|
struct phys_startup_receipt phys_sr = phys_startup();
|
||||||
struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, args_str, &window);
|
struct sim_ent_startup_receipt sim_ent_sr = sim_ent_startup();
|
||||||
|
struct sim_client_startup_receipt sim_client_sr = sim_client_startup();
|
||||||
|
struct sim_snapshot_startup_receipt sim_snapshot_sr = sim_snapshot_startup();
|
||||||
|
struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, &sim_ent_sr, &sim_client_sr, &sim_snapshot_sr, args_str, &window);
|
||||||
struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr);
|
struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr);
|
||||||
|
|
||||||
(UNUSED)user_sr;
|
(UNUSED)user_sr;
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
#define SPACE_CELL_BUCKETS_SQRT (256)
|
#define SPACE_CELL_BUCKETS_SQRT (256)
|
||||||
#define SPACE_CELL_SIZE 1.0f
|
#define SPACE_CELL_SIZE 1.0f
|
||||||
|
|
||||||
#define SIM_FPS 50.0
|
#define SIM_TICKS_PER_SECOND 100.0
|
||||||
#define SIM_TIMESCALE 1
|
#define SIM_TIMESCALE 1
|
||||||
|
|
||||||
#define SIM_PHYSICS_SUBSTEPS 4
|
#define SIM_PHYSICS_SUBSTEPS 4
|
||||||
@ -51,10 +51,10 @@
|
|||||||
#define SIM_MAX_ANGULAR_VELOCITY F32_INFINITY
|
#define SIM_MAX_ANGULAR_VELOCITY F32_INFINITY
|
||||||
|
|
||||||
/* How many ticks back in time should the user blend between?
|
/* How many ticks back in time should the user blend between?
|
||||||
* <Delay ms> = <USER_INTERP_OFFSET_TICK_RATIO> * <Sim tick rate>
|
* <Delay ms> = <USER_INTERP_RATIO> * <Tick interval>
|
||||||
* E.g: At 1.5, the user thread will render 75ms back in time (if sim thread runs at 50FPS)
|
* E.g: At 1.5, the user thread will render 75ms back in time (if sim thread runs at 50FPS)
|
||||||
*/
|
*/
|
||||||
#define USER_INTERP_OFFSET_TICK_RATIO 1.1
|
#define USER_INTERP_RATIO 1.1
|
||||||
#define USER_INTERP_ENABLED 1
|
#define USER_INTERP_ENABLED 1
|
||||||
|
|
||||||
#define COLLIDER_DEBUG 0
|
#define COLLIDER_DEBUG 0
|
||||||
|
|||||||
@ -18,7 +18,7 @@ GLOBAL struct {
|
|||||||
struct phys_startup_receipt phys_startup(void)
|
struct phys_startup_receipt phys_startup(void)
|
||||||
{
|
{
|
||||||
/* Initialize constants */
|
/* Initialize constants */
|
||||||
const f32 substep_dt = (1.f / ((f32)SIM_FPS * (f32)SIM_PHYSICS_SUBSTEPS));
|
const f32 substep_dt = (1.f / ((f32)SIM_TICKS_PER_SECOND * (f32)SIM_PHYSICS_SUBSTEPS));
|
||||||
|
|
||||||
const f32 contact_frequency = (1.f / substep_dt) / 8.f;
|
const f32 contact_frequency = (1.f / substep_dt) / 8.f;
|
||||||
const f32 contact_damping_ratio = 10;
|
const f32 contact_damping_ratio = 10;
|
||||||
@ -48,7 +48,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
|
|||||||
struct sim_ent_lookup *debug_lookup = ctx->debug_lookup;
|
struct sim_ent_lookup *debug_lookup = ctx->debug_lookup;
|
||||||
struct space *space = ctx->space;
|
struct space *space = ctx->space;
|
||||||
struct sim_ent_store *store = ctx->store;
|
struct sim_ent_store *store = ctx->store;
|
||||||
struct sim_ent *root = sim_ent_from_handle(store, store->root);
|
struct sim_ent *root = sim_ent_from_handle(store, SIM_ENT_ROOT_HANDLE);
|
||||||
|
|
||||||
for (u64 check0_index = 0; check0_index < store->num_reserved; ++check0_index) {
|
for (u64 check0_index = 0; check0_index < store->num_reserved; ++check0_index) {
|
||||||
struct sim_ent *check0 = &store->entities[check0_index];
|
struct sim_ent *check0 = &store->entities[check0_index];
|
||||||
|
|||||||
117
src/sim.c
117
src/sim.c
@ -26,6 +26,9 @@
|
|||||||
struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr,
|
struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr,
|
||||||
struct phys_startup_receipt *phys_sr,
|
struct phys_startup_receipt *phys_sr,
|
||||||
struct host_startup_receipt *host_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
|
struct sim_ent_startup_receipt *sim_ent_sr,
|
||||||
|
struct sim_client_startup_receipt *sim_client_sr,
|
||||||
|
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
|
||||||
u16 host_port)
|
u16 host_port)
|
||||||
{
|
{
|
||||||
struct arena arena = arena_alloc(GIGABYTE(64));
|
struct arena arena = arena_alloc(GIGABYTE(64));
|
||||||
@ -35,6 +38,9 @@ struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr,
|
|||||||
(UNUSED)sprite_sr;
|
(UNUSED)sprite_sr;
|
||||||
(UNUSED)phys_sr;
|
(UNUSED)phys_sr;
|
||||||
(UNUSED)host_sr;
|
(UNUSED)host_sr;
|
||||||
|
(UNUSED)sim_ent_sr;
|
||||||
|
(UNUSED)sim_client_sr;
|
||||||
|
(UNUSED)sim_snapshot_sr;
|
||||||
|
|
||||||
/* Intialize host */
|
/* Intialize host */
|
||||||
ctx->host = host_alloc(host_port);
|
ctx->host = host_alloc(host_port);
|
||||||
@ -46,9 +52,9 @@ struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr,
|
|||||||
#endif
|
#endif
|
||||||
ctx->space = space_alloc(SPACE_CELL_SIZE, SPACE_CELL_BUCKETS_SQRT);
|
ctx->space = space_alloc(SPACE_CELL_SIZE, SPACE_CELL_BUCKETS_SQRT);
|
||||||
|
|
||||||
/* Create emtpy snapshot */
|
/* Create snapshot store */
|
||||||
sim_snapshot_alloc(&ctx->world);
|
ctx->snapshot_store = sim_snapshot_store_alloc();
|
||||||
ctx->world.world_timescale = SIM_TIMESCALE;
|
ctx->world = sim_snapshot_nil();
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
@ -57,8 +63,8 @@ void sim_ctx_release(struct sim_ctx *ctx)
|
|||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
|
|
||||||
/* Release snapshot */
|
/* Release snapshot store */
|
||||||
sim_snapshot_release(&ctx->world);
|
sim_snapshot_store_release(ctx->snapshot_store);
|
||||||
|
|
||||||
/* Release bookkeeping */
|
/* Release bookkeeping */
|
||||||
space_release(ctx->space);
|
space_release(ctx->space);
|
||||||
@ -79,7 +85,7 @@ void sim_ctx_release(struct sim_ctx *ctx)
|
|||||||
|
|
||||||
INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
|
INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct sim_ent *root = sim_ent_from_handle(ctx->world.ent_store, ctx->world.ent_store->root);
|
struct sim_ent *root = sim_ent_from_handle(ctx->world->ent_store, SIM_ENT_ROOT_HANDLE);
|
||||||
root->mass_unscaled = F32_INFINITY;
|
root->mass_unscaled = F32_INFINITY;
|
||||||
root->inertia_unscaled = F32_INFINITY;
|
root->inertia_unscaled = F32_INFINITY;
|
||||||
|
|
||||||
@ -104,9 +110,8 @@ INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
|
|||||||
e->angular_ground_friction = 200;
|
e->angular_ground_friction = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Big box */
|
/* Big box */
|
||||||
#if 0
|
#if 1
|
||||||
{
|
{
|
||||||
struct sim_ent *e = sim_ent_alloc(root);
|
struct sim_ent *e = sim_ent_alloc(root);
|
||||||
|
|
||||||
@ -122,9 +127,9 @@ INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
|
|||||||
|
|
||||||
sim_ent_enable_prop(e, SIM_ENT_PROP_PHYSICAL_DYNAMIC);
|
sim_ent_enable_prop(e, SIM_ENT_PROP_PHYSICAL_DYNAMIC);
|
||||||
e->mass_unscaled = 100;
|
e->mass_unscaled = 100;
|
||||||
e->inertia_unscaled = 10;
|
e->inertia_unscaled = 50;
|
||||||
e->linear_ground_friction = 100;
|
e->linear_ground_friction = 100;
|
||||||
e->angular_ground_friction = 5;
|
e->angular_ground_friction = 50;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -153,8 +158,8 @@ INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
|
|||||||
|
|
||||||
INTERNAL struct sim_ent *spawn_test_player(struct sim_ctx *ctx)
|
INTERNAL struct sim_ent *spawn_test_player(struct sim_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct sim_ent_store *store = ctx->world.ent_store;
|
struct sim_ent_store *store = ctx->world->ent_store;
|
||||||
struct sim_ent *root = sim_ent_from_handle(store, store->root);
|
struct sim_ent *root = sim_ent_from_handle(store, SIM_ENT_ROOT_HANDLE);
|
||||||
|
|
||||||
/* Player */
|
/* Player */
|
||||||
struct sim_ent *player_ent = sim_ent_nil();
|
struct sim_ent *player_ent = sim_ent_nil();
|
||||||
@ -219,8 +224,8 @@ INTERNAL struct sim_ent *spawn_test_player(struct sim_ctx *ctx)
|
|||||||
e->layer = SIM_LAYER_RELATIVE_WEAPON;
|
e->layer = SIM_LAYER_RELATIVE_WEAPON;
|
||||||
|
|
||||||
sim_ent_enable_prop(e, SIM_ENT_PROP_WEAPON);
|
sim_ent_enable_prop(e, SIM_ENT_PROP_WEAPON);
|
||||||
//e->trigger_delay = 1.0f / 10.0f;
|
e->trigger_delay = 1.0f / 10.0f;
|
||||||
e->trigger_delay = 1.0f / 100.0f;
|
//e->trigger_delay = 1.0f / 100.0f;
|
||||||
|
|
||||||
player_ent->equipped = e->handle;
|
player_ent->equipped = e->handle;
|
||||||
}
|
}
|
||||||
@ -230,8 +235,8 @@ INTERNAL struct sim_ent *spawn_test_player(struct sim_ctx *ctx)
|
|||||||
|
|
||||||
INTERNAL struct sim_ent *spawn_test_player_camera(struct sim_ctx *ctx, struct sim_ent *player_ent)
|
INTERNAL struct sim_ent *spawn_test_player_camera(struct sim_ctx *ctx, struct sim_ent *player_ent)
|
||||||
{
|
{
|
||||||
struct sim_ent_store *store = ctx->world.ent_store;
|
struct sim_ent_store *store = ctx->world->ent_store;
|
||||||
struct sim_ent *root = sim_ent_from_handle(store, store->root);
|
struct sim_ent *root = sim_ent_from_handle(store, SIM_ENT_ROOT_HANDLE);
|
||||||
|
|
||||||
struct sim_ent *camera_ent = sim_ent_nil();
|
struct sim_ent *camera_ent = sim_ent_nil();
|
||||||
if (player_ent->valid) {
|
if (player_ent->valid) {
|
||||||
@ -257,7 +262,7 @@ INTERNAL struct sim_ent *spawn_test_player_camera(struct sim_ctx *ctx, struct si
|
|||||||
INTERNAL void release_entities_with_prop(struct sim_ctx *ctx, enum sim_ent_prop prop)
|
INTERNAL void release_entities_with_prop(struct sim_ctx *ctx, enum sim_ent_prop prop)
|
||||||
{
|
{
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
struct sim_ent_store *store = ctx->world.ent_store;
|
struct sim_ent_store *store = ctx->world->ent_store;
|
||||||
struct space *space = ctx->space;
|
struct space *space = ctx->space;
|
||||||
|
|
||||||
struct sim_ent **ents_to_release = arena_dry_push(scratch.arena, struct sim_ent *);
|
struct sim_ent **ents_to_release = arena_dry_push(scratch.arena, struct sim_ent *);
|
||||||
@ -304,8 +309,8 @@ INTERNAL void release_entities_with_prop(struct sim_ctx *ctx, enum sim_ent_prop
|
|||||||
INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, udata)
|
INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, udata)
|
||||||
{
|
{
|
||||||
struct sim_ctx *ctx = (struct sim_ctx *)udata;
|
struct sim_ctx *ctx = (struct sim_ctx *)udata;
|
||||||
struct sim_ent_store *store = ctx->world.ent_store;
|
struct sim_ent_store *store = ctx->world->ent_store;
|
||||||
struct sim_ent *root = sim_ent_from_handle(store, store->root);
|
struct sim_ent *root = sim_ent_from_handle(store, SIM_ENT_ROOT_HANDLE);
|
||||||
|
|
||||||
for (u64 i = 0; i < collision_data_array.count; ++i) {
|
for (u64 i = 0; i < collision_data_array.count; ++i) {
|
||||||
struct phys_collision_data *data = &collision_data_array.a[i];
|
struct phys_collision_data *data = &collision_data_array.a[i];
|
||||||
@ -405,31 +410,47 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
* Begin frame
|
* Begin frame
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
++ctx->world.tick;
|
struct sim_snapshot *prev_snapshot = ctx->world;
|
||||||
|
ctx->world = sim_snapshot_alloc(ctx->snapshot_store, prev_snapshot, prev_snapshot->tick + 1);
|
||||||
|
|
||||||
ctx->world.real_dt_ns = max_i64(0, target_dt_ns);
|
/* Release old snapshots */
|
||||||
ctx->world.real_time_ns += ctx->world.real_dt_ns;
|
{
|
||||||
|
/* TODO: Something better */
|
||||||
|
i64 release_tick = (i64)ctx->world->tick - 5; /* Arbitrary tick offset */
|
||||||
|
if (release_tick > 0) {
|
||||||
|
struct sim_snapshot *old = sim_snapshot_from_tick(ctx->snapshot_store, release_tick);
|
||||||
|
if (old) {
|
||||||
|
sim_snapshot_release(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx->world.world_dt_ns = max_i64(0, target_dt_ns * ctx->world.world_timescale);
|
|
||||||
ctx->world.world_time_ns += ctx->world.world_dt_ns;
|
|
||||||
|
|
||||||
f64 sim_time = SECONDS_FROM_NS(ctx->world.world_time_ns);
|
ctx->world->real_dt_ns = max_i64(0, target_dt_ns);
|
||||||
|
ctx->world->real_time_ns += ctx->world->real_dt_ns;
|
||||||
|
|
||||||
|
ctx->world->world_timescale = 1;
|
||||||
|
ctx->world->world_dt_ns = max_i64(0, target_dt_ns * ctx->world->world_timescale);
|
||||||
|
ctx->world->world_time_ns += ctx->world->world_dt_ns;
|
||||||
|
|
||||||
|
f64 sim_time = SECONDS_FROM_NS(ctx->world->world_time_ns);
|
||||||
ctx->sprite_frame_scope = sprite_scope_begin();
|
ctx->sprite_frame_scope = sprite_scope_begin();
|
||||||
|
|
||||||
f64 real_dt = SECONDS_FROM_NS(ctx->world.real_dt_ns);
|
f64 real_dt = SECONDS_FROM_NS(ctx->world->real_dt_ns);
|
||||||
f64 real_time = SECONDS_FROM_NS(ctx->world.real_time_ns);
|
f64 real_time = SECONDS_FROM_NS(ctx->world->real_time_ns);
|
||||||
f64 world_dt = SECONDS_FROM_NS(ctx->world.world_dt_ns);
|
f64 world_dt = SECONDS_FROM_NS(ctx->world->world_dt_ns);
|
||||||
f64 world_time = SECONDS_FROM_NS(ctx->world.world_time_ns);
|
f64 world_time = SECONDS_FROM_NS(ctx->world->world_time_ns);
|
||||||
(UNUSED)real_dt;
|
(UNUSED)real_dt;
|
||||||
(UNUSED)real_time;
|
(UNUSED)real_time;
|
||||||
(UNUSED)world_dt;
|
(UNUSED)world_dt;
|
||||||
(UNUSED)world_time;
|
(UNUSED)world_time;
|
||||||
|
|
||||||
struct sim_ent *root = sim_ent_from_handle(ctx->world.ent_store, ctx->world.ent_store->root);;
|
struct sim_ent_store *ent_store = ctx->world->ent_store;
|
||||||
struct sim_ent_store *ent_store = ctx->world.ent_store;
|
|
||||||
struct space *space = ctx->space;
|
struct space *space = ctx->space;
|
||||||
struct sprite_scope *sprite_frame_scope = ctx->sprite_frame_scope;
|
struct sprite_scope *sprite_frame_scope = ctx->sprite_frame_scope;
|
||||||
|
|
||||||
|
struct sim_ent *root = sim_ent_from_handle(ctx->world->ent_store, SIM_ENT_ROOT_HANDLE);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Spawn test entities
|
* Spawn test entities
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -460,8 +481,8 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
|
|
||||||
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE)) {
|
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE)) {
|
||||||
u64 atick = ent->activation_tick;
|
u64 atick = ent->activation_tick;
|
||||||
if (atick != 0 || ctx->world.tick >= atick) {
|
if (atick != 0 || ctx->world->tick >= atick) {
|
||||||
sim_ent_activate(ent, ctx->world.tick);
|
sim_ent_activate(ent, ctx->world->tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,9 +519,9 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
for (struct sim_cmd *cmd = sim_cmds.first; cmd; cmd = cmd->next) {
|
for (struct sim_cmd *cmd = sim_cmds.first; cmd; cmd = cmd->next) {
|
||||||
enum sim_cmd_kind kind = cmd->kind;
|
enum sim_cmd_kind kind = cmd->kind;
|
||||||
struct host_channel_id channel_id = cmd->channel_id;
|
struct host_channel_id channel_id = cmd->channel_id;
|
||||||
struct sim_client *client = sim_client_from_channel_id(ctx->world.client_store, channel_id);
|
struct sim_client *client = sim_client_from_channel_id(ctx->world->client_store, channel_id);
|
||||||
if (!client->valid && kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(channel_id)) {
|
if (!client->valid && kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(channel_id)) {
|
||||||
client = sim_client_alloc(ctx->world.client_store, channel_id);
|
client = sim_client_alloc(ctx->world->client_store, channel_id);
|
||||||
/* TODO: Create player ent not here */
|
/* TODO: Create player ent not here */
|
||||||
/* FIXME: Player ent never released upon disconnect */
|
/* FIXME: Player ent never released upon disconnect */
|
||||||
struct sim_ent *player_ent = spawn_test_player(ctx);
|
struct sim_ent *player_ent = spawn_test_player(ctx);
|
||||||
@ -513,13 +534,13 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Split cmds by client */
|
/* Split cmds by client */
|
||||||
client_cmds = arena_push_array_zero(scratch.arena, struct sim_cmd_list, ctx->world.client_store->num_reserved);
|
client_cmds = arena_push_array_zero(scratch.arena, struct sim_cmd_list, ctx->world->client_store->num_reserved);
|
||||||
{
|
{
|
||||||
struct sim_cmd *cmd = sim_cmds.first;
|
struct sim_cmd *cmd = sim_cmds.first;
|
||||||
while (cmd) {
|
while (cmd) {
|
||||||
struct sim_cmd *next = cmd->next;
|
struct sim_cmd *next = cmd->next;
|
||||||
struct host_channel_id channel_id = cmd->channel_id;
|
struct host_channel_id channel_id = cmd->channel_id;
|
||||||
struct sim_client *client = sim_client_from_channel_id(ctx->world.client_store, channel_id);
|
struct sim_client *client = sim_client_from_channel_id(ctx->world->client_store, channel_id);
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
struct sim_cmd_list *cmd_list = &client_cmds[client->handle.idx];
|
struct sim_cmd_list *cmd_list = &client_cmds[client->handle.idx];
|
||||||
if (cmd_list->last) {
|
if (cmd_list->last) {
|
||||||
@ -540,8 +561,8 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Process client cmds */
|
/* Process client cmds */
|
||||||
for (u64 i = 0; i < ctx->world.client_store->num_reserved; ++i) {
|
for (u64 i = 0; i < ctx->world->client_store->num_reserved; ++i) {
|
||||||
struct sim_client *client = &ctx->world.client_store->clients[i];
|
struct sim_client *client = &ctx->world->client_store->clients[i];
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
struct host_channel_id channel_id = client->channel_id;
|
struct host_channel_id channel_id = client->channel_id;
|
||||||
struct sim_cmd_list cmds = client_cmds[client->handle.idx];
|
struct sim_cmd_list cmds = client_cmds[client->handle.idx];
|
||||||
@ -633,7 +654,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||||
|
|
||||||
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) {
|
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) {
|
||||||
struct sim_client *client = sim_client_from_handle(ctx->world.client_store, ent->controlling_client);
|
struct sim_client *client = sim_client_from_handle(ctx->world->client_store, ent->controlling_client);
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
ent->control = client->control;
|
ent->control = client->control;
|
||||||
/* TODO: Move this */
|
/* TODO: Move this */
|
||||||
@ -1054,8 +1075,8 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
* Create mouse joints from client debug drag
|
* Create mouse joints from client debug drag
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
for (u64 i = 0; i < ctx->world.client_store->num_reserved; ++i) {
|
for (u64 i = 0; i < ctx->world->client_store->num_reserved; ++i) {
|
||||||
struct sim_client *client = &ctx->world.client_store->clients[i];
|
struct sim_client *client = &ctx->world->client_store->clients[i];
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
struct v2 cursor = client->cursor_pos;
|
struct v2 cursor = client->cursor_pos;
|
||||||
b32 start_dragging = client->dbg_drag_start;
|
b32 start_dragging = client->dbg_drag_start;
|
||||||
@ -1126,7 +1147,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
|
|
||||||
{
|
{
|
||||||
struct phys_ctx phys = ZI;
|
struct phys_ctx phys = ZI;
|
||||||
phys.tick = ctx->world.tick;
|
phys.tick = ctx->world->tick;
|
||||||
phys.store = ent_store;
|
phys.store = ent_store;
|
||||||
phys.space = space;
|
phys.space = space;
|
||||||
phys.contact_lookup = &ctx->contact_lookup;
|
phys.contact_lookup = &ctx->contact_lookup;
|
||||||
@ -1173,7 +1194,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||||
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_BULLET)) continue;
|
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_BULLET)) continue;
|
||||||
|
|
||||||
if (ent->activation_tick == ctx->world.tick) {
|
if (ent->activation_tick == ctx->world->tick) {
|
||||||
struct sim_ent *src = sim_ent_from_handle(ent_store, ent->bullet_src);
|
struct sim_ent *src = sim_ent_from_handle(ent_store, ent->bullet_src);
|
||||||
struct xform src_xf = sim_ent_get_xform(src);
|
struct xform src_xf = sim_ent_get_xform(src);
|
||||||
|
|
||||||
@ -1329,16 +1350,16 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
|
|||||||
* Publish tick
|
* Publish tick
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
for (u64 i = 0; i < ctx->world.client_store->num_reserved; ++i) {
|
for (u64 i = 0; i < ctx->world->client_store->num_reserved; ++i) {
|
||||||
struct sim_client *client = &ctx->world.client_store->clients[i];
|
struct sim_client *client = &ctx->world->client_store->clients[i];
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
||||||
|
|
||||||
/* TODO: Not like this */
|
/* TODO: Not like this */
|
||||||
struct sim_event snapshot_event = ZI;
|
struct sim_event snapshot_event = ZI;
|
||||||
snapshot_event.tick = ctx->world.tick;
|
snapshot_event.tick = ctx->world->tick;
|
||||||
snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT;
|
snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT;
|
||||||
snapshot_event.snapshot_data = sim_snapshot_encode(temp.arena, client, &ctx->world);
|
snapshot_event.snapshot_data = sim_snapshot_encode(temp.arena, client, ctx->world);
|
||||||
|
|
||||||
struct sim_event_list l = ZI;
|
struct sim_event_list l = ZI;
|
||||||
l.first = &snapshot_event;
|
l.first = &snapshot_event;
|
||||||
|
|||||||
11
src/sim.h
11
src/sim.h
@ -7,6 +7,9 @@
|
|||||||
struct sprite_startup_receipt;
|
struct sprite_startup_receipt;
|
||||||
struct phys_startup_receipt;
|
struct phys_startup_receipt;
|
||||||
struct host_startup_receipt;
|
struct host_startup_receipt;
|
||||||
|
struct sim_ent_startup_receipt;
|
||||||
|
struct sim_client_startup_receipt;
|
||||||
|
struct sim_snapshot_startup_receipt;
|
||||||
|
|
||||||
/* Absolute layers */
|
/* Absolute layers */
|
||||||
#define SIM_LAYER_FLOOR_DECALS -300
|
#define SIM_LAYER_FLOOR_DECALS -300
|
||||||
@ -35,14 +38,18 @@ struct sim_ctx {
|
|||||||
#endif
|
#endif
|
||||||
struct space *space;
|
struct space *space;
|
||||||
|
|
||||||
/* Tick */
|
/* Snapshot */
|
||||||
struct sim_snapshot world;
|
struct sim_snapshot_store *snapshot_store;
|
||||||
|
struct sim_snapshot *world;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TODO: Get rid of startup receipts */
|
/* TODO: Get rid of startup receipts */
|
||||||
struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr,
|
struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr,
|
||||||
struct phys_startup_receipt *phys_sr,
|
struct phys_startup_receipt *phys_sr,
|
||||||
struct host_startup_receipt *host_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
|
struct sim_ent_startup_receipt *sim_ent_sr,
|
||||||
|
struct sim_client_startup_receipt *sim_client_sr,
|
||||||
|
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
|
||||||
u16 host_port);
|
u16 host_port);
|
||||||
|
|
||||||
void sim_ctx_release(struct sim_ctx *ctx);
|
void sim_ctx_release(struct sim_ctx *ctx);
|
||||||
|
|||||||
158
src/sim_client.c
158
src/sim_client.c
@ -4,47 +4,105 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
|
|
||||||
|
/* FIXME: Default client pointers to nil */
|
||||||
|
|
||||||
#define CHANNEL_LOOKUP_BUCKETS 4096
|
#define CHANNEL_LOOKUP_BUCKETS 4096
|
||||||
|
|
||||||
/* Offset in bytes from start of store struct to start of clients array (assume adjacently allocated) */
|
/* Offset in bytes from start of store struct to start of clients array in store arena */
|
||||||
#define STORE_CLIENTS_OFFSET (sizeof(struct sim_client_store) + (sizeof(struct sim_client_store) % alignof(struct sim_client)))
|
#define STORE_CLIENTS_OFFSET (sizeof(struct sim_client_store) + (sizeof(struct sim_client_channel_lookup_bucket) * CHANNEL_LOOKUP_BUCKETS))
|
||||||
|
|
||||||
/* Accessed via client_nil() */
|
|
||||||
READONLY struct sim_client _g_sim_client_nil = { .valid = false };
|
|
||||||
READONLY struct sim_client_store _g_sim_client_store_nil = { .valid = false };
|
|
||||||
|
|
||||||
struct sim_client_channel_lookup_bucket {
|
struct sim_client_channel_lookup_bucket {
|
||||||
struct sim_client *first;
|
struct sim_client_handle first;
|
||||||
struct sim_client *last;
|
struct sim_client_handle last;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
GLOBAL struct {
|
||||||
|
struct sim_client *nil_client;
|
||||||
|
struct sim_client_store *nil_store;
|
||||||
|
} G = ZI, DEBUG_ALIAS(G, G_sim_client);
|
||||||
|
|
||||||
|
/* Accessed via sim_client_nil() */
|
||||||
|
READONLY struct sim_client **_g_sim_client_nil = &G.nil_client;
|
||||||
|
|
||||||
|
/* Accessed via sim_client_store_nil() */
|
||||||
|
READONLY struct sim_client_store **_g_sim_client_store_nil = &G.nil_store;
|
||||||
|
|
||||||
|
struct sim_client_startup_receipt sim_client_startup(void)
|
||||||
|
{
|
||||||
|
/* Setup nil store */
|
||||||
|
G.nil_store = sim_client_store_alloc(GIGABYTE(1));
|
||||||
|
|
||||||
|
/* Setup nil client */
|
||||||
|
G.nil_client = arena_push_zero(&G.nil_store->arena, struct sim_client);
|
||||||
|
G.nil_client->handle = SIM_CLIENT_NIL_HANDLE;
|
||||||
|
++G.nil_store->num_allocated;
|
||||||
|
++G.nil_store->num_reserved;
|
||||||
|
MEMZERO_STRUCT(G.nil_client);
|
||||||
|
|
||||||
|
G.nil_client->valid = false;;
|
||||||
|
G.nil_store->valid = false;
|
||||||
|
|
||||||
|
arena_set_readonly(&G.nil_store->arena);
|
||||||
|
return (struct sim_client_startup_receipt) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Store
|
* Store
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct sim_client_store *sim_client_store_alloc(u64 arena_reserve)
|
struct sim_client_store *sim_client_store_alloc(u64 arena_reserve)
|
||||||
|
{
|
||||||
|
struct sim_client_store *store;
|
||||||
{
|
{
|
||||||
struct arena arena = arena_alloc(arena_reserve);
|
struct arena arena = arena_alloc(arena_reserve);
|
||||||
u64 num_channel_lookup_buckets = CHANNEL_LOOKUP_BUCKETS;
|
store = arena_push_zero(&arena, struct sim_client_store);
|
||||||
struct sim_client_channel_lookup_bucket *channel_lookup_buckets = arena_push_array_zero(&arena, struct sim_client_channel_lookup_bucket, num_channel_lookup_buckets);
|
store->arena = arena;
|
||||||
|
}
|
||||||
|
store->valid = true;
|
||||||
|
|
||||||
struct sim_client_store *store = arena_push_zero(&arena, struct sim_client_store);
|
store->num_channel_lookup_buckets = CHANNEL_LOOKUP_BUCKETS;
|
||||||
store->clients = arena_dry_push(&arena, struct sim_client);
|
store->channel_lookup_buckets = arena_push_array_zero(&store->arena, struct sim_client_channel_lookup_bucket, store->num_channel_lookup_buckets);
|
||||||
|
|
||||||
|
store->clients = arena_dry_push(&store->arena, struct sim_client);
|
||||||
ASSERT((u8 *)store->clients - (u8 *)store == STORE_CLIENTS_OFFSET); /* Offset must be correct */
|
ASSERT((u8 *)store->clients - (u8 *)store == STORE_CLIENTS_OFFSET); /* Offset must be correct */
|
||||||
|
|
||||||
store->arena = arena;
|
|
||||||
store->num_channel_lookup_buckets = num_channel_lookup_buckets;
|
|
||||||
store->channel_lookup_buckets = channel_lookup_buckets;
|
|
||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sim_client_store_copy(struct sim_client_store *dst, struct sim_client_store *src)
|
||||||
|
{
|
||||||
|
i64 commit_diff = (i64)src->arena.pos - (i64)dst->arena.pos;
|
||||||
|
if (commit_diff > 0) {
|
||||||
|
arena_push_array(&dst->arena, u8, commit_diff);
|
||||||
|
}
|
||||||
|
arena_pop_to(&dst->arena, src->arena.pos);
|
||||||
|
|
||||||
|
dst->num_channel_lookup_buckets = src->num_channel_lookup_buckets;
|
||||||
|
dst->channel_lookup_buckets = (struct sim_client_channel_lookup_bucket *)((u8 *)dst + ((u8 *)src->channel_lookup_buckets - (u8 *)src));
|
||||||
|
|
||||||
|
dst->first_free_client = src->first_free_client;
|
||||||
|
dst->clients = (struct sim_client *)((u8 *)dst + ((u8 *)src->clients - (u8 *)src));
|
||||||
|
dst->num_allocated = src->num_allocated;
|
||||||
|
dst->num_reserved = src->num_reserved;
|
||||||
|
|
||||||
|
MEMCPY(dst->channel_lookup_buckets, src->channel_lookup_buckets, sizeof(*src->channel_lookup_buckets) * src->num_channel_lookup_buckets);
|
||||||
|
MEMCPY(dst->clients, src->clients, sizeof(*src->clients) * src->num_reserved);
|
||||||
|
}
|
||||||
|
|
||||||
void sim_client_store_release(struct sim_client_store *store)
|
void sim_client_store_release(struct sim_client_store *store)
|
||||||
{
|
{
|
||||||
arena_release(&store->arena);
|
arena_release(&store->arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sim_client_store *client_store_from_client(struct sim_client *client)
|
/* ========================== *
|
||||||
|
* Client
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_client_store *sim_client_store_from_client(struct sim_client *client)
|
||||||
{
|
{
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
u64 first_client_addr = (u64)(client - client->handle.idx);
|
u64 first_client_addr = (u64)(client - client->handle.idx);
|
||||||
@ -52,14 +110,10 @@ struct sim_client_store *client_store_from_client(struct sim_client *client)
|
|||||||
ASSERT(client_store->clients == (struct sim_client *)first_client_addr);
|
ASSERT(client_store->clients == (struct sim_client *)first_client_addr);
|
||||||
return client_store;
|
return client_store;
|
||||||
} else {
|
} else {
|
||||||
return client_store_nil();
|
return sim_client_store_nil();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Client
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id)
|
INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id)
|
||||||
{
|
{
|
||||||
return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id));
|
return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id));
|
||||||
@ -73,16 +127,16 @@ struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return client_nil();
|
return sim_client_nil();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id)
|
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id)
|
||||||
{
|
{
|
||||||
struct sim_client *res = client_nil();
|
struct sim_client *res = sim_client_nil();
|
||||||
u64 channel_hash = hash_from_channel_id(channel_id);
|
u64 channel_hash = hash_from_channel_id(channel_id);
|
||||||
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
||||||
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
||||||
for (struct sim_client *client = bucket->first; client; client = client->next_hash) {
|
for (struct sim_client *client = sim_client_from_handle(store, bucket->first); client->valid; client = sim_client_from_handle(store, client->next_in_bucket)) {
|
||||||
if (client->channel_hash == channel_hash) {
|
if (client->channel_hash == channel_hash) {
|
||||||
res = client;
|
res = client;
|
||||||
break;
|
break;
|
||||||
@ -93,10 +147,9 @@ struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, st
|
|||||||
|
|
||||||
struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_channel_id channel_id)
|
struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_channel_id channel_id)
|
||||||
{
|
{
|
||||||
struct sim_client *client = NULL;
|
|
||||||
struct sim_client_handle handle = ZI;
|
struct sim_client_handle handle = ZI;
|
||||||
if (store->first_free_client) {
|
struct sim_client *client = sim_client_from_handle(store, store->first_free_client);
|
||||||
client = store->first_free_client;
|
if (client->valid) {
|
||||||
store->first_free_client = client->next_free;
|
store->first_free_client = client->next_free;
|
||||||
handle = client->handle;
|
handle = client->handle;
|
||||||
++handle.gen;
|
++handle.gen;
|
||||||
@ -107,7 +160,7 @@ struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_
|
|||||||
++store->num_reserved;
|
++store->num_reserved;
|
||||||
}
|
}
|
||||||
++store->num_allocated;
|
++store->num_allocated;
|
||||||
*client = _g_sim_client_nil;
|
*client = *sim_client_nil();
|
||||||
client->valid = true;
|
client->valid = true;
|
||||||
client->handle = handle;
|
client->handle = handle;
|
||||||
|
|
||||||
@ -118,38 +171,55 @@ struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_
|
|||||||
/* Insert into channel lookup */
|
/* Insert into channel lookup */
|
||||||
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
||||||
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
||||||
if (bucket->last) {
|
{
|
||||||
bucket->last->next_hash = client;
|
struct sim_client *prev_in_bucket = sim_client_from_handle(store, bucket->last);
|
||||||
client->prev_hash = bucket->last;
|
if (prev_in_bucket->valid) {
|
||||||
|
prev_in_bucket->next_in_bucket = client->handle;
|
||||||
|
client->prev_in_bucket = prev_in_bucket->handle;
|
||||||
} else {
|
} else {
|
||||||
bucket->first = client;
|
bucket->first = client->handle;
|
||||||
}
|
}
|
||||||
bucket->last = client;
|
bucket->last = client->handle;
|
||||||
|
}
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sim_client_release(struct sim_client *client)
|
void sim_client_release(struct sim_client *client)
|
||||||
{
|
{
|
||||||
struct sim_client_store *store = client_store_from_client(client);
|
struct sim_client_store *store = sim_client_store_from_client(client);
|
||||||
client->valid = false;
|
client->valid = false;
|
||||||
++client->handle.gen;
|
++client->handle.gen;
|
||||||
client->next_free = store->first_free_client;
|
client->next_free = store->first_free_client;
|
||||||
store->first_free_client = client;
|
store->first_free_client = client->handle;
|
||||||
--store->num_allocated;
|
--store->num_allocated;
|
||||||
|
|
||||||
/* Remove from channel lookup */
|
/* Remove from channel lookup */
|
||||||
u64 bucket_index = client->channel_hash % store->num_channel_lookup_buckets;
|
u64 bucket_index = client->channel_hash % store->num_channel_lookup_buckets;
|
||||||
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
||||||
struct sim_client *prev = client->prev_hash;
|
struct sim_client *prev = sim_client_from_handle(store, client->prev_in_bucket);
|
||||||
struct sim_client *next = client->next_hash;
|
struct sim_client *next = sim_client_from_handle(store, client->next_in_bucket);
|
||||||
if (prev) {
|
if (prev->valid) {
|
||||||
prev->next_hash = next;
|
prev->next_in_bucket = next->handle;
|
||||||
} else {
|
} else {
|
||||||
bucket->first = next;
|
bucket->first = next->handle;
|
||||||
}
|
}
|
||||||
if (next) {
|
if (next->valid) {
|
||||||
next->prev_hash = prev;
|
next->prev_in_bucket = prev->handle;
|
||||||
} else {
|
} else {
|
||||||
bucket->last = prev;
|
bucket->last = prev->handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Lerp
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend)
|
||||||
|
{
|
||||||
|
if (c0->valid && c1->valid && c0->handle.gen == c1->handle.gen) {
|
||||||
|
c0->cursor_pos = v2_lerp(c0->cursor_pos, c1->cursor_pos, blend);
|
||||||
|
c->control.move = v2_lerp(c0->control.move, c1->control.move, blend);
|
||||||
|
c->control.focus = v2_lerp(c0->control.focus, c1->control.focus, blend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "sim_ent.h"
|
#include "sim_ent.h"
|
||||||
|
|
||||||
|
#define SIM_CLIENT_NIL_HANDLE ((struct sim_client_handle) { .gen = 0, .idx = 0 })
|
||||||
|
|
||||||
struct sim_client_channel_lookup_bucket;
|
struct sim_client_channel_lookup_bucket;
|
||||||
|
|
||||||
struct sim_client {
|
struct sim_client {
|
||||||
@ -12,9 +14,9 @@ struct sim_client {
|
|||||||
struct host_channel_id channel_id;
|
struct host_channel_id channel_id;
|
||||||
u64 channel_hash;
|
u64 channel_hash;
|
||||||
|
|
||||||
struct sim_client *next_free;
|
struct sim_client_handle next_free;
|
||||||
struct sim_client *next_hash;
|
struct sim_client_handle next_in_bucket;
|
||||||
struct sim_client *prev_hash;
|
struct sim_client_handle prev_in_bucket;
|
||||||
|
|
||||||
struct v2 cursor_pos;
|
struct v2 cursor_pos;
|
||||||
struct sim_ent_handle camera_ent;
|
struct sim_ent_handle camera_ent;
|
||||||
@ -34,30 +36,37 @@ struct sim_client_store {
|
|||||||
struct sim_client_channel_lookup_bucket *channel_lookup_buckets;
|
struct sim_client_channel_lookup_bucket *channel_lookup_buckets;
|
||||||
u64 num_channel_lookup_buckets;
|
u64 num_channel_lookup_buckets;
|
||||||
|
|
||||||
|
struct sim_client_handle first_free_client;
|
||||||
struct sim_client *clients;
|
struct sim_client *clients;
|
||||||
struct sim_client *first_free_client;
|
|
||||||
u64 num_allocated;
|
u64 num_allocated;
|
||||||
u64 num_reserved;
|
u64 num_reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
INLINE struct sim_client *client_nil(void)
|
INLINE struct sim_client *sim_client_nil(void)
|
||||||
{
|
{
|
||||||
extern READONLY struct sim_client _g_sim_client_nil;
|
extern READONLY struct sim_client **_g_sim_client_nil;
|
||||||
return &_g_sim_client_nil;
|
return *_g_sim_client_nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE struct sim_client_store *client_store_nil(void)
|
INLINE struct sim_client_store *sim_client_store_nil(void)
|
||||||
{
|
{
|
||||||
extern READONLY struct sim_client_store _g_sim_client_store_nil;
|
extern READONLY struct sim_client_store **_g_sim_client_store_nil;
|
||||||
return &_g_sim_client_store_nil;
|
return *_g_sim_client_store_nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sim_client_startup_receipt { i32 _; };
|
||||||
|
struct sim_client_startup_receipt sim_client_startup(void);
|
||||||
|
|
||||||
struct sim_client_store *sim_client_store_alloc(u64 arena_reserve);
|
struct sim_client_store *sim_client_store_alloc(u64 arena_reserve);
|
||||||
|
void sim_client_store_copy(struct sim_client_store *dst, struct sim_client_store *src);
|
||||||
void sim_client_store_release(struct sim_client_store *store);
|
void sim_client_store_release(struct sim_client_store *store);
|
||||||
struct sim_client_store *client_store_from_client(struct sim_client *client);
|
|
||||||
|
struct sim_client_store *sim_client_store_from_client(struct sim_client *client);
|
||||||
struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct sim_client_handle handle);
|
struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct sim_client_handle handle);
|
||||||
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id);
|
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id);
|
||||||
struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_channel_id channel_id);
|
struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_channel_id channel_id);
|
||||||
void sim_client_release(struct sim_client *client);
|
void sim_client_release(struct sim_client *client);
|
||||||
|
|
||||||
|
void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
157
src/sim_ent.c
157
src/sim_ent.c
@ -4,22 +4,48 @@
|
|||||||
/* Offset in bytes from start of store struct to start of entities array (assume adjacently allocated) */
|
/* Offset in bytes from start of store struct to start of entities array (assume adjacently allocated) */
|
||||||
#define STORE_ENTITIES_OFFSET (sizeof(struct sim_ent_store) + (sizeof(struct sim_ent_store) % alignof(struct sim_ent)))
|
#define STORE_ENTITIES_OFFSET (sizeof(struct sim_ent_store) + (sizeof(struct sim_ent_store) % alignof(struct sim_ent)))
|
||||||
|
|
||||||
/* Accessed via sim_ent_store_nil() */
|
/* ========================== *
|
||||||
READONLY struct sim_ent_store _g_sim_ent_store_nil = { .valid = false };
|
* Global state
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
|
||||||
|
GLOBAL struct {
|
||||||
|
struct sim_ent *nil_ent;
|
||||||
|
struct sim_ent_store *nil_store;
|
||||||
|
} G = ZI, DEBUG_ALIAS(G, G_sim_ent);
|
||||||
|
|
||||||
/* Accessed via sim_ent_nil() */
|
/* Accessed via sim_ent_nil() */
|
||||||
/* TODO: Allocate nil entity in nil store */
|
READONLY struct sim_ent **_g_sim_ent_nil = &G.nil_ent;
|
||||||
READONLY struct sim_ent _g_sim_ent_nil = {
|
|
||||||
.valid = false,
|
/* Accessed via sim_ent_store_nil() */
|
||||||
.local_xform = XFORM_IDENT_NOCAST,
|
READONLY struct sim_ent_store **_g_sim_ent_store_nil = &G.nil_store;
|
||||||
.cached_global_xform = XFORM_IDENT_NOCAST,
|
|
||||||
.cached_global_xform_dirty = false,
|
struct sim_ent_startup_receipt sim_ent_startup(void)
|
||||||
.friction = 0.5f,
|
{
|
||||||
.mass_unscaled = 1,
|
/* Setup nil store */
|
||||||
.inertia_unscaled = 1,
|
G.nil_store = sim_ent_store_alloc(GIGABYTE(1), false);
|
||||||
.sprite_local_xform = XFORM_IDENT_NOCAST,
|
|
||||||
.sprite_tint = COLOR_WHITE
|
/* Setup nil ent */
|
||||||
};
|
G.nil_ent = arena_push_zero(&G.nil_store->arena, struct sim_ent);
|
||||||
|
++G.nil_store->num_allocated;
|
||||||
|
++G.nil_store->num_reserved;
|
||||||
|
MEMZERO_STRUCT(G.nil_ent);
|
||||||
|
G.nil_ent->handle = SIM_ENT_NIL_HANDLE;
|
||||||
|
G.nil_ent->local_xform = XFORM_IDENT;
|
||||||
|
G.nil_ent->cached_global_xform = XFORM_IDENT;
|
||||||
|
G.nil_ent->cached_global_xform_dirty = false;
|
||||||
|
G.nil_ent->friction = 0.5f;
|
||||||
|
G.nil_ent->mass_unscaled = 1;
|
||||||
|
G.nil_ent->inertia_unscaled = 1;
|
||||||
|
G.nil_ent->sprite_local_xform = XFORM_IDENT;
|
||||||
|
G.nil_ent->sprite_tint = COLOR_WHITE;
|
||||||
|
|
||||||
|
G.nil_ent->valid = false;
|
||||||
|
G.nil_store->valid = false;
|
||||||
|
|
||||||
|
arena_set_readonly(&G.nil_store->arena);
|
||||||
|
return (struct sim_ent_startup_receipt) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Store allocation
|
* Store allocation
|
||||||
@ -27,43 +53,56 @@ READONLY struct sim_ent _g_sim_ent_nil = {
|
|||||||
|
|
||||||
INTERNAL struct sim_ent *sim_ent_alloc_internal(struct sim_ent_store *store);
|
INTERNAL struct sim_ent *sim_ent_alloc_internal(struct sim_ent_store *store);
|
||||||
|
|
||||||
INTERNAL void store_make_root(struct sim_ent_store *store)
|
struct sim_ent_store *sim_ent_store_alloc(u64 arena_reserve, b32 create_root)
|
||||||
{
|
{
|
||||||
struct sim_ent *root = sim_ent_alloc_internal(store);
|
struct sim_ent_store *store;
|
||||||
root->is_root = true;
|
|
||||||
root->local_xform = XFORM_IDENT;
|
|
||||||
root->cached_global_xform = XFORM_IDENT;
|
|
||||||
root->cached_global_xform_dirty = false;
|
|
||||||
store->root = root->handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_ent_store *sim_ent_store_alloc(u64 arena_reserve)
|
|
||||||
{
|
{
|
||||||
struct arena arena = arena_alloc(arena_reserve);
|
struct arena arena = arena_alloc(arena_reserve);
|
||||||
struct sim_ent_store *store = arena_push_zero(&arena, struct sim_ent_store);
|
store = arena_push_zero(&arena, struct sim_ent_store);
|
||||||
store->valid = true;
|
|
||||||
store->arena = arena;
|
store->arena = arena;
|
||||||
store->entities = arena_dry_push(&arena, struct sim_ent);
|
}
|
||||||
|
store->valid = true;
|
||||||
|
|
||||||
|
store->entities = arena_dry_push(&store->arena, struct sim_ent);
|
||||||
ASSERT((u8 *)store->entities - (u8 *)store == STORE_ENTITIES_OFFSET); /* Offset must be correct */
|
ASSERT((u8 *)store->entities - (u8 *)store == STORE_ENTITIES_OFFSET); /* Offset must be correct */
|
||||||
store_make_root(store);
|
|
||||||
|
/* Create root entity */
|
||||||
|
if (create_root) {
|
||||||
|
struct sim_ent *root = sim_ent_alloc_internal(store);
|
||||||
|
ASSERT(sim_ent_handle_eq(root->handle, SIM_ENT_ROOT_HANDLE));
|
||||||
|
root->is_root = true;
|
||||||
|
}
|
||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sim_ent_store_copy(struct sim_ent_store *dst, struct sim_ent_store *src)
|
||||||
|
{
|
||||||
|
i64 commit_diff = (i64)src->arena.pos - (i64)dst->arena.pos;
|
||||||
|
if (commit_diff > 0) {
|
||||||
|
arena_push_array(&dst->arena, u8, commit_diff);
|
||||||
|
}
|
||||||
|
arena_pop_to(&dst->arena, src->arena.pos);
|
||||||
|
|
||||||
|
dst->first_free = src->first_free;
|
||||||
|
dst->entities = (struct sim_ent *)((u8 *)dst + ((u8 *)src->entities - (u8 *)src));
|
||||||
|
dst->num_allocated = src->num_allocated;
|
||||||
|
dst->num_reserved = src->num_reserved;
|
||||||
|
|
||||||
|
MEMCPY(dst->entities, src->entities, sizeof(*src->entities) * src->num_reserved);
|
||||||
|
|
||||||
|
/* Re-initialize root in case of copy from nil store */
|
||||||
|
struct sim_ent *root = &dst->entities[0];
|
||||||
|
root->handle = SIM_ENT_ROOT_HANDLE;
|
||||||
|
root->valid = true;
|
||||||
|
root->is_root = true;
|
||||||
|
}
|
||||||
|
|
||||||
void sim_ent_store_release(struct sim_ent_store *store)
|
void sim_ent_store_release(struct sim_ent_store *store)
|
||||||
{
|
{
|
||||||
arena_release(&store->arena);
|
arena_release(&store->arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sim_ent_store_copy_replace(struct sim_ent_store *dest, struct sim_ent_store *src)
|
|
||||||
{
|
|
||||||
struct arena dest_arena = dest->arena;
|
|
||||||
struct sim_ent *dest_entities = dest->entities;
|
|
||||||
MEMCPY_STRUCT(dest, src);
|
|
||||||
arena_copy_replace(&dest_arena, &src->arena);
|
|
||||||
dest->arena = dest_arena;
|
|
||||||
dest->entities = dest_entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Entity allocation
|
* Entity allocation
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -83,7 +122,7 @@ INTERNAL struct sim_ent *sim_ent_alloc_internal(struct sim_ent_store *store)
|
|||||||
entity = arena_push(&store->arena, struct sim_ent);
|
entity = arena_push(&store->arena, struct sim_ent);
|
||||||
handle = (struct sim_ent_handle) { .gen = 1, .idx = store->num_reserved++ };
|
handle = (struct sim_ent_handle) { .gen = 1, .idx = store->num_reserved++ };
|
||||||
}
|
}
|
||||||
*entity = _g_sim_ent_nil;
|
*entity = *sim_ent_nil();
|
||||||
entity->valid = true;
|
entity->valid = true;
|
||||||
entity->handle = handle;
|
entity->handle = handle;
|
||||||
entity->cached_global_xform_dirty = true;
|
entity->cached_global_xform_dirty = true;
|
||||||
@ -506,3 +545,43 @@ void sim_ent_activate(struct sim_ent *ent, u64 current_tick)
|
|||||||
ent->activation_tick = current_tick;
|
ent->activation_tick = current_tick;
|
||||||
++ent->continuity_gen;
|
++ent->continuity_gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* 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)
|
||||||
|
&& e0->handle.gen == e1->handle.gen
|
||||||
|
&& 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_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
#include "phys.h"
|
#include "phys.h"
|
||||||
|
|
||||||
|
#define SIM_ENT_NIL_HANDLE ((struct sim_ent_handle){ .gen = 0, .idx = 0 })
|
||||||
|
#define SIM_ENT_ROOT_HANDLE ((struct sim_ent_handle){ .gen = 1, .idx = 0 })
|
||||||
|
|
||||||
enum sim_ent_prop {
|
enum sim_ent_prop {
|
||||||
SIM_ENT_PROP_NONE,
|
SIM_ENT_PROP_NONE,
|
||||||
|
|
||||||
@ -47,16 +50,6 @@ enum sim_ent_prop {
|
|||||||
SIM_ENT_PROP_COUNT
|
SIM_ENT_PROP_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sim_ent_store {
|
|
||||||
b32 valid;
|
|
||||||
struct arena arena;
|
|
||||||
u64 num_allocated;
|
|
||||||
u64 num_reserved;
|
|
||||||
struct sim_ent_handle first_free;
|
|
||||||
struct sim_ent_handle root;
|
|
||||||
struct sim_ent *entities;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sim_ent_lookup_key {
|
struct sim_ent_lookup_key {
|
||||||
u64 hash;
|
u64 hash;
|
||||||
};
|
};
|
||||||
@ -302,6 +295,16 @@ struct sim_ent {
|
|||||||
f32 shake;
|
f32 shake;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sim_ent_store {
|
||||||
|
b32 valid;
|
||||||
|
struct arena arena;
|
||||||
|
|
||||||
|
struct sim_ent_handle first_free;
|
||||||
|
struct sim_ent *entities;
|
||||||
|
u64 num_allocated;
|
||||||
|
u64 num_reserved;
|
||||||
|
};
|
||||||
|
|
||||||
struct sim_ent_array {
|
struct sim_ent_array {
|
||||||
struct sim_ent *entities;
|
struct sim_ent *entities;
|
||||||
u64 count;
|
u64 count;
|
||||||
@ -312,6 +315,13 @@ struct sim_ent_prop_array {
|
|||||||
u64 count;
|
u64 count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_ent_startup_receipt { i32 _; };
|
||||||
|
struct sim_ent_startup_receipt sim_ent_startup(void);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Handle helpers
|
* Handle helpers
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -327,14 +337,14 @@ INLINE b32 sim_ent_handle_eq(struct sim_ent_handle a, struct sim_ent_handle b)
|
|||||||
|
|
||||||
INLINE struct sim_ent *sim_ent_nil(void)
|
INLINE struct sim_ent *sim_ent_nil(void)
|
||||||
{
|
{
|
||||||
extern READONLY struct sim_ent _g_sim_ent_nil;
|
extern READONLY struct sim_ent **_g_sim_ent_nil;
|
||||||
return &_g_sim_ent_nil;
|
return *_g_sim_ent_nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE struct sim_ent_store *sim_ent_store_nil(void)
|
INLINE struct sim_ent_store *sim_ent_store_nil(void)
|
||||||
{
|
{
|
||||||
extern READONLY struct sim_ent_store _g_sim_ent_store_nil;
|
extern READONLY struct sim_ent_store **_g_sim_ent_store_nil;
|
||||||
return &_g_sim_ent_store_nil;
|
return *_g_sim_ent_store_nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -372,9 +382,9 @@ INLINE b32 sim_ent_is_valid_and_active(struct sim_ent *ent)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Entity store */
|
/* Entity store */
|
||||||
struct sim_ent_store *sim_ent_store_alloc(u64 arena_reserve);
|
struct sim_ent_store *sim_ent_store_alloc(u64 arena_reserve, b32 create_root);
|
||||||
|
void sim_ent_store_copy(struct sim_ent_store *dst, struct sim_ent_store *src);
|
||||||
void sim_ent_store_release(struct sim_ent_store *store);
|
void sim_ent_store_release(struct sim_ent_store *store);
|
||||||
void sim_ent_store_copy_replace(struct sim_ent_store *dest, struct sim_ent_store *src);
|
|
||||||
|
|
||||||
/* Entity */
|
/* Entity */
|
||||||
struct sim_ent *sim_ent_alloc(struct sim_ent *parent);
|
struct sim_ent *sim_ent_alloc(struct sim_ent *parent);
|
||||||
@ -416,4 +426,7 @@ struct sim_ent_lookup_key sim_ent_lookup_key_from_two_handles(struct sim_ent_han
|
|||||||
/* Activate */
|
/* Activate */
|
||||||
void sim_ent_activate(struct sim_ent *ent, u64 current_tick);
|
void sim_ent_activate(struct sim_ent *ent, u64 current_tick);
|
||||||
|
|
||||||
|
/* Lerp */
|
||||||
|
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -218,7 +218,6 @@ struct string sim_snapshot_encode(struct arena *arena, struct sim_client *client
|
|||||||
struct byte_writer bw = bw_from_arena(arena);
|
struct byte_writer bw = bw_from_arena(arena);
|
||||||
|
|
||||||
bw_write_var_uint(&bw, snapshot->continuity_gen);
|
bw_write_var_uint(&bw, snapshot->continuity_gen);
|
||||||
bw_write_var_sint(&bw, snapshot->publishtime_ns);
|
|
||||||
|
|
||||||
bw_write_var_sint(&bw, snapshot->real_dt_ns);
|
bw_write_var_sint(&bw, snapshot->real_dt_ns);
|
||||||
bw_write_var_sint(&bw, snapshot->real_time_ns);
|
bw_write_var_sint(&bw, snapshot->real_time_ns);
|
||||||
@ -251,14 +250,12 @@ struct string sim_snapshot_encode(struct arena *arena, struct sim_client *client
|
|||||||
return bw_get_written(&bw);
|
return bw_get_written(&bw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sim_snapshot_decode(struct string str, struct sim_snapshot *snapshot, u64 tick)
|
void sim_snapshot_decode(struct string str, struct sim_snapshot *snapshot)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct byte_reader br = br_from_buffer(str);
|
struct byte_reader br = br_from_buffer(str);
|
||||||
|
|
||||||
snapshot->tick = tick;
|
|
||||||
snapshot->continuity_gen = br_read_var_uint(&br);
|
snapshot->continuity_gen = br_read_var_uint(&br);
|
||||||
snapshot->publishtime_ns = br_read_var_sint(&br);
|
|
||||||
|
|
||||||
snapshot->real_dt_ns = br_read_var_sint(&br);
|
snapshot->real_dt_ns = br_read_var_sint(&br);
|
||||||
snapshot->real_time_ns = br_read_var_sint(&br);
|
snapshot->real_time_ns = br_read_var_sint(&br);
|
||||||
|
|||||||
@ -108,6 +108,6 @@ void sim_events_from_host_events(struct arena *arena, struct host_event_array ho
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct string sim_snapshot_encode(struct arena *arena, struct sim_client *client, struct sim_snapshot *snapshot);
|
struct string sim_snapshot_encode(struct arena *arena, struct sim_client *client, struct sim_snapshot *snapshot);
|
||||||
void sim_snapshot_decode(struct string str, struct sim_snapshot *snapshot, u64 tick);
|
void sim_snapshot_decode(struct string str, struct sim_snapshot *snapshot);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -4,15 +4,316 @@
|
|||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
|
|
||||||
void sim_snapshot_alloc(struct sim_snapshot *sim_snapshot)
|
#define TICK_LOOKUP_BUCKETS 31
|
||||||
|
|
||||||
|
INTERNAL void sim_snapshot_release_internal(struct sim_snapshot *snapshot);
|
||||||
|
|
||||||
|
struct sim_snapshot_lookup_bucket {
|
||||||
|
struct sim_snapshot *first;
|
||||||
|
struct sim_snapshot *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
GLOBAL struct {
|
||||||
|
struct arena nil_arena;
|
||||||
|
struct sim_snapshot_store *nil_snapshot_store;
|
||||||
|
struct sim_snapshot *nil_snapshot;
|
||||||
|
} G = ZI, DEBUG_ALIAS(G, G_sim_snapshot);
|
||||||
|
|
||||||
|
/* Accessed via `sim_snapshot_nil()` */
|
||||||
|
READONLY struct sim_snapshot **_g_sim_snapshot_nil = &G.nil_snapshot;
|
||||||
|
|
||||||
|
/* Accessed via `sim_snapshot_store_nil()` */
|
||||||
|
READONLY struct sim_snapshot_store **_g_sim_snapshot_store_nil = &G.nil_snapshot_store;
|
||||||
|
|
||||||
|
struct sim_snapshot_startup_receipt sim_snapshot_startup(void)
|
||||||
{
|
{
|
||||||
MEMZERO_STRUCT(sim_snapshot);
|
G.nil_arena = arena_alloc(GIGABYTE(1));
|
||||||
sim_snapshot->client_store = sim_client_store_alloc(GIGABYTE(64));
|
|
||||||
sim_snapshot->ent_store = sim_ent_store_alloc(GIGABYTE(64));
|
G.nil_snapshot_store = arena_push_zero(&G.nil_arena, struct sim_snapshot_store);
|
||||||
|
G.nil_snapshot_store->valid = false;
|
||||||
|
|
||||||
|
G.nil_snapshot = arena_push_zero(&G.nil_arena, struct sim_snapshot);
|
||||||
|
G.nil_snapshot->valid = false;
|
||||||
|
G.nil_snapshot->store = sim_snapshot_store_nil();
|
||||||
|
G.nil_snapshot->client_store = sim_client_store_nil();
|
||||||
|
G.nil_snapshot->ent_store = sim_ent_store_nil();
|
||||||
|
|
||||||
|
arena_set_readonly(&G.nil_arena);
|
||||||
|
return (struct sim_snapshot_startup_receipt ) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
void sim_snapshot_release(struct sim_snapshot *sim_snapshot)
|
/* ========================== *
|
||||||
|
* Store
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_snapshot_store *sim_snapshot_store_alloc(void)
|
||||||
{
|
{
|
||||||
sim_ent_store_release(sim_snapshot->ent_store);
|
struct sim_snapshot_store *store;
|
||||||
sim_client_store_release(sim_snapshot->client_store);
|
{
|
||||||
|
struct arena arena = arena_alloc(GIGABYTE(64));
|
||||||
|
store = arena_push_zero(&arena, struct sim_snapshot_store);
|
||||||
|
store->arena = arena;
|
||||||
|
}
|
||||||
|
store->valid = true;
|
||||||
|
store->num_lookup_buckets = TICK_LOOKUP_BUCKETS;
|
||||||
|
store->lookup_buckets = arena_push_array_zero(&store->arena, struct sim_snapshot_lookup_bucket, store->num_lookup_buckets);
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sim_snapshot_store_release(struct sim_snapshot_store *store)
|
||||||
|
{
|
||||||
|
/* Release snapshot internal memory */
|
||||||
|
struct sim_snapshot *ss = sim_snapshot_from_tick(store, store->first_tick);
|
||||||
|
while ((ss = sim_snapshot_from_tick(store, ss->next_tick))->valid) {
|
||||||
|
sim_snapshot_release_internal(ss);
|
||||||
|
}
|
||||||
|
ss = store->first_free_snapshot;
|
||||||
|
while ((ss = ss->next_free)) {
|
||||||
|
sim_snapshot_release_internal(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release store */
|
||||||
|
arena_release(&store->arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Alloc
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
/* Produces a new snapshot at `tick` with data copied from `src` snapshot. */
|
||||||
|
struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick)
|
||||||
|
{
|
||||||
|
if (tick == 0) {
|
||||||
|
return sim_snapshot_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sim_client_store *client_store = NULL;
|
||||||
|
struct sim_ent_store *ent_store = NULL;
|
||||||
|
struct sim_snapshot *ss;
|
||||||
|
{
|
||||||
|
ss = store->first_free_snapshot;
|
||||||
|
if (ss) {
|
||||||
|
store->first_free_snapshot = ss->next_free;
|
||||||
|
client_store = ss->client_store;
|
||||||
|
ent_store = ss->ent_store;
|
||||||
|
} else {
|
||||||
|
ss = arena_push(&store->arena, struct sim_snapshot);
|
||||||
|
/* NOTE: These should be released in `sim_snapshot_release_internal` */
|
||||||
|
ent_store = sim_ent_store_alloc(GIGABYTE(64), true);
|
||||||
|
client_store = sim_client_store_alloc(GIGABYTE(64));
|
||||||
|
}
|
||||||
|
MEMZERO_STRUCT(ss);
|
||||||
|
}
|
||||||
|
ss->tick = tick;
|
||||||
|
ss->valid = true;
|
||||||
|
ss->store = store;
|
||||||
|
ss->client_store = client_store;
|
||||||
|
ss->ent_store = ent_store;
|
||||||
|
++store->num_ticks;
|
||||||
|
|
||||||
|
/* Copy src */
|
||||||
|
ss->real_dt_ns = src->real_dt_ns;
|
||||||
|
ss->real_time_ns = src->real_time_ns;
|
||||||
|
ss->world_timescale = src->world_timescale;
|
||||||
|
ss->world_dt_ns = src->world_dt_ns;
|
||||||
|
ss->world_time_ns = src->world_time_ns;
|
||||||
|
ss->received_at_ns = src->received_at_ns;
|
||||||
|
ss->continuity_gen = src->continuity_gen;
|
||||||
|
ss->local_client = src->local_client;
|
||||||
|
sim_client_store_copy(ss->client_store, src->client_store);
|
||||||
|
sim_ent_store_copy(ss->ent_store, src->ent_store);
|
||||||
|
|
||||||
|
/* Release duplicate tick if it exists */
|
||||||
|
{
|
||||||
|
struct sim_snapshot *existing = sim_snapshot_from_tick(store, tick);
|
||||||
|
if (existing->valid) {
|
||||||
|
sim_snapshot_release(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Linear search to insert snapshot in tick order */
|
||||||
|
{
|
||||||
|
struct sim_snapshot *prev = sim_snapshot_from_tick(store, store->last_tick);
|
||||||
|
while (prev->valid) {
|
||||||
|
if (prev->tick < tick) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = sim_snapshot_from_tick(store, prev->prev_tick);
|
||||||
|
}
|
||||||
|
if (prev->valid) {
|
||||||
|
struct sim_snapshot *next = sim_snapshot_from_tick(store, prev->next_tick);
|
||||||
|
if (next->valid) {
|
||||||
|
next->prev_tick = tick;
|
||||||
|
} else {
|
||||||
|
store->last_tick = tick;
|
||||||
|
}
|
||||||
|
prev->next_tick = ss->tick;
|
||||||
|
} else {
|
||||||
|
struct sim_snapshot *first = sim_snapshot_from_tick(store, store->first_tick);
|
||||||
|
if (first->valid) {
|
||||||
|
ss->next_tick = first->tick;
|
||||||
|
first->prev_tick = tick;
|
||||||
|
} else {
|
||||||
|
store->last_tick = tick;
|
||||||
|
}
|
||||||
|
store->first_tick = tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert into lookup */
|
||||||
|
{
|
||||||
|
u64 bucket_index = tick % store->num_lookup_buckets;
|
||||||
|
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
|
||||||
|
if (bucket->last) {
|
||||||
|
bucket->last->next_in_bucket = ss;
|
||||||
|
ss->prev_in_bucket = bucket->last;
|
||||||
|
} else {
|
||||||
|
bucket->first = ss;
|
||||||
|
}
|
||||||
|
bucket->last = ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTE: Only releases snapshot into store's free list.
|
||||||
|
* Use `sim_snapshot_release_internal` to release allocated snapshot memory back to system */
|
||||||
|
void sim_snapshot_release(struct sim_snapshot *ss)
|
||||||
|
{
|
||||||
|
struct sim_snapshot_store *store = ss->store;
|
||||||
|
|
||||||
|
/* Remove from lookup */
|
||||||
|
{
|
||||||
|
u64 bucket_index = ss->tick % store->num_lookup_buckets;
|
||||||
|
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
|
||||||
|
struct sim_snapshot *prev = ss->prev_in_bucket;
|
||||||
|
struct sim_snapshot *next = ss->next_in_bucket;
|
||||||
|
if (prev) {
|
||||||
|
prev->next_in_bucket = next;
|
||||||
|
} else {
|
||||||
|
bucket->first = next;
|
||||||
|
}
|
||||||
|
if (next) {
|
||||||
|
next->prev_in_bucket = prev;
|
||||||
|
} else {
|
||||||
|
bucket->last = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove from snapshot list */
|
||||||
|
{
|
||||||
|
struct sim_snapshot *prev = sim_snapshot_from_tick(store, ss->prev_tick);
|
||||||
|
struct sim_snapshot *next = sim_snapshot_from_tick(store, ss->next_tick);
|
||||||
|
if (prev->valid) {
|
||||||
|
prev->next_tick = next->tick;
|
||||||
|
} else {
|
||||||
|
store->first_tick = next->tick;
|
||||||
|
}
|
||||||
|
if (next->valid) {
|
||||||
|
next->prev_tick = prev->tick;
|
||||||
|
} else {
|
||||||
|
store->last_tick = prev->tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ss->valid = false;
|
||||||
|
ss->next_free = store->first_free_snapshot;
|
||||||
|
store->first_free_snapshot = ss;
|
||||||
|
--store->num_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL void sim_snapshot_release_internal(struct sim_snapshot *snapshot)
|
||||||
|
{
|
||||||
|
sim_client_store_release(snapshot->client_store);
|
||||||
|
sim_ent_store_release(snapshot->ent_store);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Lookup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u64 tick)
|
||||||
|
{
|
||||||
|
struct sim_snapshot *ss = sim_snapshot_nil();
|
||||||
|
if (tick > 0) {
|
||||||
|
u64 bucket_index = tick % store->num_lookup_buckets;
|
||||||
|
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
|
||||||
|
for (struct sim_snapshot *search = bucket->first; search; search = search->next_in_bucket) {
|
||||||
|
if (search->tick == tick) {
|
||||||
|
ss = search;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Lerp
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
|
||||||
|
/* New snapshot will be allocated with same tick as ss0 or ss1, so the result should go into a different store */
|
||||||
|
ASSERT(ss0->store != store && ss1->store != store);
|
||||||
|
|
||||||
|
struct sim_snapshot *ss;
|
||||||
|
b32 should_blend = true;
|
||||||
|
if (ss0->continuity_gen == ss1->continuity_gen && 0 < blend && blend < 1) {
|
||||||
|
ss = sim_snapshot_alloc(store, ss0, ss0->tick);
|
||||||
|
} else if (math_round_to_int64(blend) <= 0) {
|
||||||
|
ss = sim_snapshot_alloc(store, ss0, ss0->tick);
|
||||||
|
should_blend = false;
|
||||||
|
} else {
|
||||||
|
ss = sim_snapshot_alloc(store, ss1, ss1->tick);
|
||||||
|
should_blend = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ss0->valid || !ss1->valid) {
|
||||||
|
/* New snapshot allocation caused one of the src snapshots original to release.
|
||||||
|
* ss0 & ss1 should be from a separate store than the allocating one. */
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_blend) {
|
||||||
|
/* Blend time */
|
||||||
|
ss->real_dt_ns = math_lerp_i64(ss0->real_dt_ns, ss1->real_dt_ns, blend);
|
||||||
|
ss->real_time_ns = math_lerp_i64(ss0->real_time_ns, ss1->real_time_ns, blend);
|
||||||
|
ss->world_timescale = math_lerp_f64(ss0->world_timescale, ss1->world_timescale, blend);
|
||||||
|
ss->world_dt_ns = math_lerp_i64(ss0->world_dt_ns, ss1->world_dt_ns, blend);
|
||||||
|
ss->world_time_ns = math_lerp_i64(ss0->world_time_ns, ss1->world_time_ns, blend);
|
||||||
|
|
||||||
|
/* Blend clients */
|
||||||
|
{
|
||||||
|
__profscope(snapshot_lerp_clients);
|
||||||
|
u64 num_clients = min_u64(ss0->client_store->num_reserved, ss1->client_store->num_reserved);
|
||||||
|
for (u64 i = 0; i < num_clients; ++i) {
|
||||||
|
struct sim_client *c = &ss->client_store->clients[i];
|
||||||
|
struct sim_client *c0 = &ss0->client_store->clients[i];
|
||||||
|
struct sim_client *c1 = &ss1->client_store->clients[i];
|
||||||
|
sim_client_lerp(c, c0, c1, blend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Blend entities */
|
||||||
|
{
|
||||||
|
__profscope(snapshot_lerp_entities);
|
||||||
|
u64 num_entities = min_u64(ss0->ent_store->num_reserved, ss1->ent_store->num_reserved);
|
||||||
|
for (u64 i = 0; i < num_entities; ++i) {
|
||||||
|
struct sim_ent *e = &ss->ent_store->entities[i];
|
||||||
|
struct sim_ent *e0 = &ss0->ent_store->entities[i];
|
||||||
|
struct sim_ent *e1 = &ss1->ent_store->entities[i];
|
||||||
|
sim_ent_lerp(e, e0, e1, blend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ss;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,32 @@
|
|||||||
#include "sim_ent.h"
|
#include "sim_ent.h"
|
||||||
#include "sim_client.h"
|
#include "sim_client.h"
|
||||||
|
|
||||||
|
struct sim_snapshot_store {
|
||||||
|
b32 valid;
|
||||||
|
struct arena arena;
|
||||||
|
|
||||||
|
/* Snapshots sorted by tick (low to high) */
|
||||||
|
u64 first_tick;
|
||||||
|
u64 last_tick;
|
||||||
|
u64 num_ticks;
|
||||||
|
|
||||||
|
struct sim_snapshot *first_free_snapshot;
|
||||||
|
|
||||||
|
/* Tick -> snapshot lookup */
|
||||||
|
u64 num_lookup_buckets;
|
||||||
|
struct sim_snapshot_lookup_bucket *lookup_buckets;
|
||||||
|
};
|
||||||
|
|
||||||
struct sim_snapshot {
|
struct sim_snapshot {
|
||||||
u64 continuity_gen; /* Starts at 1 */
|
/* Managed by store */
|
||||||
u64 tick; /* Starts at 1 */
|
b32 valid;
|
||||||
i64 publishtime_ns; /* When was this tick simulated in program time */
|
u64 tick;
|
||||||
|
struct sim_snapshot_store *store;
|
||||||
|
struct sim_snapshot *next_free;
|
||||||
|
struct sim_snapshot *next_in_bucket;
|
||||||
|
struct sim_snapshot *prev_in_bucket;
|
||||||
|
u64 prev_tick;
|
||||||
|
u64 next_tick;
|
||||||
|
|
||||||
/* Real time (increases with clock assuming no lag) */
|
/* Real time (increases with clock assuming no lag) */
|
||||||
i64 real_dt_ns;
|
i64 real_dt_ns;
|
||||||
@ -18,13 +40,58 @@ struct sim_snapshot {
|
|||||||
i64 world_dt_ns;
|
i64 world_dt_ns;
|
||||||
i64 world_time_ns;
|
i64 world_time_ns;
|
||||||
|
|
||||||
|
/* When was this snapshot received */
|
||||||
|
i64 received_at_ns;
|
||||||
|
|
||||||
|
/* If != previous tick's continuity then don't lerp */
|
||||||
|
u64 continuity_gen;
|
||||||
|
|
||||||
|
/* Which client in the client store represents the local host in the original snapshot */
|
||||||
struct sim_client_handle local_client;
|
struct sim_client_handle local_client;
|
||||||
|
|
||||||
struct sim_client_store *client_store;
|
struct sim_client_store *client_store;
|
||||||
struct sim_ent_store *ent_store;
|
struct sim_ent_store *ent_store;
|
||||||
};
|
};
|
||||||
|
|
||||||
void sim_snapshot_alloc(struct sim_snapshot *sim_snapshot);
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_snapshot_startup_receipt { i32 _; };
|
||||||
|
struct sim_snapshot_startup_receipt sim_snapshot_startup(void);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Store
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_snapshot_store *sim_snapshot_store_alloc(void);
|
||||||
|
void sim_snapshot_store_release(struct sim_snapshot_store *store);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Nil
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INLINE struct sim_snapshot *sim_snapshot_nil(void)
|
||||||
|
{
|
||||||
|
extern READONLY struct sim_snapshot **_g_sim_snapshot_nil;
|
||||||
|
return *_g_sim_snapshot_nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
INLINE struct sim_snapshot_store *sim_snapshot_store_nil(void)
|
||||||
|
{
|
||||||
|
extern READONLY struct sim_snapshot_store **_g_sim_snapshot_store_nil;
|
||||||
|
return *_g_sim_snapshot_store_nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Snapshot
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick);
|
||||||
void sim_snapshot_release(struct sim_snapshot *sim_snapshot);
|
void sim_snapshot_release(struct sim_snapshot *sim_snapshot);
|
||||||
|
|
||||||
|
struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u64 tick);
|
||||||
|
|
||||||
|
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
#include "collider.h"
|
#include "collider.h"
|
||||||
|
|
||||||
|
/* FIXME: Default space entry & cell pointers to nil */
|
||||||
|
|
||||||
/* Offset in bytes from start of space struct to start of entry array (assume adjacently allocated) */
|
/* Offset in bytes from start of space struct to start of entry array (assume adjacently allocated) */
|
||||||
#define SPACE_ENTRIES_OFFSET (sizeof(struct space) + (sizeof(struct space) % alignof(struct space_entry)))
|
#define SPACE_ENTRIES_OFFSET (sizeof(struct space) + (sizeof(struct space) % alignof(struct space_entry)))
|
||||||
|
|
||||||
|
|||||||
413
src/user.c
413
src/user.c
@ -49,6 +49,10 @@ GLOBAL struct {
|
|||||||
struct host *host;
|
struct host *host;
|
||||||
struct string connect_address_str;
|
struct string connect_address_str;
|
||||||
|
|
||||||
|
struct sim_snapshot_store *sim_snapshot_store; /* Contains buffered snapshots from sim */
|
||||||
|
struct sim_snapshot_store *world_snapshot_store; /* Contains single world snapshot from result of blending sim snapshots */
|
||||||
|
struct sim_snapshot *world;
|
||||||
|
|
||||||
/* Usage stats */
|
/* Usage stats */
|
||||||
i64 last_second_reset_ns;
|
i64 last_second_reset_ns;
|
||||||
struct second_stat client_bytes_read;
|
struct second_stat client_bytes_read;
|
||||||
@ -67,8 +71,6 @@ GLOBAL struct {
|
|||||||
|
|
||||||
struct xform world_to_ui_xf;
|
struct xform world_to_ui_xf;
|
||||||
|
|
||||||
struct sim_snapshot world;
|
|
||||||
|
|
||||||
struct bind_state bind_states[USER_BIND_KIND_COUNT];
|
struct bind_state bind_states[USER_BIND_KIND_COUNT];
|
||||||
|
|
||||||
b32 debug_camera;
|
b32 debug_camera;
|
||||||
@ -81,6 +83,13 @@ GLOBAL struct {
|
|||||||
struct sys_mutex sys_events_mutex;
|
struct sys_mutex sys_events_mutex;
|
||||||
struct arena sys_events_arena;
|
struct arena sys_events_arena;
|
||||||
|
|
||||||
|
i64 real_dt_ns;
|
||||||
|
i64 real_time_ns;
|
||||||
|
|
||||||
|
/* Calculated from <last snapshot receive time + time since packet receive> */
|
||||||
|
i64 sim_time_ns;
|
||||||
|
i64 sim_time_smoothed_ns;
|
||||||
|
|
||||||
/* Per-frame */
|
/* Per-frame */
|
||||||
struct v2 screen_size;
|
struct v2 screen_size;
|
||||||
struct v2 screen_cursor;
|
struct v2 screen_cursor;
|
||||||
@ -148,6 +157,9 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
struct mixer_startup_receipt *mixer_sr,
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
struct phys_startup_receipt *phys_sr,
|
struct phys_startup_receipt *phys_sr,
|
||||||
struct host_startup_receipt *host_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
|
struct sim_ent_startup_receipt *sim_ent_sr,
|
||||||
|
struct sim_client_startup_receipt *sim_client_sr,
|
||||||
|
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
|
||||||
struct string connect_address_str,
|
struct string connect_address_str,
|
||||||
struct sys_window *window)
|
struct sys_window *window)
|
||||||
{
|
{
|
||||||
@ -161,11 +173,18 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
(UNUSED)mixer_sr;
|
(UNUSED)mixer_sr;
|
||||||
(UNUSED)phys_sr;
|
(UNUSED)phys_sr;
|
||||||
(UNUSED)host_sr;
|
(UNUSED)host_sr;
|
||||||
|
(UNUSED)sim_ent_sr;
|
||||||
|
(UNUSED)sim_client_sr;
|
||||||
|
(UNUSED)sim_snapshot_sr;
|
||||||
|
|
||||||
G.arena = arena_alloc(GIGABYTE(64));
|
G.arena = arena_alloc(GIGABYTE(64));
|
||||||
G.sys_events_mutex = sys_mutex_alloc();
|
G.sys_events_mutex = sys_mutex_alloc();
|
||||||
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
||||||
sim_snapshot_alloc(&G.world);
|
|
||||||
|
/* Snapshot store */
|
||||||
|
G.sim_snapshot_store = sim_snapshot_store_alloc();
|
||||||
|
G.world_snapshot_store = sim_snapshot_store_alloc();
|
||||||
|
G.world = sim_snapshot_nil();
|
||||||
|
|
||||||
//struct sock_address bind_addr = sock_address_from_any_local_interface_with_dynamic_port();
|
//struct sock_address bind_addr = sock_address_from_any_local_interface_with_dynamic_port();
|
||||||
G.host = host_alloc(0);
|
G.host = host_alloc(0);
|
||||||
@ -176,11 +195,13 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
G.final_cmd_buffer = renderer_cmd_buffer_alloc();
|
G.final_cmd_buffer = renderer_cmd_buffer_alloc();
|
||||||
G.backbuffer_cmd_buffer = renderer_cmd_buffer_alloc();
|
G.backbuffer_cmd_buffer = renderer_cmd_buffer_alloc();
|
||||||
|
|
||||||
|
G.real_time_ns = sys_time_ns();
|
||||||
|
|
||||||
G.window = window;
|
G.window = window;
|
||||||
sys_window_register_event_callback(G.window, &window_event_callback);
|
sys_window_register_event_callback(G.window, &window_event_callback);
|
||||||
|
|
||||||
if (connect_address_str.len == 0) {
|
if (connect_address_str.len == 0) {
|
||||||
G.sim_ctx = sim_ctx_alloc(sprite_sr, phys_sr, host_sr, 12345);
|
G.sim_ctx = sim_ctx_alloc(sprite_sr, phys_sr, host_sr, sim_ent_sr, sim_client_sr, sim_snapshot_sr, 12345);
|
||||||
G.connect_address_str = LIT("127.0.0.1:12345");
|
G.connect_address_str = LIT("127.0.0.1:12345");
|
||||||
G.sim_thread = sys_thread_alloc(&user_sim_thread_entry_point, G.sim_ctx, LIT("[P2] Sim thread"));
|
G.sim_thread = sys_thread_alloc(&user_sim_thread_entry_point, G.sim_ctx, LIT("[P2] Sim thread"));
|
||||||
} else {
|
} else {
|
||||||
@ -237,159 +258,6 @@ INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event)
|
|||||||
sys_mutex_unlock(&lock);
|
sys_mutex_unlock(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Sim -> user communication
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
INTERNAL struct blend_tick *blend_tick_alloc(void)
|
|
||||||
{
|
|
||||||
struct blend_tick *bt = NULL;
|
|
||||||
if (G.head_free_blend_tick) {
|
|
||||||
bt = G.head_free_blend_tick;
|
|
||||||
G.head_free_blend_tick = bt->next;
|
|
||||||
*bt = (struct blend_tick) {
|
|
||||||
.sim_snapshot = bt->sim_snapshot
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
bt = arena_push_zero(&G.arena, struct blend_tick);
|
|
||||||
sim_snapshot_alloc(&bt->sim_snapshot);
|
|
||||||
}
|
|
||||||
if (G.head_blend_tick) {
|
|
||||||
bt->next = G.head_blend_tick;
|
|
||||||
G.head_blend_tick->prev = bt;
|
|
||||||
}
|
|
||||||
G.head_blend_tick = bt;
|
|
||||||
return bt;
|
|
||||||
}
|
|
||||||
|
|
||||||
INTERNAL void blend_tick_release(struct blend_tick *bt)
|
|
||||||
{
|
|
||||||
struct blend_tick *next = bt->next;
|
|
||||||
struct blend_tick *prev = bt->prev;
|
|
||||||
|
|
||||||
/* Remove from list */
|
|
||||||
if (next) {
|
|
||||||
next->prev = prev;
|
|
||||||
}
|
|
||||||
if (prev) {
|
|
||||||
prev->next = next;
|
|
||||||
}
|
|
||||||
if (bt == G.head_blend_tick) {
|
|
||||||
G.head_blend_tick = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add to free list */
|
|
||||||
bt->next = G.head_free_blend_tick;
|
|
||||||
bt->prev = NULL;
|
|
||||||
G.head_free_blend_tick = bt;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct interp_ticks {
|
|
||||||
struct sim_snapshot *from_tick;
|
|
||||||
struct sim_snapshot *to_tick;
|
|
||||||
};
|
|
||||||
|
|
||||||
INTERNAL struct interp_ticks pull_ticks(i64 blend_time_ns)
|
|
||||||
{
|
|
||||||
__prof;
|
|
||||||
|
|
||||||
/* Find newest stored tick */
|
|
||||||
struct sim_snapshot *newest_tick = NULL;
|
|
||||||
for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) {
|
|
||||||
if (!newest_tick || bt->sim_snapshot.tick > newest_tick->tick) {
|
|
||||||
newest_tick = &bt->sim_snapshot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_snapshot *from_tick;
|
|
||||||
struct sim_snapshot *to_tick;
|
|
||||||
if (newest_tick && sim_get_latest_tick_continuity_gen() == newest_tick->continuity_gen) {
|
|
||||||
/* Pull new tick from sim thread if necessary */
|
|
||||||
if (!newest_tick || sim_get_latest_tick() > newest_tick->tick) {
|
|
||||||
struct blend_tick *latest_bt = blend_tick_alloc();
|
|
||||||
newest_tick = &latest_bt->sim_snapshot;
|
|
||||||
sim_get_latest_tick(newest_tick);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find oldest tick */
|
|
||||||
struct sim_snapshot *oldest_tick = NULL;
|
|
||||||
for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) {
|
|
||||||
if (!oldest_tick || bt->sim_snapshot.tick < oldest_tick->tick) {
|
|
||||||
oldest_tick = &bt->sim_snapshot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find closest ticks to blend time */
|
|
||||||
from_tick = oldest_tick;
|
|
||||||
to_tick = newest_tick;
|
|
||||||
for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) {
|
|
||||||
i64 bt_time_ns = bt->sim_snapshot.publishtime_ns;
|
|
||||||
|
|
||||||
if (bt_time_ns < blend_time_ns && bt_time_ns > from_tick->publishtime_ns) {
|
|
||||||
from_tick = &bt->sim_snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bt_time_ns > blend_time_ns && bt_time_ns < to_tick->publishtime_ns) {
|
|
||||||
to_tick = &bt->sim_snapshot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free any unused old ticks */
|
|
||||||
{
|
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
||||||
struct blend_tick **bts_to_free = arena_dry_push(scratch.arena, struct blend_tick *);
|
|
||||||
u64 bts_to_free_count = 0;
|
|
||||||
|
|
||||||
for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) {
|
|
||||||
i64 bt_time_ns = bt->sim_snapshot.publishtime_ns;
|
|
||||||
if (bt_time_ns < from_tick->publishtime_ns) {
|
|
||||||
*arena_push(scratch.arena, struct blend_tick *) = bt;
|
|
||||||
++bts_to_free_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u64 i = 0; i < bts_to_free_count; ++i) {
|
|
||||||
blend_tick_release(bts_to_free[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch_end(scratch);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Latest tick is discontinuous */
|
|
||||||
|
|
||||||
/* Free all blend ticks */
|
|
||||||
{
|
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
||||||
struct blend_tick **bts_to_free = arena_dry_push(scratch.arena, struct blend_tick *);
|
|
||||||
u64 bts_to_free_count = 0;
|
|
||||||
for (struct blend_tick *bt = G.head_blend_tick; bt; bt = bt->next) {
|
|
||||||
*arena_push(scratch.arena, struct blend_tick *) = bt;
|
|
||||||
++bts_to_free_count;
|
|
||||||
}
|
|
||||||
for (u64 i = 0; i < bts_to_free_count; ++i) {
|
|
||||||
blend_tick_release(bts_to_free[i]);
|
|
||||||
}
|
|
||||||
scratch_end(scratch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate new blend tick */
|
|
||||||
struct blend_tick *bt = blend_tick_alloc();
|
|
||||||
struct sim_snapshot *tick = &bt->sim_snapshot;
|
|
||||||
sim_get_latest_tick(tick);
|
|
||||||
|
|
||||||
from_tick = tick;
|
|
||||||
to_tick = tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (struct interp_ticks) {
|
|
||||||
.from_tick = from_tick,
|
|
||||||
.to_tick = to_tick
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Debug draw
|
* Debug draw
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -523,101 +391,13 @@ INTERNAL void user_update(void)
|
|||||||
* Begin frame
|
* Begin frame
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
i64 now_ns = sys_time_ns();
|
G.real_dt_ns = sys_time_ns() - G.real_time_ns;
|
||||||
|
G.real_time_ns += G.real_dt_ns;
|
||||||
G.screen_size = sys_window_get_size(G.window);
|
G.screen_size = sys_window_get_size(G.window);
|
||||||
|
|
||||||
struct sim_ent_store *store = G.world.ent_store;
|
|
||||||
struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
|
struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
|
||||||
struct sim_cmd_list cmd_list = ZI;
|
struct sim_cmd_list cmd_list = ZI;
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* ========================== *
|
|
||||||
* Interpolate between sim ticks
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
{
|
|
||||||
__profscope(interpolate_ticks);
|
|
||||||
|
|
||||||
#if USER_INTERP_ENABLED
|
|
||||||
/* TODO: Use actual fps of sim thread (will differ from SIM_FPS if sim thread is lagging) to hide lag with slow-motion? */
|
|
||||||
i64 blend_time_offset_ns = NS_FROM_SECONDS((1.0 / SIM_FPS) * USER_INTERP_OFFSET_TICK_RATIO);
|
|
||||||
i64 blend_time_ns = G.time_ns > blend_time_offset_ns ? G.time_ns - blend_time_offset_ns : 0;
|
|
||||||
|
|
||||||
/* Pull ticks */
|
|
||||||
struct interp_ticks interp_ticks = pull_ticks(blend_time_ns);
|
|
||||||
struct sim_snapshot *t0 = interp_ticks.from_tick;
|
|
||||||
struct sim_snapshot *t1 = interp_ticks.to_tick;
|
|
||||||
|
|
||||||
f32 tick_blend = 0;
|
|
||||||
{
|
|
||||||
i64 t0_time_ns = t0->publishtime_ns;
|
|
||||||
i64 t1_time_ns = t1->publishtime_ns;
|
|
||||||
if (t1_time_ns > t0_time_ns) {
|
|
||||||
f64 t0_t1_elapsed = SECONDS_FROM_NS(t1_time_ns - t0_time_ns);
|
|
||||||
f64 t0_blend_elapsed = SECONDS_FROM_NS(blend_time_ns - t0_time_ns);
|
|
||||||
tick_blend = t0_blend_elapsed / t0_t1_elapsed;
|
|
||||||
}
|
|
||||||
tick_blend = clamp_f32(tick_blend, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
world_copy_replace(&G.sim_snapshot, t0);
|
|
||||||
|
|
||||||
/* Blend world globals */
|
|
||||||
G.sim_snapshot.time_ns = math_lerp_i64(t0->time_ns, t1->time_ns, (f64)tick_blend);
|
|
||||||
G.sim_snapshot.dt_ns = math_lerp_i64(t0->dt_ns, t1->dt_ns, (f64)tick_blend);
|
|
||||||
|
|
||||||
/* Blend entities */
|
|
||||||
u64 num_entities = min_u64(t0->ent_store->num_reserved, t1->ent_store->num_reserved);
|
|
||||||
{
|
|
||||||
__profscope(tick_blending);
|
|
||||||
for (u64 i = 0; i < num_entities; ++i) {
|
|
||||||
struct sim_ent *e = &store->entities[i];
|
|
||||||
struct sim_ent *e0 = &t0->ent_store->entities[i];
|
|
||||||
struct sim_ent *e1 = &t1->ent_store->entities[i];
|
|
||||||
|
|
||||||
if (e0->valid && e1->valid
|
|
||||||
&& sim_ent_has_prop(e0, SIM_ENT_PROP_ACTIVE) && sim_ent_has_prop(e1, SIM_ENT_PROP_ACTIVE)
|
|
||||||
&& e0->handle.gen == e1->handle.gen
|
|
||||||
&& e0->continuity_gen == e1->continuity_gen) {
|
|
||||||
e->local_xform = xform_lerp(e0->local_xform, e1->local_xform, tick_blend);
|
|
||||||
|
|
||||||
if (e->is_top) {
|
|
||||||
/* TODO: Cache parent & child xforms in sim thread */
|
|
||||||
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, tick_blend));
|
|
||||||
}
|
|
||||||
|
|
||||||
e->control_force = math_lerp_f32(e0->control_force, e1->control_force, tick_blend);
|
|
||||||
e->control_torque = math_lerp_f32(e0->control_torque, e1->control_torque, tick_blend);
|
|
||||||
|
|
||||||
e->linear_velocity = v2_lerp(e0->linear_velocity, e1->linear_velocity, tick_blend);
|
|
||||||
e->angular_velocity = math_lerp_angle(e0->angular_velocity, e1->angular_velocity, tick_blend);
|
|
||||||
|
|
||||||
e->control.move = v2_lerp(e0->control.move, e1->control.move, tick_blend);
|
|
||||||
e->control.focus = v2_lerp(e0->control.focus, e1->control.focus, tick_blend);
|
|
||||||
|
|
||||||
e->sprite_local_xform = xform_lerp(e0->sprite_local_xform, e1->sprite_local_xform, tick_blend);
|
|
||||||
e->animation_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend);
|
|
||||||
e->animation_frame = (u32)math_round_to_int(math_lerp_f32(e0->animation_frame, e1->animation_frame, tick_blend));
|
|
||||||
|
|
||||||
e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, tick_blend);
|
|
||||||
e->camera_xform_target = xform_lerp(e0->camera_xform_target, e1->camera_xform_target, tick_blend);
|
|
||||||
e->shake = math_lerp_f32(e0->shake, e1->shake, tick_blend);
|
|
||||||
|
|
||||||
e->tracer_gradient_start = v2_lerp(e0->tracer_gradient_start, e1->tracer_gradient_start, tick_blend);
|
|
||||||
e->tracer_gradient_end = v2_lerp(e0->tracer_gradient_end, e1->tracer_gradient_end, tick_blend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
struct interp_ticks interp_ticks = pull_ticks(G.time);
|
|
||||||
world_copy_replace(&G.sim_snapshot, interp_ticks.to_tick);
|
|
||||||
tick_is_first_frame = G.sim_snapshot.tick == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Process host events into sim events
|
* Process host events into sim events
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -659,12 +439,22 @@ INTERNAL void user_update(void)
|
|||||||
|
|
||||||
case SIM_EVENT_KIND_SNAPSHOT:
|
case SIM_EVENT_KIND_SNAPSHOT:
|
||||||
{
|
{
|
||||||
#if 0
|
#if 1
|
||||||
u64 oldest_tick = snapshot_store_get_oldest_tick(G.snapshot_store);
|
struct sim_snapshot *existing = sim_snapshot_from_tick(G.sim_snapshot_store, tick);
|
||||||
u64 newest_tick = snapshot_store_get_newest_tick(G.snapshot_store);
|
if (!existing->valid && tick > G.world->tick) {
|
||||||
|
u64 delta_src_tick = 0;
|
||||||
|
struct sim_snapshot *delta_src = sim_snapshot_from_tick(G.sim_snapshot_store, delta_src_tick);
|
||||||
|
ASSERT(delta_src->tick == delta_src_tick); /* User should always have src tick present */
|
||||||
|
|
||||||
|
struct string encoded = event->snapshot_data;
|
||||||
|
struct sim_snapshot *ss = sim_snapshot_alloc(G.sim_snapshot_store, delta_src, tick);
|
||||||
|
sim_snapshot_decode(encoded, ss);
|
||||||
|
ss->received_at_ns = G.real_time_ns;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct string encoded = event->snapshot_data;
|
||||||
|
sim_snapshot_decode(encoded, &G.world, tick);
|
||||||
#endif
|
#endif
|
||||||
struct string snapshot_data = event->snapshot_data;
|
|
||||||
sim_snapshot_decode(snapshot_data, &G.world, tick);
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
@ -672,6 +462,82 @@ INTERNAL void user_update(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Create user world from blended snapshots
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
{
|
||||||
|
struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->last_tick);
|
||||||
|
|
||||||
|
/* Calculate sim time based on last received snapshot time,
|
||||||
|
* then smooth it out to prevent sudden jumps in rendering due to snapshot receive time variance. */
|
||||||
|
/* TODO: Use a value that indicates desired dt to next frame, rather than real dt from last frame? */
|
||||||
|
f64 sim_time_smooth_rate_ns = SECONDS_FROM_NS(G.real_dt_ns) / 0.05;
|
||||||
|
|
||||||
|
i64 time_since_newest_tick_ns = G.real_time_ns - newest_snapshot->received_at_ns;
|
||||||
|
G.sim_time_ns = newest_snapshot->real_time_ns + time_since_newest_tick_ns;
|
||||||
|
G.sim_time_smoothed_ns += (G.sim_time_ns - G.sim_time_smoothed_ns) * sim_time_smooth_rate_ns;
|
||||||
|
|
||||||
|
#if USER_INTERP_ENABLED
|
||||||
|
i64 render_time_ns = G.sim_time_smoothed_ns - (USER_INTERP_RATIO * newest_snapshot->real_dt_ns);
|
||||||
|
|
||||||
|
/* Get two snapshots nearest to render time */
|
||||||
|
struct sim_snapshot *left_snapshot = sim_snapshot_nil();
|
||||||
|
struct sim_snapshot *right_snapshot = newest_snapshot;
|
||||||
|
{
|
||||||
|
struct sim_snapshot *snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->first_tick);
|
||||||
|
while (snapshot->valid) {
|
||||||
|
i64 ss_time_ns = snapshot->real_time_ns;
|
||||||
|
u64 next_tick = snapshot->next_tick;
|
||||||
|
if (ss_time_ns < render_time_ns && ss_time_ns > left_snapshot->real_time_ns) {
|
||||||
|
if (left_snapshot->valid) {
|
||||||
|
/* Snapshot no longer needed since render time has passed, release it */
|
||||||
|
sim_snapshot_release(left_snapshot);
|
||||||
|
}
|
||||||
|
left_snapshot = snapshot;
|
||||||
|
}
|
||||||
|
if (ss_time_ns > render_time_ns && ss_time_ns < right_snapshot->real_time_ns) {
|
||||||
|
right_snapshot = snapshot;
|
||||||
|
}
|
||||||
|
snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, next_tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G.world->valid) {
|
||||||
|
sim_snapshot_release(G.world);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create world from blended snapshots */
|
||||||
|
if (left_snapshot->valid && right_snapshot->valid) {
|
||||||
|
f64 blend = (f64)(render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns);
|
||||||
|
G.world = sim_snapshot_alloc_from_lerp(G.world_snapshot_store, left_snapshot, right_snapshot, blend);
|
||||||
|
} else if (left_snapshot->valid) {
|
||||||
|
G.world = sim_snapshot_alloc(G.world_snapshot_store, left_snapshot, left_snapshot->tick);
|
||||||
|
} else if (right_snapshot->valid) {
|
||||||
|
G.world = sim_snapshot_alloc(G.world_snapshot_store, right_snapshot, right_snapshot->tick);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Release sim snapshots all except for newest tick */
|
||||||
|
{
|
||||||
|
struct sim_snapshot *snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->first_tick);
|
||||||
|
while (snapshot->valid) {
|
||||||
|
u64 next_tick = snapshot->next_tick;
|
||||||
|
if (snapshot->tick != newest_snapshot->tick) {
|
||||||
|
sim_snapshot_release(snapshot);
|
||||||
|
}
|
||||||
|
snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, next_tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G.world->tick != newest_snapshot->tick) {
|
||||||
|
if (G.world->valid) {
|
||||||
|
sim_snapshot_release(G.world);
|
||||||
|
}
|
||||||
|
G.world = sim_snapshot_alloc(G.world_snapshot_store, newest_snapshot, newest_snapshot->tick);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Process sys events into user bind state
|
* Process sys events into user bind state
|
||||||
@ -766,16 +632,16 @@ INTERNAL void user_update(void)
|
|||||||
* Find local entities
|
* Find local entities
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct sim_client *local_client = sim_client_from_handle(G.world.client_store, G.world.local_client);
|
struct sim_client *local_client = sim_client_from_handle(G.world->client_store, G.world->local_client);
|
||||||
struct sim_ent *local_player = sim_ent_from_handle(G.world.ent_store, local_client->control_ent);
|
struct sim_ent *local_player = sim_ent_from_handle(G.world->ent_store, local_client->control_ent);
|
||||||
struct sim_ent *local_camera = sim_ent_from_handle(G.world.ent_store, local_client->camera_ent);
|
struct sim_ent *local_camera = sim_ent_from_handle(G.world->ent_store, local_client->camera_ent);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Apply shake
|
* Apply shake
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
for (u64 ent_index = 0; ent_index < store->num_reserved; ++ent_index) {
|
for (u64 ent_index = 0; ent_index < G.world->ent_store->num_reserved; ++ent_index) {
|
||||||
struct sim_ent *ent = &store->entities[ent_index];
|
struct sim_ent *ent = &G.world->ent_store->entities[ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||||
|
|
||||||
/* How much time between camera shakes */
|
/* How much time between camera shakes */
|
||||||
@ -783,7 +649,7 @@ INTERNAL void user_update(void)
|
|||||||
f32 shake = ent->shake;
|
f32 shake = ent->shake;
|
||||||
if (shake > 0) {
|
if (shake > 0) {
|
||||||
u64 basis = hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&ent->handle));
|
u64 basis = hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&ent->handle));
|
||||||
u64 angle_seed0 = basis + (u64)(G.world.world_time_ns / frequency_ns);
|
u64 angle_seed0 = basis + (u64)(G.world->world_time_ns / frequency_ns);
|
||||||
u64 angle_seed1 = angle_seed0 + 1;
|
u64 angle_seed1 = angle_seed0 + 1;
|
||||||
f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU);
|
f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU);
|
||||||
f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU);
|
f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU);
|
||||||
@ -793,7 +659,7 @@ INTERNAL void user_update(void)
|
|||||||
struct v2 vec1 = v2_with_len(v2_from_angle(angle1), shake);
|
struct v2 vec1 = v2_with_len(v2_from_angle(angle1), shake);
|
||||||
|
|
||||||
/* TODO: Cubic interp? */
|
/* TODO: Cubic interp? */
|
||||||
f32 blend = (f32)(G.world.world_time_ns % frequency_ns) / (f32)frequency_ns;
|
f32 blend = (f32)(G.world->world_time_ns % frequency_ns) / (f32)frequency_ns;
|
||||||
struct v2 vec = v2_lerp(vec0, vec1, blend);
|
struct v2 vec = v2_lerp(vec0, vec1, blend);
|
||||||
|
|
||||||
struct xform xf = sim_ent_get_xform(ent);
|
struct xform xf = sim_ent_get_xform(ent);
|
||||||
@ -942,8 +808,8 @@ INTERNAL void user_update(void)
|
|||||||
/* Copy valid entities */
|
/* Copy valid entities */
|
||||||
{
|
{
|
||||||
__profscope(copy_sprites_for_sorting);
|
__profscope(copy_sprites_for_sorting);
|
||||||
for (u64 ent_index = 0; ent_index < store->num_reserved; ++ent_index) {
|
for (u64 ent_index = 0; ent_index < G.world->ent_store->num_reserved; ++ent_index) {
|
||||||
struct sim_ent *ent = &store->entities[ent_index];
|
struct sim_ent *ent = &G.world->ent_store->entities[ent_index];
|
||||||
if (sim_ent_is_valid_and_active(ent)) {
|
if (sim_ent_is_valid_and_active(ent)) {
|
||||||
*arena_push(scratch.arena, struct sim_ent *) = ent;
|
*arena_push(scratch.arena, struct sim_ent *) = ent;
|
||||||
++sorted_count;
|
++sorted_count;
|
||||||
@ -970,7 +836,7 @@ INTERNAL void user_update(void)
|
|||||||
|
|
||||||
struct sprite_tag sprite = ent->sprite;
|
struct sprite_tag sprite = ent->sprite;
|
||||||
|
|
||||||
struct sim_ent *parent = sim_ent_from_handle(store, ent->parent);
|
struct sim_ent *parent = sim_ent_from_handle(G.world->ent_store, ent->parent);
|
||||||
|
|
||||||
struct xform xf = sim_ent_get_xform(ent);
|
struct xform xf = sim_ent_get_xform(ent);
|
||||||
struct xform parent_xf = sim_ent_get_xform(parent);
|
struct xform parent_xf = sim_ent_get_xform(parent);
|
||||||
@ -1155,8 +1021,8 @@ INTERNAL void user_update(void)
|
|||||||
/* Draw contact constraint */
|
/* Draw contact constraint */
|
||||||
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTACT_CONSTRAINT)) {
|
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTACT_CONSTRAINT)) {
|
||||||
struct phys_contact_constraint *data = &ent->contact_constraint_data;
|
struct phys_contact_constraint *data = &ent->contact_constraint_data;
|
||||||
struct sim_ent *e0 = sim_ent_from_handle(store, data->e0);
|
struct sim_ent *e0 = sim_ent_from_handle(G.world->ent_store, data->e0);
|
||||||
struct sim_ent *e1 = sim_ent_from_handle(store, data->e1);
|
struct sim_ent *e1 = sim_ent_from_handle(G.world->ent_store, data->e1);
|
||||||
(UNUSED)e0;
|
(UNUSED)e0;
|
||||||
(UNUSED)e1;
|
(UNUSED)e1;
|
||||||
|
|
||||||
@ -1621,25 +1487,30 @@ INTERNAL void user_update(void)
|
|||||||
if (font) {
|
if (font) {
|
||||||
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
||||||
|
|
||||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim tick: %F"), FMT_UINT(G.world.tick)));
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim tick: %F"), FMT_UINT(G.world->tick)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim real time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.world.real_time_ns))));
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.sim_time_ns))));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim world time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.world.world_time_ns))));
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim time (smoothed): %F"), FMT_FLOAT(SECONDS_FROM_NS(G.sim_time_smoothed_ns))));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim entities: %F/%F"), FMT_UINT(G.world.ent_store->num_allocated), FMT_UINT(G.world.ent_store->num_reserved)));
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("world time: %F"), FMT_FLOAT(SECONDS_FROM_NS(G.world->world_time_ns))));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("sim entities: %F/%F"), FMT_UINT(G.world->ent_store->num_allocated), FMT_UINT(G.world->ent_store->num_reserved)));
|
||||||
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data read: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 2)));
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data read: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 2)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data sent: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1000 / 1000, 2)));
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data sent: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1000 / 1000, 2)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
|
|
||||||
|
pos.y += spacing;
|
||||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory usage: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 2)));
|
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory usage: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 2)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
@ -1718,8 +1589,8 @@ INTERNAL void user_update(void)
|
|||||||
/* Update network usage stats */
|
/* Update network usage stats */
|
||||||
G.client_bytes_read.last_second_end = G.host->bytes_received;
|
G.client_bytes_read.last_second_end = G.host->bytes_received;
|
||||||
G.client_bytes_sent.last_second_end = G.host->bytes_sent;
|
G.client_bytes_sent.last_second_end = G.host->bytes_sent;
|
||||||
if (now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
|
if (G.real_time_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
|
||||||
G.last_second_reset_ns = now_ns;
|
G.last_second_reset_ns = G.real_time_ns;
|
||||||
G.client_bytes_read.last_second = G.client_bytes_read.last_second_end - G.client_bytes_read.last_second_start;
|
G.client_bytes_read.last_second = G.client_bytes_read.last_second_end - G.client_bytes_read.last_second_start;
|
||||||
G.client_bytes_sent.last_second = G.client_bytes_sent.last_second_end - G.client_bytes_sent.last_second_start;
|
G.client_bytes_sent.last_second = G.client_bytes_sent.last_second_end - G.client_bytes_sent.last_second_start;
|
||||||
G.client_bytes_read.last_second_start = G.client_bytes_read.last_second_end;
|
G.client_bytes_read.last_second_start = G.client_bytes_read.last_second_end;
|
||||||
@ -1858,7 +1729,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
|
|||||||
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_sim_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_sim_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
struct sim_ctx *ctx = (struct sim_ctx *)arg;
|
struct sim_ctx *ctx = (struct sim_ctx *)arg;
|
||||||
i64 target_dt_ns = NS_FROM_SECONDS(1) / SIM_FPS;;
|
i64 target_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;;
|
||||||
while (!atomic_i32_eval(&G.sim_thread_shutdown)) {
|
while (!atomic_i32_eval(&G.sim_thread_shutdown)) {
|
||||||
sim_update(ctx, target_dt_ns);
|
sim_update(ctx, target_dt_ns);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,9 @@ struct sound_startup_receipt;
|
|||||||
struct mixer_startup_receipt;
|
struct mixer_startup_receipt;
|
||||||
struct phys_startup_receipt;
|
struct phys_startup_receipt;
|
||||||
struct host_startup_receipt;
|
struct host_startup_receipt;
|
||||||
|
struct sim_ent_startup_receipt;
|
||||||
|
struct sim_client_startup_receipt;
|
||||||
|
struct sim_snapshot_startup_receipt;
|
||||||
|
|
||||||
enum user_bind_kind {
|
enum user_bind_kind {
|
||||||
USER_BIND_KIND_NONE,
|
USER_BIND_KIND_NONE,
|
||||||
@ -60,6 +63,9 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
struct mixer_startup_receipt *mixer_sr,
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
struct phys_startup_receipt *phys_sr,
|
struct phys_startup_receipt *phys_sr,
|
||||||
struct host_startup_receipt *host_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
|
struct sim_ent_startup_receipt *sim_ent_sr,
|
||||||
|
struct sim_client_startup_receipt *sim_client_sr,
|
||||||
|
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
|
||||||
struct string connect_address_str,
|
struct string connect_address_str,
|
||||||
struct sys_window *window);
|
struct sys_window *window);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user