three-virtual-geometry
Version:
Virtual geometry for three.js WebGPU: automatic cluster LOD and GPU-driven culling for billions of triangles. Independent implementation, not affiliated with Epic Games.
121 lines (120 loc) • 6.74 kB
TypeScript
/**
* One-call scene import: every static mesh of an Object3D (e.g. a loaded glTF scene) becomes VirtualGeometry geometry.
*
* Meshes are grouped by (geometry, geometry group, material, render flags), so repeated geometry becomes one
* VirtualMesh with one instance per occurrence (InstancedMesh instances included). Each unique geometry range
* is built once. Skinned and morphing meshes, points, lines and sprites are left alone and keep rendering.
*/
import * as THREE from 'three/webgpu';
import { type VirtualGeometryCachedBuildOptions } from '../io/cache.js';
import { type VirtualMeshBuildOptions, type VirtualMeshData, type VirtualMeshSource } from '../preprocess/buildVirtualMesh.js';
import type { VirtualGeometry } from '../runtime/VirtualGeometry.js';
import type { VirtualMesh, VirtualMeshOptions } from '../runtime/VirtualMesh.js';
/** Builds one DAG. Same signature as `buildVirtualMesh`, so a caching builder drops in here or via `options.builder`. */
export type VirtualGeometryBuilder = (source: VirtualMeshSource, options?: VirtualMeshBuildOptions) => Promise<VirtualMeshData>;
export interface VirtualGeometryImportOptions {
/**
* DAG build options for every geometry (`onProgress` is driven by the import; use `onProgress` below), including
* `cache` options for the default cached builder, e.g. `{ cache: { maxBytes: 2 * 1024 ** 3 } }`.
*/
build?: Omit<VirtualGeometryCachedBuildOptions, 'onProgress'>;
/** Options for each created VirtualMesh (e.g. `maxDrawDistance`), or a function returning them per group. */
mesh?: VirtualMeshOptions | ((group: VirtualGeometryImportGroup) => VirtualMeshOptions | undefined);
/** Return false to leave a mesh as it is (it keeps rendering normally). Called for every compatible mesh. */
filter?: (mesh: THREE.Mesh) => boolean;
/**
* true (default): the VirtualMeshes are added to `object` and the converted meshes stop rendering (their
* leaf meshes are taken out of the graph, meshes with children stay with their layers cleared; `dispose()`
* restores both). false: nothing in the scene changes; add `result.object` yourself.
*/
replace?: boolean;
/** Overall progress in 0..1 while building. May be async (awaited), e.g. to let the page repaint. */
onProgress?: (fraction: number) => void | Promise<void>;
/** Converts a source material. Default: node materials are cloned, classic mesh materials go through `toNodeMaterial`. */
material?: (material: THREE.Material, group: VirtualGeometryImportGroup) => THREE.NodeMaterial | null;
/** DAG builder (default `buildVirtualMesh`), e.g. a cached one with the same signature. */
builder?: VirtualGeometryBuilder;
}
/** Meshes that share geometry, geometry range, material and render flags: one VirtualMesh. */
export interface VirtualGeometryImportGroup {
geometry: THREE.BufferGeometry;
/** Index range (vertex range for non-indexed geometry) this group draws: one entry of `geometry.groups`, clipped to the draw range. */
range: {
start: number;
count: number;
};
material: THREE.Material;
/** Column-major world matrix per instance (16 floats each), in the order of `instances`. */
matrices: Float32Array;
/** rgba per instance, from `InstancedMesh.instanceColor` (null when no source has instance colors). */
colors: Float32Array | null;
/** Source of each instance: the mesh, and the instance index for an InstancedMesh (else -1). */
instances: {
object: THREE.Mesh;
index: number;
}[];
/** Per-vertex colors are used (`material.vertexColors` and a `color` attribute), like three would. */
vertexColors: boolean;
castShadow: boolean;
receiveShadow: boolean;
/** False when the mesh or one of its ancestors is hidden. */
visible: boolean;
renderOrder: number;
layers: number;
/** From the first source mesh. */
name: string;
userData: Record<string, unknown>;
}
/** Why a mesh is left alone, or null when it can be converted. */
export declare function incompatibility(mesh: THREE.Object3D): string | null;
/**
* Collects the convertible meshes under `root` into instance groups (pure CPU work, no GPU, testable in Node).
* World matrices are read after updating them, so the result matches what three would render now.
*/
export declare function collectVirtualGeometryGroups(root: THREE.Object3D, filter?: (mesh: THREE.Mesh) => boolean): {
groups: VirtualGeometryImportGroup[];
meshes: THREE.Mesh<THREE.BufferGeometry<THREE.NormalBufferAttributes, THREE.BufferGeometryEventMap>, THREE.Material<THREE.MaterialEventMap>[] | THREE.Material<THREE.MaterialEventMap>, THREE.Object3DEventMap>[];
skipped: {
object: THREE.Object3D;
reason: string;
}[];
};
/** Result of `VirtualGeometry.add()`: the created meshes, and how to undo the import. */
export declare class VirtualGeometryImport {
private readonly context;
/** Holds every created VirtualMesh. Added to the imported object unless `replace: false`. */
readonly object: THREE.Group<THREE.Object3DEventMap>;
/** One VirtualMesh per entry of `groups`. */
readonly meshes: VirtualMesh[];
groups: VirtualGeometryImportGroup[];
/** Meshes that were left alone, and why. */
readonly skipped: {
object: THREE.Object3D;
reason: string;
}[];
readonly stats: {
sourceMeshes: number;
instances: number;
uniqueGeometries: number;
buildMs: number;
};
private readonly hidden;
/** Converted leaf meshes taken out of the graph, with their former parent (restored by `dispose`). */
private readonly detached;
constructor(context: VirtualGeometry, collected: ReturnType<typeof collectVirtualGeometryGroups>);
/** @internal Stops the given source meshes from rendering (restored by `dispose`). */
hide(meshes: THREE.Mesh[], root: THREE.Object3D): void;
/** World matrix of a source object, also when it (or its ancestors) were taken out of the graph. */
private worldMatrixOf;
/**
* Re-reads every source mesh's current world matrix into the instances (call after moving the imported
* object or animating its nodes). Instances are baked in world space at import time otherwise.
*/
syncTransforms(): void;
/** Removes the VirtualMeshes and lets the source meshes render again. */
dispose(): void;
}
/**
* Converts every compatible mesh under `object` to VirtualGeometry geometry. See `VirtualGeometry.add`.
*/
export declare function virtualMeshesFromObject3D(context: VirtualGeometry, object: THREE.Object3D, options?: VirtualGeometryImportOptions): Promise<VirtualGeometryImport>;