world blend wip
This commit is contained in:
parent
5ca777f646
commit
55311f7b98
@ -38,7 +38,7 @@ i64 SaturateI64(i64 v) { return v < 0 ? 0 : v > 1 ? 1 : v; }
|
||||
f64 SaturateF64(f64 v) { return v < 0 ? 0 : v > 1 ? 1 : v; }
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Float ops
|
||||
//~ Numeric ops
|
||||
|
||||
//- Sign
|
||||
|
||||
|
||||
@ -234,7 +234,7 @@ i64 SaturateI64(i64 v);
|
||||
f64 SaturateF64(f64 v);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Float ops
|
||||
//~ Numeric ops
|
||||
|
||||
//- Round
|
||||
#define RoundF32 roundf
|
||||
@ -257,6 +257,8 @@ f64 SaturateF64(f64 v);
|
||||
#define ModF64 fmod
|
||||
|
||||
//- Abs
|
||||
#define AbsI32 abs
|
||||
#define AbsI64 llabs
|
||||
#define AbsF32 fabsf
|
||||
#define AbsF64 fabs
|
||||
|
||||
|
||||
@ -89,39 +89,39 @@ u32 countof(T arr[N])
|
||||
#define ClampF64 (f64)clamp
|
||||
|
||||
//- Round
|
||||
#define RoundF32 (f32)round
|
||||
#define RoundF64 (f64)round
|
||||
#define RoundF32(a) round((f32)(a))
|
||||
#define RoundF64(a) round((f64)(a))
|
||||
|
||||
//- Floor
|
||||
#define FloorF32 (f32)floor
|
||||
#define FloorF64 (f64)floor
|
||||
#define FloorF32(a) floor((f32)(a))
|
||||
#define FloorF64(a) floor((f64)(a))
|
||||
|
||||
//- Ceil
|
||||
#define CeilF32 (f32)ceil
|
||||
#define CeilF64 (f64)ceil
|
||||
#define CeilF32(a) ceil((f32)(a))
|
||||
#define CeilF64(a) ceil((f64)(a))
|
||||
|
||||
//- Trunc
|
||||
#define TruncF32 (f32)trunc
|
||||
#define TruncF64 (f64)trunc
|
||||
#define TruncF32(a) trunc((f32)(a))
|
||||
#define TruncF64(a) trunc((f64)(a))
|
||||
|
||||
//- Mod
|
||||
#define ModF32 (f32)fmod
|
||||
#define ModF64 (f64)fmod
|
||||
#define ModF32(a, b) fmod((f32)(a), (f32)(b))
|
||||
#define ModF64(a, b) fmod((f64)(a), (f64)(b))
|
||||
|
||||
//- Abs
|
||||
#define AbsF32 (f32)abs
|
||||
#define AbsF64 (f64)abs
|
||||
#define AbsF32(a) abs((f32)(a))
|
||||
#define AbsF64(a) abs((f64)(a))
|
||||
|
||||
//- Sign
|
||||
#define SignF32 (f32)sign
|
||||
#define SignF64 (f64)sign
|
||||
#define SignF32(a) sign((f32)(a))
|
||||
#define SignF64(a) sign((f64)(a))
|
||||
|
||||
//- Smoothstep
|
||||
#define SmoothstepF32 (f32)smoothstep
|
||||
#define SmoothstepF64 (f64)smoothstep
|
||||
#define SmoothstepF32(a, b, t) smoothstep((f32)(a), (f32)(b), (f32)(t))
|
||||
#define SmoothstepF64(a, b, t) smoothstep((f64)(a), (f64)(b), (f64)(t))
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Float ops
|
||||
//~ Numeric ops
|
||||
|
||||
//- Normalize
|
||||
|
||||
|
||||
10
src/config.h
10
src/config.h
@ -1,20 +1,14 @@
|
||||
// Project-wide configurable constants
|
||||
|
||||
// How many ticks back in time should the user thread blend between?
|
||||
// <Delay> = <USER_INTERP_RATIO> * <Tick interval>
|
||||
// E.g: At 1.5, the user thread will render 75ms back in time if the sim runs at 50tps
|
||||
#define USER_INTERP_RATIO 1.2
|
||||
#define USER_INTERP_ENABLED 1
|
||||
|
||||
#define SIM_MAX_PING 5.0
|
||||
|
||||
#define SIM_PHYSICS_SUBSTEPS 4
|
||||
|
||||
#define SIM_TICKS_PER_SECOND 64
|
||||
#define SIM_TICKS_PER_SECOND 32
|
||||
#define SIM_TICK_INTERVAL_NS (NsFromSeconds(1) / SIM_TICKS_PER_SECOND)
|
||||
// Like USER_INTERP_RATIO, but applies to snapshots received by the local sim from the
|
||||
// master sim (how far back in time should the client render the server's state)
|
||||
#define SIM_CLIENT_INTERP_RATIO 2.0
|
||||
// #define SIM_CLIENT_INTERP_RATIO 2.0
|
||||
|
||||
|
||||
#define GPU_NAMES IsRtcEnabled
|
||||
|
||||
@ -363,7 +363,6 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
P_World *sim_world = P_AcquireWorld();
|
||||
P_World *predict_world = P_AcquireWorld();
|
||||
P_World *local_world = P_AcquireWorld();
|
||||
|
||||
i64 local_controls_cap = NextPow2U64(SIM_MAX_PING * SIM_TICKS_PER_SECOND);
|
||||
P_Control *local_controls = PushStructs(perm, P_Control, local_controls_cap);
|
||||
@ -524,6 +523,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
frame->quads_arena = AcquireArena(Gibi(64));
|
||||
frame->dverts_arena = AcquireArena(Gibi(64));
|
||||
frame->dvert_idxs_arena = AcquireArena(Gibi(64));
|
||||
frame->local_world = P_AcquireWorld();
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
@ -577,11 +577,13 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
Arena *old_quads_arena = frame->quads_arena;
|
||||
Arena *old_dverts_arena = frame->dverts_arena;
|
||||
Arena *old_dvert_idxs_arena = frame->dvert_idxs_arena;
|
||||
P_World *old_local_world = frame->local_world;
|
||||
ZeroStruct(frame);
|
||||
frame->arena = old_arena;
|
||||
frame->quads_arena = old_quads_arena;
|
||||
frame->dverts_arena = old_dverts_arena;
|
||||
frame->dvert_idxs_arena = old_dvert_idxs_arena;
|
||||
frame->local_world = old_local_world;
|
||||
}
|
||||
frame->cl = G_PrepareCommandList(G_QueueKind_Direct);
|
||||
ResetArena(frame->arena);
|
||||
@ -1006,8 +1008,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
Vec2 look_pos = frame->look;
|
||||
look_pos = RotateVec2(look_pos, InvertRot(rot));
|
||||
|
||||
P_Ent *player = P_EntFromKey(local_world->last_frame, V.player_key);
|
||||
P_Ent *guy = P_EntFromKey(local_world->last_frame, player->guy);
|
||||
P_Ent *player = P_EntFromKey(frame->local_world->last_frame, V.player_key);
|
||||
P_Ent *guy = P_EntFromKey(frame->local_world->last_frame, player->guy);
|
||||
Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid;
|
||||
Vec2 screen_center = MulVec2(frame->screen_dims, 0.5);
|
||||
target_camera_pos = guy_center;
|
||||
@ -1363,12 +1365,13 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
// If the server reports it has too many of our commands buffered up, we
|
||||
// slow the rate of prediction.
|
||||
|
||||
f64 dilation_factor = 0;
|
||||
{
|
||||
// How many buffered commands of ours we'd like the server to have
|
||||
i64 target_buffered_controls_count = 1;
|
||||
f64 rtt_bias_factor = 10.0;
|
||||
|
||||
f64 dilation_factor = SmoothstepF64(
|
||||
dilation_factor = SmoothstepF64(
|
||||
-(SIM_TICKS_PER_SECOND * smoothed_rtt * rtt_bias_factor),
|
||||
(SIM_TICKS_PER_SECOND * smoothed_rtt * rtt_bias_factor),
|
||||
target_buffered_controls_count - remote_buffered_controls_count
|
||||
@ -1630,7 +1633,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
predict_world->seed = sim_world->seed;
|
||||
|
||||
P_ClearFrames(predict_world, I64Min, first_predict_tick - 1);
|
||||
P_ClearFrames(predict_world, I64Min, MinI64(first_predict_tick - 1, prev_frame->blend_from_tick - 1));
|
||||
predict_frame = P_PushFrame(predict_world, P_FrameFromTick(sim_world, first_predict_tick), first_predict_tick);
|
||||
|
||||
for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick)
|
||||
@ -1833,21 +1836,92 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
//////////////////////////////
|
||||
//- Update local world
|
||||
|
||||
// TODO: Remove this
|
||||
P_Frame *prev_local_frame = prev_frame->local_world->last_frame;
|
||||
P_Frame *local_frame = &P_NilFrame;
|
||||
{
|
||||
if (local_world->tiles_hash != predict_world->tiles_hash)
|
||||
if (frame->local_world->tiles_hash != predict_world->tiles_hash)
|
||||
{
|
||||
local_world->tiles_hash = predict_world->tiles_hash;
|
||||
CopyStructs(local_world->tiles, predict_world->tiles, P_TilesCount);
|
||||
frame->local_world->tiles_hash = predict_world->tiles_hash;
|
||||
CopyStructs(frame->local_world->tiles, predict_world->tiles, P_TilesCount);
|
||||
frame->tiles_dirty = 1;
|
||||
}
|
||||
local_world->seed = predict_world->seed;
|
||||
frame->local_world->seed = predict_world->seed;
|
||||
P_ClearFrames(frame->local_world, I64Min, I64Max);
|
||||
|
||||
P_ClearFrames(local_world, I64Min, local_world->last_frame->tick - 1);
|
||||
local_frame = P_PushFrame(local_world, predict_world->last_frame, predict_world->last_frame->tick);
|
||||
i64 target_blend_dt_ns = frame->dt_ns + frame->dt_ns * dilation_factor;
|
||||
i64 blend_dt_ns = frame->dt_ns;
|
||||
|
||||
// LogDebugF("First frame: %F, Last frame: %F", FmtSint(local_world->first_frame->tick), FmtSint(local_world->last_frame->tick));
|
||||
V.target_blend_time_ns += target_blend_dt_ns;
|
||||
V.blend_time_ns += blend_dt_ns;
|
||||
|
||||
// How many ticks back in time should the user thread blend between?
|
||||
// <Delay> = <USER_INTERP_RATIO> * <Tick interval>
|
||||
// E.g: At 1.5, the world will render 75ms back in time if the sim runs at 50tps
|
||||
f32 interp_ratio = TweakFloat("Interpolation ratio", 1.2, 0, 5);
|
||||
if (predict_to != prev_frame_predict_to)
|
||||
{
|
||||
i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio;
|
||||
V.target_blend_time_ns = predict_world->last_frame->time_ns - delay_ns;
|
||||
}
|
||||
|
||||
f64 blend_to_target_lerp_rate = 0.05;
|
||||
V.blend_time_ns = LerpI64(V.blend_time_ns, V.target_blend_time_ns, blend_to_target_lerp_rate);
|
||||
if (AbsI64(V.blend_time_ns - V.target_blend_time_ns) > (SIM_TICK_INTERVAL_NS * interp_ratio * 2))
|
||||
{
|
||||
LogDebugF("Blend reset");
|
||||
V.blend_time_ns = V.target_blend_time_ns;
|
||||
}
|
||||
|
||||
if (TweakBool("Interpolation enabled", 1))
|
||||
{
|
||||
P_Frame *left_frame = &P_NilFrame;
|
||||
P_Frame *right_frame = &P_NilFrame;
|
||||
for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
|
||||
{
|
||||
if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
|
||||
{
|
||||
right_frame = tmp;
|
||||
left_frame = tmp->prev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (P_IsFrameNil(left_frame) || P_IsFrameNil(right_frame))
|
||||
{
|
||||
right_frame = predict_world->last_frame;
|
||||
left_frame = right_frame->prev;
|
||||
}
|
||||
|
||||
frame->blend_from_tick = left_frame->tick;
|
||||
frame->blend_to_tick = right_frame->tick;
|
||||
|
||||
local_frame = P_PushFrame(frame->local_world, left_frame, left_frame->tick);
|
||||
{
|
||||
f64 blend_t = (f64)(V.blend_time_ns - left_frame->time_ns) / (f64)(right_frame->time_ns - left_frame->time_ns);
|
||||
|
||||
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
|
||||
{
|
||||
P_Ent *left = ent;
|
||||
P_Ent *right = P_EntFromKey(right_frame, ent->key);
|
||||
|
||||
ent->exists = LerpF32(left->exists, right->exists, blend_t);
|
||||
if (!P_IsEntNil(right))
|
||||
{
|
||||
ent->health = LerpF32(left->health, right->health, blend_t);
|
||||
ent->xf.t = LerpVec2(left->xf.t, right->xf.t, blend_t);
|
||||
ent->xf.r = SlerpVec2(left->xf.r, right->xf.r, blend_t);
|
||||
ent->walk_time_accum_ns = LerpI64(left->walk_time_accum_ns, right->walk_time_accum_ns, blend_t);
|
||||
ent->v = LerpVec2(left->v, right->v, blend_t);
|
||||
ent->w = LerpF32(left->w, right->w, blend_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
local_frame = P_PushFrame(frame->local_world, predict_world->last_frame, predict_world->last_frame->tick);
|
||||
}
|
||||
|
||||
|
||||
P_DebugDrawFrame(local_frame);
|
||||
@ -1858,6 +1932,34 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // TODO: Remove this
|
||||
// P_Frame *local_frame = &P_NilFrame;
|
||||
// {
|
||||
// if (frame->local_world->tiles_hash != predict_world->tiles_hash)
|
||||
// {
|
||||
// frame->local_world->tiles_hash = predict_world->tiles_hash;
|
||||
// CopyStructs(frame->local_world->tiles, predict_world->tiles, P_TilesCount);
|
||||
// frame->tiles_dirty = 1;
|
||||
// }
|
||||
// frame->local_world->seed = predict_world->seed;
|
||||
|
||||
// P_ClearFrames(frame->local_world, I64Min, frame->local_world->last_frame->tick - 1);
|
||||
// local_frame = P_PushFrame(frame->local_world, predict_world->last_frame, predict_world->last_frame->tick);
|
||||
|
||||
// // LogDebugF("First frame: %F, Last frame: %F", FmtSint(frame->local_world->first_frame->tick), FmtSint(frame->local_world->last_frame->tick));
|
||||
|
||||
|
||||
// P_DebugDrawFrame(local_frame);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// {
|
||||
// i64 delay_ns = NsFromSeconds(100);
|
||||
|
||||
@ -1883,8 +1985,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
//////////////////////////////
|
||||
//- Query local player
|
||||
|
||||
P_Ent *local_player = P_EntFromKey(local_world->last_frame, V.player_key);
|
||||
P_Ent *local_guy = P_EntFromKey(local_world->last_frame, local_player->guy);
|
||||
P_Ent *local_player = P_EntFromKey(frame->local_world->last_frame, V.player_key);
|
||||
P_Ent *local_guy = P_EntFromKey(frame->local_world->last_frame, local_player->guy);
|
||||
|
||||
//////////////////////////////
|
||||
//- Compute crosshair position
|
||||
@ -2082,7 +2184,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
{
|
||||
// TODO: Real world query
|
||||
P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor });
|
||||
for (P_Ent *ent = P_FirstEnt(local_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
|
||||
for (P_Ent *ent = P_FirstEnt(frame->local_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
|
||||
{
|
||||
P_Shape ent_shape = P_WorldShapeFromEnt(ent);
|
||||
b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0;
|
||||
@ -2300,7 +2402,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
if (bullet->is_bullet)
|
||||
{
|
||||
// FIXME: Use 'last visible' pos
|
||||
P_Ent *old_bullet = P_EntFromKey(local_world->last_frame->prev, bullet->key);
|
||||
P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key);
|
||||
Vec2 start = old_bullet->xf.t;
|
||||
Vec2 end = bullet->xf.t;
|
||||
if (P_IsEntNil(old_bullet))
|
||||
@ -2374,7 +2476,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
if (bullet->is_bullet && bullet->has_hit)
|
||||
{
|
||||
// FIXME: Use actual velocity
|
||||
P_Ent *old_bullet = P_EntFromKey(local_frame->prev, bullet->key);
|
||||
P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key);
|
||||
Vec2 start = old_bullet->xf.t;
|
||||
Vec2 end = bullet->xf.t;
|
||||
if (P_IsEntNil(old_bullet))
|
||||
@ -4166,10 +4268,10 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
UI_BuildLabelF("Predicted world entities: %F", FmtSint(predict_world->last_frame->ents_count));
|
||||
UI_BuildLabelF("Predicted world constraints: %F", FmtSint(predict_world->last_frame->constraints_count));
|
||||
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
|
||||
UI_BuildLabelF("Local world seed: 0x%F", FmtHex(local_world->seed));
|
||||
UI_BuildLabelF("Local world tick: %F", FmtSint(local_world->last_frame->tick));
|
||||
UI_BuildLabelF("Local world entities: %F", FmtSint(local_world->last_frame->ents_count));
|
||||
UI_BuildLabelF("Local world constraints: %F", FmtSint(local_world->last_frame->constraints_count));
|
||||
UI_BuildLabelF("Local world seed: 0x%F", FmtHex(frame->local_world->seed));
|
||||
UI_BuildLabelF("Local world tick: %F", FmtSint(frame->local_world->last_frame->tick));
|
||||
UI_BuildLabelF("Local world entities: %F", FmtSint(frame->local_world->last_frame->ents_count));
|
||||
UI_BuildLabelF("Local world constraints: %F", FmtSint(frame->local_world->last_frame->constraints_count));
|
||||
}
|
||||
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
|
||||
{
|
||||
@ -4795,7 +4897,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
G_CopyCpuToTexture(
|
||||
frame->cl,
|
||||
gpu_tiles_res, VEC3I32(0, 0, 0),
|
||||
local_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
|
||||
frame->local_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
|
||||
RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1))
|
||||
);
|
||||
}
|
||||
@ -5175,6 +5277,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
G_CommitCommandList(frame->cl);
|
||||
|
||||
i32 vsync = !!TweakBool("Vsync", 1);
|
||||
vsync = 1;
|
||||
UI_EndFrame(ui_frame, vsync);
|
||||
}
|
||||
|
||||
|
||||
@ -228,12 +228,16 @@ Struct(V_Frame)
|
||||
|
||||
Embed(V_SharedFrame, shared_frame);
|
||||
|
||||
P_World *local_world;
|
||||
RandState rand;
|
||||
|
||||
NET_Key sim_key;
|
||||
NET_Key desired_sim_key;
|
||||
f64 predict_tick_accum;
|
||||
|
||||
i64 blend_from_tick;
|
||||
i64 blend_to_tick;
|
||||
|
||||
Button held_buttons[Button_COUNT];
|
||||
V_Palette palette;
|
||||
|
||||
@ -260,6 +264,9 @@ Struct(V_Ctx)
|
||||
|
||||
i64 connect_try_ns;
|
||||
|
||||
i64 target_blend_time_ns;
|
||||
i64 blend_time_ns;
|
||||
|
||||
// Notifications
|
||||
V_Notif *first_notif;
|
||||
V_Notif *last_notif;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user