From cd4035742538ea43e9027c749ba750eb657eece3 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Wed, 16 Sep 2026 13:28:16 +1000 Subject: [PATCH] Add lerping + z-level docs I have no idea if these are still being used. --- src/en/robust-toolbox/transform/lerping.md | 36 ++++++++++++++++++++++ src/en/robust-toolbox/z-levels.md | 36 ++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/en/robust-toolbox/transform/lerping.md create mode 100644 src/en/robust-toolbox/z-levels.md diff --git a/src/en/robust-toolbox/transform/lerping.md b/src/en/robust-toolbox/transform/lerping.md new file mode 100644 index 0000000000..6ac2a937e1 --- /dev/null +++ b/src/en/robust-toolbox/transform/lerping.md @@ -0,0 +1,36 @@ +# Lerping + Corrections + +## Lerping + +With ss14 being tick-based we need to render entities in-between ticks. To do this we apply lerping for the following scenarios: +1. Our local entity we need to lerp from tick A -> tick B +2. For remote entities we lerp from tick A -> tick B. Due to the state buffer this will often be 1+ ticks behind. However if tick B does not exist and we receive tick C instead we use that as the target rather than jumping a tick. +3. With z-levels we need to be able to lerp cross-parent and also use z-position — essentially updating the lerp's source position to a new one. + +If the correction is large or we otherwise want a snap (e.g. containers, teleports) we need to be able to cancel the lerp entirely. + +Something else that makes it a pain is handling multiple transform updates inside one tick, most notably physics substepping, so we need to know what the transform was at the start and end of a tick is. + +For the z-level related mechanics to it see z-levels.md + +## Correction +If mispredicts happen then the above instantly snaps — this necessitates smoothing errors out. + +## CVars + +Ideally you don't need to touch these but the net.interp* values handle the interpolation distance and corrections. + +## How it works + +Transform itself purely keeps simulation state and nothing to do with rendering. All of the rendering values are handle via the client's TransformSystem. + +Everything gets handled in 2 spots: +1. ClientGameStateManager handles dropped ticks — if tick B is missing we lerp from tick A -> tick C. This is done via interpolation lookahead. +2. TransformSystem.FrameUpdate handles the remaining data. + +In TransformSystem we cache all snapped moves so we know when we need to ignore lerping, but if there are no snaps then we try and figure out if we need to: +- Snap due to interp distance being too far +- Re-parent the entity (grid-traversal and z-levels predominantly — TryCreateEndpoint) +- Handle corrections + +Most of the complexity comes from handling prediction, specifically ignoring any rollback / replay transform states and only comparing about the start / end states of a tick. diff --git a/src/en/robust-toolbox/z-levels.md b/src/en/robust-toolbox/z-levels.md new file mode 100644 index 0000000000..741b78386d --- /dev/null +++ b/src/en/robust-toolbox/z-levels.md @@ -0,0 +1,36 @@ +# Z-levels + +## What are they + +z-levels are a way to sandwich maps together done via ZMapNetwork. This entails: +- ZLevelMapNetworkComponent, meta component stored in nullspace. +- ZLevelMapComponent, added to component maps. +- ZLevelGridComponent, used to keep grids aligned via soft joints. These are done manually as physics does not currently support cross-map joints. +- ZLevelPositionComponent, used on entities with z-level heights — this is what also hooks into transform-lerping (see lerping.md) + +These systems are also used: +- ClientZLevelSystem, this handles gluing lerping with TransformSystem. +- ZLevelGridSyncSystem, this is what runs the soft-joints for ZLevelGridComponent. +- ZLevelSystem handles the map-network + the physics itself. +- ZLevelPhysicsSystem does the meat of the work around entities traversing z-levels. + +## CVars + +These are under zlevel.* predominantly around controlling the fake "physics" side of it. + +## Visuals + +If you wish to affect the visuals then use ZLevelVisualsEvent — used for the projection offset if you wish for the z-levels to not be perfectly stacked, and a relevant overlay. +Overlays get the zlevel offset passed in via `OverlayDrawArgs` and know where they are in the stack, relatively speaking. + +For world overlays you only want applied once, e.g. damage borders, night-vision, use PostZLevel as the space to avoid them accidentally being applied on every z-level render. + +## Physics + +This is handled in two main ways: +1. ZLevelGridSync runs soft physics between linked grids, preferring the smaller grid to move more towards the larger grid. Generally you should only need to set the CVars for this under zlevel.* +2. ZLevelPhysics handles the normal entity z-level interactions. + +The physics handles 2 parts: +1. Integrating z-level physics velocities in the tick with substepping so high-velocities don't clip. +2. Handling "supports" / "highground" so entities can't drop a z-level where they wouldn't be able to intersect normally.