Why Autodesk Forge Viewer is so fast.

The short answer: the Forge Viewer (internally called LMV, Large Model Viewer) barely uses Three.js as a rendering engine. It uses a forked r71 for the math library, the WebGL state wrapper and the material/shader plumbing, and replaced everything above that with its own machinery built for one job. The Three.js version is obsolete because nothing they depend on lives in the parts that changed since 2015, and upgrading would mean re-porting a decade of divergence for no gain.

What actually makes the difference:

  • No scene graph. Three.js gives every mesh an Object3D with a matrix, a parent, children, and a per-frame updateMatrixWorld walk. LMV stores a model as a flat FragmentList: packed typed arrays for transforms, bounds, material ids and geometry ids. A million fragments is a few contiguous buffers, not a million JS objects. That is cache-friendly, garbage-free and trivially iterable.
  • BVH-driven, screen-size-aware traversal. The SVF/OTG loader ships a precomputed bounding volume hierarchy. Each frame the iterator walks it front to back, frustum-culls whole subtrees, and skips anything whose projected size is below a pixel threshold. Modern Three.js frustum-culls per object and draws everything that passes, including thousands of bolts that cover half a pixel.
  • Progressive, time-budgeted rendering. LMV does not try to draw the whole model each frame. It draws the largest, nearest fragments within a frame budget, presents that, and keeps filling in over subsequent frames while the camera is still. When you orbit, it drops the tail and stays at frame rate. Three.js has no notion of “good enough for this frame”; the frame takes as long as the draw list takes.
  • Consolidation and instancing at load time. Fragments sharing a material are merged into large buffers, and repeated geometry is drawn instanced. Draw calls drop by one or two orders of magnitude. Three.js has BatchedMesh and InstancedMesh now, but you have to build that yourself, and most loaders produce one mesh per element.
  • Compact geometry format. Interleaved vertex buffers, quantized positions and packed normals, deduplicated geometry hashes across models in OTG. Less GPU memory, less upload, more of the model fits.
  • GPU-side picking and overlays. An id buffer is rendered alongside the colour buffer, so selection and hover are a pixel read, not a CPU raycast against millions of triangles. Selection highlight, ghosting and section planes are done through render targets and a single uber-shader rather than by swapping materials and redrawing.
  • Streaming and paging. Geometry loads on demand in BVH priority order, so the big visible stuff appears first, and it can unload geometry under memory pressure. The viewer is usable long before the model is fully resident.

So, the frame rate is not a property of the Three.js version. It comes from the file format, the flat data model, the BVH and the progressive renderer, which is why Autodesk had no incentive to chase upstream.

One correction to the premise: other engines do reach this class. xeokit uses data-texture geometry with the same flat philosophy, That Open Engine builds fragment models on modern Three.js, and Speckle takes a similar path. What they share with LMV is that they abandoned the per-object scene graph. A stock Three.js app with one mesh per element cannot get there, and that is the comparison most people are making.

Leave a Reply

Your email address will not be published. Required fields are marked *