@bitruvius/tiles3d
v0.3.2
Published
Bitruvius SDK pure 3D-Tiles streaming engine (gaussian splat, textured mesh, point cloud): codec- and renderer-agnostic traversal, cache, scheduler, and ENU packing (no WebGL/DOM/MapLibre)
Readme
@bitruvius/tiles3d
The 3D Tiles streaming brain: traversal, cache, scheduler and ENU packing, with no renderer and no codec attached.
3D Tiles is an OGC Community Standard. This package implements the published specification, so it streams any conformant tileset rather than one vendor's deployment of it.
Internal building block. This package exists so that
@bitruvius/sdk-maplibreand the Bitruvius codecs can resolve their dependencies on npm. It has no standalone product story. Unless you are deliberately building against it, install the SDK instead.
Why it is its own package
It sits underneath both the codecs and the renderer, and it cannot depend on either.
A codec such as @bitruvius/turbo-spz or
@bitruvius/bvc packs a decoded tile into the shared
ENU texture layout inside its own worker, so it imports packEnu and PackedSplatTile from here.
The MapLibre SDK consumes that same layout on the other side and already depends on the codecs. Put
the layout and the engines in the SDK and every codec would have to depend on the package that
depends on it. Publishing the shared core below both is what breaks the cycle on npm.
The second reason is that the streaming machinery is not specific to 3D Tiles.
@bitruvius/i3s is a different standard with a
different tree and a different wire format, and it imports this package's StreamingCache,
FetchScheduler, frustum primitives, CameraState and SceneAnchor instead of forking them. Two
format packages share one streaming implementation, and neither depends on the other.
The third reason is the constraint that makes both of those possible: nothing here touches WebGL,
the DOM or MapLibre, and nothing here imports a codec. Decoders are injected, output goes out
through onTileReady / onTileRelease callbacks, and the whole package runs and is tested headless.
What is in it
Three per-frame engines over one shared core:
| Engine | Content it drives |
| --- | --- |
| TilesetEngine | Gaussian splats, decoded through an injected GltfSplatDecoder |
| Tiles3DMeshEngine | Textured mesh and photogrammetry, including planet-scale tilesets |
| Tiles3DPointEngine | Point clouds (.pnts and glTF POINTS) |
Each one runs the same loop: traverse, schedule the fetch, decode, emit, evict. What they share:
- Tileset parsing.
tileset.jsoninto aTiletree with cumulative ECEF transforms, OBB bounding volumes, lazy external-tileset expansion, and 3D Tiles 1.1 implicit tiling (quadtree and octree.subtreefiles). - Traversal. Screen-space-error refine for both
ADDandREPLACE, frustum cull, near-first foveated load priority, and progressive per-path reveal: a path that is not ready is covered by its nearest ready ancestor, so a region reveals each child as that child lands rather than waiting on its slowest sibling, and no frame has a hole. - Cache. Resident vertex-count and byte (VRAM) budgets, LRU eviction that never touches the keep set, a tile used this frame, or a recently visible one, so the far LOD pyramid survives a pan.
- Scheduler. Bounded-concurrency priority fetch pump with per-tile abort and re-ranking as the camera moves. The transport is injected, so it is testable headless.
- Adaptive memory controller. A slow feedback loop that adds to the far-field SSE target until
residency settles just under budget. Coarsening the far field can never create a hole, and the
near field always refines to
maxSse, so pressure is paid where you will not see it instead of by evicting a visible tile. - Placement and packing.
sceneAnchorFromEcefcomposes the ECEF to mercator anchor that keeps relative coordinates f32-safe;packEnurebases a decoded tile into that shared ENU frame and the global texture layout;measureGroundEzreads a dataset's true ground from the histogram knee of a few decoded coarse tiles, because a root box centre is a useless ground proxy. - Source resolution.
resolveSourcetakes a Cesium ion asset id or a direct tileset URL and returns the JSON, base URL, reusable auth, detected vendor and declared vertical CRS. URLs are tried public-first and only retried with a token on 401/403.
How it fits
import { resolveSource, sceneAnchorFromEcef, TilesetEngine, type ReadyTile } from '@bitruvius/tiles3d';
const { json, baseUrl, requestInit } = await resolveSource(tilesetUrl);
const anchor = sceneAnchorFromEcef(rootCenterEcef);
const engine = new TilesetEngine(
json,
baseUrl,
anchor,
decoder,
{
onTileReady: (t: ReadyTile) => renderer.upload(t.packed, t.count, t.xyz, t.packedSh),
onTileRelease: (_id, handle) => renderer.release(handle),
onChange: () => requestAnimationFrame(frame),
},
{ requestInit }, // the auth resolveSource already worked out; drop it for a public tileset
);
function frame() {
engine.update({ viewProjEcef, cameraEcef, viewportHeight, fovYRad });
renderer.draw(engine.renderHandles());
}Pass requestInit through or a secured ion or ArcGIS tileset resolves and then 401s on every tile.
The sixth argument is EngineOptions and is entirely optional: auth, requestInit, the cache
budget and decodeConcurrency. The LOD target, maxSse, is a mutable field on the engine rather
than a construction option, so a viewer can retune it between frames.
The handle you return from onTileReady is yours; the engine stores it and hands it back verbatim
on onTileRelease. The camera you pass in is plain ECEF, so the engine carries no mercator or
viewer-specific math.
Should you depend on it directly
Probably not. Depend on it if you are driving 3D Tiles into a renderer that is not MapLibre, or
writing your own decoder against the packed ENU layout. For anything on a MapLibre map, install
@bitruvius/sdk-maplibre, where these
engines already sit behind one-call layers.
Its only runtime dependency is
@bitruvius/geo-core.
Trademarks
3D Tiles and I3S are OGC Community Standards. OGC is a trademark of the Open Geospatial Consortium. Esri, ArcGIS and I3S are trademarks of Environmental Systems Research Institute, Inc. Cesium and 3D Tiles are trademarks of Cesium GS, Inc. SPZ is a trademark of Niantic, Inc. Khronos and glTF are trademarks of The Khronos Group Inc. MapLibre is a trademark of the MapLibre organization. All other marks are the property of their respective owners.
These names are used solely to describe the data formats this software interoperates with. WebGL is a trademark of The Khronos Group Inc.
Bitruvius is not affiliated with, sponsored by, or endorsed by any of them, and no such relationship is implied. Implementing a published specification is not a claim of certification: Bitruvius has not undergone OGC compliance testing for any standard.
License
Proprietary. The full terms ship as LICENSE inside this package, and are readable
before installing at cdn.bitruvius.com/legal/sdk-license-v1.txt.
© Bitruvius, Inc.
