Skip to content

LODService

Streaming LOD service for chunked models. Manages baseline/detail geometry per chunk based on frustum, screen-space coverage, exposure, and an octree-driven evaluation. Enforces a detail-geometry cache budget with watermark-based eviction.

Access

Registered under the name "lod". Depends on loader and mesh. Activates automatically once chunked models register LOD-tagged meshes (via userData.xvLod).

ts
import { LODService } from "@optellix/xviewr-sdk";

await viewer.ready();
const lod = viewer.getService<LODService>("lod");
lod?.updateConfig({ detailUpgradePixels: 200 });
console.log(lod?.getStats());

Public API

Lifecycle

  • init(context: ServiceContext): Promise<void>
  • destroy(): Promise<void>
  • getStatus(): ServiceStatus

Inspection

  • getStats(): LodStats — counts, byte totals, cache pressure, eviction tallies.
  • getEntries(): LodEntry[] — registered LOD entries.

Control

  • updateConfig(config: Partial<LodConfig>): void
  • update(): void — manual evaluation tick. Normally driven by the viewer frame loop.
  • setMoving(moving: boolean): void — pauses detail upgrades while the camera is moving.
  • setLoadingPhase(loading: boolean): void — pauses evaluation during bulk loads.
  • setViewportHeight(height: number): void

Chunked loads can also pass showOnly: "lod0" | "lod1". showOnly: "lod0" loads the baseline chunks and prevents detail upgrades. showOnly: "lod1" still displays the baseline first, eagerly requests detail after baseline registration/loading, then upgrades as detail chunks arrive and keeps upgraded meshes on detail.

Types

LodEntry

ts
interface LodEntry {
  object: THREE.Object3D;
  chunkId: number;
  activeLevel: "baseline" | "detail";
  baselineUrl: string;
  detailUrl?: string;
  baselineLevel?: "lod0" | "lod1";
  detailLevel?: "lod0" | "lod1";
  showOnly?: "lod0" | "lod1" | null;
  exposureScore: number;
  priority: number;
  worldSphere: THREE.Sphere;
  frustumVisible: boolean;
  screenVisible: boolean;
  displayVisible: boolean;
  lodVisible: boolean;
  screenDiameterPx: number;
  detailCandidate: boolean;
  detailLoaded: boolean;
  baselineGeometry?: THREE.BufferGeometry;
  detailGeometry?: THREE.BufferGeometry;
}

LodTransitionMode

ts
type LodTransitionMode = "swap" | "hide-preview";

LodConfig (selected fields)

  • minPixels — minimum screen-space diameter for visibility. Default 150.
  • aggressiveness0..1. Default 0.5.
  • interiorCullMultiplier — extra culling for interior chunks. Default 2.
  • screenHysteresis — prevents flicker around the visibility threshold. Default 0.15.
  • zoomDeltaThreshold, rotationDeltaThreshold, positionDeltaThreshold — movement detection.
  • movementDebounceMs — settling delay after camera stops. Default 200.
  • maxOctreeDepth, maxLeafSize — octree tuning.
  • clusterMinPixels, clusterFarMultiplier, clusterFarDistanceFraction — far-field clustering.
  • frustumMargin — fractional padding. Default 0.15.
  • detailTransitionModeswap or hide-preview.
  • showOnly — optional preferred asset level, "lod0" or "lod1".
  • downgradeDetailOnMove — downgrade visible detail meshes to baseline while moving. Default true.
  • detailUpgradePixels — pixel threshold to upgrade to detail. Default 150.
  • detailExteriorEagerFactor, detailHideRatioExterior, detailHideRatioInterior.
  • detailEvictDelayMs — idle eviction delay. Default 30000.
  • detailCacheBudgetBytes — default 512 MiB.
  • detailCacheHighWatermark, detailCacheLowWatermark0..1.
  • detailCacheMinResidencyMs, detailReloadCooldownMs, detailBudgetSuppressionMs.
  • debugLodMaterials, debugBaselineColor, debugDetailColor — debug overlay.
  • exposureBatchSize, exposureMaxDistanceMultiplier.

LodStats

Includes active, loading, totalEntries, baselineEntries, detailEntries, geometry byte counts, frustum/screen/LOD visibility counts, detail candidate/ready counts, loading/loaded/evictable chunk counts, cache bytes and watermarks, peak cache bytes, largest chunk bytes, lifetime totals (detailLoadedBytesTotal, detailLoadedChunksTotal, detailEvictedBytesTotal, detailEvictedGeometriesTotal, detailEvictionsTotal), and per-reason eviction counts (detailBudgetEvictions, detailBudgetDowngrades, detailIdleEvictions, detailUnloadEvictions, detailDisposeEvictions).

Events

EventPayload
lod:activated{ ... }
lod:entry-registered{ ... }
lod:exposure-updated{ ... }
lod:detail-requested{ ... }
lod:detail-loaded{ ... }
lod:detail-load-failed{ ... }
lod:detail-evicted{ ... }
lod:level-changed{ ... }

Example

ts
const lod = viewer.getService<LODService>("lod");
if (!lod) return;

lod.updateConfig({
  detailCacheBudgetBytes: 1024 * 1024 * 1024,
  detailUpgradePixels: 220,
  downgradeDetailOnMove: false,
  debugLodMaterials: false,
});

viewer.eventBus.on("lod:level-changed", (e) => {
  console.log("level changed", e);
});

setInterval(() => {
  const s = lod.getStats();
  console.log(
    `detail: ${s.detailEntries}/${s.totalEntries}, cache: ${(s.detailCacheBytes / 1e6).toFixed(1)}MB`,
  );
}, 2000);

XViewr SDK and xConvert CLI documentation.