npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bitruvius/mesh

v0.3.1

Published

Bitruvius mesh renderer: raw-WebGL2 unlit-textured triangle/point rendering of OGC 3D-Tiles / I3S meshes (PackedMeshTile), in the shared ENU frame

Downloads

361

Readme

@bitruvius/mesh

The WebGL2 renderer behind every Bitruvius mesh layer: decoded 3D Tiles and I3S geometry, drawn in the shared ENU frame.

Internal building block. This package exists so that @bitruvius/sdk-maplibre and 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.

This package does one job. It takes a decoded tile, uploads it to the GPU, and draws it in the frame the host camera hands it. It never fetches a tileset, never parses a container, and never touches a map object.

Why it is separate

Decoding a tileset and drawing it want opposite things. Decoding belongs off the main thread, in a worker, where no GL context is in reach. Drawing needs the one GL context the page owns and has to run inside the host's render callback. So the two halves meet at a contract rather than in a file: PackedMeshTile is defined in @bitruvius/geo-core, the decoders (@bitruvius/gltf, @bitruvius/i3s, @bitruvius/tiles3d-mesh) produce it, and this package consumes it. Nothing here knows which format the bytes arrived in.

That buys two things worth the package boundary.

One renderer, several formats. A b3dm tile from a 3D Tiles service and a node from an I3S scene service reach this renderer as the same struct-of-arrays shape, so both get the same shaders, the same alpha handling, the same picking and the same memory accounting. A new container has to reach the packed vocabulary, not write a renderer.

One renderer, several hosts. The renderer takes a placement matrix (ENU to web-mercator world) and a per-frame mvpWorld, and composes the rest itself. It has two dependencies, @bitruvius/geo-core and @bitruvius/render-runtime, and MapLibre is not one of them. Six layers in the SDK drive it, and several more join its shadow pass, without it ever knowing a map exists.

The shared shadow pass forced the same split from the other direction. ShadowCoordinator lives here, one per GL context, and builds a single cascade atlas that every registered dataset casts into and samples, so a building shadows a point cloud and both shadow the terrain from one depth pass. The caster contract itself sits in @bitruvius/render-runtime, which is why the point cloud and splat renderers can join that atlas without depending on this package at all.

What is inside

Tiles3DMeshRenderer is the bulk of it: upload and release against the streaming engine's handle model, an opaque pass and a back-to-front transparent pass, per-tile VAOs and buffers, base-color texture upload, instancing with ref-counted shared templates, GPU feature-id picking, depth-grid probes, clipping planes, sun lighting and cascaded shadows. Planet-scale tiles carry a baked per-vertex mercator offset and are placed from mercator instead of ENU, which is what keeps a global tileset from swimming far from its anchor.

Around it sit the small, pure pieces that the renderer would otherwise hide inline, each unit tested on its own:

  • drawStateFor maps a glTF material's alpha mode and sidedness to a WebGL2 draw state, including the rule that an OPAQUE material's alpha must be ignored rather than multiplied in.
  • chooseTextureFormat and detectTextureCaps pick the best KTX2 transcode target the device supports (BC7, ASTC, ETC2, BC3, BC1) and fall back to uncompressed RGBA.
  • gpuBytesOf reports a tile's true resident cost so the engine's memory budget is not fiction.
  • placementFlipsWinding catches the mirrored placement matrix that would otherwise cull the wrong faces.
  • packFeatureStyles builds the one-texel-per-feature buffer that hides, recolors and fades BIM elements without a per-frame cost.
  • featureFootprints and featureTerrainOffsets seat each building of a sea-level dataset on its own patch of ground.
  • ShadowCoordinator, shadowReceiverGrid and TerrainShadowGridReceiver cover the shared cascade atlas and the ground that receives it.

Using it directly

You supply the GL context, the placement, and a camera per frame:

import { Tiles3DMeshRenderer } from '@bitruvius/mesh';

const renderer = new Tiles3DMeshRenderer(gl); // a WebGL2RenderingContext
renderer.placement = enuToMercator; // ENU to web-mercator world, column-major Mat4

// on tile ready, from a streaming engine's onTileReady:
const handle = renderer.upload(packedTile); // PackedMeshTile, from @bitruvius/gltf or @bitruvius/i3s

// each frame, inside the host's render callback:
renderer.render({ mvpWorld, viewportWidth, viewportHeight }, [handle], []);

// on eviction, from onTileRelease:
renderer.release(handle);

render takes the per-frame camera, the primary handles, and the coarse fallback handles it should draw only where no finer tile covered the screen. That third argument is why an engine can hand over a half-loaded tree without holes or coarse-over-fine show-through.

Depend on this package directly only if you are writing your own layer or host integration against a Bitruvius decoder. If you want 3D Tiles or I3S content on a map, install @bitruvius/sdk-maplibre, which wires this renderer to the streaming engines, the codecs and the map for you.

Trademarks

Esri, ArcGIS and I3S are trademarks of Environmental Systems Research Institute, Inc. Cesium and 3D Tiles are trademarks of Cesium GS, Inc. Khronos, glTF, KTX and WebGL 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. Bitruvius is not affiliated with, sponsored by, or endorsed by any of them, and no such relationship is implied.

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.