Instanced Rendering API Reference
This document describes the instanced rendering system for resource entities (trees, rocks, ores, herbs) in Hyperscape.Overview
Hyperscape uses instanced rendering to dramatically reduce draw calls for resource entities. Instead of O(n) draw calls per resource, the system uses O(1) draw calls per unique model per LOD level.Architecture
GLBResourceInstancer
Location:packages/shared/src/systems/shared/world/GLBResourceInstancer.ts
Pools instances by model path with separate InstancedMesh per LOD level.
Constructor
Methods
addInstance(entity: ResourceEntity, lodLevel: number): number
Adds an entity instance at the specified LOD level.
Returns: Instance index
Example:
removeInstance(entity: ResourceEntity): void
Removes an entity instance from all LOD levels.
updateInstance(entity: ResourceEntity, lodLevel: number): void
Updates instance transform and visibility.
setLODLevel(entity: ResourceEntity, newLOD: number): void
Switches entity to a different LOD level with hysteresis to prevent flickering.
dispose(): void
Cleans up all instanced meshes and releases GPU resources.
GLBTreeInstancer
Location:packages/shared/src/systems/shared/world/GLBTreeInstancer.ts
Specialized instancer for tree resources with dissolve materials and depleted model support.
Depleted Models (NEW)
Trees can specifydepletedModelPath and depletedModelScale in their resource configuration:
Methods
transitionToDepleted(entity: ResourceEntity): boolean
Transitions entity from tree to stump model.
Returns: true if transition succeeded, false if depleted model not configured
Example:
getHighlightMesh(entity: ResourceEntity): Mesh | null (NEW)
Returns a highlight mesh for hover/selection on instanced entities.
Usage:
InstancedModelVisualStrategy
Location:packages/shared/src/entities/world/visuals/InstancedModelVisualStrategy.ts
Thin wrapper strategy that uses instancers with invisible collision proxies for raycasting.
Constructor
Methods
onDepleted(): boolean (CHANGED)
Handles resource depletion.
Returns:
true= strategy handled depletion (instanced stump)false= ResourceEntity should load individual depleted model
void to boolean
Migration:
getHighlightMesh(ctx: RenderContext): Mesh | null (NEW)
Returns highlight mesh for instanced entity.
Usage:
ResourceEntity
Location:packages/shared/src/entities/world/ResourceEntity.ts
Methods
getHighlightRoot(): Object3D (NEW)
Returns the root object for highlighting (supports instanced meshes).
Usage:
EntityHighlightService now supports instanced entity highlighting.
Configuration
Resource Manifest
AdddepletedModelPath and depletedModelScale to resource configs:
Performance Benefits
Draw Call Reduction
Before (individual meshes):- 1000 oak trees = 1000 draw calls
- 500 willow trees = 500 draw calls
- Total: 1500 draw calls
- 1000 oak trees = 1 draw call (per LOD level)
- 500 willow trees = 1 draw call (per LOD level)
- Total: 2-6 draw calls (depending on LOD levels active)
Memory Efficiency
- Single geometry shared across all instances
- Minimal per-instance data (transform matrix)
- Automatic LOD switching reduces vertex count at distance
LOD System Integration
Instanced rendering integrates with distance-based LOD:Fallback Behavior
If instancing fails (e.g., model loading error), the system automatically falls back toStandardModelVisualStrategy:
Collision Proxies
Instanced entities use invisible collision proxies for raycasting:- Proxy persists across state transitions (tree → stump)
- Enables mouse hover and selection on instanced meshes
- Minimal performance overhead
Model Cache Integration
Index Buffer Type Preservation (CRITICAL FIX)
Location:packages/shared/src/utils/rendering/ModelCache.ts
Issue: Model cache was not preserving original index buffer type (Uint16Array vs Uint32Array), causing silent geometry corruption and RangeError crashes.
Fix: Cache version bumped to 4, now preserves index buffer type:
Related APIs
EntityHighlightService
Location:packages/shared/src/systems/client/interaction/services/EntityHighlightService.ts
Now supports instanced highlight meshes via getHighlightRoot():
ResourceVisualStrategy
Location:packages/shared/src/entities/world/visuals/ResourceVisualStrategy.ts
Base interface for all resource visual strategies.
onDepleted(): boolean
Called when resource is depleted.
Returns:
true= strategy handled depletionfalse= entity should load depleted model
getHighlightMesh(ctx: RenderContext): Mesh | null (OPTIONAL)
Returns highlight mesh for entity.
Default: Returns null (no custom highlight)
Example: Creating a Custom Instanced Resource
Debugging
Check Instance Counts
Verify Geometry Disposal
Monitor Draw Calls
Use Chrome DevTools Performance tab:- Record performance
- Look for “Draw calls” in GPU section
- Should see dramatic reduction with instancing
Performance Considerations
When to Use Instancing
Good candidates:- Many instances of same model (>10)
- Static or infrequently updated transforms
- Shared material properties
- Unique models (only 1-2 instances)
- Frequently changing materials
- Per-instance material variations
Memory Trade-offs
Pros:- Reduced draw calls (major GPU performance win)
- Shared geometry (reduced VRAM)
- Efficient transform updates
- All instances share same material
- Matrix updates require buffer uploads
- Collision proxies add minimal overhead
Related Documentation
- CLAUDE.md - Performance Optimizations section
- AGENTS.md - Instanced Rendering section
- Model Cache Integrity