Appearance
AdvancedMeshService
High-performance spatial mesh queries backed by a pluggable provider registry (BVH, Octree, SpatialHash). Sits on top of MeshService and adds contact/proximity neighbour queries, radius search, N-nearest search, raycasting, bounds/frustum intersection, clustering, per-mesh performance analysis, and advanced pipeline stages. The service warms up its broad-phase index lazily and keeps a per-query cache for contact-graph traversals.
Access
Registered under the name "advanced-mesh". Depends on mesh.
ts
import { AdvancedMeshService } from "@optellix/xviewr-sdk";
await viewer.ready();
const advancedMesh = viewer.getService<AdvancedMeshService>("advanced-mesh");
await advancedMesh?.warmupSpatialIndex();Providers are auto-registered at init (spatial-hash, octree, bvh). Call enableProvider(name, config?) to activate one before running queries; the service also auto-enables a broad-phase provider (spatial-hash, falling back to octree) the first time a query needs one.
Public API
Lifecycle
init(context: ServiceContext): Promise<void>destroy(): Promise<void>getStatus(): ServiceStatusconfigure(config: Partial<AdvancedMeshServiceConfig>): void
Provider management
registerProvider(factory: SpatialProviderFactory): voidenableProvider(name, config?: SpatialProviderConfig): Promise<void>disableProvider(name: string): Promise<void>getAvailableProviders(): string[]getActiveProviders(): string[]getProviderInfo(name: string): anygetProviderStats(name: string): anywarmupSpatialIndex(): Promise<void>— force-build the broad-phase index before the first interactive query.beginProviderUpdateBatch(): void/endProviderUpdateBatch(options?: { timeBudgetMs?: number }): Promise<void>— batch mesh registration events so providers rebuild once. Contact cache is cleared on flush.
Neighbour queries
findContactNeighbours(sources: THREE.Object3D[], options?: ContactNeighbourQueryOptions): Promise<ContactNeighbourQueryResult>— BFS through contact graph. Broad-phase AABB filter, followed by sampled BVH proximity check, with optional exact BVH fallback. Results are cached per option-shape until any mesh event fires.findProximityNeighbours(sources: THREE.Object3D[], options: ProximityNeighbourQueryOptions): Promise<SpatialQueryResult[]>— meshes whose bounds land inside a sphere around the source selection (or around each source individually).
Spatial queries
findMeshesInRadius(center, radius, options?: SpatialQueryOptions): Promise<SpatialQueryResult[]>findNearestMeshes(point, count, options?): Promise<MeshDistanceResult[]>raycast(ray, options?: RaycastOptions): Promise<RaycastResult[]>raycastFirst(ray, options?: RaycastOptions): Promise<RaycastResult | null>findIntersections(bounds: THREE.Box3, options?: SpatialQueryOptions): Promise<IntersectionResult[]>findInFrustum(frustum, options?): Promise<SpatialQueryResult[]>checkCollision(meshA, meshB): Promise<boolean>findCollisions(mesh): Promise<CollisionResult[]>performFrustumCulling(camera, includeOcclusion?): Promise<CullingResult>executeAdvancedQuery(query: AdvancedMeshQuery): Promise<SpatialQueryResult[]>
Clustering and analysis
clusterMeshes(algorithm?: ClusteringAlgorithm, parameters?: any): Promise<MeshCluster[]>— built-in algorithms:SPATIAL_PROXIMITY,MATERIAL_BASED,SIZE_BASED.analyzeMeshPerformance(meshes?): Promise<MeshPerformanceAnalysis[]>getPerformanceStats(): { totalQueries; averageQueryTime; memoryUsage; activeSpatialStructures; contactGraphCacheEntries; activeProviders; providerStats }
Pipeline stages
AdvancedPipelineStage extends the mesh processing pipeline with:
ts
enum AdvancedPipelineStage {
BVH_BUILD = "bvh-build",
CLUSTERING = "clustering",
LOD_GENERATE = "lod-generate",
PERFORMANCE_ANALYSIS = "performance-analysis",
QUALITY_ANALYSIS = "quality-analysis",
}The service registers a default PERFORMANCE_ANALYSIS handler that attaches per-mesh analysis onto XViewerMeshMetadata.performanceAnalysis when performanceMonitoring is enabled.
Types
ts
interface ContactNeighbourQueryOptions extends SpatialQueryOptions {
depth?: number; // BFS depth (default 1)
thresholdMm?: number; // contact gap tolerance in mm (default 0)
worldUnitsPerMillimeter?: number; // scale from mm to world units (default 1)
precise?: boolean; // run BVH sampled check (default true)
exactFallback?: boolean; // exact BVH-vs-BVH check on miss (default false)
maxSampledVertices?: number; // per-mesh sampling cap (default 96)
includeHidden?: boolean;
maxCandidatesPerSource?: number;
maxPreciseChecks?: number;
useCache?: boolean; // default true
debug?: boolean; // log per-query stats
}
interface ContactNeighbourQueryResult {
neighbours: THREE.Object3D[];
stats: ContactNeighbourQueryStats;
}
interface ContactNeighbourQueryStats {
depth: number;
thresholdWorld: number;
sources: number;
broadPhaseProvider: string | null;
broadPhaseCandidates: number;
preciseChecks: number;
sampledChecks: number;
exactChecks: number;
cacheHits: number;
cacheMisses: number;
bvhBuilds: number;
queryTimeMs: number;
sourceBreakdown: ContactNeighbourSourceStats[];
}
interface ProximityNeighbourQueryOptions extends SpatialQueryOptions {
radius: number;
centerMode?: "selection" | "individual"; // union vs. per-source spheres
includeHidden?: boolean;
}
interface SpatialQueryOptions {
includeMetadata?: boolean;
sortBy?: "distance" | "size" | "name" | "layer";
sortOrder?: "asc" | "desc";
maxResults?: number;
filterPredicate?: (
mesh: THREE.Object3D,
metadata?: XViewerMeshMetadata,
) => boolean;
}Cache invalidation
The contact-neighbour cache is cleared automatically when:
mesh:registered,mesh:unregistered, ormesh:updatedfires on the event bus.mesh:display-updatedfires (visibility or material change).endProviderUpdateBatch()flushes queued provider updates.
Events
| Event | Payload |
|---|---|
advanced-mesh:provider-registered | { provider, capabilities } |
advanced-mesh:provider-initialized | { provider, meshCount, initTime } |
advanced-mesh:contact-neighbours-queried | { sources, neighbours, stats } |
advanced-mesh:clustering-completed | { algorithm, clusterCount, processingTime } |
advanced-mesh:performance-analysis-complete | { totalMeshes, analysisTime, recommendations } |
Example
ts
const advancedMesh = viewer.getService<AdvancedMeshService>("advanced-mesh");
if (!advancedMesh) return;
// Ensure the broad-phase index is ready before running interactive queries.
await advancedMesh.warmupSpatialIndex();
// Batch mesh registrations so providers rebuild once.
advancedMesh.beginProviderUpdateBatch();
await loader.loadGlbDetachedBatch(urls);
await advancedMesh.endProviderUpdateBatch({ timeBudgetMs: 20 });
// Contact-graph BFS with a 0.5 mm gap tolerance.
const { neighbours, stats } = await advancedMesh.findContactNeighbours(
selection,
{ depth: 2, thresholdMm: 0.5, precise: true, exactFallback: false },
);
console.log(neighbours.length, "contact neighbours in", stats.queryTimeMs, "ms");
// Anything within 25 mm of the selection centroid.
const nearby = await advancedMesh.findProximityNeighbours(selection, {
radius: 25,
centerMode: "selection",
});