diff --git a/src/app/app_core.c b/src/app/app_core.c index 8f61963d..a8a2bdc3 100644 --- a/src/app/app_core.c +++ b/src/app/app_core.c @@ -247,7 +247,7 @@ void P_AppStartup(String args_str) /* Interface systems */ PB_StartupReceipt playback_sr = PB_Startup(&mixer_sr); - struct user_startup_receipt user_sr = user_startup(&font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &sim_sr, connect_address); + UserStartupReceipt user_sr = StartupUser(&font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &sim_sr, connect_address); (UNUSED)user_sr; (UNUSED)playback_sr; diff --git a/src/user/user_core.c b/src/user/user_core.c index 2112c682..a0708f9f 100644 --- a/src/user/user_core.c +++ b/src/user/user_core.c @@ -1,15 +1,16 @@ -/* ========================== * - * Startup - * ========================== */ +SharedUserState shared_user_state = ZI; -struct user_startup_receipt user_startup(F_StartupReceipt *font_sr, - S_StartupReceipt *sprite_sr, - D_StartupReceipt *draw_sr, - AC_StartupReceipt *asset_cache_sr, - SND_StartupReceipt *sound_sr, - MIX_StartupReceipt *mixer_sr, - SimStartupReceipt *sim_sr, - String connect_address_str) +//////////////////////////////// +//~ Startup + +UserStartupReceipt StartupUser(F_StartupReceipt *font_sr, + S_StartupReceipt *sprite_sr, + D_StartupReceipt *draw_sr, + AC_StartupReceipt *asset_cache_sr, + SND_StartupReceipt *sound_sr, + MIX_StartupReceipt *mixer_sr, + SimStartupReceipt *sim_sr, + String connect_address_str) { __prof; (UNUSED)font_sr; @@ -19,86 +20,93 @@ struct user_startup_receipt user_startup(F_StartupReceipt *font_sr, (UNUSED)sound_sr; (UNUSED)mixer_sr; (UNUSED)sim_sr; + SharedUserState *g = &shared_user_state; SetGstat(GSTAT_DEBUG_STEPS, U64Max); - G.arena = AllocArena(Gibi(64)); - G.real_time_ns = P_TimeNs(); + g->arena = AllocArena(Gibi(64)); + g->real_time_ns = P_TimeNs(); /* TODO: Remove this */ - G.connect_address_str = CopyString(G.arena, connect_address_str); + g->connect_address_str = CopyString(g->arena, connect_address_str); /* Initialize average dt to a reasonable value */ - G.average_local_to_user_snapshot_publish_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; + g->average_local_to_user_snapshot_publish_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; /* User blend clients */ - G.user_client_store = sim_client_store_alloc(); - G.user_unblended_client = sim_client_alloc(G.user_client_store); - G.user_blended_client = sim_client_alloc(G.user_client_store); - G.ss_blended = sim_snapshot_nil(); + g->user_client_store = sim_client_store_alloc(); + g->user_unblended_client = sim_client_alloc(g->user_client_store); + g->user_blended_client = sim_client_alloc(g->user_client_store); + g->ss_blended = sim_snapshot_nil(); /* Local to user client */ - G.local_to_user_client_store = sim_client_store_alloc(); - G.local_to_user_client = sim_client_alloc(G.local_to_user_client_store); + g->local_to_user_client_store = sim_client_store_alloc(); + g->local_to_user_client = sim_client_alloc(g->local_to_user_client_store); /* GPU handles */ - G.world_to_ui_xf = XformIdentity; - G.world_to_render_xf = XformIdentity; - G.render_sig = GPU_AllocRenderSig(); + g->world_to_ui_xf = XformIdentity; + g->world_to_render_xf = XformIdentity; + g->render_sig = GPU_AllocRenderSig(); - G.console_logs_arena = AllocArena(Gibi(64)); - //P_RegisterLogCallback(debug_console_log_callback, P_LogLevel_Success); - P_RegisterLogCallback(debug_console_log_callback, P_LogLevel_Debug); + g->console_logs_arena = AllocArena(Gibi(64)); + //P_RegisterLogCallback(ConsoleLogCallback, P_LogLevel_Success); + P_RegisterLogCallback(ConsoleLogCallback, P_LogLevel_Debug); - G.window = P_AllocWindow(); - G.swapchain = GPU_AllocSwapchain(G.window, VEC2I32(100, 100)); - P_ShowWindow(G.window); + g->window = P_AllocWindow(); + g->swapchain = GPU_AllocSwapchain(g->window, VEC2I32(100, 100)); + P_ShowWindow(g->window); /* Start jobs */ - P_Run(1, user_update_job, 0, P_Pool_User, P_Priority_High, &G.shutdown_job_counters); - P_Run(1, local_sim_job, 0, P_Pool_Sim, P_Priority_High, &G.shutdown_job_counters); - P_OnExit(&user_shutdown); + P_Run(1, UpdateUserJob, 0, P_Pool_User, P_Priority_High, &g->shutdown_job_counters); + P_Run(1, SimJob, 0, P_Pool_Sim, P_Priority_High, &g->shutdown_job_counters); + P_OnExit(&ShutdownUser); - return (struct user_startup_receipt) { 0 }; + return (UserStartupReceipt) { 0 }; } -internal P_ExitFuncDef(user_shutdown) +//////////////////////////////// +//~ Shutdown + +P_ExitFuncDef(ShutdownUser) { __prof; - Atomic32FetchSet(&G.shutdown, 1); - P_WaitOnCounter(&G.shutdown_job_counters); - P_ReleaseWindow(G.window); + SharedUserState *g = &shared_user_state; + Atomic32FetchSet(&g->shutdown, 1); + P_WaitOnCounter(&g->shutdown_job_counters); + P_ReleaseWindow(g->window); } -/* ========================== * - * Debug draw - * ========================== */ +//////////////////////////////// +//~ Debug draw - /* TODO: remove this (testing) */ -internal void debug_draw_xform(Xform xf, u32 color_x, u32 color_y) +//- Draw xform +void DebugDrawXform(Xform xf, u32 color_x, u32 color_y) { + SharedUserState *g = &shared_user_state; f32 thickness = 2.f; f32 arrowhead_len = 15.f; - Vec2 pos = MulXformV2(G.world_to_ui_xf, xf.og); - Vec2 x_ray = MulXformBasisV2(G.world_to_ui_xf, RightFromXform(xf)); - Vec2 y_ray = MulXformBasisV2(G.world_to_ui_xf, UpFromXform(xf)); + Vec2 pos = MulXformV2(g->world_to_ui_xf, xf.og); + Vec2 x_ray = MulXformBasisV2(g->world_to_ui_xf, RightFromXform(xf)); + Vec2 y_ray = MulXformBasisV2(g->world_to_ui_xf, UpFromXform(xf)); f32 ray_scale = 1; x_ray = MulVec2(x_ray, ray_scale); y_ray = MulVec2(y_ray, ray_scale); - D_DrawArrowRay(G.render_sig, pos, x_ray, thickness, arrowhead_len, color_x); - D_DrawArrowRay(G.render_sig, pos, y_ray, thickness, arrowhead_len, color_y); + D_DrawArrowRay(g->render_sig, pos, x_ray, thickness, arrowhead_len, color_x); + D_DrawArrowRay(g->render_sig, pos, y_ray, thickness, arrowhead_len, color_y); //u32 color_quad = Rgba32F(0, 1, 1, 0.3); //Quad quad = QuadFromRect(RectFromScalar(0, 0, 1, -1)); //quad = MulXformQuad(xf, ScaleQuad(quad, 0.075f)); - //D_DrawQuad(G.render_sig, quad, color); + //D_DrawQuad(g->render_sig, quad, color); } -internal void debug_draw_movement(Entity *ent) +//- Draw movement +void DebugDrawMovement(Entity *ent) { + SharedUserState *g = &shared_user_state; f32 thickness = 2.f; f32 arrow_len = 15.f; @@ -107,15 +115,17 @@ internal void debug_draw_movement(Entity *ent) Xform xf = sim_ent_get_xform(ent); Vec2 velocity = ent->linear_velocity; - Vec2 pos = MulXformV2(G.world_to_ui_xf, xf.og); - Vec2 vel_ray = MulXformBasisV2(G.world_to_ui_xf, velocity); + Vec2 pos = MulXformV2(g->world_to_ui_xf, xf.og); + Vec2 vel_ray = MulXformBasisV2(g->world_to_ui_xf, velocity); - if (Vec2Len(vel_ray) > 0.00001) { - D_DrawArrowRay(G.render_sig, pos, vel_ray, thickness, arrow_len, color_vel); + if (Vec2Len(vel_ray) > 0.00001) + { + D_DrawArrowRay(g->render_sig, pos, vel_ray, thickness, arrow_len, color_vel); } } -internal String get_ent_debug_text(Arena *arena, Entity *ent) +//- Debug string +String DebugStringFromEntity(Arena *arena, Entity *ent) { TempArena scratch = BeginScratch(arena); Snapshot *ss = ent->ss; @@ -129,13 +139,20 @@ internal String get_ent_debug_text(Arena *arena, Entity *ent) { b32 transmitting = sim_ent_has_prop(ent, SEPROP_SYNC_SRC); b32 receiving = sim_ent_has_prop(ent, SEPROP_SYNC_DST); - if (transmitting & receiving) { + if (transmitting & receiving) + { result.len += CopyString(arena, Lit(" networked (sending & receiving)")).len; - } else if (transmitting) { + } + else if (transmitting) + { result.len += CopyString(arena, Lit(" networked (sending)")).len; - } else if (receiving) { + } + else if (receiving) + { result.len += CopyString(arena, Lit(" networked (receiving)")).len; - } else { + } + else + { result.len += CopyString(arena, Lit(" local")).len; } } @@ -147,10 +164,13 @@ internal String get_ent_debug_text(Arena *arena, Entity *ent) { result.len += CopyString(arena, Lit("props: 0x")).len; - for (u64 chunk_index = countof(ent->props); chunk_index-- > 0;) { + for (u64 chunk_index = countof(ent->props); chunk_index-- > 0;) + { u64 chunk = ent->props[chunk_index]; - for (u64 part_index = 8; part_index-- > 0;) { - if ((chunk_index != (countof(ent->props) - 1)) || ((chunk_index * 64) + (part_index * 8)) <= SEPROP_COUNT) { + for (u64 part_index = 8; part_index-- > 0;) + { + if ((chunk_index != (countof(ent->props) - 1)) || ((chunk_index * 64) + (part_index * 8)) <= SEPROP_COUNT) + { u8 part = (chunk >> (part_index * 8)) & 0xFF; StringFromChar(arena, hex[(part >> 4) & 0x0F]); StringFromChar(arena, hex[(part >> 0) & 0x0F]); @@ -161,11 +181,13 @@ internal String get_ent_debug_text(Arena *arena, Entity *ent) result.len += CopyString(arena, Lit("\n")).len; } - if (!sim_ent_id_eq(ent->parent, SIM_ENT_ROOT_ID)) { + if (!sim_ent_id_eq(ent->parent, SIM_ENT_ROOT_ID)) + { result.len += StringFormat(arena, Lit("parent: [%F]\n"), FmtUid(ent->parent.uid)).len; } - if (!sim_ent_id_is_nil(ent->next) || !sim_ent_id_is_nil(ent->prev)) { + if (!sim_ent_id_is_nil(ent->next) || !sim_ent_id_is_nil(ent->prev)) + { result.len += StringFormat(arena, Lit("prev: [%F]\n"), FmtUid(ent->prev.uid)).len; result.len += StringFormat(arena, Lit("next: [%F]\n"), FmtUid(ent->next.uid)).len; } @@ -184,16 +206,19 @@ internal String get_ent_debug_text(Arena *arena, Entity *ent) result.len += StringFormat(arena, Lit("collision dir: (%F, %F)\n"), FmtFloat(ent->collision_dir.x), FmtFloat(ent->collision_dir.y)).len; /* Children */ - if (!sim_ent_id_is_nil(ent->first) || !sim_ent_id_is_nil(ent->last)) { + if (!sim_ent_id_is_nil(ent->first) || !sim_ent_id_is_nil(ent->last)) + { Entity *child = sim_ent_from_id(ss, ent->first); - if (!sim_ent_id_eq(ent->first, ent->last) || !child->valid) { + if (!sim_ent_id_eq(ent->first, ent->last) || !child->valid) + { result.len += StringFormat(arena, Lit("first child: [%F]\n"), FmtUid(ent->first.uid)).len; result.len += StringFormat(arena, Lit("last child: [%F]\n"), FmtUid(ent->last.uid)).len; } - while (child->valid) { + while (child->valid) + { result.len += CopyString(arena, Lit("\n---------------------------------\n")).len; result.len += CopyString(arena, Lit("CHILD\n")).len; - String child_text = get_ent_debug_text(scratch.arena, child); + String child_text = DebugStringFromEntity(scratch.arena, child); result.len += IndentString(arena, child_text, 4).len; child = sim_ent_from_id(ss, child->next); } @@ -203,39 +228,45 @@ internal String get_ent_debug_text(Arena *arena, Entity *ent) return result; } -/* ========================== * - * Debug console - * ========================== */ +//////////////////////////////// +//~ Console -internal P_LogEventCallbackFuncDef(debug_console_log_callback, log) +//- Console log callback +P_LogEventCallbackFuncDef(ConsoleLogCallback, log) { __prof; - P_Lock lock = P_LockE(&G.console_logs_mutex); + SharedUserState *g = &shared_user_state; + P_Lock lock = P_LockE(&g->console_logs_mutex); { - struct console_log *clog = PushStruct(G.console_logs_arena, struct console_log); + ConsoleLog *clog = PushStruct(g->console_logs_arena, ConsoleLog); clog->level = log.level; - clog->msg = CopyString(G.console_logs_arena, log.msg); + clog->msg = CopyString(g->console_logs_arena, log.msg); clog->datetime = log.datetime; clog->time_ns = log.time_ns; - if (G.last_console_log) { - G.last_console_log->next = clog; - clog->prev = G.last_console_log; + if (g->last_console_log) + { + g->last_console_log->next = clog; + clog->prev = g->last_console_log; /* Alternating color index between logs of same level */ - i32 *color_index = &G.console_log_color_indices[log.level]; + i32 *color_index = &g->console_log_color_indices[log.level]; clog->color_index = *color_index; *color_index = 1 - *color_index; - } else { - G.first_console_log = clog; } - G.last_console_log = clog; + else + { + g->first_console_log = clog; + } + g->last_console_log = clog; } P_Unlock(&lock); } -internal void draw_debug_console(i32 level, b32 minimized) +//- Draw console +void DebugDrawConsole(i32 level, b32 minimized) { __prof; + SharedUserState *g = &shared_user_state; TempArena scratch = BeginScratchNoConflict(); Vec2 desired_start_pos = VEC2(10, minimized ? 100 : 600); @@ -263,31 +294,38 @@ internal void draw_debug_console(i32 level, b32 minimized) f32 bounds_top = F32Infinity; f32 bounds_bottom = -F32Infinity; - if (G.console_logs_height < desired_start_pos.y) { - draw_pos.y = G.console_logs_height; + if (g->console_logs_height < desired_start_pos.y) + { + draw_pos.y = g->console_logs_height; } - G.console_logs_height = 0; + g->console_logs_height = 0; i64 now_ns = P_TimeNs(); F_Font *font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f); - if (font) { - P_Lock lock = P_LockE(&G.console_logs_mutex); + if (font) + { + P_Lock lock = P_LockE(&g->console_logs_mutex); { - for (struct console_log *log = G.last_console_log; log; log = log->prev) { + for (ConsoleLog *log = g->last_console_log; log; log = log->prev) + { f32 opacity = 0.75; - if (minimized) { + if (minimized) + { f32 lin = 1.0 - ClampF64((f64)(now_ns - log->time_ns) / (f64)fade_time_ns, 0, 1); opacity *= PowF32(lin, fade_curve); } - if (draw_pos.y > -desired_start_pos.y && opacity > 0) { - if (log->level <= level) { + if (draw_pos.y > -desired_start_pos.y && opacity > 0) + { + if (log->level <= level) + { /* Draw background */ u32 color = colors[log->level][log->color_index]; - D_DrawQuad(G.render_sig, QuadFromRect(log->bounds), Alpha32F(color, opacity)); + D_DrawQuad(g->render_sig, QuadFromRect(log->bounds), Alpha32F(color, opacity)); /* Draw text */ String text = log->msg; - if (!minimized) { + if (!minimized) + { P_DateTime datetime = log->datetime; text = StringFormat( scratch.arena, @@ -300,7 +338,7 @@ internal void draw_debug_console(i32 level, b32 minimized) } D_TextParams params = D_TEXTPARAMS(.font = font, .pos = draw_pos, .offset_y = DRAW_TEXT_OFFSET_Y_BOTTOM, .color = Alpha32F(ColorWhite, opacity), .str = text); - Rect bounds = draw_text(G.render_sig, params); + Rect bounds = draw_text(g->render_sig, params); Rect draw_bounds = bounds; draw_bounds.x -= bg_margin; @@ -313,49 +351,55 @@ internal void draw_debug_console(i32 level, b32 minimized) bounds_top = MinF32(bounds_top, draw_bounds.y); bounds_bottom = MaxF32(bounds_bottom, draw_bounds.y + draw_bounds.height); } - } else { + } + else + { break; } } } P_Unlock(&lock); } - if (bounds_top < F32Infinity && bounds_bottom > -F32Infinity) { - G.console_logs_height = bounds_bottom - bounds_top; + if (bounds_top < F32Infinity && bounds_bottom > -F32Infinity) + { + g->console_logs_height = bounds_bottom - bounds_top; } EndScratch(scratch); } -/* ========================== * - * Sort entities - * ========================== */ +//////////////////////////////// +//~ Sort entities -internal MergesortCompareFuncDef(ent_draw_order_cmp, arg_a, arg_b, _) +MergesortCompareFuncDef(EntitySortCmp, arg_a, arg_b, _) { Entity *a = *(Entity **)arg_a; Entity *b = *(Entity **)arg_b; i32 result = 0; - if (result == 0) { + if (result == 0) + { /* Sort by light */ b32 a_cmp = sim_ent_has_prop(a, SEPROP_LIGHT_TEST); b32 b_cmp = sim_ent_has_prop(b, SEPROP_LIGHT_TEST); result = (a_cmp > b_cmp) - (a_cmp < b_cmp); } - if (result == 0) { + if (result == 0) + { /* Sort by layer */ i32 a_cmp = a->layer; i32 b_cmp = b->layer; result = (a_cmp < b_cmp) - (a_cmp > b_cmp); } - if (result == 0) { + if (result == 0) + { /* Sort by sprite */ u64 a_cmp = a->sprite.hash; u64 b_cmp = b->sprite.hash; result = (a_cmp < b_cmp) - (a_cmp > b_cmp); } - if (result == 0) { + if (result == 0) + { /* Sort by activation */ u64 a_cmp = a->activation_tick; u64 b_cmp = b->activation_tick; @@ -365,136 +409,146 @@ internal MergesortCompareFuncDef(ent_draw_order_cmp, arg_a, arg_b, _) return result; } -/* ========================== * - * Update - * ========================== */ +//////////////////////////////// +//~ User update -internal void user_update(P_Window *window) +void UpdateUser(P_Window *window) { __prof; - + SharedUserState *g = &shared_user_state; TempArena scratch = BeginScratchNoConflict(); - /* ========================== * - * Begin frame - * ========================== */ - - G.real_dt_ns = P_TimeNs() - G.real_time_ns; - G.real_time_ns += G.real_dt_ns; - G.screen_size = P_GetWindowSize(window); - + //- Begin frame + g->real_dt_ns = P_TimeNs() - g->real_time_ns; + g->real_time_ns += g->real_dt_ns; + g->screen_size = P_GetWindowSize(window); S_Scope *sprite_frame_scope = S_BeginScope(); - /* ========================== * - * Pull latest local sim snapshot - * ========================== */ + //- Pull latest local sim snapshot { __profn("Pull snapshot"); - P_Lock lock = P_LockE(&G.local_to_user_client_mutex); - u64 old_last_tick = G.user_unblended_client->last_tick; - u64 last_tick = G.local_to_user_client->last_tick; - if (last_tick > old_last_tick) { - Snapshot *src = sim_snapshot_from_tick(G.local_to_user_client, last_tick); - sim_snapshot_alloc(G.user_unblended_client, src, src->tick); - G.last_local_to_user_snapshot_published_at_ns = G.local_to_user_client_publish_time_ns; - G.average_local_to_user_snapshot_publish_dt_ns -= G.average_local_to_user_snapshot_publish_dt_ns / 50; - G.average_local_to_user_snapshot_publish_dt_ns += G.local_to_user_client_publish_dt_ns / 50; + P_Lock lock = P_LockE(&g->local_to_user_client_mutex); + u64 old_last_tick = g->user_unblended_client->last_tick; + u64 last_tick = g->local_to_user_client->last_tick; + if (last_tick > old_last_tick) + { + Snapshot *src = sim_snapshot_from_tick(g->local_to_user_client, last_tick); + sim_snapshot_alloc(g->user_unblended_client, src, src->tick); + g->last_local_to_user_snapshot_published_at_ns = g->local_to_user_client_publish_time_ns; + g->average_local_to_user_snapshot_publish_dt_ns -= g->average_local_to_user_snapshot_publish_dt_ns / 50; + g->average_local_to_user_snapshot_publish_dt_ns += g->local_to_user_client_publish_dt_ns / 50; } P_Unlock(&lock); } - /* ========================== * - * Create user world from blended snapshots - * ========================== */ + //- Create user world from blended snapshots { __profn("Blend snapshots"); /* How along are we between sim ticks (0 = start of tick, 1 = end of tick) */ f64 tick_progress = 0; - i64 next_tick_expected_ns = G.last_local_to_user_snapshot_published_at_ns + G.average_local_to_user_snapshot_publish_dt_ns; - if (next_tick_expected_ns > G.last_local_to_user_snapshot_published_at_ns) { - tick_progress = (f64)(G.real_time_ns - G.last_local_to_user_snapshot_published_at_ns) / (f64)(next_tick_expected_ns - G.last_local_to_user_snapshot_published_at_ns); + i64 next_tick_expected_ns = g->last_local_to_user_snapshot_published_at_ns + g->average_local_to_user_snapshot_publish_dt_ns; + if (next_tick_expected_ns > g->last_local_to_user_snapshot_published_at_ns) + { + tick_progress = (f64)(g->real_time_ns - g->last_local_to_user_snapshot_published_at_ns) / (f64)(next_tick_expected_ns - g->last_local_to_user_snapshot_published_at_ns); } /* Predict local sim time based on average snapshot publish dt. */ - Snapshot *newest_snapshot = sim_snapshot_from_tick(G.user_unblended_client, G.user_unblended_client->last_tick); - G.local_sim_last_known_time_ns = newest_snapshot->sim_time_ns; - G.local_sim_last_known_tick = newest_snapshot->tick; - if (Atomic32Fetch(&G.user_paused)) { - G.local_sim_predicted_time_ns = G.local_sim_last_known_tick; - } else { - G.local_sim_predicted_time_ns = newest_snapshot->sim_time_ns + (newest_snapshot->sim_dt_ns * tick_progress); + Snapshot *newest_snapshot = sim_snapshot_from_tick(g->user_unblended_client, g->user_unblended_client->last_tick); + g->local_sim_last_known_time_ns = newest_snapshot->sim_time_ns; + g->local_sim_last_known_tick = newest_snapshot->tick; + if (Atomic32Fetch(&g->user_paused)) + { + g->local_sim_predicted_time_ns = g->local_sim_last_known_tick; + } + else + { + g->local_sim_predicted_time_ns = newest_snapshot->sim_time_ns + (newest_snapshot->sim_dt_ns * tick_progress); } - if (USER_INTERP_ENABLED && !Atomic32Fetch(&G.user_paused)) { + if (USER_INTERP_ENABLED && !Atomic32Fetch(&g->user_paused)) + { /* Determine render time */ - G.render_time_target_ns = G.local_sim_predicted_time_ns - (USER_INTERP_RATIO * G.average_local_to_user_snapshot_publish_dt_ns); - if (G.average_local_to_user_snapshot_publish_dt_ns > 0) { + g->render_time_target_ns = g->local_sim_predicted_time_ns - (USER_INTERP_RATIO * g->average_local_to_user_snapshot_publish_dt_ns); + if (g->average_local_to_user_snapshot_publish_dt_ns > 0) + { /* Increment render time based on average publish dt */ - f64 sim_publish_timescale = (f64)newest_snapshot->sim_dt_ns / (f64)G.average_local_to_user_snapshot_publish_dt_ns; - G.render_time_ns += G.real_dt_ns * sim_publish_timescale; + f64 sim_publish_timescale = (f64)newest_snapshot->sim_dt_ns / (f64)g->average_local_to_user_snapshot_publish_dt_ns; + g->render_time_ns += g->real_dt_ns * sim_publish_timescale; } - i64 render_time_target_diff_ns = G.render_time_target_ns - G.render_time_ns; - if (render_time_target_diff_ns > NsFromSeconds(0.010) || render_time_target_diff_ns < NsFromSeconds(-0.005)) { + i64 render_time_target_diff_ns = g->render_time_target_ns - g->render_time_ns; + if (render_time_target_diff_ns > NsFromSeconds(0.010) || render_time_target_diff_ns < NsFromSeconds(-0.005)) + { /* Snap render time if it gets too out of sync with target render time */ - G.render_time_ns = G.render_time_target_ns; + g->render_time_ns = g->render_time_target_ns; } /* Get two snapshots nearest to render time */ Snapshot *left_snapshot = sim_snapshot_nil(); Snapshot *right_snapshot = newest_snapshot; { - Snapshot *ss = sim_snapshot_from_tick(G.user_unblended_client, G.user_unblended_client->first_tick); - while (ss->valid) { + Snapshot *ss = sim_snapshot_from_tick(g->user_unblended_client, g->user_unblended_client->first_tick); + while (ss->valid) + { u64 next_tick = ss->next_tick; i64 ss_time_ns = ss->sim_time_ns; - if (ss_time_ns < G.render_time_ns && ss_time_ns > left_snapshot->sim_time_ns) { + if (ss_time_ns < g->render_time_ns && ss_time_ns > left_snapshot->sim_time_ns) + { left_snapshot = ss; } - if (ss_time_ns > G.render_time_ns && ss_time_ns < right_snapshot->sim_time_ns) { + if (ss_time_ns > g->render_time_ns && ss_time_ns < right_snapshot->sim_time_ns) + { right_snapshot = ss; } - ss = sim_snapshot_from_tick(G.user_unblended_client, next_tick); + ss = sim_snapshot_from_tick(g->user_unblended_client, next_tick); } } /* Create world from blended snapshots */ - if (left_snapshot->valid && right_snapshot->valid) { - f64 blend = (f64)(G.render_time_ns - left_snapshot->sim_time_ns) / (f64)(right_snapshot->sim_time_ns - left_snapshot->sim_time_ns); - G.ss_blended = sim_snapshot_alloc_from_lerp(G.user_blended_client, left_snapshot, right_snapshot, blend); - } else if (left_snapshot->valid) { - G.ss_blended = sim_snapshot_alloc(G.user_blended_client, left_snapshot, left_snapshot->tick); - } else if (right_snapshot->valid) { - G.ss_blended = sim_snapshot_alloc(G.user_blended_client, right_snapshot, right_snapshot->tick); + if (left_snapshot->valid && right_snapshot->valid) + { + f64 blend = (f64)(g->render_time_ns - left_snapshot->sim_time_ns) / (f64)(right_snapshot->sim_time_ns - left_snapshot->sim_time_ns); + g->ss_blended = sim_snapshot_alloc_from_lerp(g->user_blended_client, left_snapshot, right_snapshot, blend); + } + else if (left_snapshot->valid) + { + g->ss_blended = sim_snapshot_alloc(g->user_blended_client, left_snapshot, left_snapshot->tick); + } + else if (right_snapshot->valid) + { + g->ss_blended = sim_snapshot_alloc(g->user_blended_client, right_snapshot, right_snapshot->tick); } /* Release unneeded unblended snapshots */ - if (left_snapshot->tick > 0) { - sim_snapshot_release_ticks_in_range(G.user_unblended_client, 0, left_snapshot->tick - 1); + if (left_snapshot->tick > 0) + { + sim_snapshot_release_ticks_in_range(g->user_unblended_client, 0, left_snapshot->tick - 1); } - } else { + } + else + { /* Interp disabled, just copy latest snapshot */ - G.render_time_target_ns = newest_snapshot->sim_time_ns; - G.render_time_ns = newest_snapshot->sim_time_ns; - G.ss_blended = sim_snapshot_alloc(G.user_blended_client, newest_snapshot, newest_snapshot->tick); + g->render_time_target_ns = newest_snapshot->sim_time_ns; + g->render_time_ns = newest_snapshot->sim_time_ns; + g->ss_blended = sim_snapshot_alloc(g->user_blended_client, newest_snapshot, newest_snapshot->tick); /* Release unneeded unblended snapshots */ - if (newest_snapshot->tick > 0) { - sim_snapshot_release_ticks_in_range(G.user_unblended_client, 0, newest_snapshot->tick - 1); + if (newest_snapshot->tick > 0) + { + sim_snapshot_release_ticks_in_range(g->user_unblended_client, 0, newest_snapshot->tick - 1); } } /* Release unneeded blended snapshots */ - if (G.ss_blended->tick > 0) { - sim_snapshot_release_ticks_in_range(G.user_blended_client, 0, G.ss_blended->tick - 1); - sim_snapshot_release_ticks_in_range(G.user_blended_client, G.ss_blended->tick + 1, U64Max); + if (g->ss_blended->tick > 0) + { + sim_snapshot_release_ticks_in_range(g->user_blended_client, 0, g->ss_blended->tick - 1); + sim_snapshot_release_ticks_in_range(g->user_blended_client, g->ss_blended->tick + 1, U64Max); } } - /* ========================== * - * Process sys events into user bind state - * ========================== */ + //- Process sys events into user bind state { __profn("Process sys events"); @@ -502,170 +556,197 @@ internal void user_update(P_Window *window) P_WindowEventArray events = P_PopWindowEvents(scratch.arena, window); /* Reset bind pressed / released states */ - for (u32 i = 0; i < countof(G.bind_states); ++i) { - G.bind_states[i] = (struct bind_state) { - .is_held = G.bind_states[i].is_held + for (u32 i = 0; i < countof(g->bind_states); ++i) + { + g->bind_states[i] = (BindState) { + .is_held = g->bind_states[i].is_held }; } - for (u64 ent_index = 0; ent_index < events.count; ++ent_index) { + for (u64 ent_index = 0; ent_index < events.count; ++ent_index) + { P_WindowEvent *event = &events.events[ent_index]; - if (event->kind == P_WindowEventKind_Quit) { + if (event->kind == P_WindowEventKind_Quit) + { P_Exit(); } - if (event->kind == P_WindowEventKind_ButtonUp) { + if (event->kind == P_WindowEventKind_ButtonUp) + { /* Escape quit */ - if (event->button == P_Btn_ESC) { + if (event->button == P_Btn_ESC) + { P_Exit(); } } /* Update mouse pos */ - if (event->kind == P_WindowEventKind_CursorMove) { - G.screen_cursor = event->cursor_position; + if (event->kind == P_WindowEventKind_CursorMove) + { + g->screen_cursor = event->cursor_position; } /* Update bind states */ - if ((event->kind == P_WindowEventKind_ButtonDown|| event->kind == P_WindowEventKind_ButtonUp)) { + if ((event->kind == P_WindowEventKind_ButtonDown || event->kind == P_WindowEventKind_ButtonUp)) + { P_Btn button = event->button; button = button >= P_Btn_Count ? P_Btn_None : button; - enum user_bind_kind bind = g_binds[button]; - if (bind) { + BindKind bind = g_binds[button]; + if (bind) + { b32 pressed = event->kind == P_WindowEventKind_ButtonDown; #if 0 b32 out_of_bounds = button >= P_Btn_M1 && button <= P_Btn_M5 && - (G.ui_cursor.x < 0 || - G.ui_cursor.y < 0 || - G.ui_cursor.x > G.ui_size.x || - G.ui_cursor.y > G.ui_size.y); + (g->ui_cursor.x < 0 || + g->ui_cursor.y < 0 || + g->ui_cursor.x > g->ui_size.x || + g->ui_cursor.y > g->ui_size.y); #else b32 out_of_bounds = 0; #endif - G.bind_states[bind].is_held = pressed && !out_of_bounds; - if (pressed) { - if (!out_of_bounds) { - ++G.bind_states[bind].num_presses_and_repeats; - if (event->is_repeat) { - ++G.bind_states[bind].num_repeats; - } else { - ++G.bind_states[bind].num_presses; + g->bind_states[bind].is_held = pressed && !out_of_bounds; + if (pressed) + { + if (!out_of_bounds) + { + ++g->bind_states[bind].num_presses_and_repeats; + if (event->is_repeat) + { + ++g->bind_states[bind].num_repeats; + } + else + { + ++g->bind_states[bind].num_presses; } } - } else { - ++G.bind_states[bind].num_releases; + } + else + { + ++g->bind_states[bind].num_releases; } } } } } - /* ========================== * - * Find local entities - * ========================== */ + //- Find local entities - Entity *local_player = sim_ent_from_id(G.ss_blended, G.ss_blended->local_player); - Entity *local_control = sim_ent_from_id(G.ss_blended, local_player->player_control_ent); - Entity *local_camera = sim_ent_from_id(G.ss_blended, local_player->player_camera_ent); + Entity *local_player = sim_ent_from_id(g->ss_blended, g->ss_blended->local_player); + Entity *local_control = sim_ent_from_id(g->ss_blended, local_player->player_control_ent); + Entity *local_camera = sim_ent_from_id(g->ss_blended, local_player->player_camera_ent); - /* ========================== * - * Find hovered entity - * ========================== */ + //- Find hovered entity Entity *hovered_ent = sim_ent_nil(); { - Xform mouse_xf = XformFromPos(G.world_cursor); + Xform mouse_xf = XformFromPos(g->world_cursor); CLD_Shape mouse_shape = ZI; mouse_shape.points[0] = VEC2(0, 0); mouse_shape.count = 1; mouse_shape.radius = 0.01f; - for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { - Entity *ent = &G.ss_blended->ents[ent_index]; + for (u64 ent_index = 0; ent_index < g->ss_blended->num_ents_reserved; ++ent_index) + { + Entity *ent = &g->ss_blended->ents[ent_index]; if (!sim_ent_is_valid_and_active(ent)) continue; CLD_Shape ent_collider = ent->local_collider; - if (ent_collider.count > 0) { + if (ent_collider.count > 0) + { /* TODO: Can just use boolean GJK */ Xform ent_xf = sim_ent_get_xform(ent); CLD_CollisionData collision_result = CLD_CollisionDataFromShapes(&ent_collider, &mouse_shape, ent_xf, mouse_xf); - if (collision_result.num_points > 0) { - hovered_ent = sim_ent_from_id(G.ss_blended, ent->top); + if (collision_result.num_points > 0) + { + hovered_ent = sim_ent_from_id(g->ss_blended, ent->top); break; } } } } - /* ========================== * - * Update user state from binds - * ========================== */ + //- Update user state from binds /* Test fullscreen */ { - if (G.bind_states[USER_BIND_KIND_FULLSCREEN].num_presses && G.bind_states[USER_BIND_KIND_FULLSCREEN_MOD].is_held) { + if (g->bind_states[BindKind_Fullscreen].num_presses && g->bind_states[BindKind_FullscreenMod].is_held) + { P_WindowSettings settings = P_GetWindowSettings(window); settings.flags ^= P_WindowSettingsFlag_Fullscreen; P_UpdateWindowSettings(window, &settings); } } - if (G.bind_states[USER_BIND_KIND_DEBUG_DRAW].num_presses > 0) { - G.debug_draw = !G.debug_draw; + if (g->bind_states[BindKind_DebugDraw].num_presses > 0) + { + g->debug_draw = !g->debug_draw; } - if (G.bind_states[USER_BIND_KIND_DEBUG_TOGGLE_TOPMOST].num_presses > 0) { + if (g->bind_states[BindKind_DebugToggleTopmost].num_presses > 0) + { P_ToggleWindowTopmost(window); P_LogSuccessF("Toggle topmost"); } - if (G.bind_states[USER_BIND_KIND_DEBUG_CONSOLE].num_presses > 0) { - G.debug_console = !G.debug_console; + if (g->bind_states[BindKind_DebugConsole].num_presses > 0) + { + g->debug_console = !g->debug_console; } - if (G.bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) { - G.debug_camera = !G.debug_camera; + if (g->bind_states[BindKind_DebugCamera].num_presses > 0) + { + g->debug_camera = !g->debug_camera; } { - if (G.bind_states[USER_BIND_KIND_DEBUG_FOLLOW].num_presses > 0) { - if (sim_ent_id_is_nil(G.debug_following)) { - G.debug_following = hovered_ent->id; - } else { - G.debug_following = SIM_ENT_NIL_ID; + if (g->bind_states[BindKind_DebugFollow].num_presses > 0) + { + if (sim_ent_id_is_nil(g->debug_following)) + { + g->debug_following = hovered_ent->id; + } + else + { + g->debug_following = SIM_ENT_NIL_ID; } } - if (!sim_ent_id_is_nil(G.debug_following)) { - Entity *follow_ent = sim_ent_from_id(G.ss_blended, G.debug_following); + if (!sim_ent_id_is_nil(g->debug_following)) + { + Entity *follow_ent = sim_ent_from_id(g->ss_blended, g->debug_following); Entity *follow_camera = sim_ent_nil(); - for (u64 i = 0; i < G.ss_blended->num_ents_reserved; ++i) { - Entity *ent = &G.ss_blended->ents[i]; - Entity *ent_camera_follow = sim_ent_from_id(G.ss_blended, ent->camera_follow); - if (ent_camera_follow->valid && ent_camera_follow == follow_ent) { + for (u64 i = 0; i < g->ss_blended->num_ents_reserved; ++i) + { + Entity *ent = &g->ss_blended->ents[i]; + Entity *ent_camera_follow = sim_ent_from_id(g->ss_blended, ent->camera_follow); + if (ent_camera_follow->valid && ent_camera_follow == follow_ent) + { follow_camera = ent; break; } } - if (follow_camera->valid) { + if (follow_camera->valid) + { local_camera = follow_camera; - } else { - G.debug_following = SIM_ENT_NIL_ID; + } + else + { + g->debug_following = SIM_ENT_NIL_ID; } } } - /* ========================== * - * Apply shake - * ========================== */ + //- Apply shake - for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { - Entity *ent = &G.ss_blended->ents[ent_index]; + for (u64 ent_index = 0; ent_index < g->ss_blended->num_ents_reserved; ++ent_index) + { + Entity *ent = &g->ss_blended->ents[ent_index]; if (!sim_ent_is_valid_and_active(ent)) continue; /* How much time between camera shakes */ i64 frequency_ns = NsFromSeconds(0.01f); f32 shake = ent->shake; - if (shake > 0) { - u64 angle_seed0 = ent->id.uid.lo + (u64)(G.ss_blended->sim_time_ns / frequency_ns); + if (shake > 0) + { + u64 angle_seed0 = ent->id.uid.lo + (u64)(g->ss_blended->sim_time_ns / frequency_ns); u64 angle_seed1 = angle_seed0 + 1; f32 angle0 = RandF64FromSeed(angle_seed0, 0, Tau); f32 angle1 = RandF64FromSeed(angle_seed1, 0, Tau); @@ -675,7 +756,7 @@ internal void user_update(P_Window *window) Vec2 vec1 = Vec2WithLen(Vec2FromAngle(angle1), shake); /* TODO: Cubic interp? */ - f32 blend = (f32)(G.ss_blended->sim_time_ns % frequency_ns) / (f32)frequency_ns; + f32 blend = (f32)(g->ss_blended->sim_time_ns % frequency_ns) / (f32)frequency_ns; Vec2 vec = LerpVec2(vec0, vec1, blend); Xform xf = sim_ent_get_xform(ent); @@ -684,189 +765,198 @@ internal void user_update(P_Window *window) } } - /* ========================== * - * Update ui to screen xform from screen size - * ========================== */ + //- Update ui to screen xform from screen size - if (G.debug_camera) { - G.ui_size = G.screen_size; - G.ui_to_screen_xf = XformIdentity; - G.ui_to_screen_xf.og = RoundVec2(G.ui_to_screen_xf.og); - } else { + if (g->debug_camera) + { + g->ui_size = g->screen_size; + g->ui_to_screen_xf = XformIdentity; + g->ui_to_screen_xf.og = RoundVec2(g->ui_to_screen_xf.og); + } + else + { /* Determine ui size by camera & window dimensions */ f32 aspect_ratio = (f32)(DEFAULT_CAMERA_WIDTH / DEFAULT_CAMERA_HEIGHT); - if (local_camera->valid) { + if (local_camera->valid) + { Xform quad_xf = MulXform(sim_ent_get_xform(local_camera), local_camera->camera_quad_xform); Vec2 camera_size = ScaleFromXform(quad_xf); - if (!IsVec2Zero(camera_size)) { + if (!IsVec2Zero(camera_size)) + { aspect_ratio = camera_size.x / camera_size.y; } } - f32 width = G.screen_size.x; - f32 height = G.screen_size.y; - if (width / height > aspect_ratio) { + f32 width = g->screen_size.x; + f32 height = g->screen_size.y; + if (width / height > aspect_ratio) + { width = height * aspect_ratio; - } else { + } + else + { height = CeilF32(width / aspect_ratio); } - G.ui_size = VEC2(width, height); + g->ui_size = VEC2(width, height); /* Center ui in window */ - f32 x = RoundF32(G.screen_size.x / 2 - width / 2); - f32 y = RoundF32(G.screen_size.y / 2 - height / 2); - G.ui_to_screen_xf = XformFromTrs(TRS(.t = VEC2(x, y))); - G.ui_to_screen_xf.og = RoundVec2(G.ui_to_screen_xf.og); + f32 x = RoundF32(g->screen_size.x / 2 - width / 2); + f32 y = RoundF32(g->screen_size.y / 2 - height / 2); + g->ui_to_screen_xf = XformFromTrs(TRS(.t = VEC2(x, y))); + g->ui_to_screen_xf.og = RoundVec2(g->ui_to_screen_xf.og); } - G.ui_cursor = MulXformV2(InvertXform(G.ui_to_screen_xf), G.screen_cursor); + g->ui_cursor = MulXformV2(InvertXform(g->ui_to_screen_xf), g->screen_cursor); - /* ========================== * - * Update world to ui xform from camera - * ========================== */ + //- Update world to ui xform from camera - if (G.debug_camera) { - G.world_to_ui_xf = XformWIthWorldRotation(G.world_to_ui_xf, 0); + if (g->debug_camera) + { + g->world_to_ui_xf = XformWIthWorldRotation(g->world_to_ui_xf, 0); - Vec2 world_cursor = InvertXformMulV2(G.world_to_ui_xf, G.ui_cursor); + Vec2 world_cursor = InvertXformMulV2(g->world_to_ui_xf, g->ui_cursor); /* Pan view */ - if (G.bind_states[USER_BIND_KIND_PAN].is_held) { - if (!G.debug_camera_panning) { - G.debug_camera_pan_start = world_cursor; - G.debug_camera_panning = 1; + if (g->bind_states[BindKind_Pan].is_held) + { + if (!g->debug_camera_panning) + { + g->debug_camera_pan_start = world_cursor; + g->debug_camera_panning = 1; } - Vec2 offset = NegVec2(SubVec2(G.debug_camera_pan_start, world_cursor)); - G.world_to_ui_xf = TranslateXform(G.world_to_ui_xf, offset); - world_cursor = InvertXformMulV2(G.world_to_ui_xf, G.ui_cursor); - G.debug_camera_pan_start = world_cursor; - } else { - G.debug_camera_panning = 0; + Vec2 offset = NegVec2(SubVec2(g->debug_camera_pan_start, world_cursor)); + g->world_to_ui_xf = TranslateXform(g->world_to_ui_xf, offset); + world_cursor = InvertXformMulV2(g->world_to_ui_xf, g->ui_cursor); + g->debug_camera_pan_start = world_cursor; + } + else + { + g->debug_camera_panning = 0; } /* Zoom view */ - i32 input_zooms = G.bind_states[USER_BIND_KIND_ZOOM_IN].num_presses - G.bind_states[USER_BIND_KIND_ZOOM_OUT].num_presses; - if (input_zooms != 0) { + i32 input_zooms = g->bind_states[BindKind_ZoomIn].num_presses - g->bind_states[BindKind_ZoomOut].num_presses; + if (input_zooms != 0) + { /* Zoom to cursor */ f32 zoom_rate = 2; f32 zoom = PowF32(zoom_rate, input_zooms); - G.world_to_ui_xf = TranslateXform(G.world_to_ui_xf, world_cursor); - G.world_to_ui_xf = ScaleXform(G.world_to_ui_xf, VEC2(zoom, zoom)); - G.world_to_ui_xf = TranslateXform(G.world_to_ui_xf, NegVec2(world_cursor)); + g->world_to_ui_xf = TranslateXform(g->world_to_ui_xf, world_cursor); + g->world_to_ui_xf = ScaleXform(g->world_to_ui_xf, VEC2(zoom, zoom)); + g->world_to_ui_xf = TranslateXform(g->world_to_ui_xf, NegVec2(world_cursor)); } - G.world_to_ui_xf.og = RoundVec2(G.world_to_ui_xf.og); - } else { + g->world_to_ui_xf.og = RoundVec2(g->world_to_ui_xf.og); + } + else + { Xform xf = sim_ent_get_xform(local_camera); Vec2 world_center = xf.og; f32 rot = RotationFromXform(xf); /* Scale view into viewport based on camera size */ - Vec2 scale = G.ui_size; + Vec2 scale = g->ui_size; { Xform quad_xf = MulXform(xf, local_camera->camera_quad_xform); Vec2 camera_size = ScaleFromXform(quad_xf); - if (!IsVec2Zero(camera_size)) { - scale = DivVec2Vec2(G.ui_size, camera_size); + if (!IsVec2Zero(camera_size)) + { + scale = DivVec2Vec2(g->ui_size, camera_size); } } scale.x = MinF32(scale.x, scale.y); scale.y = scale.x; - Vec2 ui_center = MulVec2(G.ui_size, 0.5); + Vec2 ui_center = MulVec2(g->ui_size, 0.5); Trs trs = TRS(.t = SubVec2(ui_center, world_center), .r = rot, .s = scale); Vec2 pivot = world_center; - G.world_to_ui_xf = XformIdentity; - G.world_to_ui_xf = TranslateXform(G.world_to_ui_xf, pivot); - G.world_to_ui_xf = TranslateXform(G.world_to_ui_xf, trs.t); - G.world_to_ui_xf = RotateXform(G.world_to_ui_xf, trs.r); - G.world_to_ui_xf = ScaleXform(G.world_to_ui_xf, trs.s); - G.world_to_ui_xf = TranslateXform(G.world_to_ui_xf, NegVec2(pivot)); - G.world_to_ui_xf.og = RoundVec2(G.world_to_ui_xf.og); + g->world_to_ui_xf = XformIdentity; + g->world_to_ui_xf = TranslateXform(g->world_to_ui_xf, pivot); + g->world_to_ui_xf = TranslateXform(g->world_to_ui_xf, trs.t); + g->world_to_ui_xf = RotateXform(g->world_to_ui_xf, trs.r); + g->world_to_ui_xf = ScaleXform(g->world_to_ui_xf, trs.s); + g->world_to_ui_xf = TranslateXform(g->world_to_ui_xf, NegVec2(pivot)); + g->world_to_ui_xf.og = RoundVec2(g->world_to_ui_xf.og); } - G.world_cursor = InvertXformMulV2(G.world_to_ui_xf, G.ui_cursor); + g->world_cursor = InvertXformMulV2(g->world_to_ui_xf, g->ui_cursor); - /* ========================== * - * Update world to render xform from world to ui xform - * ========================== */ + //- Update world to render xform from world to ui xform b32 effects_disabled = 0; - G.render_size = RoundVec2(VEC2(RENDER_WIDTH, RENDER_HEIGHT)); + g->render_size = RoundVec2(VEC2(RENDER_WIDTH, RENDER_HEIGHT)); - if (G.debug_camera) { - G.render_size = G.ui_size; + if (g->debug_camera) + { + g->render_size = g->ui_size; effects_disabled = 1; - G.world_to_render_xf = G.world_to_ui_xf; - } else { - Xform ui_to_world_xf = InvertXform(G.world_to_ui_xf); - Vec2 world_center = MulXformV2(ui_to_world_xf, MulVec2(G.ui_size, 0.5)); + g->world_to_render_xf = g->world_to_ui_xf; + } + else + { + Xform ui_to_world_xf = InvertXform(g->world_to_ui_xf); + Vec2 world_center = MulXformV2(ui_to_world_xf, MulVec2(g->ui_size, 0.5)); Vec2 scale = VEC2(PIXELS_PER_UNIT, PIXELS_PER_UNIT); Xform xf = XformIdentity; - xf = TranslateXform(xf, MulVec2(G.render_size, 0.5)); + xf = TranslateXform(xf, MulVec2(g->render_size, 0.5)); xf = ScaleXform(xf, scale); xf = TranslateXform(xf, MulVec2(world_center, -1)); xf.og = RoundVec2(xf.og); - G.world_to_render_xf = xf; + g->world_to_render_xf = xf; } - /* ========================== * - * Update render to ui xform - * ========================== */ + //- Update render to ui xform { - Xform world_to_ui_xf = G.world_to_ui_xf; - Xform world_to_render_xf = G.world_to_render_xf; + Xform world_to_ui_xf = g->world_to_ui_xf; + Xform world_to_render_xf = g->world_to_render_xf; Xform render_to_world_xf = InvertXform(world_to_render_xf); Xform render_to_ui_xf = MulXform(world_to_ui_xf, render_to_world_xf); - G.render_to_ui_xf = render_to_ui_xf; + g->render_to_ui_xf = render_to_ui_xf; } - /* ========================== * - * Update listener from view - * ========================== */ + //- Update listener from view { Vec2 up = VEC2(0, -1); - Vec2 ui_center = MulVec2(G.ui_size, 0.5f); - Vec2 listener_pos = InvertXformMulV2(G.world_to_ui_xf, ui_center); - Vec2 listener_dir = NormVec2(InvertXformBasisMulV2(G.world_to_ui_xf, up)); + Vec2 ui_center = MulVec2(g->ui_size, 0.5f); + Vec2 listener_pos = InvertXformMulV2(g->world_to_ui_xf, ui_center); + Vec2 listener_dir = NormVec2(InvertXformBasisMulV2(g->world_to_ui_xf, up)); MIX_UpdateListener(listener_pos, listener_dir); } - /* ========================== * - * Draw grid - * ========================== */ + //- Draw grid { f32 thickness = 2; - Vec2 offset = NegVec2(MulXformV2(G.world_to_render_xf, VEC2(0, 0))); - f32 spacing = ScaleFromXform(G.world_to_render_xf).x; + Vec2 offset = NegVec2(MulXformV2(g->world_to_render_xf, VEC2(0, 0))); + f32 spacing = ScaleFromXform(g->world_to_render_xf).x; - Vec2 pos = InvertXformMulV2(G.world_to_render_xf, VEC2(0, 0)); - Vec2 size = InvertXformBasisMulV2(G.world_to_render_xf, G.render_size); + Vec2 pos = InvertXformMulV2(g->world_to_render_xf, VEC2(0, 0)); + Vec2 size = InvertXformBasisMulV2(g->world_to_render_xf, g->render_size); u32 color0 = Rgba32F(0.17f, 0.17f, 0.17f, 1.f); u32 color1 = Rgba32F(0.15f, 0.15f, 0.15f, 1.f); - D_DrawGrid(G.render_sig, XformFromRect(RectFromVec2(pos, size)), color0, color1, Rgba32(0x3f, 0x3f, 0x3f, 0xFF), ColorRed, ColorGreen, thickness, spacing, offset); + D_DrawGrid(g->render_sig, XformFromRect(RectFromVec2(pos, size)), color0, color1, Rgba32(0x3f, 0x3f, 0x3f, 0xFF), ColorRed, ColorGreen, thickness, spacing, offset); } #if 0 - /* ========================== * - * Alloc / release tile cache entries - * ========================== */ + //- Alloc / release tile cache entries - /* Alloc entries from new sim chunks */ + /* Alloc entries from new sim chunks */ - for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { - Entity *chunk_ent = &G.ss_blended->ents[ent_index]; - if (sim_ent_is_valid_and_active(chunk_ent) && sim_ent_has_prop(chunk_ent, SEPROP_TILE_CHUNK)) { + for (u64 ent_index = 0; ent_index < g->ss_blended->num_ents_reserved; ++ent_index) + { + Entity *chunk_ent = &g->ss_blended->ents[ent_index]; + if (sim_ent_is_valid_and_active(chunk_ent) && sim_ent_has_prop(chunk_ent, SEPROP_TILE_CHUNK)) + { struct user_tile_cache_entry *entry = user_tile_cache_entry_from_chunk_pos(chunk_ent->tile_chunk_pos); - if (!entry->valid) { + if (!entry->valid) + { entry = user_tile_cache_entry_alloc(chunk_ent->tile_chunk_pos); } } @@ -874,37 +964,46 @@ internal void user_update(P_Window *window) /* Release entries with invalid sim chunks */ - for (u64 entry_index = 0; entry_index < G.tile_cache.num_reserved_entries; ++entry_index) { - struct tile_cache_entry *entry = &G.tile_cache.entries[entry_index]; - if (entry->valid) { + for (u64 entry_index = 0; entry_index < g->tile_cache.num_reserved_entries; ++entry_index) + { + struct tile_cache_entry *entry = &g->tile_cache.entries[entry_index]; + if (entry->valid) + { Entity *chunk_ent = sim_ent_from_chunk_pos(entry->pos); - if (!chunk_ent->valid) { + if (!chunk_ent->valid) + { user_tile_cache_entry_release(entry); } } } - /* ========================== * - * Draw dirty tile cache entries - * ========================== */ + //- Draw dirty tile cache entries - for (u64 entry_index = 0; entry_index < G.tile_cache.num_reserved_entries; ++entry_index) { - struct tile_cache_entry *entry = &G.tile_cache.entries[entry_index]; - if (entry->valid) { + for (u64 entry_index = 0; entry_index < g->tile_cache.num_reserved_entries; ++entry_index) + { + struct tile_cache_entry *entry = &g->tile_cache.entries[entry_index]; + if (entry->valid) + { Vec2I32 chunk_pos = entry->pos; Entity *chunk_ent = sim_ent_from_chunk_pos(chunk_pos); - if (entry->applied_dirty_gen != chunk_ent->dirty_gen) { + if (entry->applied_dirty_gen != chunk_ent->dirty_gen) + { entry->applied_dirty_gen = chunk_ent->dirty_gen; /* TODO: Autotiling */ String data = sim_ent_get_chunk_tile_data(chunk_ent); u64 tile_count = data.len; - if (tile_count == SIM_TILES_PER_CHUNK_SQRT * SIM_TILES_PER_CHUNK_SQRT) { - for (u64 y_in_chunk = 0; y_in_chunk < SIM_TILES_PER_CHUNK_SQRT; ++y_in_chunk) { - for (u64 x_in_chunk = 0; x_in_chunk < SIM_TILES_PER_CHUNK_SQRT; ++x_in_chunk) { + if (tile_count == SIM_TILES_PER_CHUNK_SQRT * SIM_TILES_PER_CHUNK_SQRT) + { + for (u64 y_in_chunk = 0; y_in_chunk < SIM_TILES_PER_CHUNK_SQRT; ++y_in_chunk) + { + for (u64 x_in_chunk = 0; x_in_chunk < SIM_TILES_PER_CHUNK_SQRT; ++x_in_chunk) + { } } - } else { + } + else + { /* TODO: Clear gpu buffer if it exists */ } } @@ -912,12 +1011,15 @@ internal void user_update(P_Window *window) } #if 0 - for (u64 entry_index = 0; entry_index < G.tile_cache.num_reserved_entries; ++entry_index) { - struct tile_cache_entry *entry = &G.tile_cache.entries[entry_index]; - if (entry->valid) { + for (u64 entry_index = 0; entry_index < g->tile_cache.num_reserved_entries; ++entry_index) + { + struct tile_cache_entry *entry = &g->tile_cache.entries[entry_index]; + if (entry->valid) + { Vec2I32 chunk_pos = entry->pos; Entity *chunk_ent = sim_ent_from_chunk_pos(chunk_pos); - if (entry->applied_dirty_gen != chunk_ent->dirty_gen) { + if (entry->applied_dirty_gen != chunk_ent->dirty_gen) + { entry->applied_dirty_gen = chunk_ent->dirty_gen; /* Retreive surrounding chunk info since we're auto-tiling @@ -951,9 +1053,7 @@ internal void user_update(P_Window *window) #endif #endif - /* ========================== * - * Sort drawable entities - * ========================== */ + //- Sort drawable entities Entity **sorted = PushDry(scratch.arena, Entity *); u64 sorted_count = 0; @@ -961,9 +1061,11 @@ internal void user_update(P_Window *window) /* Copy valid entities */ { __profn("Build ents list for sorting"); - for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { - Entity *ent = &G.ss_blended->ents[ent_index]; - if (sim_ent_is_valid_and_active(ent)) { + for (u64 ent_index = 0; ent_index < g->ss_blended->num_ents_reserved; ++ent_index) + { + Entity *ent = &g->ss_blended->ents[ent_index]; + if (sim_ent_is_valid_and_active(ent)) + { *PushStructNoZero(scratch.arena, Entity *) = ent; ++sorted_count; } @@ -972,29 +1074,28 @@ internal void user_update(P_Window *window) /* Sort */ { __profn("Sort ents"); - Mergesort(sorted, sorted_count, sizeof(*sorted), ent_draw_order_cmp, 0); + Mergesort(sorted, sorted_count, sizeof(*sorted), EntitySortCmp, 0); } } - /* ========================== * - * Draw entities - * ========================== */ + //- Draw entities { __profn("Draw entities"); - for (u64 sorted_index = 0; sorted_index < sorted_count; ++sorted_index) { + for (u64 sorted_index = 0; sorted_index < sorted_count; ++sorted_index) + { Entity *ent = sorted[sorted_index]; if (!sim_ent_is_valid_and_active(ent)) continue; //if (S_IsTagNil(ent->sprite)) continue; S_Tag sprite = ent->sprite; - Entity *parent = sim_ent_from_id(G.ss_blended, ent->parent); + Entity *parent = sim_ent_from_id(g->ss_blended, ent->parent); Xform xf = sim_ent_get_xform(ent); Xform parent_xf = sim_ent_get_xform(parent); - b32 skip_debug_draw = !G.debug_camera && ent == local_camera; + b32 skip_debug_draw = !g->debug_camera && ent == local_camera; skip_debug_draw = skip_debug_draw || sim_ent_has_prop(ent, SEPROP_MOTOR_JOINT); b32 skip_debug_draw_transform = sim_ent_has_prop(ent, SEPROP_CAMERA); @@ -1005,7 +1106,8 @@ internal void user_update(P_Window *window) /* Draw tracer */ /* TODO: Enable this */ #if 0 - if (sim_ent_has_prop(ent, SEPROP_TRACER)) { + if (sim_ent_has_prop(ent, SEPROP_TRACER)) + { Vec2 velocity = ent->tracer_start_velocity; Vec2 a = ent->tracer_start; @@ -1020,11 +1122,15 @@ internal void user_update(P_Window *window) f32 opacity_a = 1; f32 opacity_b = 1; - if (Vec2LenSq(vcd) != 0) { - if (DotVec2(velocity, vca) <= 0) { + if (Vec2LenSq(vcd) != 0) + { + if (DotVec2(velocity, vca) <= 0) + { a = c; opacity_a = 0; - } else { + } + else + { opacity_a = DotVec2(vcd, vca) / Vec2LenSq(vcd); } opacity_a = ClampF32(opacity_a, 0, 1); @@ -1035,48 +1141,56 @@ internal void user_update(P_Window *window) u32 color_start = Rgba32F(1, 0.5, 0, opacity_a); u32 color_end = Rgba32F(1, 0.8, 0.4, opacity_b); - if (opacity_b > 0.99f) { - D_DrawCircle(G.render_sig, b, thickness / 2, color_end, 20); + if (opacity_b > 0.99f) + { + D_DrawCircle(g->render_sig, b, thickness / 2, color_end, 20); } - D_DrawLineGradient(G.render_sig, a, b, thickness, color_start, color_end); + D_DrawLineGradient(g->render_sig, a, b, thickness, color_start, color_end); } #endif /* Draw sprite */ - if (!S_IsTagNil(sprite)) { + if (!S_IsTagNil(sprite)) + { S_Sheet *sheet = S_SheetFromTagAsync(sprite_frame_scope, sprite); S_Texture *texture = S_TextureFromTagAsync(sprite_frame_scope, sprite); /* TODO: Fade in placeholder if texture isn't loaded */ - if (sheet->loaded && texture->loaded) { + if (sheet->loaded && texture->loaded) + { b32 is_light = sim_ent_has_prop(ent, SEPROP_LIGHT_TEST); Vec3 emittance = ent->sprite_emittance; u32 tint = ent->sprite_tint; S_Frame frame = S_FrameFromIndex(sheet, ent->animation_frame); D_MaterialParams params = D_MATERIALPARAMS(.xf = sprite_xform, .texture = texture->gp_texture, .tint = tint, .clip = frame.clip, .is_light = is_light, .light_emittance = emittance); - D_DrawMaterial(G.render_sig, params); + D_DrawMaterial(g->render_sig, params); } } /* Draw tiles */ /* TODO: Something better */ - if (sim_ent_has_prop(ent, SEPROP_TILE_CHUNK)) { + if (sim_ent_has_prop(ent, SEPROP_TILE_CHUNK)) + { Vec2I32 chunk_index = ent->tile_chunk_index; S_Tag tile_sprite = S_TagFromPath(Lit("sprite/tile.ase")); S_Texture *tile_texture = S_TextureFromTagAsync(sprite_frame_scope, tile_sprite); - if (tile_texture->loaded) { + if (tile_texture->loaded) + { f32 tile_size = 1.f / SIM_TILES_PER_UNIT_SQRT; - for (i32 tile_y = 0; tile_y < SIM_TILES_PER_CHUNK_SQRT; ++tile_y) { - for (i32 tile_x = 0; tile_x < SIM_TILES_PER_CHUNK_SQRT; ++tile_x) { + for (i32 tile_y = 0; tile_y < SIM_TILES_PER_CHUNK_SQRT; ++tile_y) + { + for (i32 tile_x = 0; tile_x < SIM_TILES_PER_CHUNK_SQRT; ++tile_x) + { Vec2I32 local_tile_index = VEC2I32(tile_x, tile_y); TileKind tile = ent->tile_chunk_tiles[local_tile_index.x + (local_tile_index.y * SIM_TILES_PER_CHUNK_SQRT)]; //if (tile > -1) { - if (tile == SIM_TILE_KIND_WALL) { + if (tile == SIM_TILE_KIND_WALL) + { Vec2I32 world_tile_index = sim_world_tile_index_from_local_tile_index(chunk_index, local_tile_index); Vec2 pos = sim_pos_from_world_tile_index(world_tile_index); Xform tile_xf = XformFromRect(RectFromVec2(pos, VEC2(tile_size, tile_size))); D_MaterialParams params = D_MATERIALPARAMS(.xf = tile_xf, .texture = tile_texture->gp_texture, .is_light = 1, .light_emittance = VEC3(0, 0, 0)); - D_DrawMaterial(G.render_sig, params); + D_DrawMaterial(g->render_sig, params); } } } @@ -1084,74 +1198,84 @@ internal void user_update(P_Window *window) } /* Debug draw entity info */ - if (G.debug_draw && !skip_debug_draw) { + if (g->debug_draw && !skip_debug_draw) + { TempArena temp = BeginTempArena(scratch.arena); - if (sim_ent_has_prop(ent, SEPROP_KINEMATIC) || sim_ent_has_prop(ent, SEPROP_DYNAMIC)) { - debug_draw_movement(ent); + if (sim_ent_has_prop(ent, SEPROP_KINEMATIC) || sim_ent_has_prop(ent, SEPROP_DYNAMIC)) + { + DebugDrawMovement(ent); } /* Draw xform */ - if (!skip_debug_draw_transform) { + if (!skip_debug_draw_transform) + { u32 color_x = Rgba32F(1, 0, 0, 0.5); u32 color_y = Rgba32F(0, 1, 0, 0.5); - debug_draw_xform(xf, color_x, color_y); + DebugDrawXform(xf, color_x, color_y); } /* Draw AABB */ - if (ent->local_collider.count > 0) { + if (ent->local_collider.count > 0) + { Aabb aabb = CLD_AabbFromShape(&ent->local_collider, xf); f32 thickness = 1; u32 color = Rgba32F(1, 0, 1, 0.5); Quad quad = QuadFromAabb(aabb); - quad = MulXformQuad(G.world_to_ui_xf, quad); - D_DrawQuadLine(G.render_sig, quad, thickness, color); + quad = MulXformQuad(g->world_to_ui_xf, quad); + D_DrawQuadLine(g->render_sig, quad, thickness, color); } /* Draw focus arrow */ - if (ent == local_control || sim_ent_id_eq(ent->id, G.debug_following)) { + if (ent == local_control || sim_ent_id_eq(ent->id, g->debug_following)) + { S_Sheet *sheet = S_SheetFromTagAsync(sprite_frame_scope, ent->sprite); S_Slice slice = S_SliceFromNameIndex(sheet, Lit("attach.wep"), ent->animation_frame); Vec2 start = MulXformV2(sprite_xform, slice.center); - start = MulXformV2(G.world_to_ui_xf, start); + start = MulXformV2(g->world_to_ui_xf, start); Vec2 end = AddVec2(xf.og, ent->control.focus); - end = MulXformV2(G.world_to_ui_xf, end); - D_DrawArrowLine(G.render_sig, start, end, 3, 10, Rgba32F(1, 1, 1, 0.5)); + end = MulXformV2(g->world_to_ui_xf, end); + D_DrawArrowLine(g->render_sig, start, end, 3, 10, Rgba32F(1, 1, 1, 0.5)); } #if 0 /* Draw slices */ - if (!S_IsTagNil(ent->sprite)) { + if (!S_IsTagNil(ent->sprite)) + { S_Sheet *sheet = S_SheetFromTagAsync(sprite_frame_scope, sprite); u32 quad_color = Rgba32F(1, 0, 0.5, 1); u32 point_color = Rgba32F(1, 0, 0, 1); u32 ray_color = Rgba32F(1, 0, 0.5, 1); - for (u64 i = 0; i < sheet->slice_groups_count; ++i) { + for (u64 i = 0; i < sheet->slice_groups_count; ++i) + { S_SheetSliceGroup *group = &sheet->slice_groups[i]; if (StringEndsWith(group->name, Lit(".ray"))) continue; - for (u32 j = 0; j < group->per_frame_count; ++j) { + for (u32 j = 0; j < group->per_frame_count; ++j) + { S_Slice slice = group->frame_slices[(ent->animation_frame * group->per_frame_count) + j]; Vec2 center = MulXformV2(sprite_xform, slice.center); - center = MulXformV2(G.world_to_ui_xf, center); + center = MulXformV2(g->world_to_ui_xf, center); - if (!slice.has_ray) { + if (!slice.has_ray) + { Quad quad = QuadFromRect(slice.rect); quad = MulXformQuad(sprite_xform, quad); - quad = MulXformQuad(G.world_to_ui_xf, quad); - D_DrawQuadLine(G.render_sig, quad, 2, quad_color); + quad = MulXformQuad(g->world_to_ui_xf, quad); + D_DrawQuadLine(g->render_sig, quad, 2, quad_color); } - D_DrawCircle(G.render_sig, center, 3, point_color, 20); + D_DrawCircle(g->render_sig, center, 3, point_color, 20); - if (slice.has_ray) { + if (slice.has_ray) + { Vec2 ray = MulXformBasisV2(sprite_xform, slice.dir); - ray = MulXformBasisV2(G.world_to_ui_xf, ray); + ray = MulXformBasisV2(g->world_to_ui_xf, ray); ray = Vec2WithLen(ray, 25); - D_DrawArrowRay(G.render_sig, center, ray, 2, 10, ray_color); + D_DrawArrowRay(g->render_sig, center, ray, 2, 10, ray_color); } } } @@ -1160,74 +1284,80 @@ internal void user_update(P_Window *window) /* Draw weld joint */ #if 0 - if (sim_ent_has_prop(ent, SEPROP_WELD_JOINT)) { - Entity *e1 = sim_ent_from_id(G.ss_blended, ent->weld_joint_data.e1); + if (sim_ent_has_prop(ent, SEPROP_WELD_JOINT)) + { + Entity *e1 = sim_ent_from_id(g->ss_blended, ent->weld_joint_data.e1); Xform e1_xf = sim_ent_get_xform(e1); u32 color = ColorYellow; f32 radius = 3; Vec2 point = MulXformV2(e1_xf, ent->weld_joint_data.point_local_e1); - point = MulXformV2(G.world_to_ui_xf, point); - D_DrawCircle(G.render_sig, point, radius, color, 10); + point = MulXformV2(g->world_to_ui_xf, point); + D_DrawCircle(g->render_sig, point, radius, color, 10); DEBUGBREAKABLE; } #endif /* Draw mouse joint */ - if (sim_ent_has_prop(ent, SEPROP_MOUSE_JOINT)) { - Entity *target = sim_ent_from_id(G.ss_blended, ent->mouse_joint_data.target); + if (sim_ent_has_prop(ent, SEPROP_MOUSE_JOINT)) + { + Entity *target = sim_ent_from_id(g->ss_blended, ent->mouse_joint_data.target); Xform target_xf = sim_ent_get_xform(target); u32 color = ColorWhite; Vec2 point_start = MulXformV2(target_xf, ent->mouse_joint_data.point_local_start); - Vec2 point_end = G.world_cursor; - point_start = MulXformV2(G.world_to_ui_xf, point_start); - point_end = MulXformV2(G.world_to_ui_xf, point_end); - D_DrawArrowLine(G.render_sig, point_start, point_end, 3, 10, color); - D_DrawCircle(G.render_sig, point_start, 4, color, 10); + Vec2 point_end = g->world_cursor; + point_start = MulXformV2(g->world_to_ui_xf, point_start); + point_end = MulXformV2(g->world_to_ui_xf, point_end); + D_DrawArrowLine(g->render_sig, point_start, point_end, 3, 10, color); + D_DrawCircle(g->render_sig, point_start, 4, color, 10); } /* Draw collider */ - if (ent->local_collider.count > 0) { + if (ent->local_collider.count > 0) + { CLD_Shape collider = ent->local_collider; u32 color = Rgba32F(1, 1, 0, 0.5); f32 thickness = 2; { /* Draw collider using support points */ u32 detail = 32; - Xform collider_draw_xf = MulXform(G.world_to_ui_xf, xf); - D_DrawColliderLine(G.render_sig, collider, collider_draw_xf, thickness, color, detail); + Xform collider_draw_xf = MulXform(g->world_to_ui_xf, xf); + D_DrawColliderLine(g->render_sig, collider, collider_draw_xf, thickness, color, detail); } { /* Draw collider shape points */ - for (u32 i = 0; i < collider.count; ++i) { - Vec2 p = MulXformV2(MulXform(G.world_to_ui_xf, xf), collider.points[i]); - D_DrawCircle(G.render_sig, p, 3, ColorBlue, 10); + for (u32 i = 0; i < collider.count; ++i) + { + Vec2 p = MulXformV2(MulXform(g->world_to_ui_xf, xf), collider.points[i]); + D_DrawCircle(g->render_sig, p, 3, ColorBlue, 10); } } - if (collider.count == 1 && collider.radius > 0) { + if (collider.count == 1 && collider.radius > 0) + { /* Draw upwards line for circle */ Vec2 start = xf.og; Vec2 end = CLD_SupportPointFromDir(&collider, xf, NegVec2(xf.by)).p; - start = MulXformV2(G.world_to_ui_xf, start); - end = MulXformV2(G.world_to_ui_xf, end); - D_DrawLine(G.render_sig, start, end, thickness, color); + start = MulXformV2(g->world_to_ui_xf, start); + end = MulXformV2(g->world_to_ui_xf, end); + D_DrawLine(g->render_sig, start, end, thickness, color); } #if 0 /* Draw support point at focus dir */ { Vec2 p = collider_support_point(&collider, xf, ent->control.focus); - p = MulXformV2(G.world_to_ui_xf, p); - D_DrawCircle(G.render_sig, p, 3, ColorRed, 10); + p = MulXformV2(g->world_to_ui_xf, p); + D_DrawCircle(g->render_sig, p, 3, ColorRed, 10); } #endif } /* Draw contact constraint */ - if (sim_ent_has_prop(ent, SEPROP_CONTACT_CONSTRAINT)) { + if (sim_ent_has_prop(ent, SEPROP_CONTACT_CONSTRAINT)) + { ContactConstraint *data = &ent->contact_constraint_data; - Entity *e0 = sim_ent_from_id(G.ss_blended, data->e0); - Entity *e1 = sim_ent_from_id(G.ss_blended, data->e1); + Entity *e0 = sim_ent_from_id(g->ss_blended, data->e0); + Entity *e1 = sim_ent_from_id(g->ss_blended, data->e1); (UNUSED)e0; (UNUSED)e1; @@ -1235,14 +1365,15 @@ internal void user_update(P_Window *window) /* Draw contact points */ { f32 radius = 5; - for (u32 i = 0; i < data->num_points; ++i) { + for (u32 i = 0; i < data->num_points; ++i) + { u32 color = (data->skip_solve || data->wrong_dir) ? Alpha32F(ColorYellow, 0.3) : Rgba32F(0.8, 0.2, 0.2, 1); ContactPoint point = data->points[i]; Vec2 dbg_pt = point.dbg_pt; /* Draw point */ { - D_DrawCircle(G.render_sig, MulXformV2(G.world_to_ui_xf, dbg_pt), radius, color, 10); + D_DrawCircle(g->render_sig, MulXformV2(g->world_to_ui_xf, dbg_pt), radius, color, 10); } /* Draw normal */ @@ -1250,15 +1381,16 @@ internal void user_update(P_Window *window) f32 len = 0.1f; f32 arrow_thickness = 2; f32 arrow_height = 5; - Vec2 start = MulXformV2(G.world_to_ui_xf, dbg_pt); - Vec2 end = MulXformV2(G.world_to_ui_xf, AddVec2(dbg_pt, MulVec2(NormVec2(data->normal), len))); - D_DrawArrowLine(G.render_sig, start, end, arrow_thickness, arrow_height, color); + Vec2 start = MulXformV2(g->world_to_ui_xf, dbg_pt); + Vec2 end = MulXformV2(g->world_to_ui_xf, AddVec2(dbg_pt, MulVec2(NormVec2(data->normal), len))); + D_DrawArrowLine(g->render_sig, start, end, arrow_thickness, arrow_height, color); } #if 0 /* Draw contact info */ { F_Font *disp_font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f); - if (disp_font) { + if (disp_font) + { f32 offset_px = 10; String fmt = Lit( @@ -1272,17 +1404,17 @@ internal void user_update(P_Window *window) "num contacts: %F" ); String text = StringFormat(temp.arena, fmt, - FmtUint(e0->handle.idx), - FmtUint(e1->handle.idx), - FmtHex(point.id), - FmtFloat(point.normal_impulse), - FmtFloat(point.tangent_impulse), - FmtFloatP(point.starting_separation, 6), - FmtFloatP(data->normal.x, 6), FmtFloatP(data->normal.y, 6), - FmtUint(data->num_points)); + FmtUint(e0->handle.idx), + FmtUint(e1->handle.idx), + FmtHex(point.id), + FmtFloat(point.normal_impulse), + FmtFloat(point.tangent_impulse), + FmtFloatP(point.starting_separation, 6), + FmtFloatP(data->normal.x, 6), FmtFloatP(data->normal.y, 6), + FmtUint(data->num_points)); - draw_text(G.render_sig, disp_font, AddVec2(RoundVec2(MulXformV2(G.world_to_ui_xf, dbg_pt)), VEC2(0, offset_px)), text); + draw_text(g->render_sig, disp_font, AddVec2(RoundVec2(MulXformV2(g->world_to_ui_xf, dbg_pt)), VEC2(0, offset_px)), text); } } #endif @@ -1293,11 +1425,12 @@ internal void user_update(P_Window *window) /* Draw collision debug */ #if COLLIDER_DEBUG - if (sim_ent_has_prop(ent, SEPROP_COLLISION_DEBUG)) { + if (sim_ent_has_prop(ent, SEPROP_COLLISION_DEBUG)) + { CollisionDebugData *data = &ent->collision_debug_data; CLD_CollisionData collision_reuslt = data->collision_result; - Entity *e0 = sim_ent_from_id(G.ss_blended, data->e0); - Entity *e1 = sim_ent_from_id(G.ss_blended, data->e1); + Entity *e0 = sim_ent_from_id(g->ss_blended, data->e0); + Entity *e1 = sim_ent_from_id(g->ss_blended, data->e1); CLD_Shape e0_collider = e0->local_collider; CLD_Shape e1_collider = e1->local_collider; (UNUSED)e0_collider; @@ -1308,10 +1441,10 @@ internal void user_update(P_Window *window) { f32 radius = 4; u32 color = Rgba32F(1, 1, 0, 0.5); - Vec2 a = MulXformV2(G.world_to_ui_xf, data->closest0); - Vec2 b = MulXformV2(G.world_to_ui_xf, data->closest1); - D_DrawCircle(G.render_sig, a, radius, color, 10); - D_DrawCircle(G.render_sig, b, radius, color, 10); + Vec2 a = MulXformV2(g->world_to_ui_xf, data->closest0); + Vec2 b = MulXformV2(g->world_to_ui_xf, data->closest1); + D_DrawCircle(g->render_sig, a, radius, color, 10); + D_DrawCircle(g->render_sig, b, radius, color, 10); } #endif @@ -1326,30 +1459,30 @@ internal void user_update(P_Window *window) u32 color_a_clipped = Rgba32F(1, 0, 0, 1); u32 color_b_clipped = Rgba32F(0, 1, 0, 1); { - Vec2 a = MulXformV2(G.world_to_ui_xf, collision_reuslt.a0); - Vec2 b = MulXformV2(G.world_to_ui_xf, collision_reuslt.b0); - D_DrawLine(G.render_sig, a, b, thickness, color_line); - D_DrawCircle(G.render_sig, a, radius, color_a, 10); - D_DrawCircle(G.render_sig, b, radius, color_b, 10); + Vec2 a = MulXformV2(g->world_to_ui_xf, collision_reuslt.a0); + Vec2 b = MulXformV2(g->world_to_ui_xf, collision_reuslt.b0); + D_DrawLine(g->render_sig, a, b, thickness, color_line); + D_DrawCircle(g->render_sig, a, radius, color_a, 10); + D_DrawCircle(g->render_sig, b, radius, color_b, 10); - Vec2 a_clipped = MulXformV2(G.world_to_ui_xf, collision_reuslt.a0_clipped); - Vec2 b_clipped = MulXformV2(G.world_to_ui_xf, collision_reuslt.b0_clipped); - D_DrawLine(G.render_sig, a_clipped, b_clipped, thickness, color_line_clipped); - D_DrawCircle(G.render_sig, a_clipped, radius, color_a_clipped, 10); - D_DrawCircle(G.render_sig, b_clipped, radius, color_b_clipped, 10); + Vec2 a_clipped = MulXformV2(g->world_to_ui_xf, collision_reuslt.a0_clipped); + Vec2 b_clipped = MulXformV2(g->world_to_ui_xf, collision_reuslt.b0_clipped); + D_DrawLine(g->render_sig, a_clipped, b_clipped, thickness, color_line_clipped); + D_DrawCircle(g->render_sig, a_clipped, radius, color_a_clipped, 10); + D_DrawCircle(g->render_sig, b_clipped, radius, color_b_clipped, 10); } { - Vec2 a = MulXformV2(G.world_to_ui_xf, collision_reuslt.a1); - Vec2 b = MulXformV2(G.world_to_ui_xf, collision_reuslt.b1); - D_DrawLine(G.render_sig, a, b, thickness, color_line); - D_DrawCircle(G.render_sig, a, radius, color_a, 10); - D_DrawCircle(G.render_sig, b, radius, color_b, 10); + Vec2 a = MulXformV2(g->world_to_ui_xf, collision_reuslt.a1); + Vec2 b = MulXformV2(g->world_to_ui_xf, collision_reuslt.b1); + D_DrawLine(g->render_sig, a, b, thickness, color_line); + D_DrawCircle(g->render_sig, a, radius, color_a, 10); + D_DrawCircle(g->render_sig, b, radius, color_b, 10); - Vec2 a_clipped = MulXformV2(G.world_to_ui_xf, collision_reuslt.a1_clipped); - Vec2 b_clipped = MulXformV2(G.world_to_ui_xf, collision_reuslt.b1_clipped); - D_DrawLine(G.render_sig, a_clipped, b_clipped, thickness, color_line_clipped); - D_DrawCircle(G.render_sig, a_clipped, radius, color_a_clipped, 10); - D_DrawCircle(G.render_sig, b_clipped, radius, color_b_clipped, 10); + Vec2 a_clipped = MulXformV2(g->world_to_ui_xf, collision_reuslt.a1_clipped); + Vec2 b_clipped = MulXformV2(g->world_to_ui_xf, collision_reuslt.b1_clipped); + D_DrawLine(g->render_sig, a_clipped, b_clipped, thickness, color_line_clipped); + D_DrawCircle(g->render_sig, a_clipped, radius, color_a_clipped, 10); + D_DrawCircle(g->render_sig, b_clipped, radius, color_b_clipped, 10); } } @@ -1360,8 +1493,10 @@ internal void user_update(P_Window *window) #if 0 /* Only draw points with large separation */ b32 should_draw = 0; - for (u32 i = 0; i < data->num_points; ++i) { - if (data->points[i].starting_separation < -0.1) { + for (u32 i = 0; i < data->num_points; ++i) + { + if (data->points[i].starting_separation < -0.1) + { should_draw = 1; break; } @@ -1370,12 +1505,14 @@ internal void user_update(P_Window *window) b32 should_draw = 1; #endif - if (should_draw) { + if (should_draw) + { #if 0 /* Test info */ { F_Font *disp_font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f); - if (disp_font) { + if (disp_font) + { f32 offset_px = 10; String fmt = Lit( "e0 pos: (%F, %F)\n" @@ -1384,13 +1521,13 @@ internal void user_update(P_Window *window) "e1 rot: %F\n" ); String text = StringFormat(temp.arena, fmt, - FmtFloatP(e0_xf.og.x, 24), FmtFloatP(e0_xf.og.y, 24), - FmtFloatP(RotationFromXform(e0_xf), 24), - FmtFloatP(e1_xf.og.x, 24), FmtFloatP(e1_xf.og.y, 24), - FmtFloatP(RotationFromXform(e1_xf), 24)); + FmtFloatP(e0_xf.og.x, 24), FmtFloatP(e0_xf.og.y, 24), + FmtFloatP(RotationFromXform(e0_xf), 24), + FmtFloatP(e1_xf.og.x, 24), FmtFloatP(e1_xf.og.y, 24), + FmtFloatP(RotationFromXform(e1_xf), 24)); - draw_text(G.render_sig, disp_font, AddVec2(RoundVec2(MulXformV2(G.world_to_ui_xf, VEC2(0, 0))), VEC2(0, offset_px)), text); + draw_text(g->render_sig, disp_font, AddVec2(RoundVec2(MulXformV2(g->world_to_ui_xf, VEC2(0, 0))), VEC2(0, offset_px)), text); } } #endif @@ -1405,9 +1542,9 @@ internal void user_update(P_Window *window) Vec2Array m = CLD_Menkowski(temp.arena, &e0_collider, &e1_collider, e0_xf, e1_xf, detail); - for (u64 i = 0; i < m.count; ++i) m.points[i] = MulXformV2(G.world_to_ui_xf, m.points[i]); - D_DrawPolyLine(G.render_sig, m, 1, thickness, color); - //D_DrawPoly(G.render_sig, m, color); + for (u64 i = 0; i < m.count; ++i) m.points[i] = MulXformV2(g->world_to_ui_xf, m.points[i]); + D_DrawPolyLine(g->render_sig, m, 1, thickness, color); + //D_DrawPoly(g->render_sig, m, color); } /* Draw CLD_PointCloud */ @@ -1417,9 +1554,10 @@ internal void user_update(P_Window *window) Vec2Array m = CLD_PointCloud(temp.arena, &e0_collider, &e1_collider, e0_xf, e1_xf); - for (u64 i = 0; i < m.count; ++i) { - Vec2 p = MulXformV2(G.world_to_ui_xf, m.points[i]); - D_DrawCircle(G.render_sig, p, radius, color, 10); + for (u64 i = 0; i < m.count; ++i) + { + Vec2 p = MulXformV2(g->world_to_ui_xf, m.points[i]); + D_DrawCircle(g->render_sig, p, radius, color, 10); } } @@ -1432,9 +1570,9 @@ internal void user_update(P_Window *window) .points = collision_reuslt.prototype.points, .count = collision_reuslt.prototype.len }; - for (u64 i = 0; i < m.count; ++i) m.points[i] = MulXformV2(G.world_to_ui_xf, m.points[i]); - D_DrawPolyLine(G.render_sig, m, 1, thickness, color); - for (u64 i = 0; i < m.count; ++i) D_DrawCircle(G.render_sig, m.points[i], 10, color, 10); + for (u64 i = 0; i < m.count; ++i) m.points[i] = MulXformV2(g->world_to_ui_xf, m.points[i]); + D_DrawPolyLine(g->render_sig, m, 1, thickness, color); + for (u64 i = 0; i < m.count; ++i) D_DrawCircle(g->render_sig, m.points[i], 10, color, 10); } /* Draw simplex */ @@ -1447,23 +1585,27 @@ internal void user_update(P_Window *window) CLD_MenkowskiSimplex simplex = collision_reuslt.simplex; Vec2 simplex_points[] = { simplex.a.p, simplex.b.p, simplex.c.p }; - for (u64 i = 0; i < countof(simplex_points); ++i) simplex_points[i] = MulXformV2(G.world_to_ui_xf, simplex_points[i]); + for (u64 i = 0; i < countof(simplex_points); ++i) simplex_points[i] = MulXformV2(g->world_to_ui_xf, simplex_points[i]); Vec2Array simplex_array = { .count = simplex.len, .points = simplex_points }; - if (simplex.len >= 1) { + if (simplex.len >= 1) + { u32 color = simplex.len == 1 ? color_first : (simplex.len == 2 ? color_second : color_third); - D_DrawCircle(G.render_sig, simplex_array.points[0], thickness * 3, color, 10); + D_DrawCircle(g->render_sig, simplex_array.points[0], thickness * 3, color, 10); } - if (simplex.len >= 2) { + if (simplex.len >= 2) + { u32 color = simplex.len == 2 ? color_first : color_second; - D_DrawCircle(G.render_sig, simplex_array.points[1], thickness * 3, color, 10); + D_DrawCircle(g->render_sig, simplex_array.points[1], thickness * 3, color, 10); } - if (simplex.len >= 3) { + if (simplex.len >= 3) + { u32 color = color_first; - D_DrawCircle(G.render_sig, simplex_array.points[2], thickness * 3, color, 10); + D_DrawCircle(g->render_sig, simplex_array.points[2], thickness * 3, color, 10); } - if (simplex.len >= 2) { - D_DrawPolyLine(G.render_sig, simplex_array, simplex.len > 2, thickness, line_color); + if (simplex.len >= 2) + { + D_DrawPolyLine(g->render_sig, simplex_array, simplex.len > 2, thickness, line_color); } } @@ -1473,9 +1615,9 @@ internal void user_update(P_Window *window) f32 len = 0.1f; f32 arrow_thickness = 4; f32 arrowhead_height = 10; - Vec2 start = MulXformV2(G.world_to_ui_xf, VEC2(0, 0)); - Vec2 end = MulXformV2(G.world_to_ui_xf, MulVec2(NormVec2(collision_reuslt.normal), len)); - D_DrawArrowLine(G.render_sig, start, end, arrow_thickness, arrowhead_height, color); + Vec2 start = MulXformV2(g->world_to_ui_xf, VEC2(0, 0)); + Vec2 end = MulXformV2(g->world_to_ui_xf, MulVec2(NormVec2(collision_reuslt.normal), len)); + D_DrawArrowLine(g->render_sig, start, end, arrow_thickness, arrowhead_height, color); } } #endif @@ -1483,26 +1625,28 @@ internal void user_update(P_Window *window) #endif /* Draw hierarchy */ - if (sim_ent_has_prop(parent, SEPROP_ACTIVE) && !parent->is_root) { + if (sim_ent_has_prop(parent, SEPROP_ACTIVE) && !parent->is_root) + { u32 color = Rgba32F(0.6, 0.6, 1, 0.75); f32 thickness = 2; f32 arrow_height = 15; - Vec2 start = MulXformV2(G.world_to_ui_xf, xf.og); - Vec2 end = MulXformV2(G.world_to_ui_xf, parent_xf.og); - D_DrawArrowLine(G.render_sig, start, end, thickness, arrow_height, color); + Vec2 start = MulXformV2(g->world_to_ui_xf, xf.og); + Vec2 end = MulXformV2(g->world_to_ui_xf, parent_xf.og); + D_DrawArrowLine(g->render_sig, start, end, thickness, arrow_height, color); } /* Draw camera rect */ - if (sim_ent_has_prop(ent, SEPROP_CAMERA)) { + if (sim_ent_has_prop(ent, SEPROP_CAMERA)) + { u32 color = ent == local_camera ? Rgba32F(1, 1, 1, 0.5) : Rgba32F(0, 0.75, 0, 0.5); f32 thickness = 3; Xform quad_xf = MulXform(xf, ent->camera_quad_xform); Quad quad = MulXformQuad(quad_xf, CenteredUnitSquareQuad); - quad = MulXformQuad(G.world_to_ui_xf, quad); + quad = MulXformQuad(g->world_to_ui_xf, quad); - D_DrawQuadLine(G.render_sig, quad, thickness, color); + D_DrawQuadLine(g->render_sig, quad, thickness, color); } EndTempArena(temp); @@ -1511,45 +1655,48 @@ internal void user_update(P_Window *window) } /* Draw crosshair or show cursor */ - if (!G.debug_camera) { + if (!g->debug_camera) + { __profn("Draw crosshair"); - Vec2 crosshair_pos = G.ui_cursor; + Vec2 crosshair_pos = g->ui_cursor; S_Tag crosshair = S_TagFromPath(Lit("sprite/crosshair.ase")); S_Texture *t = S_TextureFromTagAsync(sprite_frame_scope, crosshair); Vec2 size = VEC2(t->width, t->height); Xform xf = XformFromTrs(TRS(.t = crosshair_pos, .s = size)); - D_DrawUiRect(G.render_sig, D_UIRECTPARAMS(.xf = xf, .texture = t->gp_texture)); + D_DrawUiRect(g->render_sig, D_UIRECTPARAMS(.xf = xf, .texture = t->gp_texture)); } /* FIXME: Enable this */ #if 0 { __profn("Update window cursor"); - if (G.debug_camera) { - P_DisableWindoweCursorClip(G.window); - P_ShowWindowCursor(G.window); - } else { + if (g->debug_camera) + { + P_DisableWindoweCursorClip(g->window); + P_ShowWindowCursor(g->window); + } + else + { S_Texture *t = S_TextureFromTagAsync(sprite_frame_scope, S_TagFromPath(Lit("sprite/crosshair.ase"))); Vec2 size = VEC2(t->width, t->height); - Rect cursor_clip = RectFromVec2(G.ui_screen_offset, G.ui_size); + Rect cursor_clip = RectFromVec2(g->ui_screen_offset, g->ui_size); cursor_clip.pos = AddVec2(cursor_clip.pos, MulVec2(size, 0.5f)); cursor_clip.pos = AddVec2(cursor_clip.pos, VEC2(1, 1)); cursor_clip.size = SubVec2(cursor_clip.size, size); - P_HideWindowCursor(G.window); - P_EnableWindoweCursorClip(G.window, cursor_clip); + P_HideWindowCursor(g->window); + P_EnableWindoweCursorClip(g->window, cursor_clip); } } #endif - /* ========================== * - * Create user sim cmd - * ========================== */ + //- Create user sim cmd { /* Queue player move cmd */ f32 move_speed = 1.0f; - //if (G.bind_states[USER_BIND_KIND_WALK].is_held) { - if (G.bind_states[USER_BIND_KIND_FULLSCREEN_MOD].is_held) { + //if (g->bind_states[BindKind_Walk].is_held) { + if (g->bind_states[BindKind_FullscreenMod].is_held) + { //const f32 walk_ratio = 0.25f; const f32 walk_ratio = 0.05f; move_speed *= walk_ratio; @@ -1557,31 +1704,34 @@ internal void user_update(P_Window *window) Vec2 input_move_dir = ZI; { - for (enum user_bind_kind bind = 0; bind < (i32)countof(G.bind_states); ++bind) { - struct bind_state state = G.bind_states[bind]; + for (BindKind bind = 0; bind < (i32)countof(g->bind_states); ++bind) + { + BindState state = g->bind_states[bind]; - if (!state.is_held && state.num_presses <= 0) { + if (!state.is_held && state.num_presses <= 0) + { continue; } - switch (bind) { + switch (bind) + { /* Movement */ - case USER_BIND_KIND_MOVE_UP: + case BindKind_MoveUp: { input_move_dir.y -= 1; } break; - case USER_BIND_KIND_MOVE_DOWN: + case BindKind_MoveDown: { input_move_dir.y += 1; } break; - case USER_BIND_KIND_MOVE_LEFT: + case BindKind_MoveLeft: { input_move_dir.x -= 1; } break; - case USER_BIND_KIND_MOVE_RIGHT: + case BindKind_MoveRight: { input_move_dir.x += 1; } break; @@ -1590,97 +1740,113 @@ internal void user_update(P_Window *window) } } - input_move_dir = InvertXformBasisMulV2(G.world_to_ui_xf, input_move_dir); /* Make move dir relative to world view */ + input_move_dir = InvertXformBasisMulV2(g->world_to_ui_xf, input_move_dir); /* Make move dir relative to world view */ input_move_dir = MulVec2(NormVec2(input_move_dir), move_speed); } - if (!G.debug_camera) { - G.focus_send = SubVec2(G.world_cursor, sim_ent_get_xform(local_control).og); + if (!g->debug_camera) + { + g->focus_send = SubVec2(g->world_cursor, sim_ent_get_xform(local_control).og); } - Vec2 input_aim_dir = G.focus_send; + Vec2 input_aim_dir = g->focus_send; /* Queue player control cmd */ { ControlData control = ZI; control.move = input_move_dir; control.focus = input_aim_dir; - control.dbg_cursor = G.world_cursor; + control.dbg_cursor = g->world_cursor; - struct bind_state fire_state = G.bind_states[USER_BIND_KIND_FIRE]; - struct bind_state fire_alt_state = G.bind_states[USER_BIND_KIND_FIRE_ALT]; - struct bind_state drag_state = G.bind_states[USER_BIND_KIND_DEBUG_DRAG]; - struct bind_state delete_state = G.bind_states[USER_BIND_KIND_DEBUG_DELETE]; - struct bind_state clear_state = G.bind_states[USER_BIND_KIND_DEBUG_CLEAR]; - struct bind_state spawn1_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN1]; - struct bind_state spawn2_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN2]; - struct bind_state spawn3_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN3]; - struct bind_state spawn4_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN4]; - struct bind_state walls_state = G.bind_states[USER_BIND_KIND_DEBUG_WALLS]; - struct bind_state pause_state = G.bind_states[USER_BIND_KIND_DEBUG_PAUSE]; - struct bind_state step_state = G.bind_states[USER_BIND_KIND_DEBUG_STEP]; - struct bind_state tile_state = G.bind_states[USER_BIND_KIND_TILE_TEST]; - struct bind_state explode_state = G.bind_states[USER_BIND_KIND_DEBUG_EXPLODE]; - struct bind_state teleport_state = G.bind_states[USER_BIND_KIND_DEBUG_TELEPORT]; + BindState fire_state = g->bind_states[BindKind_Fire]; + BindState fire_alt_state = g->bind_states[BindKind_AltFire]; + BindState drag_state = g->bind_states[BindKind_DebugDrag]; + BindState delete_state = g->bind_states[BindKind_DebugDelete]; + BindState clear_state = g->bind_states[BindKind_DebugClear]; + BindState spawn1_state = g->bind_states[BindKind_DebugSpawn1]; + BindState spawn2_state = g->bind_states[BindKind_DebugSpawn2]; + BindState spawn3_state = g->bind_states[BindKind_DebugSpawn3]; + BindState spawn4_state = g->bind_states[BindKind_DebugSpawn4]; + BindState walls_state = g->bind_states[BindKind_DebugWalls]; + BindState pause_state = g->bind_states[BindKind_DebugPause]; + BindState step_state = g->bind_states[BindKind_DebugStep]; + BindState tile_state = g->bind_states[BindKind_TestTile]; + BindState explode_state = g->bind_states[BindKind_DebugExplode]; + BindState teleport_state = g->bind_states[BindKind_DebugTeleport]; - if (fire_state.num_presses || fire_state.is_held) { + if (fire_state.num_presses || fire_state.is_held) + { control.flags |= SIM_CONTROL_FLAG_FIRE; } - if (fire_alt_state.num_presses || fire_alt_state.is_held) { + if (fire_alt_state.num_presses || fire_alt_state.is_held) + { control.flags |= SIM_CONTROL_FLAG_FIRE_ALT; } - if (drag_state.num_presses || drag_state.is_held) { + if (drag_state.num_presses || drag_state.is_held) + { control.flags |= SIM_CONTROL_FLAG_DRAG; } - if (delete_state.num_presses || delete_state.is_held) { + if (delete_state.num_presses || delete_state.is_held) + { control.flags |= SIM_CONTROL_FLAG_DELETE; } - if (clear_state.num_presses_and_repeats) { + if (clear_state.num_presses_and_repeats) + { control.flags |= SIM_CONTROL_FLAG_CLEAR_ALL; } - if (spawn1_state.num_presses_and_repeats) { + if (spawn1_state.num_presses_and_repeats) + { control.flags |= SIM_CONTROL_FLAG_SPAWN1_TEST; } - if (spawn2_state.num_presses_and_repeats) { + if (spawn2_state.num_presses_and_repeats) + { control.flags |= SIM_CONTROL_FLAG_SPAWN2_TEST; } - if (spawn3_state.num_presses_and_repeats) { + if (spawn3_state.num_presses_and_repeats) + { control.flags |= SIM_CONTROL_FLAG_SPAWN3_TEST; } - if (spawn4_state.num_presses_and_repeats) { + if (spawn4_state.num_presses_and_repeats) + { control.flags |= SIM_CONTROL_FLAG_SPAWN4_TEST; } - if (walls_state.num_presses_and_repeats) { + if (walls_state.num_presses_and_repeats) + { control.flags |= SIM_CONTROL_FLAG_WALLS_TEST; } - if (tile_state.num_presses || tile_state.is_held) { + if (tile_state.num_presses || tile_state.is_held) + { control.flags |= SIM_CONTROL_FLAG_TILE_TEST; } - if (explode_state.num_presses_and_repeats) { + if (explode_state.num_presses_and_repeats) + { control.flags |= SIM_CONTROL_FLAG_EXPLODE_TEST; } - if (teleport_state.num_presses_and_repeats || (G.debug_camera && teleport_state.is_held)) { + if (teleport_state.num_presses_and_repeats || (g->debug_camera && teleport_state.is_held)) + { control.flags |= SIM_CONTROL_FLAG_TELEPORT_TEST; } - if (pause_state.num_presses) { - Atomic32FetchXor(&G.user_paused, 1); + if (pause_state.num_presses) + { + Atomic32FetchXor(&g->user_paused, 1); } - Atomic32FetchAdd(&G.user_paused_steps, step_state.num_presses_and_repeats); + Atomic32FetchAdd(&g->user_paused_steps, step_state.num_presses_and_repeats); /* Set user sim control */ { - P_Lock lock = P_LockE(&G.user_sim_cmd_mutex); + P_Lock lock = P_LockE(&g->user_sim_cmd_mutex); /* Reset flags */ - if (G.user_sim_cmd_gen != G.last_user_sim_cmd_gen) { - G.user_sim_cmd_control.flags = 0; - G.last_user_sim_cmd_gen = G.user_sim_cmd_gen; + if (g->user_sim_cmd_gen != g->last_user_sim_cmd_gen) + { + g->user_sim_cmd_control.flags = 0; + g->last_user_sim_cmd_gen = g->user_sim_cmd_gen; } - u32 old_flags = G.user_sim_cmd_control.flags; - G.user_sim_cmd_control = control; - G.user_sim_cmd_control.flags |= old_flags; - G.user_hovered_ent = hovered_ent->id; + u32 old_flags = g->user_sim_cmd_control.flags; + g->user_sim_cmd_control = control; + g->user_sim_cmd_control.flags |= old_flags; + g->user_hovered_ent = hovered_ent->id; P_Unlock(&lock); } } @@ -1688,13 +1854,15 @@ internal void user_update(P_Window *window) #if RtcIsEnabled /* Gjk steps */ { - if (G.bind_states[USER_BIND_KIND_RESET_DEBUG_STEPS].num_presses_and_repeats > 0) { + if (g->bind_states[BindKind_ResetDebugSteps].num_presses_and_repeats > 0) + { SetGstat(GSTAT_DEBUG_STEPS, 0); } i32 add_steps = 0; - add_steps += G.bind_states[USER_BIND_KIND_INCR_DEBUG_STEPS].num_presses_and_repeats; - add_steps -= G.bind_states[USER_BIND_KIND_DECR_DEBUG_STEPS].num_presses_and_repeats; - if (add_steps != 0) { + add_steps += g->bind_states[BindKind_IncrementDebugSteps].num_presses_and_repeats; + add_steps -= g->bind_states[BindKind_DecrementDebugSteps].num_presses_and_repeats; + if (add_steps != 0) + { AddGstat(GSTAT_DEBUG_STEPS, add_steps); } } @@ -1704,52 +1872,51 @@ internal void user_update(P_Window *window) { /* Update network usage stats */ i64 stat_now_ns = P_TimeNs(); - G.net_bytes_read.last_second_end = GetGstat(GSTAT_SOCK_BYTES_RECEIVED); - G.net_bytes_sent.last_second_end = GetGstat(GSTAT_SOCK_BYTES_SENT); - if (stat_now_ns - G.last_second_reset_ns > NsFromSeconds(1)) { - G.last_second_reset_ns = stat_now_ns; - G.net_bytes_read.last_second = G.net_bytes_read.last_second_end - G.net_bytes_read.last_second_start; - G.net_bytes_sent.last_second = G.net_bytes_sent.last_second_end - G.net_bytes_sent.last_second_start; - G.net_bytes_read.last_second_start = G.net_bytes_read.last_second_end; - G.net_bytes_sent.last_second_start = G.net_bytes_sent.last_second_end; + g->net_bytes_read.last_second_end = GetGstat(GSTAT_SOCK_BYTES_RECEIVED); + g->net_bytes_sent.last_second_end = GetGstat(GSTAT_SOCK_BYTES_SENT); + if (stat_now_ns - g->last_second_reset_ns > NsFromSeconds(1)) + { + g->last_second_reset_ns = stat_now_ns; + g->net_bytes_read.last_second = g->net_bytes_read.last_second_end - g->net_bytes_read.last_second_start; + g->net_bytes_sent.last_second = g->net_bytes_sent.last_second_end - g->net_bytes_sent.last_second_start; + g->net_bytes_read.last_second_start = g->net_bytes_read.last_second_end; + g->net_bytes_sent.last_second_start = g->net_bytes_sent.last_second_end; } } - /* ========================== * - * Draw ent debug info - * ========================== */ + //- Draw ent debug info - if (G.debug_draw && hovered_ent->valid) { + if (g->debug_draw && hovered_ent->valid) + { Entity *ent = hovered_ent; - Vec2 pos = AddVec2(G.ui_cursor, VEC2(15, 15)); + Vec2 pos = AddVec2(g->ui_cursor, VEC2(15, 15)); F_Font *font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f); - if (font) { + if (font) + { TempArena temp = BeginTempArena(scratch.arena); String dbg_text = ZI; dbg_text.text = PushDry(temp.arena, u8); - dbg_text.len += get_ent_debug_text(temp.arena, ent).len; - draw_text(G.render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = dbg_text)); + dbg_text.len += DebugStringFromEntity(temp.arena, ent).len; + draw_text(g->render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = dbg_text)); EndTempArena(temp); } } - /* ========================== * - * Query vram - * ========================== */ + //- Query vram GPU_MemoryInfo vram = GPU_QueryMemoryInfo(); - /* ========================== * - * Draw global debug info - * ========================== */ + //- Draw global debug info - if (G.debug_draw) { + if (g->debug_draw) + { __profn("Draw debug info"); F_Font *font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f); - if (font) { + if (font) + { TempArena temp = BeginTempArena(scratch.arena); String text = ZI; text.text = PushDry(temp.arena, u8); @@ -1759,32 +1926,32 @@ internal void user_update(P_Window *window) text.len += CopyString(temp.arena, Lit("\n")).len; #endif - text.len += StringFormat(temp.arena, Lit("blended world entities: %F/%F"), FmtUint(G.ss_blended->num_ents_allocated), FmtUint(G.ss_blended->num_ents_reserved)).len; + text.len += StringFormat(temp.arena, Lit("blended world entities: %F/%F"), FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved)).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("blended world tick: %F"), FmtUint(G.ss_blended->tick)).len; + text.len += StringFormat(temp.arena, Lit("blended world tick: %F"), FmtUint(g->ss_blended->tick)).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("blended world time: %F"), FmtFloat(SecondsFromNs(G.ss_blended->sim_time_ns))).len; + text.len += StringFormat(temp.arena, Lit("blended world time: %F"), FmtFloat(SecondsFromNs(g->ss_blended->sim_time_ns))).len; text.len += CopyString(temp.arena, Lit("\n")).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("average local sim publish dt: %F"), FmtFloat(SecondsFromNs(G.average_local_to_user_snapshot_publish_dt_ns))).len; + text.len += StringFormat(temp.arena, Lit("average local sim publish dt: %F"), FmtFloat(SecondsFromNs(g->average_local_to_user_snapshot_publish_dt_ns))).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("local sim last known tick: %F"), FmtUint(G.local_sim_last_known_tick)).len; + text.len += StringFormat(temp.arena, Lit("local sim last known tick: %F"), FmtUint(g->local_sim_last_known_tick)).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("local sim last known time: %F"), FmtFloat(SecondsFromNs(G.local_sim_last_known_time_ns))).len; + text.len += StringFormat(temp.arena, Lit("local sim last known time: %F"), FmtFloat(SecondsFromNs(g->local_sim_last_known_time_ns))).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("local sim predicted time: %F"), FmtFloat(SecondsFromNs(G.local_sim_predicted_time_ns))).len; + text.len += StringFormat(temp.arena, Lit("local sim predicted time: %F"), FmtFloat(SecondsFromNs(g->local_sim_predicted_time_ns))).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("render time target: %F"), FmtFloat(SecondsFromNs(G.render_time_target_ns))).len; + text.len += StringFormat(temp.arena, Lit("render time target: %F"), FmtFloat(SecondsFromNs(g->render_time_target_ns))).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("render time: %F"), FmtFloat(SecondsFromNs(G.render_time_ns))).len; + text.len += StringFormat(temp.arena, Lit("render time: %F"), FmtFloat(SecondsFromNs(g->render_time_ns))).len; text.len += CopyString(temp.arena, Lit("\n")).len; text.len += CopyString(temp.arena, Lit("\n")).len; @@ -1792,7 +1959,7 @@ internal void user_update(P_Window *window) text.len += CopyString(temp.arena, Lit("\n")).len; text.len += CopyString(temp.arena, Lit("\n")).len; - Vec2 world_cursor = G.world_cursor; + Vec2 world_cursor = g->world_cursor; text.len += StringFormat(temp.arena, Lit("cursor world: %F, %F"), FmtFloat(world_cursor.x), FmtFloat(world_cursor.y)).len; text.len += CopyString(temp.arena, Lit("\n")).len; @@ -1809,10 +1976,10 @@ internal void user_update(P_Window *window) text.len += CopyString(temp.arena, Lit("\n")).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("Network read: %F mbit/s"), FmtFloat((f64)G.net_bytes_read.last_second * 8 / 1000 / 1000)).len; + text.len += StringFormat(temp.arena, Lit("Network read: %F mbit/s"), FmtFloat((f64)g->net_bytes_read.last_second * 8 / 1000 / 1000)).len; text.len += CopyString(temp.arena, Lit("\n")).len; - text.len += StringFormat(temp.arena, Lit("Network write: %F mbit/s"), FmtFloat((f64)G.net_bytes_sent.last_second * 8 / 1000 / 1000)).len; + text.len += StringFormat(temp.arena, Lit("Network write: %F mbit/s"), FmtFloat((f64)g->net_bytes_sent.last_second * 8 / 1000 / 1000)).len; text.len += CopyString(temp.arena, Lit("\n")).len; text.len += StringFormat(temp.arena, Lit("Ping (real): %F ms"), FmtFloat(SecondsFromNs(local_player->player_last_rtt_ns) * 1000)).len; @@ -1845,37 +2012,36 @@ internal void user_update(P_Window *window) //text.len += CopyString(temp.arena, Lit("\n")).len; #endif - //draw_text(G.render_sig, font, pos, StringFormat(temp.arena, Lit("blended world entities: %F/%F"), FmtUint(G.ss_blended->num_ents_allocated), FmtUint(G.ss_blended->num_ents_reserved))); - //draw_text(G.render_sig, font, pos, text); + //draw_text(g->render_sig, font, pos, StringFormat(temp.arena, Lit("blended world entities: %F/%F"), FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved))); + //draw_text(g->render_sig, font, pos, text); - Vec2 pos = VEC2(10, G.ui_size.y); + Vec2 pos = VEC2(10, g->ui_size.y); D_TextOffsetY offset_y = DRAW_TEXT_OFFSET_Y_BOTTOM; - draw_text(G.render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = text, .offset_y = offset_y, .color = ColorWhite)); + draw_text(g->render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = text, .offset_y = offset_y, .color = ColorWhite)); EndTempArena(temp); } } { #if DeveloperIsEnabled - b32 console_minimized = !G.debug_console; - i32 console_level = console_minimized ? P_LogLevel_Success: P_LogLevel_Debug; - draw_debug_console(console_level, console_minimized); + b32 console_minimized = !g->debug_console; + i32 console_level = console_minimized ? P_LogLevel_Success : P_LogLevel_Debug; + DebugDrawConsole(console_level, console_minimized); #else - if (G.debug_draw) { - draw_debug_console(P_LogLevel_Info, 0); + if (g->debug_draw) + { + DebugDrawConsole(P_LogLevel_Info, 0); } #endif } - /* ========================== * - * Render - * ========================== */ + //- Render { __profn("Render"); - Vec2I32 world_resolution = RoundVec2ToVec2I32(G.render_size); - Vec2I32 user_resolution = RoundVec2ToVec2I32(G.ui_size); - Vec2I32 backbuffer_resolution = RoundVec2ToVec2I32(G.screen_size); + Vec2I32 world_resolution = RoundVec2ToVec2I32(g->render_size); + Vec2I32 user_resolution = RoundVec2ToVec2I32(g->ui_size); + Vec2I32 backbuffer_resolution = RoundVec2ToVec2I32(g->screen_size); /* Draw world to user texture */ GPU_Resource *render_texture = 0; @@ -1883,35 +2049,37 @@ internal void user_update(P_Window *window) GPU_RenderParams params = ZI; params.ui_size = user_resolution; params.render_size = world_resolution; - params.world_to_render_xf = G.world_to_render_xf; - params.render_to_ui_xf = G.render_to_ui_xf; + params.world_to_render_xf = g->world_to_render_xf; + params.render_to_ui_xf = g->render_to_ui_xf; params.effects_disabled = effects_disabled; - render_texture = GPU_RunRender(G.render_sig, params); + render_texture = GPU_RunRender(g->render_sig, params); } /* Present */ - GPU_PresentSwapchain(G.swapchain, backbuffer_resolution, render_texture, G.ui_to_screen_xf, VSYNC); + GPU_PresentSwapchain(g->swapchain, backbuffer_resolution, render_texture, g->ui_to_screen_xf, VSYNC); } - /* ========================== * - * End frame cache scopes - * ========================== */ + //- End frame cache scopes S_EndScope(sprite_frame_scope); EndScratch(scratch); } -internal P_JobDef(user_update_job, _) +//- User update job + +P_JobDef(UpdateUserJob, _) { + SharedUserState *g = &shared_user_state; i64 time_ns = P_TimeNs(); - while (!Atomic32Fetch(&G.shutdown)) { - P_Window *window = G.window; + while (!Atomic32Fetch(&g->shutdown)) + { + P_Window *window = g->window; { __profn("User sleep"); { __profn("Swapchain wait"); - GPU_WaitOnSwapchain(G.swapchain); + GPU_WaitOnSwapchain(g->swapchain); } { __profn("Frame limiter wait"); @@ -1919,32 +2087,23 @@ internal P_JobDef(user_update_job, _) time_ns = P_TimeNs(); } } - user_update(window); + UpdateUser(window); } } +//////////////////////////////// +//~ Generate user input cmds - - -/* ========================== * - * Local sim thread - * ========================== */ - - - - - - - - -internal void generate_user_input_cmds(Client *user_input_client, u64 tick) +void GenerateuserInputCmds(Client *user_input_client, u64 tick) { + SharedUserState *g = &shared_user_state; Snapshot *prev_user_input_ss = sim_snapshot_from_tick(user_input_client, user_input_client->last_tick); Snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, tick); Entity *user_input_root = sim_ent_from_id(user_input_ss, SIM_ENT_ROOT_ID); /* Find / create local control cmd ent */ Entity *control_cmd = sim_ent_find_first_match_one(user_input_ss, SEPROP_CMD); - if (!control_cmd->valid) { + if (!control_cmd->valid) + { control_cmd = sim_ent_alloc_sync_src(user_input_root); control_cmd->cmd_kind = SIM_CMD_KIND_CONTROL; control_cmd->predictor = user_input_client->player_id; @@ -1952,55 +2111,32 @@ internal void generate_user_input_cmds(Client *user_input_client, u64 tick) sim_ent_activate(control_cmd, user_input_ss->tick); } { - P_Lock lock = P_LockE(&G.user_sim_cmd_mutex); + P_Lock lock = P_LockE(&g->user_sim_cmd_mutex); /* Update control cmd */ { - control_cmd->cmd_control = G.user_sim_cmd_control; - control_cmd->cmd_control_hovered_ent = G.user_hovered_ent; + control_cmd->cmd_control = g->user_sim_cmd_control; + control_cmd->cmd_control_hovered_ent = g->user_hovered_ent; } #if 0 /* Create chat cmd */ - if (G.user_sim_cmd_chat.len > 0) { + if (g->user_sim_cmd_chat.len > 0) + { Entity *chat_cmd = sim_ent_alloc_sync_src(user_input_root); chat_cmd->cmd_kind = SIM_CMD_KIND_CHAT; //chat_cmd->chat_msg = ZI } #endif - ++G.user_sim_cmd_gen; + ++g->user_sim_cmd_gen; P_Unlock(&lock); } } +//////////////////////////////// +//~ Sim update - - - - - - - - - - - - - -struct sim_ss_decode_node { - Client *client; - u64 tick; - u64 base_tick; - String tmp_encoded; - struct sim_ss_decode_node *next; -}; - -struct sim_decode_queue { - struct sim_ss_decode_node *first; - struct sim_ss_decode_node *last; -}; - - -internal P_JobDef(local_sim_job, _) +P_JobDef(SimJob, _) { + SharedUserState *g = &shared_user_state; #if 0 struct host_listen_address local_listen_addr = host_listen_address_from_local_name(Lit("LOCAL_SIM")); struct host_listen_address net_listen_addr = host_listen_address_from_net_port(12345); @@ -2012,11 +2148,14 @@ internal P_JobDef(local_sim_job, _) b32 is_master = 0; N_Host *host; - if (G.connect_address_str.len > 0) { + if (g->connect_address_str.len > 0) + { host = N_AllocHost(0); - P_Address addr = P_AddressFromString(G.connect_address_str); + P_Address addr = P_AddressFromString(g->connect_address_str); N_Connect(host, addr); - } else { + } + else + { host = N_AllocHost(12345); is_master = 1; } @@ -2074,7 +2213,8 @@ internal P_JobDef(local_sim_job, _) i64 real_dt_ns = 0; i64 step_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; f64 compute_timescale = 1.0; - while (!Atomic32Fetch(&G.shutdown)) { + while (!Atomic32Fetch(&g->shutdown)) + { TempArena scratch = BeginScratchNoConflict(); { __profn("Sim sleep"); @@ -2089,27 +2229,36 @@ internal P_JobDef(local_sim_job, _) N_EventList host_events = N_BeginUpdate(scratch.arena, host); /* Read net messages */ - struct sim_decode_queue queue = ZI; + DecodeQueue queue = ZI; { - for (N_Event *event = host_events.first; event; event = event->next) { + for (N_Event *event = host_events.first; event; event = event->next) + { N_ChannelId channel_id = event->channel_id; Client *client = sim_client_from_channel_id(store, channel_id); - switch (event->kind) { + switch (event->kind) + { case N_EventKind_ChannelOpened: { - if (!client->valid) { - if (is_master) { + if (!client->valid) + { + if (is_master) + { /* Create remote client */ client = sim_client_alloc(store); sim_client_set_channel_id(client, channel_id); - } else { + } + else + { /* Create master client */ - if (!master_client->valid) { + if (!master_client->valid) + { client = sim_client_alloc(store); sim_client_set_channel_id(client, channel_id); master_client = client; master_blended_client = sim_client_alloc(store); - } else { + } + else + { /* We already have a master client */ Assert(0); } @@ -2119,22 +2268,26 @@ internal P_JobDef(local_sim_job, _) case N_EventKind_Msg: { - if (client->valid) { + if (client->valid) + { BB_Buff msg_bb = BitbuffFromString(event->msg); BB_Reader msg_br = BB_ReaderFromBuff(&msg_bb); u64 ack = BB_ReadUV(&msg_br); u64 double_ack = BB_ReadUV(&msg_br); - if (ack > client->ack) { + if (ack > client->ack) + { client->ack = ack; } - if (double_ack > client->double_ack) { + if (double_ack > client->double_ack) + { client->double_ack = double_ack; } /* Read & queue incoming snapshots for decoding */ u64 tmp_encoded_len = BB_ReadUV(&msg_br); - while (tmp_encoded_len > 0) { + while (tmp_encoded_len > 0) + { u8 *tmp_encoded_bytes = BB_ReadBytesRaw(&msg_br, tmp_encoded_len); if (!tmp_encoded_bytes) break; @@ -2149,43 +2302,57 @@ internal P_JobDef(local_sim_job, _) if (!tmp_encoded.text) tmp_encoded.len = 0; Snapshot *base_ss = sim_snapshot_from_tick(client, base_tick); - if (base_ss->tick == base_tick) { - if (is_master) { + if (base_ss->tick == base_tick) + { + if (is_master) + { /* Queue incoming slave client snapshot for decoding */ //b32 should_decode = tick == client->highest_received_tick + 1 || client->highest_received_tick == 0; b32 should_decode = tick > client->highest_received_tick; - if (should_decode) { - struct sim_ss_decode_node *node = PushStruct(scratch.arena, struct sim_ss_decode_node); + if (should_decode) + { + DecodeQueueNode *node = PushStruct(scratch.arena, DecodeQueueNode); node->client = client; node->tick = tick; node->base_tick = base_tick; node->tmp_encoded = tmp_encoded; - if (queue.last) { + if (queue.last) + { queue.last->next = node; - } else { + } + else + { queue.first = node; } queue.last = node; - if (tick > client->highest_received_tick) { + if (tick > client->highest_received_tick) + { client->highest_received_tick = tick; } } - } else { + } + else + { /* Decode incoming master client snapshots for decoding (only the newest one) */ b32 should_decode = client == master_client && tick > client->highest_received_tick; - if (should_decode) { - struct sim_ss_decode_node *node = queue.first ? queue.first : PushStruct(scratch.arena, struct sim_ss_decode_node); + if (should_decode) + { + DecodeQueueNode *node = queue.first ? queue.first : PushStruct(scratch.arena, DecodeQueueNode); node->client = client; node->tick = tick; node->base_tick = base_tick; node->tmp_encoded = tmp_encoded; queue.first = node; queue.last = node; - if (tick > client->highest_received_tick) { + if (tick > client->highest_received_tick) + { client->highest_received_tick = tick; - if (average_master_receive_dt_ns == 0) { + if (average_master_receive_dt_ns == 0) + { average_master_receive_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; - } else { + } + else + { average_master_receive_dt_ns -= average_master_receive_dt_ns / 50; average_master_receive_dt_ns += (real_time_ns - last_tick_from_master_received_at_ns) / 50; } @@ -2193,7 +2360,9 @@ internal P_JobDef(local_sim_job, _) } } } - } else { + } + else + { /* We do not have the tick that the incoming delta is based from */ Assert(0); } @@ -2209,12 +2378,14 @@ internal P_JobDef(local_sim_job, _) } /* Decode incoming snapshots */ - for (struct sim_ss_decode_node *n = queue.first; n; n = n->next) { + for (DecodeQueueNode *n = queue.first; n; n = n->next) + { Client *client = n->client; u64 base_tick = n->base_tick; u64 tick = n->tick; Snapshot *base_ss = sim_snapshot_from_tick(client, base_tick); - if (base_ss->tick == base_tick) { + if (base_ss->tick == base_tick) + { BB_Buff bb = BitbuffFromString(n->tmp_encoded); BB_Reader br = BB_ReaderFromBuff(&bb); @@ -2223,53 +2394,68 @@ internal P_JobDef(local_sim_job, _) sim_snapshot_decode(&br, ss); /* Assume all incoming ents want to be sync srcs */ - for (u64 i = 0; i < ss->num_ents_reserved; ++i) { + for (u64 i = 0; i < ss->num_ents_reserved; ++i) + { Entity *ent = &ss->ents[i]; - if (ent->valid && sim_ent_has_prop(ent, SEPROP_SYNC_DST)) { + if (ent->valid && sim_ent_has_prop(ent, SEPROP_SYNC_DST)) + { sim_ent_disable_prop(ent, SEPROP_SYNC_DST); sim_ent_enable_prop(ent, SEPROP_SYNC_SRC); } } - } else { + } + else + { /* We do not have the tick that the incoming delta is based from. * This decode should never have been queued in the first place. */ Assert(0); } } - if (!is_master && !initialized_from_master) { - if (master_client->valid && master_client->last_tick > 0) { + if (!is_master && !initialized_from_master) + { + if (master_client->valid && master_client->last_tick > 0) + { initialized_from_master = 1; - } else { + } + else + { goto skip_step; } } - b32 should_step = !Atomic32Fetch(&G.user_paused); - if (Atomic32Fetch(&G.user_paused_steps) > 0) { + b32 should_step = !Atomic32Fetch(&g->user_paused); + if (Atomic32Fetch(&g->user_paused_steps) > 0) + { should_step = 1; - Atomic32FetchAdd(&G.user_paused_steps, -1); + Atomic32FetchAdd(&g->user_paused_steps, -1); } - if (!should_step) { + if (!should_step) + { goto skip_step; } /* Update networked clients */ u64 oldest_client_ack = 0; - for (u64 i = 0; i < store->num_clients_reserved; ++i) { + for (u64 i = 0; i < store->num_clients_reserved; ++i) + { Client *client = &store->clients[i]; - if (client->valid && client != local_client && client != publish_client && client != user_input_client && client != master_client) { + if (client->valid && client != local_client && client != publish_client && client != user_input_client && client != master_client) + { client->last_rtt_ns = N_GetChannelLastRttNs(host, client->channel_id); /* Release unneeded received snapshots */ /* TDOO: Cap how many client snapshots we're willing to retain */ - if (client->double_ack > 0) { + if (client->double_ack > 0) + { u64 keep_tick = MinU64(client->double_ack, local_client->last_tick); - if (keep_tick > 0) { + if (keep_tick > 0) + { sim_snapshot_release_ticks_in_range(client, 0, keep_tick - 1); } } - if (client->ack < oldest_client_ack || oldest_client_ack == 0) { + if (client->ack < oldest_client_ack || oldest_client_ack == 0) + { oldest_client_ack = client->ack; } } @@ -2278,10 +2464,12 @@ internal P_JobDef(local_sim_job, _) /* Release unneeded published snapshots */ { u64 keep_tick = oldest_client_ack; - if (keep_tick == 0 && publish_client->last_tick > 0) { + if (keep_tick == 0 && publish_client->last_tick > 0) + { keep_tick = publish_client->last_tick - 1; } - if (keep_tick > 0) { + if (keep_tick > 0) + { --keep_tick; } sim_snapshot_release_ticks_in_range(publish_client, 0, keep_tick); @@ -2290,7 +2478,8 @@ internal P_JobDef(local_sim_job, _) /* Release old local snapshots */ { u64 keep_range = 50; - if (local_client->last_tick > keep_range) { + if (local_client->last_tick > keep_range) + { u64 keep_tick = local_client->last_tick - keep_range; sim_snapshot_release_ticks_in_range(local_client, 0, keep_tick); } @@ -2307,7 +2496,8 @@ internal P_JobDef(local_sim_job, _) - if (is_master) { + if (is_master) + { /* Step master */ u64 prev_tick = local_client->last_tick; u64 next_tick = prev_tick + 1; @@ -2320,9 +2510,11 @@ internal P_JobDef(local_sim_job, _) ctx.publish_client = publish_client; Snapshot *prev_world = sim_snapshot_from_tick(local_client, prev_tick); ctx.world = sim_snapshot_alloc(local_client, prev_world, next_tick); - generate_user_input_cmds(user_input_client, next_tick); + GenerateuserInputCmds(user_input_client, next_tick); sim_step(&ctx); - } else if (master_client->valid) { + } + else if (master_client->valid) + { /* Step client */ /* TODO: Eventually determine master tick based on a delay to allow for jitter and also interpolation so we can lower snapshot publish frequency */ @@ -2334,7 +2526,8 @@ internal P_JobDef(local_sim_job, _) /* How along are we between master sim ticks (0 = start of tick, 1 = end of tick) */ f64 tick_progress = 0; i64 next_tick_expected_ns = last_tick_from_master_received_at_ns + average_master_receive_dt_ns; - if (next_tick_expected_ns > last_tick_from_master_received_at_ns) { + if (next_tick_expected_ns > last_tick_from_master_received_at_ns) + { tick_progress = (f64)(real_time_ns - last_tick_from_master_received_at_ns) / (f64)(next_tick_expected_ns - last_tick_from_master_received_at_ns); } @@ -2344,12 +2537,14 @@ internal P_JobDef(local_sim_job, _) /* Determine blend time */ i64 master_blend_time_target_ns = master_sim_predicted_time_ns - (SIM_CLIENT_INTERP_RATIO * average_master_receive_dt_ns); - if (average_master_receive_dt_ns > 0) { + if (average_master_receive_dt_ns > 0) + { master_blend_time_ns += real_dt_ns; } i64 blend_time_target_diff_ns = master_blend_time_target_ns - master_blend_time_ns; - if (blend_time_target_diff_ns > NsFromSeconds(0.100) || blend_time_target_diff_ns < NsFromSeconds(-0.100)) { + if (blend_time_target_diff_ns > NsFromSeconds(0.100) || blend_time_target_diff_ns < NsFromSeconds(-0.100)) + { /* Snap blend time if it gets too far from target blend time */ master_blend_time_ns = master_blend_time_target_ns; } @@ -2361,13 +2556,16 @@ internal P_JobDef(local_sim_job, _) Snapshot *right_snapshot = newest_snapshot; { Snapshot *ss = sim_snapshot_from_tick(master_client, master_client->first_tick); - while (ss->valid) { + while (ss->valid) + { u64 next_tick = ss->next_tick; i64 ss_time_ns = ss->sim_time_ns; - if (ss_time_ns < master_blend_time_ns && ss_time_ns > left_snapshot->sim_time_ns) { + if (ss_time_ns < master_blend_time_ns && ss_time_ns > left_snapshot->sim_time_ns) + { left_snapshot = ss; } - if (ss_time_ns > master_blend_time_ns && ss_time_ns < right_snapshot->sim_time_ns) { + if (ss_time_ns > master_blend_time_ns && ss_time_ns < right_snapshot->sim_time_ns) + { right_snapshot = ss; } ss = sim_snapshot_from_tick(master_client, next_tick); @@ -2376,33 +2574,43 @@ internal P_JobDef(local_sim_job, _) /* Create world from blended master snapshots */ f64 blend = 0; - if (left_snapshot->valid && right_snapshot->valid && right_snapshot->tick > left_snapshot->tick) { + if (left_snapshot->valid && right_snapshot->valid && right_snapshot->tick > left_snapshot->tick) + { blend = (f64)(master_blend_tick - left_snapshot->tick) / (f64)(right_snapshot->tick - left_snapshot->tick); f64 epsilon = 0.001; - if (blend < epsilon) { + if (blend < epsilon) + { master_ss_is_blended = 0; master_ss = left_snapshot; - } else if (blend > 1 - epsilon) { + } + else if (blend > 1 - epsilon) + { master_ss_is_blended = 0; master_ss = right_snapshot; - } else { + } + else + { master_ss_is_blended = 1; master_ss = sim_snapshot_alloc_from_lerp(master_blended_client, left_snapshot, right_snapshot, blend); /* Release unneeded blended master snapshots */ - if (master_ss->tick > 0) { + if (master_ss->tick > 0) + { sim_snapshot_release_ticks_in_range(master_blended_client, 0, master_ss->tick - 1); sim_snapshot_release_ticks_in_range(master_blended_client, master_ss->tick + 1, U64Max); } } - } else { + } + else + { master_ss_is_blended = 0; master_ss = left_snapshot->valid ? left_snapshot : right_snapshot; } /* Release unneeded master snapshots */ u64 keep_master_tick = MinU64(left_snapshot->tick, master_client->double_ack); - if (keep_master_tick > 0) { + if (keep_master_tick > 0) + { sim_snapshot_release_ticks_in_range(master_client, 0, keep_master_tick - 1); } @@ -2425,7 +2633,8 @@ internal P_JobDef(local_sim_job, _) #endif } - if (master_ss->valid) { + if (master_ss->valid) + { Entity *master_player = sim_ent_find_first_match_one(master_ss, SEPROP_PLAYER_IS_MASTER); /* Update ent id from master */ @@ -2436,7 +2645,8 @@ internal P_JobDef(local_sim_job, _) /* Check for misprediction */ u64 mispredicted_tick = 0; - if (!master_ss_is_blended) { + if (!master_ss_is_blended) + { /* TODO: Actually check for misprediction rather than triggering mispredict any time a new master snapshot is received */ mispredicted_tick = master_ss->tick; } @@ -2444,9 +2654,11 @@ internal P_JobDef(local_sim_job, _) u64 step_base_tick = local_client->last_tick; u64 step_end_tick = step_base_tick + 1; - if (mispredicted_tick > 0) { + if (mispredicted_tick > 0) + { step_base_tick = mispredicted_tick; - if (step_end_tick <= step_base_tick) { + if (step_end_tick <= step_base_tick) + { step_end_tick = step_base_tick + 1; } } @@ -2463,20 +2675,27 @@ internal P_JobDef(local_sim_job, _) */ { i64 cmds_ahead_on_master = (i64)master_client->ack - (i64)master_client->last_tick; - if (cmds_ahead_on_master < -3 || cmds_ahead_on_master > 10) { + if (cmds_ahead_on_master < -3 || cmds_ahead_on_master > 10) + { /* Cmds are too far from master time, snap step end tick */ i64 rtt_ns = master_client->last_rtt_ns; f64 rtt_tick_ratio = (f64)(rtt_ns + (step_dt_ns - 1)) / (f64)step_dt_ns; i64 num_predict_ticks = RoundF64ToI64(rtt_tick_ratio) + 5; step_end_tick = master_client->last_tick + num_predict_ticks; compute_timescale = 1.1; - } else if (cmds_ahead_on_master > 2) { + } + else if (cmds_ahead_on_master > 2) + { /* Slow down simulation to dial back how far ahead we are predicting and bring local sim time closer to master sim time */ compute_timescale = 1.1; - } else if (cmds_ahead_on_master < 1) { + } + else if (cmds_ahead_on_master < 1) + { /* Speed up simulation rate predict more ticks and give master more inputs to work with */ compute_timescale = 0.9; - } else { + } + else + { /* Server's cmd buffer is in a healthy range */ compute_timescale = 1; } @@ -2484,10 +2703,14 @@ internal P_JobDef(local_sim_job, _) /* Sync master with local base tick */ Snapshot *base_ss = sim_snapshot_from_tick(local_client, step_base_tick); - if (mispredicted_tick) { - if (base_ss->valid) { + if (mispredicted_tick) + { + if (base_ss->valid) + { sim_snapshot_sync_ents(base_ss, master_ss, master_player->id, 0); - } else { + } + else + { base_ss = sim_snapshot_alloc(local_client, master_ss, step_base_tick); } } @@ -2496,7 +2719,7 @@ internal P_JobDef(local_sim_job, _) sim_snapshot_release_ticks_in_range(local_client, step_base_tick + 1, U64Max); /* Step */ - generate_user_input_cmds(user_input_client, step_end_tick); + GenerateuserInputCmds(user_input_client, step_end_tick); { SimStepCtx ctx = ZI; ctx.is_master = is_master; @@ -2508,9 +2731,11 @@ internal P_JobDef(local_sim_job, _) u64 step_tick = step_base_tick + 1; Snapshot *prev_ss = base_ss; - while (step_tick <= step_end_tick) { + while (step_tick <= step_end_tick) + { ctx.world = sim_snapshot_alloc(local_client, prev_ss, step_tick); - if (!mispredicted_tick && step_tick == step_end_tick) { + if (!mispredicted_tick && step_tick == step_end_tick) + { sim_snapshot_sync_ents(ctx.world, master_ss, master_player->id, SIM_SYNC_FLAG_NOSYNC_PREDICTABLES); } sim_step(&ctx); @@ -2522,9 +2747,11 @@ internal P_JobDef(local_sim_job, _) } /* Publish snapshot to remote clients */ - for (u64 i = 0; i < store->num_clients_reserved; ++i) { + for (u64 i = 0; i < store->num_clients_reserved; ++i) + { Client *client = &store->clients[i]; - if (client->valid && client != user_input_client && client != local_client && client != publish_client) { + if (client->valid && client != user_input_client && client != local_client && client != publish_client) + { BB_Writer msg_bw = BB_WriterFromBuff(&msg_writer_bb); BB_WriteUV(&msg_bw, client->highest_received_tick); /* ack */ @@ -2532,15 +2759,19 @@ internal P_JobDef(local_sim_job, _) Snapshot *base_ss = sim_snapshot_from_tick(publish_client, client->ack); Snapshot *publish_ss; - if (client == master_client) { + if (client == master_client) + { /* If sending to master, start sending all snapshots since last ack */ publish_ss = sim_snapshot_from_closest_tick_gte(publish_client, base_ss->tick + 1); - } else { + } + else + { /* If sending to slave, only send latest snapshot */ publish_ss = sim_snapshot_from_tick(publish_client, publish_client->last_tick); } - while (publish_ss->valid) { + while (publish_ss->valid) + { BB_Writer snapshot_bw = BB_WriterFromBuff(&snapshot_writer_bb); String tmp_snapshot_encoded = ZI; { @@ -2566,23 +2797,25 @@ internal P_JobDef(local_sim_job, _) /* Copy local snapshot to user client */ { Snapshot *local_ss = sim_snapshot_from_tick(local_client, local_client->last_tick); - if (local_ss->valid) { + if (local_ss->valid) + { /* TODO: Double buffer */ - P_Lock lock = P_LockE(&G.local_to_user_client_mutex); - sim_snapshot_alloc(G.local_to_user_client, local_ss, local_ss->tick); + P_Lock lock = P_LockE(&g->local_to_user_client_mutex); + sim_snapshot_alloc(g->local_to_user_client, local_ss, local_ss->tick); i64 publish_ns = P_TimeNs(); - if (last_publish_to_user_ns == 0) { - last_publish_to_user_ns = publish_ns - G.average_local_to_user_snapshot_publish_dt_ns; + if (last_publish_to_user_ns == 0) + { + last_publish_to_user_ns = publish_ns - g->average_local_to_user_snapshot_publish_dt_ns; } - G.local_to_user_client_publish_dt_ns = publish_ns - last_publish_to_user_ns; - G.local_to_user_client_publish_time_ns = publish_ns; + g->local_to_user_client_publish_dt_ns = publish_ns - last_publish_to_user_ns; + g->local_to_user_client_publish_time_ns = publish_ns; last_publish_to_user_ns = publish_ns; - sim_snapshot_release_ticks_in_range(G.local_to_user_client, 0, local_ss->tick - 1); + sim_snapshot_release_ticks_in_range(g->local_to_user_client, 0, local_ss->tick - 1); P_Unlock(&lock); } } -skip_step: + skip_step: /* Send host messages */ N_EndUpdate(host); diff --git a/src/user/user_core.h b/src/user/user_core.h index f992cdab..df5a7e53 100644 --- a/src/user/user_core.h +++ b/src/user/user_core.h @@ -1,153 +1,147 @@ -enum user_bind_kind { - USER_BIND_KIND_NONE, +//////////////////////////////// +//~ Binds - USER_BIND_KIND_MOVE_UP, - USER_BIND_KIND_MOVE_DOWN, - USER_BIND_KIND_MOVE_LEFT, - USER_BIND_KIND_MOVE_RIGHT, - USER_BIND_KIND_WALK, - USER_BIND_KIND_FIRE, - USER_BIND_KIND_FIRE_ALT, +//- Bind kinds +typedef i32 BindKind; enum +{ + BindKind_None, - /* Testing */ + BindKind_MoveUp, + BindKind_MoveDown, + BindKind_MoveLeft, + BindKind_MoveRight, + BindKind_Walk, + BindKind_Fire, + BindKind_AltFire, - USER_BIND_KIND_TILE_TEST, - - USER_BIND_KIND_DEBUG_CLEAR, - USER_BIND_KIND_DEBUG_SPAWN1, - USER_BIND_KIND_DEBUG_SPAWN2, - USER_BIND_KIND_DEBUG_SPAWN3, - USER_BIND_KIND_DEBUG_SPAWN4, - USER_BIND_KIND_DEBUG_WALLS, - USER_BIND_KIND_DEBUG_FOLLOW, - USER_BIND_KIND_DEBUG_DRAW, - USER_BIND_KIND_DEBUG_CONSOLE, - USER_BIND_KIND_DEBUG_CAMERA, - USER_BIND_KIND_DEBUG_PAUSE, - USER_BIND_KIND_DEBUG_STEP, - USER_BIND_KIND_DEBUG_DRAG, - USER_BIND_KIND_DEBUG_DELETE, - USER_BIND_KIND_DEBUG_TELEPORT, - USER_BIND_KIND_DEBUG_EXPLODE, - USER_BIND_KIND_DEBUG_TOGGLE_TOPMOST, - USER_BIND_KIND_FULLSCREEN_MOD, - USER_BIND_KIND_FULLSCREEN, - USER_BIND_KIND_ZOOM_IN, - USER_BIND_KIND_ZOOM_OUT, - USER_BIND_KIND_PAN, + BindKind_TestTile, + BindKind_DebugClear, + BindKind_DebugSpawn1, + BindKind_DebugSpawn2, + BindKind_DebugSpawn3, + BindKind_DebugSpawn4, + BindKind_DebugWalls, + BindKind_DebugFollow, + BindKind_DebugDraw, + BindKind_DebugConsole, + BindKind_DebugCamera, + BindKind_DebugPause, + BindKind_DebugStep, + BindKind_DebugDrag, + BindKind_DebugDelete, + BindKind_DebugTeleport, + BindKind_DebugExplode, + BindKind_DebugToggleTopmost, + BindKind_FullscreenMod, + BindKind_Fullscreen, + BindKind_ZoomIn, + BindKind_ZoomOut, + BindKind_Pan, #if RtcIsEnabled /* Debug */ - USER_BIND_KIND_RESET_DEBUG_STEPS, - USER_BIND_KIND_INCR_DEBUG_STEPS, - USER_BIND_KIND_DECR_DEBUG_STEPS, + BindKind_ResetDebugSteps, + BindKind_IncrementDebugSteps, + BindKind_DecrementDebugSteps, #endif - USER_BIND_KIND_COUNT + BindKind_Count }; -struct user_startup_receipt { i32 _; }; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct bind_state +//- Test bindings + +/* TODO: Remove this */ +Global Readonly BindKind g_binds[P_Btn_Count] = { + [P_Btn_W] = BindKind_MoveUp, + [P_Btn_S] = BindKind_MoveDown, + [P_Btn_A] = BindKind_MoveLeft, + [P_Btn_D] = BindKind_MoveRight, + [P_Btn_M1] = BindKind_Fire, + [P_Btn_M2] = BindKind_AltFire, +#if 0 + [P_Btn_Alt] = BindKind_Walk, +#endif + + /* Testing */ + [P_Btn_Z] = BindKind_TestTile, + [P_Btn_M5] = BindKind_DebugDrag, + [P_Btn_M4] = BindKind_DebugDelete, + [P_Btn_F] = BindKind_DebugExplode, + [P_Btn_T] = BindKind_DebugTeleport, + [P_Btn_C] = BindKind_DebugClear, + [P_Btn_1] = BindKind_DebugSpawn1, + [P_Btn_2] = BindKind_DebugSpawn2, + [P_Btn_3] = BindKind_DebugSpawn3, + [P_Btn_4] = BindKind_DebugSpawn4, + [P_Btn_G] = BindKind_DebugWalls, + [P_Btn_N] = BindKind_DebugStep, + [P_Btn_Q] = BindKind_DebugFollow, + [P_Btn_F1] = BindKind_DebugPause, + [P_Btn_F2] = BindKind_DebugCamera, + [P_Btn_F3] = BindKind_DebugDraw, + [P_Btn_F4] = BindKind_DebugToggleTopmost, + [P_Btn_GraveAccent] = BindKind_DebugConsole, + [P_Btn_Alt] = BindKind_FullscreenMod, + [P_Btn_Enter] = BindKind_Fullscreen, + [P_Btn_MWheelUp] = BindKind_ZoomIn, + [P_Btn_MWheelDown] = BindKind_ZoomOut, + [P_Btn_M3] = BindKind_Pan, + +#if RtcIsEnabled + [P_Btn_ForwardSlash] = BindKind_ResetDebugSteps, + [P_Btn_Comma] = BindKind_DecrementDebugSteps, + [P_Btn_Period] = BindKind_IncrementDebugSteps +#endif +}; + +//////////////////////////////// +//~ Stats + +Struct(SecondsStat) +{ + u64 last_second_start; + u64 last_second_end; + u64 last_second; +}; + +//////////////////////////////// +//~ Console log + +Struct(ConsoleLog) +{ + String msg; + i32 level; + i32 color_index; + P_DateTime datetime; + i64 time_ns; + Rect bounds; + ConsoleLog *prev; + ConsoleLog *next; +}; + +//////////////////////////////// +//~ Sim decode queue + +Struct(DecodeQueueNode) +{ + Client *client; + u64 tick; + u64 base_tick; + String tmp_encoded; + DecodeQueueNode *next; +}; + +Struct(DecodeQueue) +{ + DecodeQueueNode *first; + DecodeQueueNode *last; +}; + +//////////////////////////////// +//~ Shared state + +Struct(BindState) { b32 is_held; /* Is this bind held down this frame */ u32 num_presses; /* How many times was this bind's pressed since last frame */ @@ -156,62 +150,7 @@ struct bind_state u32 num_releases; /* How many times was this bind released since last frame */ }; -struct second_stat -{ - u64 last_second_start; - u64 last_second_end; - u64 last_second; -}; - -struct console_log -{ - String msg; - i32 level; - i32 color_index; - P_DateTime datetime; - i64 time_ns; - Rect bounds; - struct console_log *prev; - struct console_log *next; -}; - -struct sim_ss_decode_node -{ - Client *client; - u64 tick; - u64 base_tick; - String tmp_encoded; - struct sim_ss_decode_node *next; -}; - -struct sim_decode_queue -{ - struct sim_ss_decode_node *first; - struct sim_ss_decode_node *last; -}; - - - - - - - - - - - - - - - - - - - - - - -Global struct +Struct(SharedUserState) { Atomic32 shutdown; P_Counter shutdown_job_counters; @@ -230,13 +169,13 @@ Global struct /* Usage stats */ i64 last_second_reset_ns; - struct second_stat net_bytes_read; - struct second_stat net_bytes_sent; + SecondsStat net_bytes_read; + SecondsStat net_bytes_sent; /* Gpu resources */ GPU_RenderSig *render_sig; - struct bind_state bind_states[USER_BIND_KIND_COUNT]; + BindState bind_states[BindKind_Count]; /* Debug camera */ EntityId debug_following; @@ -248,8 +187,8 @@ Global struct /* Debug console */ P_Mutex console_logs_mutex; Arena *console_logs_arena; - struct console_log *first_console_log; - struct console_log *last_console_log; + ConsoleLog *first_console_log; + ConsoleLog *last_console_log; i32 console_log_color_indices[P_LogLevel_Count]; f32 console_logs_height; b32 debug_console; @@ -306,136 +245,58 @@ Global struct Vec2 world_cursor; Vec2 focus_send; -} G = ZI, DebugAlias(G, G_user); - -/* ========================== * - * Bind state - * ========================== */ - - /* TODO: Remove this */ - -Global Readonly enum user_bind_kind g_binds[P_Btn_Count] = { - [P_Btn_W] = USER_BIND_KIND_MOVE_UP, - [P_Btn_S] = USER_BIND_KIND_MOVE_DOWN, - [P_Btn_A] = USER_BIND_KIND_MOVE_LEFT, - [P_Btn_D] = USER_BIND_KIND_MOVE_RIGHT, - //[P_Btn_Alt] = USER_BIND_KIND_WALK, - [P_Btn_M1] = USER_BIND_KIND_FIRE, - [P_Btn_M2] = USER_BIND_KIND_FIRE_ALT, - - /* Testing */ - - [P_Btn_Z] = USER_BIND_KIND_TILE_TEST, - - [P_Btn_M5] = USER_BIND_KIND_DEBUG_DRAG, - [P_Btn_M4] = USER_BIND_KIND_DEBUG_DELETE, - [P_Btn_F] = USER_BIND_KIND_DEBUG_EXPLODE, - [P_Btn_T] = USER_BIND_KIND_DEBUG_TELEPORT, - [P_Btn_C] = USER_BIND_KIND_DEBUG_CLEAR, - [P_Btn_1] = USER_BIND_KIND_DEBUG_SPAWN1, - [P_Btn_2] = USER_BIND_KIND_DEBUG_SPAWN2, - [P_Btn_3] = USER_BIND_KIND_DEBUG_SPAWN3, - [P_Btn_4] = USER_BIND_KIND_DEBUG_SPAWN4, - [P_Btn_G] = USER_BIND_KIND_DEBUG_WALLS, - [P_Btn_N] = USER_BIND_KIND_DEBUG_STEP, - [P_Btn_Q] = USER_BIND_KIND_DEBUG_FOLLOW, - [P_Btn_F1] = USER_BIND_KIND_DEBUG_PAUSE, - [P_Btn_F2] = USER_BIND_KIND_DEBUG_CAMERA, - [P_Btn_F3] = USER_BIND_KIND_DEBUG_DRAW, - [P_Btn_F4] = USER_BIND_KIND_DEBUG_TOGGLE_TOPMOST, - [P_Btn_GraveAccent] = USER_BIND_KIND_DEBUG_CONSOLE, - [P_Btn_Alt] = USER_BIND_KIND_FULLSCREEN_MOD, - [P_Btn_Enter] = USER_BIND_KIND_FULLSCREEN, - [P_Btn_MWheelUp] = USER_BIND_KIND_ZOOM_IN, - [P_Btn_MWheelDown] = USER_BIND_KIND_ZOOM_OUT, - [P_Btn_M3] = USER_BIND_KIND_PAN, - -#if RtcIsEnabled - /* Debug */ - [P_Btn_ForwardSlash] = USER_BIND_KIND_RESET_DEBUG_STEPS, - [P_Btn_Comma] = USER_BIND_KIND_DECR_DEBUG_STEPS, - [P_Btn_Period] = USER_BIND_KIND_INCR_DEBUG_STEPS -#endif }; -/* ========================== * - * Startup - * ========================== */ +extern SharedUserState shared_user_state; -struct user_startup_receipt user_startup(F_StartupReceipt *font_sr, - S_StartupReceipt *sprite_sr, - D_StartupReceipt *draw_sr, - AC_StartupReceipt *asset_cache_sr, - SND_StartupReceipt *sound_sr, - MIX_StartupReceipt *mixer_sr, - SimStartupReceipt *sim_sr, - String connect_address_str) +//////////////////////////////// +//~ Startup -internal P_ExitFuncDef(user_shutdown) +Struct(UserStartupReceipt) { i32 _; }; +UserStartupReceipt StartupUser(F_StartupReceipt *font_sr, + S_StartupReceipt *sprite_sr, + D_StartupReceipt *draw_sr, + AC_StartupReceipt *asset_cache_sr, + SND_StartupReceipt *sound_sr, + MIX_StartupReceipt *mixer_sr, + SimStartupReceipt *sim_sr, + String connect_address_str); -/* ========================== * - * Debug draw - * ========================== */ +//////////////////////////////// +//~ Shutdown - /* TODO: remove this (testing) */ -internal void debug_draw_xform(Xform xf, u32 color_x, u32 color_y) +P_ExitFuncDef(ShutdownUser); -internal void debug_draw_movement(Entity *ent) +//////////////////////////////// +//~ Debug draw operations -internal String get_ent_debug_text(Arena *arena, Entity *ent) +void DebugDrawXform(Xform xf, u32 color_x, u32 color_y); +void DebugDrawMovement(Entity *ent); +String DebugStringFromEntity(Arena *arena, Entity *ent); -/* ========================== * - * Debug console - * ========================== */ +//////////////////////////////// +//~ Console draw operations -internal P_LogEventCallbackFuncDef(debug_console_log_callback, log) +P_LogEventCallbackFuncDef(ConsoleLogCallback, log); +void DebugDrawConsole(i32 level, b32 minimized); -internal void draw_debug_console(i32 level, b32 minimized) +//////////////////////////////// +//~ Entity sortign -/* ========================== * - * Sort entities - * ========================== */ +MergesortCompareFuncDef(EntitySortCmp, arg_a, arg_b, _); -internal MergesortCompareFuncDef(ent_draw_order_cmp, arg_a, arg_b, _) +//////////////////////////////// +//~ User update -/* ========================== * - * Update - * ========================== */ +void UpdateUser(P_Window *window); +P_JobDef(UpdateUserJob, _); -internal void user_update(P_Window *window) +//////////////////////////////// +//~ User input cmds -internal P_JobDef(user_update_job, _) +void GenerateuserInputCmds(Client *user_input_client, u64 tick); +//////////////////////////////// +//~ Sim update - - -/* ========================== * - * Local sim thread - * ========================== */ - - - - - - - - -internal void generate_user_input_cmds(Client *user_input_client, u64 tick) - - - - - - - - - - - - - - - - - -internal P_JobDef(local_sim_job, _) +P_JobDef(SimJob, _);