A floating origin keeps local 3D coordinates near zero to preserve precision far from the global origin, without changing global positions.
dotnet add package \ RecursiveAI.OneGame.Physics
Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.
using OneGame.Core; using OneGame.Physics; var space = new PhysicsSpace3D(); var body = space.AddSphere( Fixed.One, new FVec3(1000, 0, 0)); space.ShiftOrigin(new FVec3(1000, 0, 0)); Console.WriteLine( $"Local X: {space.Position(body).X}"); var global = space.Origin + space.Position(body); Console.WriteLine($"Global X: {global.X}");
Prints:
Local X: 0 Global X: 1000
The sphere starts at global X = 1000. Shifting by 1000 moves its local X to zero and stores that offset in Origin; adding the two still gives 1000.
Offsets must lie on the physics 2⁻³¹ grid (even Fixed.Raw), and the accumulated origin must fit signed 32-bit world units; PhysicsSpace3D rejects out-of-range coordinates.
Unzip the example, then dotnet run.
Fixed is a deterministic fixed-point number type; FVec3 is a three-dimensional vector of those numbers.
Variations
Accumulate a second shift
// After the shift in the snippet at the top: space.ShiftOrigin(new FVec3(100, 0, 0)); Console.WriteLine($"Local X: {space.Position(body).X}"); Console.WriteLine($"Global X: {(space.Origin + space.Position(body)).X}");
Prints these additional lines:
Local X: -100 Global X: 1000
Related recipes
Draw a 3D physics overlay · Handle trigger events
The short answer
Call PhysicsSpace3D.ShiftOrigin(offset) from the authority’s deterministic simulation logic—the game instance that decides the shared state. Read a body’s global position as space.Origin + space.Position(body). The shift changes local coordinates and the stored origin together; it does not move the body to another global location.
This API belongs to PhysicsSpace3D. PhysicsSpace2D does not expose world-origin shifting. Do not copy this call into a PhysicsSpace2D example or treat view transforms as an equivalent physics operation.
How do I keep floating-origin physics deterministic?
Fixed-point inputs alone do not make a physics solver deterministic. PhysicsSpace3D converts positions through a checked numeric bridge and steps OnePhysics on one thread. OnePhysics uses IEEE +, −, ×, ÷ and sqrt without fused operations, so its documented arithmetic produces the same bits on x64, arm64 and WebAssembly. The range and determinism guide explains the checked conversions; the underlying solver is not fixed-point-only.
For origin shifting, the key decision is who chooses the shift and when it happens. In onegame the authority makes that decision in simulation code. Every replay must encounter the same operation with the same offset at the same point in the tick sequence. A camera is view state and can differ between players, so its current position must not decide when the physics world rebases.
Register the 3D space with PhysicsSpace3D.AddTo(world) in the game’s system registration path. It becomes a hidden table named physics3d, with a step after entities. That registration must also happen when constructing a world for loading a save. The console example uses a standalone space to isolate the coordinate change; it does not install a second step system or choose a rebasing policy for a game.
The documented local coordinate interval includes the exact endpoints ±2²¹. Shift before local positions approach that limit. One possible policy is to shift by +1000 on X when a tracked body’s local X reaches 1000, as in the example, with a matching rule for negative travel. This is an example threshold, not a library default. Apply the same rule at the same simulation tick on every replay, and account for all active bodies, not merely the camera position. The accumulated origin has a separate requirement: it must fit signed 32-bit world units.
Offsets must be representable on the physics 2⁻³¹ grid, which means an even Fixed.Raw value. Integer offsets such as 1000 in the sample meet that requirement. Do not construct arbitrary raw fixed-point offsets and assume every representable onegame number can cross the bridge to physics coordinates unchanged.
The sign matters. A positive X shift increases the accumulated X origin and subtracts that offset from local body X coordinates. The sample starts a sphere at X = 1000 and shifts by +1000. Local X becomes zero, while global X remains 1000. A second shift of +100 makes local X −100 and origin X 1100; the global sum is still 1000. These calculations are useful when checking world-space markers against physics-local positions.
A shift is part of physics state, not a display adjustment. The accumulated origin is saved and hashed. Two spaces with different origins deliberately have different hashes even if their global body positions appear identical. Do not normalize those hashes away in a drift check: the different local coordinate frames are a real state difference that can affect the continuation of a simulation.
Snapshots preserve the coordinate frame so restored physics can continue with the same origin. Views should draw from the state intended for them: the physics table is hidden, and clients normally receive mirrored synced properties rather than the entire authority physics world. Decide whether a mirrored value is local or global and use that convention consistently. The 3D physics documentation describes the coordinate bridge and rebasing contract.