Skip to main content

Model Cache Fixes (February 2026)

Commit: c98f1cce4240b5d4d7a459f60f47a927fe606d2b
PR: #935
Author: tcm390

Summary

Fixed two critical bugs in the IndexedDB processed model cache that caused missing objects (altars, trees) and lost textures (white/wrong colors) after browser restart.

Bug 1: Missing Objects

Symptoms

  • Objects disappear after browser restart
  • Common missing objects: altars, trees, rocks
  • Models with duplicate mesh names affected most

Root Cause

serializeNode() used findIndex-by-name to map hierarchy nodes to mesh data:
Models with duplicate mesh names (common: "", “Cube”, “Cube”) all resolved to the same index. During deserialization, Three.js add() auto-removes from previous parent, so only the last reference survived.

Fix

Use Map<Object3D, number> identity map built during traversal:
Result: Each node gets unique index regardless of name, all objects preserved.

Bug 2: Lost Textures

Symptoms

  • Textures appear white or wrong color after browser restart
  • Affects all textured models
  • Cache appears to load but materials are broken

Root Cause

Textures were serialized as ephemeral blob: URLs but never reloaded during deserialization:

Fix

Extract raw RGBA pixels via canvas getImageData() (synchronous) and restore as THREE.DataTexture:
Result: Textures persist correctly across browser restarts, no async loading race conditions.

Additional Fix: Grey Tree Materials

Symptom

Trees appear grey instead of green after cache load.

Root Cause

createDissolveMaterial() used instanceof MeshStandardMaterial which fails for MeshStandardNodeMaterial in the WebGPU build:

Fix

Replace with duck-type property check:

Cache Version Bump

Bumped PROCESSED_CACHE_VERSION from 2 to 3 to invalidate broken cache entries:
All users will automatically rebuild cache on first load after update.

Debugging Tools

Disable Cache

Clear Cache

Inspect Cache

Error Logging

Cache errors are now logged to console:

Performance Impact

Cache Hit (After Fix)

  • Load Time: ~50ms (IndexedDB read + deserialization)
  • Texture Restoration: Synchronous (no async loading)
  • Memory: Same as uncached (DataTexture uses same memory as Image)

Cache Miss

  • Load Time: ~500-2000ms (GLTF parse + processing)
  • Texture Loading: Async (may cause flicker)
  • Memory: Same (textures loaded either way)

Migration Guide

For Users

No action needed - cache version bump triggers automatic rebuild. If you see missing objects or white textures:
  1. Clear cache: indexedDB.deleteDatabase('hyperscape-processed-models')
  2. Reload page
  3. Cache will rebuild with fixed serialization

For Developers

Testing cache serialization:
Adding new texture types: Ensure texture serialization handles your texture type:

Duplicate Mesh Names

Common Patterns:
  • Blender exports: "", “Cube”, “Cube.001”, “Cube.002”
  • GLTF defaults: “Mesh_0”, “Mesh_1”, “Mesh_1” (duplicate)
  • Empty names: "", "", ""
Why It Happens: Modeling tools don’t enforce unique names, GLTF spec doesn’t require them. Solution: Identity map (object reference) instead of name-based lookup.

Blob URL Lifecycle

Why blob: URLs fail:
  1. Created via URL.createObjectURL(blob)
  2. Valid only for current page session
  3. Revoked on page unload or manual URL.revokeObjectURL()
  4. Invalid after browser restart
Solution: Store raw pixel data, not URLs.

Testing

Test Cases

packages/shared/src/utils/rendering/tests/ModelCache.test.ts:

References