Exact fixed-point geometry. Float in, float out, zero drift inside.
Floating point geometry drifts. Every transform, every frame, every physics tick adds a tiny error. Over time vertices shift, collision checks disagree, and multiplayer clients desync. The root cause is always the same: floating point math is approximate by design.
base_2 intercepts your geometry once, freezes it to exact 64-bit integers, and gives you back two representations:
- Exact
i64— for logic, physics, collision. Deterministic on a given platform. - Clean
f32— freshly derived from the exact source every frame, for your GPU.
Error is introduced exactly once, at freeze time, bounded at ≤ 2^-33 of your input unit. It does not grow with subsequent operations.
use base_2::api::Mesh;
// load from any float source — STL, OBJ, GLTF, engine mesh, etc.
let mesh = Mesh::from_floats(&float_verts)
.translate(10.0, 0.0, 0.0)
.rotate_quat(0.0, 0.0, 0.707, 0.707);
// compute once per frame
let pts = mesh.compute();
let gpu = pts.to_f32(); // clean floats for renderer — derived fresh each frame
let logic = pts.to_i64(); // exact integers for physics and collision// freeze on load — once
let mesh = Mesh::from_floats(&engine_mesh.vertices);
// every frame
mesh.translate(player.x, player.y, player.z)
.rotate_quat(player.qx, player.qy, player.qz, player.qw);
let pts = mesh.compute();
renderer.upload(pts.to_f32()); // GPU gets clean floats
physics.update(pts.to_i64()); // logic gets exact integersNo epsilon tuning. No tolerance stacking. No "works on my machine" bugs. Multiplayer clients running the same logic get the same integers — deterministic on a given platform.
Internally, 1 unit = 2^-32 of your input unit.
- Resolution: 2^-32 per input unit
- Range: ±2^31 input units with
i64, ±2^95 input units withi128 - Overflow: predictable —
i64always uses all 64 bits, worst-case behavior is known upfront
Units are user-defined. Pass millimeters, game units, or anything else — as long as you are consistent.
mesh.translate(x, y, z) // set translation
mesh.scale(x, y, z) // set scale
mesh.rotate(rx, ry, rz) // Euler angles — simple but has gimbal lock
mesh.rotate_quat(qx, qy, qz, qw) // quaternion — preferred, no gimbal lockAll setters overwrite — pass the absolute current transform each frame, not a delta.
Transforms are applied fresh from the original frozen points every call to compute().
The 2^-32 scaling is not arbitrary. It means every coordinate uses all 64 bits, giving uniform precision across the entire range. There is no false headroom, no "works on small inputs but breaks on large ones." Overflow behavior is always worst-case, always documented, always the same.
It is the fixed-point equivalent of Rust's ownership model: honest about what it costs, in exchange for guarantees you can rely on.
Apache 2.0