Skip to content

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 | null
  • findByUnifiedId(id): THREE.Object3D | null
  • getMetadataById(xvId): XViewerMeshMetadata | null
  • getAllMeshes(): 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 | null
  • getRegistryStats(): { totalMeshes; indexedMeshes; fileCount; layerCount }

Neighbour queries

  • findNeighboursByContact(sources, depth?: number): THREE.Object3D[] — bounding-box contact, BFS to depth.
  • findNeighboursByRadius(sources, radius, options?: MeshRadiusNeighbourOptions): THREE.Object3D[]

Display operations

Targets accept string (xvId), THREE.Object3D, or arrays of either.

  • hide(targets): number
  • show(targets): number
  • showAll(): void
  • ghost(targets, options?: MeshGhostOptions): number — semi-transparent overlay.
  • xray(targets, options?: MeshXRayOptions): number
  • isolate(targets, options?: MeshIsolateOptions): void — hides, ghosts, or x-rays the rest depending on ghostOthers / xrayOthers.
  • clearIsolation(): void
  • clearGhosting(): void
  • clearXRay(): void
  • restore(targets?): void — restore original materials and visibility.
  • getDisplayIntent(object): MeshDisplayIntent

Pipeline

  • addPipelineFunction(stage: PipelineStage, fn: MeshPipelineFunction, options?: { priority?: number; name?: string }): void
  • removePipelineFunction(stage: PipelineStage, fn: MeshPipelineFunction): void
  • setPipelineStageEnabled(stage: PipelineStage, enabled: boolean): void
  • processMeshThroughPipeline(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

EventPayload
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. Returns true if any edges were decoded.
  • loadEdgesFromJson(url: string, scene: THREE.Object3D): Promise<{ success: boolean; appliedCount: number }> — fetches url or url + ".gz" and attaches edges to nodes by userData.xv_uuid.
  • buildEdgeLinesPerMesh(scene: THREE.Object3D, options?: EdgeLineOptions): THREE.Object3D[] — builds one LineSegments2 child per node that has edge data. Returns the created objects.
  • disposeEdgeLines(scene: THREE.Object3D): void — removes and disposes every LineSegments2 with userData.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 });

XViewr SDK and xConvert CLI documentation.