entity observation cache

This commit is contained in:
jacob 2026-03-19 18:33:22 -05:00
parent a6f9bcf4f3
commit 039e354a9d
3 changed files with 235 additions and 52 deletions

View File

@ -84,6 +84,7 @@ Struct(P_DebugDrawNode)
#define P_RollTimeNs NsFromSeconds(0.5) #define P_RollTimeNs NsFromSeconds(0.5)
#define P_RollTurnTimeNs (NsFromSeconds(0.1)) #define P_RollTurnTimeNs (NsFromSeconds(0.1))
#define P_RollTimeoutNs NsFromSeconds(0.5) #define P_RollTimeoutNs NsFromSeconds(0.5)
#define P_ObservationLifetimeNs NsFromSeconds(2)
Struct(P_Control) Struct(P_Control)
{ {
@ -118,6 +119,11 @@ Struct(P_Ent)
i64 created_at_tick; i64 created_at_tick;
f64 lifetime_seconds; f64 lifetime_seconds;
//- Client data
i64 local_observation_ns;
b32 has_observed;
//- Build data //- Build data
u64 continuity_gen; u64 continuity_gen;

View File

@ -1892,6 +1892,61 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Observe entities
{
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
P_EntKey key = ent->key;
i64 expiration_ns = P_ObservationLifetimeNs;
i64 observation_time_ns = 0;
{
V_ObservationBin *bin = &V.observation_bins[key.v % countof(V.observation_bins)];
V_Observation *obs = bin->first;
for (; obs; obs = obs->next)
{
if (obs->key == key.v)
{
break;
}
}
if (obs)
{
if (obs->time_ns + expiration_ns > frame->time_ns)
{
// Re-observe expired observation
DllQueueRemove(V.first_observation, V.last_observation, obs);
DllQueuePush(V.first_observation, V.last_observation, obs);
obs->time_ns = frame->time_ns;
}
}
else
{
if (V.first_observation && V.first_observation->time_ns + expiration_ns <= frame->time_ns)
{
// Remove expired observation for reuse
obs = V.first_observation;
V_ObservationBin *old_bin = &V.observation_bins[obs->key % countof(V.observation_bins)];
DllQueueRemove(V.first_observation, V.last_observation, obs);
DllQueueRemoveNP(old_bin->first, old_bin->last, obs, next_in_bin, prev_in_bin);
}
else
{
obs = PushStruct(perm, V_Observation);
}
obs->key = key.v;
obs->time_ns = frame->time_ns;
DllQueuePush(V.first_observation, V.last_observation, obs);
DllQueuePushNP(bin->first, bin->last, obs, next_in_bin, prev_in_bin);
}
observation_time_ns = obs->time_ns;
}
ent->local_observation_ns = observation_time_ns;
ent->has_observed = observation_time_ns == frame->time_ns;
}
}
@ -2341,72 +2396,165 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Push test bullet particles //- Push bullet trail particles
// TODO: Not like this // TODO: Not like this
for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) // for (P_Ent *trail = P_FirstEnt(local_frame); !P_IsEntNil(trail); trail = P_NextEnt(trail))
{ // {
if (bullet->is_bullet) // if (trail->is_trail)
{ // {
// FIXME: Use 'last visible' pos // // FIXME: Use 'last visible' pos
P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key); // P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key);
Vec2 start = old_bullet->xf.t; // Vec2 start = old_bullet->xf.t;
Vec2 end = bullet->xf.t; // Vec2 end = bullet->xf.t;
if (P_IsEntNil(old_bullet)) // if (P_IsEntNil(old_bullet))
{ // {
start = end; // start = end;
} // }
b32 skip = 0; // b32 skip = 0;
if (bullet->has_hit) // if (bullet->has_hit)
{ // {
Vec2 hit_pos = bullet->hit_entry; // Vec2 hit_pos = bullet->hit_entry;
if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) // if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0)
{ // {
skip = 1; // skip = 1;
} // }
// V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red); // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red);
// V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple); // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple);
end = hit_pos; // end = hit_pos;
} // }
if (!skip) // if (!skip)
{ // {
{ // {
f32 trail_len = Vec2Len(SubVec2(end, start)); // f32 trail_len = Vec2Len(SubVec2(end, start));
f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second // f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second
particles_count = MaxF32(particles_count, 1); // particles_count = MaxF32(particles_count, 1);
f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); // f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start)));
// f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); // // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
V_Emitter emitter = Zi; // V_Emitter emitter = Zi;
{ // {
emitter.kind = V_ParticleKind_BulletTrail; // emitter.kind = V_ParticleKind_BulletTrail;
emitter.count = particles_count; // emitter.count = particles_count;
f32 angle_spread = Tau / 4.0; // f32 angle_spread = Tau / 4.0;
emitter.angle.min = angle - angle_spread / 2; // emitter.angle.min = angle - angle_spread / 2;
emitter.angle.max = angle + angle_spread / 2; // emitter.angle.max = angle + angle_spread / 2;
emitter.pos.p0 = start; // emitter.pos.p0 = start;
emitter.pos.p1 = end; // emitter.pos.p1 = end;
// emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); // // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); // // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
emitter.speed.min = -1; // emitter.speed.min = -1;
emitter.speed.max = 1; // emitter.speed.max = 1;
} // }
V_PushParticles(emitter); // V_PushParticles(emitter);
} // }
} // }
} // }
} // }
// //////////////////////////////
// //- Push test bullet particles
// // TODO: Not like this
// for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
// {
// if (bullet->is_bullet)
// {
// // FIXME: Use 'last visible' pos
// 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))
// {
// start = end;
// }
// b32 skip = 0;
// if (bullet->has_hit)
// {
// Vec2 hit_pos = bullet->hit_entry;
// if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0)
// {
// skip = 1;
// }
// // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red);
// // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple);
// end = hit_pos;
// }
// if (!skip)
// {
// {
// f32 trail_len = Vec2Len(SubVec2(end, start));
// f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second
// particles_count = MaxF32(particles_count, 1);
// f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start)));
// // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
// V_Emitter emitter = Zi;
// {
// emitter.kind = V_ParticleKind_BulletTrail;
// emitter.count = particles_count;
// f32 angle_spread = Tau / 4.0;
// emitter.angle.min = angle - angle_spread / 2;
// emitter.angle.max = angle + angle_spread / 2;
// emitter.pos.p0 = start;
// emitter.pos.p1 = end;
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// emitter.speed.min = -1;
// emitter.speed.max = 1;
// }
// V_PushParticles(emitter);
// }
// }
// }
// }
@ -2615,6 +2763,10 @@ void V_TickForever(WaveLaneCtx *lane)
// } // }
} }
////////////////////////////// //////////////////////////////
//- Push test explosion //- Push test explosion

View File

@ -227,6 +227,27 @@ Struct(V_TimelineMarker)
f64 tick; f64 tick;
}; };
////////////////////////////////////////////////////////////
//~ Observation types
#define V_ObservationBinsCount Kibi(32)
Struct(V_Observation)
{
V_Observation *next;
V_Observation *prev;
V_Observation *next_in_bin;
V_Observation *prev_in_bin;
u64 key;
i64 time_ns;
};
Struct(V_ObservationBin)
{
V_Observation *first;
V_Observation *last;
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ State types //~ State types
@ -283,6 +304,10 @@ Struct(V_Ctx)
i64 connect_try_ns; i64 connect_try_ns;
V_ObservationBin observation_bins[V_ObservationBinsCount];
V_Observation *first_observation;
V_Observation *last_observation;
// Notifications // Notifications
V_Notif *first_notif; V_Notif *first_notif;
V_Notif *last_notif; V_Notif *last_notif;