Skip to main content

Model Cache System

The Model Cache system provides efficient loading and caching of 3D models (GLB/GLTF files) with automatic material setup, LOD generation, and persistent IndexedDB storage.
Model cache code lives in packages/shared/src/utils/rendering/ModelCache.ts.

Overview

The cache prevents loading the same GLB file multiple times by:
  • In-memory caching: Loaded models are cached and cloned for new instances
  • Material sharing: All instances of a model share the same materials (reduces draw calls)
  • IndexedDB persistence: Processed models are stored in IndexedDB to skip GLTF parsing on subsequent sessions
  • Automatic LOD generation: Optional decimated meshes and octahedral impostors for distant rendering

Basic Usage

Processed Model Cache (IndexedDB)

The cache stores processed models in IndexedDB to skip expensive GLTF parsing on subsequent loads.

What Gets Cached

Geometry Data:
  • Vertex positions, normals, UVs, colors
  • Index buffers
  • Skinning data (weights, indices)
Material Properties:
  • Colors (base, emissive)
  • PBR values (roughness, metalness)
  • Texture pixel data (RGBA arrays)
  • Transparency, alpha test, side
Scene Hierarchy:
  • Node tree structure
  • Mesh-to-node mapping
  • Bone hierarchy (for skinned meshes)
Animations:
  • Keyframe tracks (position, rotation, scale)
  • Track timing and interpolation
Metadata:
  • Collision data from GLB extras
  • Cache version and timestamp

Cache Version

The cache uses version 3 (as of Feb 2026). When the version changes, all cached entries are automatically invalidated.
Version History:
  • v1: Initial implementation
  • v2: Added texture caching
  • v3: Fixed missing objects bug (identity-based mesh mapping) and texture restoration bug (DataTexture pixel extraction)

Cache Invalidation

Cached models are invalidated when:
  • Cache version changes (code update)
  • Source GLB file size changes (asset update)
  • IndexedDB is cleared by browser

Bug Fixes (PR #935)

Missing Objects Bug

Issue: Models with duplicate mesh names (e.g., multiple meshes named “Cube”) had missing objects after cache restoration. Only the last mesh with each name appeared in the scene. Root Cause: serializeNode() used findIndex-by-name to map hierarchy nodes to mesh data. When multiple meshes shared the same name, they all resolved to the same index. During deserialization, Three.js add() auto-removes children from their previous parent, so only the last reference survived. Fix: Use object-identity mapping instead of name-based lookup:
Impact: Altars, complex buildings, and multi-part models now restore correctly from cache.

Lost Textures Bug

Issue: Textures appeared white or wrong colors after browser restart. Models loaded correctly on first load but lost textures when restored from IndexedDB cache. Root Cause: Textures were serialized as ephemeral blob: URLs but never reloaded during deserialization. The blob URLs became invalid after page refresh. Fix: Extract raw RGBA pixel data synchronously and restore as DataTexture:
Benefits:
  • Synchronous restoration (no async loading race conditions)
  • No network requests for cached textures
  • Textures persist across browser restarts

Grey Tree Materials Bug

Issue: Trees appeared grey in WebGPU builds after cache restoration. Root Cause: createDissolveMaterial() used instanceof MeshStandardMaterial which fails for MeshStandardNodeMaterial in WebGPU builds (separate classes). Fix: Duck-type property check instead of instanceof:

Disabling the Cache

For debugging or testing, you can disable the processed model cache:
When disabled, models are always loaded from GLTF (no IndexedDB reads/writes).

Error Handling

The cache includes comprehensive error logging:
Failure Modes:
  • IndexedDB unavailable → Falls back to GLTF parsing
  • Deserialization error → Clears cache entry and retries
  • Corrupted cache entry → Automatically invalidated on version mismatch

Performance

Cache Hit Benefits:
  • Skips GLTF binary parsing (~20-100ms per model)
  • Skips texture decoding (already in RGBA format)
  • Immediate geometry reconstruction from typed arrays
  • No network requests for textures
Typical Savings:
  • Small models (items): 20-40ms
  • Medium models (NPCs): 40-80ms
  • Large models (buildings): 80-150ms

Material Sharing

By default, all instances of a model share the same materials:
Benefits of Sharing:
  • Reduces draw calls (GPU can batch instances)
  • Lower memory usage (one material per model type)
  • Faster cloning (no material creation)
When to Disable:
  • Custom per-instance colors
  • Dynamic material properties
  • Instance-specific textures

LOD Integration

The cache integrates with the LOD system for automatic level-of-detail generation:
See LOD System for complete details.

Cache Statistics

Cache Management

Preloading

Preload multiple models in parallel for faster initial load:

WebGPU Compatibility

All cached materials are converted to MeshStandardNodeMaterial for WebGPU/TSL support:
This ensures:
  • Proper PBR lighting with sun/moon
  • WebGPU-native TSL dissolve effects
  • Consistent material behavior across renderers

Collision Data

Models can embed collision data in GLB extras (injected by inject-model-collision.ts):
The collision data travels with the asset and is automatically extracted during loading.