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.
68 lines (67 loc) • 4.16 kB
TypeScript
import type { VirtualMeshData } from '../preprocess/buildVirtualMesh.js';
/** Camera parameters the LOD test needs. Same values the GPU pass uses. */
export interface CutView {
/** Column-major world-to-view matrix (camera.matrixWorldInverse.elements). */
viewMatrix: ArrayLike<number>;
/** projection[1][1] * viewportHeight / 2: converts view-space error to pixels. */
projScale: number;
near: number;
/**
* Uniform scale of the instance, applied to the sphere radii and errors as the GPU does. `viewMatrix`
* must then include the instance's scale in its centers too (view * model). Default 1.
*/
scale?: number;
}
/**
* CPU reference for the GPU LOD selection (no frustum culling, identity model matrix).
* A meshlet is in the cut when its own projected error is within `threshold` pixels and its
* parent's projected error is not. Returns one flag per meshlet.
*
* Use it to test the DAG (see `verifyCutCoverage`) or to pick LODs on the CPU, e.g. for physics or AI.
*/
export declare function selectCut(data: VirtualMeshData, view: CutView, threshold: number): Uint8Array;
/**
* CPU reference for the GPU selection with LOD blending (see `VirtualGeometry.lodBlend`): every meshlet that is in
* the cut for some threshold in [threshold, threshold * blend], with the blend steps it draws, [lo, hi) of
* 0..LOD_FADE_STEPS - 1. `hi[i] === 0` means meshlet i is not drawn. The meshlets with lo <= s < hi form a complete
* cut for every step s.
*/
export declare function selectBlendCut(data: VirtualMeshData, view: CutView, threshold: number, blend: number): {
lo: Uint8Array;
hi: Uint8Array;
};
/**
* Checks that a cut draws no surface twice: no selected meshlet has a selected meshlet among its coarser
* replacements (recursively). Returns the indices of selected meshlets that overlap a coarser selected one.
*/
export declare function verifyCutOverlap(data: VirtualMeshData, selected: Uint8Array): number[];
/**
* Checks that a cut has no holes: every leaf meshlet (LOD 0) is covered by a selected meshlet on its
* path to the coarsest level (itself or one of its replacements, recursively).
* Returns the indices of uncovered leaves (empty array = valid cut).
*/
export declare function verifyCutCoverage(data: VirtualMeshData, selected: Uint8Array): number[];
/**
* CPU reference for the GPU instance pass: the contiguous range of meshlets (sorted by LOD level)
* that can possibly pass the LOD test for an instance, from distance bounds alone.
*
* Every LOD and parent sphere lies inside `lodBoundsSphere`, so the distance to any of them is within
* [D - R, D + R]. A level can contribute only if its smallest own error could be within the threshold
* at the farthest distance and its largest parent error could exceed it at the nearest. With `blend` (see
* `selectBlendCut`), the band [threshold, threshold * blend].
* Returns [start, end) or [0, 0] when nothing can be selected.
*/
export declare function instanceMeshletRange(data: VirtualMeshData, view: CutView, threshold: number, blend?: number): [number, number];
/**
* CPU reference for the GPU fast path of the instance pass. Returns the range [start, end) when every
* meshlet in it is guaranteed to be selected by the LOD test, for any distance the instance can have, and
* the range is at most FAST_PATH_MAX_MESHLETS long. Otherwise returns null (use the per-meshlet pass).
*
* Guarantee per level, over the distance band [dMin, dMax] of `instanceMeshletRange`:
* - largest own error projects to <= threshold at dMin (its closest possible distance), and
* - smallest parent error projects to > threshold at dMax (its farthest possible distance).
* A small relative margin keeps float rounding on the GPU from flipping a borderline meshlet.
* With `blend`, guaranteed means drawn over every pixel: own error below the band, parent error above it.
* The caller must also check that the instance is fully inside the frustum (see `cullBoundsSphere`).
*/
export declare function instanceGuaranteedRange(data: VirtualMeshData, view: CutView, threshold: number, blend?: number): [number, number] | null;