Appearance
MeshService
Central registry, query, and display-state manager for meshes added to the viewer. Assigns deterministic IDs, computes metadata (bounding box, center, layer, owner file), indexes by name/layer/file/bounds, and provides hide/show/ghost/x-ray/isolate operations with material restoration. Also runs a pluggable five-stage pipeline on registered meshes.
Access
Registered under the name "mesh". Depends on loader and material.
ts
import { MeshService } from "@optellix/xviewr-sdk";
await viewer.ready();
const mesh = viewer.getService<MeshService>("mesh");
const stats = mesh?.getRegistryStats();Meshes loaded via LoaderService are registered automatically. Custom geometry must be registered explicitly:
ts
const metadata = await mesh.registerMesh(myObject, { ownerFile: "custom" });Public API
Lifecycle
init(context: ServiceContext): Promise<void>destroy(): Promise<void>getStatus(): ServiceStatus
Registration
registerMesh(mesh, options?): Promise<XViewerMeshMetadata>— options:ownerFile,parentPath,customId,skipPipeline.unregisterMesh(xvId: string): Promise<void>updateMeshMetadata(xvId: string, changes: Partial<XViewerMeshMetadata>): void
Lookup
getById(xvId): THREE.Object3D | nullfindByUnifiedId(id): THREE.Object3D | nullgetMetadataById(xvId): XViewerMeshMetadata | nullgetAllMeshes(): THREE.Object3D[]find(predicate: MeshPredicate): THREE.Object3D[]findByName(name): THREE.Object3D[]findByLayer(layer): THREE.Object3D[]findByOwnerFile(filename): THREE.Object3D[]findInBounds(bounds: THREE.Box3): THREE.Object3D[]getChildren(xvId): THREE.Object3D[]getOwnerFile(xvId): string | nullgetRegistryStats(): { totalMeshes; indexedMeshes; fileCount; layerCount }
Neighbour queries
findNeighboursByContact(sources, depth?: number): THREE.Object3D[]— bounding-box contact, BFS todepth.findNeighboursByRadius(sources, radius, options?: MeshRadiusNeighbourOptions): THREE.Object3D[]
Display operations
Targets accept string (xvId), THREE.Object3D, or arrays of either.
hide(targets): numbershow(targets): numbershowAll(): voidghost(targets, options?: MeshGhostOptions): number— semi-transparent overlay.xray(targets, options?: MeshXRayOptions): numberisolate(targets, options?: MeshIsolateOptions): void— hides, ghosts, or x-rays the rest depending onghostOthers/xrayOthers.clearIsolation(): voidclearGhosting(): voidclearXRay(): voidrestore(targets?): void— restore original materials and visibility.getDisplayIntent(object): MeshDisplayIntent
Pipeline
addPipelineFunction(stage: PipelineStage, fn: MeshPipelineFunction, options?: { priority?: number; name?: string }): voidremovePipelineFunction(stage: PipelineStage, fn: MeshPipelineFunction): voidsetPipelineStageEnabled(stage: PipelineStage, enabled: boolean): voidprocessMeshThroughPipeline(mesh, metadata, stage): Promise<void>processAllMeshes(stage: PipelineStage): Promise<void>
PipelineStage enum: PRE_PROCESS, COMPUTE, METADATA, INDEX, POST_PROCESS.
Types
XViewerMeshMetadata
ts
interface XViewerMeshMetadata {
xvId: string;
xvPath: string;
xvName: string;
xvLayer?: string;
xvBoundingBox?: THREE.Box3;
xvCenterPoint?: THREE.Vector3;
xvVolume?: number;
xvSurfaceArea?: number;
xvOwnerFile?: string;
xvTags?: string[];
xvParentId?: string;
xvChildIds?: string[];
xvCreatedAt: number;
xvUpdatedAt: number;
[key: string]: any;
}MeshNeighbourOptions
ts
interface MeshNeighbourOptions {
includeHidden?: boolean;
includeContainers?: boolean;
}
interface MeshRadiusNeighbourOptions extends MeshNeighbourOptions {
centerMode?: "selection" | "individual"; // default: "selection"
}MeshPredicate
ts
type MeshPredicate = (
mesh: THREE.Object3D,
metadata: XViewerMeshMetadata,
) => boolean;MeshPipelineFunction / MeshPipelineStage
ts
interface MeshPipelineFunction {
(
mesh: THREE.Object3D,
metadata: XViewerMeshMetadata,
context: ServiceContext,
): Promise<void> | void;
}
interface MeshPipelineStage {
name: string;
priority: number;
enabled: boolean;
functions: MeshPipelineFunction[];
}MeshLifecycleEvents
Map of event names to payloads. See Events below.
Label / LabelFilter
Used by the label service for attaching typed metadata to a target mesh.
ts
interface Label {
id: string;
type: string;
targetMeshId: string;
localPosition: [number, number, number];
payload?: Record<string, any>;
tags?: string[];
createdAt: number;
updatedAt: number;
}
interface LabelFilter {
types?: string[];
meshIds?: string[];
tags?: string[];
}Events
| Event | Payload |
|---|---|
mesh:registered | { mesh; metadata } |
mesh:unregistered | { xvId; mesh; metadata } |
mesh:updated | { mesh; metadata; changes } |
mesh:display-updated | { object; intent: MeshDisplayIntent } |
mesh:pipeline-started | { stage; mesh; metadata } |
mesh:pipeline-completed | { stage; mesh; metadata } |
mesh:pipeline-error | { stage; mesh; metadata; error } |
mesh:batch-processed | { count; stage } |
Edge line utilities
Helpers for loading and rendering edge overlays (line drawings on top of meshes). Edge payloads live in userData["xde:edges"] as arrays of { pts: number[]; len?: number }.
ts
import {
decodeInlineEdges,
loadEdgesFromJson,
buildEdgeLinesPerMesh,
disposeEdgeLines,
} from "@optellix/xviewr-sdk";
import type { EdgeLineOptions } from "@optellix/xviewr-sdk";decodeInlineEdges(scene: THREE.Object3D): boolean— decode base64 edge payloads embedded in GLB extras. Handles chunked (xde:edges_count+xde:edges_0..N) and single-string formats. Returnstrueif any edges were decoded.loadEdgesFromJson(url: string, scene: THREE.Object3D): Promise<{ success: boolean; appliedCount: number }>— fetchesurlorurl + ".gz"and attaches edges to nodes byuserData.xv_uuid.buildEdgeLinesPerMesh(scene: THREE.Object3D, options?: EdgeLineOptions): THREE.Object3D[]— builds oneLineSegments2child per node that has edge data. Returns the created objects.disposeEdgeLines(scene: THREE.Object3D): void— removes and disposes everyLineSegments2withuserData.isEdges.
ts
interface EdgeLineOptions {
color?: number; // default 0x000000
linewidth?: number; // default 1.5 (LineMaterial units)
resolution?: THREE.Vector2; // default window or 1920x1080
}Example
ts
const mesh = viewer.getService<MeshService>("mesh");
if (!mesh) return;
// Isolate a part and ghost the rest
const target = mesh.findByName("M8 bolt")[0];
mesh.isolate(target, { ghostOthers: true, opacity: 0.1 });
// Find everything within 50mm of the selection
const nearby = mesh.findNeighboursByRadius([target], 50, {
centerMode: "individual",
});
// Custom pipeline stage
mesh.addPipelineFunction(
PipelineStage.METADATA,
(object, metadata) => {
if (metadata.xvName.startsWith("FIX_")) metadata.xvTags = ["fixture"];
},
{ name: "tag-fixtures" },
);
// Edge overlays
decodeInlineEdges(viewer.scene);
buildEdgeLinesPerMesh(viewer.scene, { color: 0x222222, linewidth: 2 });