three-cad-viewer 4.3.8 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{README.md → Readme.md} +7 -5
- package/dist/core/picking-controller.d.ts +125 -0
- package/dist/core/studio-manager.d.ts +7 -0
- package/dist/core/types.d.ts +24 -6
- package/dist/core/viewer-state.d.ts +1 -1
- package/dist/core/viewer.d.ts +44 -47
- package/dist/index.d.ts +5 -5
- package/dist/rendering/environment.d.ts +3 -19
- package/dist/rendering/highlight.d.ts +209 -0
- package/dist/rendering/id-picking.d.ts +464 -0
- package/dist/rendering/light-detection.d.ts +3 -3
- package/dist/rendering/material-factory.d.ts +11 -7
- package/dist/rendering/picked.d.ts +55 -0
- package/dist/rendering/studio-composer.d.ts +3 -3
- package/dist/rendering/tree-model.d.ts +1 -1
- package/dist/scene/clipping.d.ts +53 -0
- package/dist/scene/nestedgroup.d.ts +46 -4
- package/dist/scene/objectgroup.d.ts +17 -0
- package/dist/scene/render-shape.d.ts +3 -19
- package/dist/three-cad-viewer.css +54 -20
- package/dist/three-cad-viewer.esm.js +4867 -1562
- package/dist/three-cad-viewer.esm.js.map +1 -1
- package/dist/three-cad-viewer.esm.min.js +3 -3
- package/dist/three-cad-viewer.js +4867 -1562
- package/dist/three-cad-viewer.min.js +4 -4
- package/dist/tools/cad_tools/measure.d.ts +8 -9
- package/dist/tools/cad_tools/mesh-measure.d.ts +264 -0
- package/dist/tools/cad_tools/select.d.ts +4 -3
- package/dist/tools/cad_tools/tools.d.ts +17 -5
- package/dist/tools/cad_tools/ui.d.ts +8 -6
- package/dist/ui/display.d.ts +25 -2
- package/dist/utils/utils.d.ts +1 -1
- package/package.json +4 -1
- package/dist/rendering/raycast.d.ts +0 -111
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import type { ComponentRegistry } from "./id-picking.js";
|
|
3
|
+
/**
|
|
4
|
+
* Shader-based component highlight (compact graph).
|
|
5
|
+
*
|
|
6
|
+
* Design:
|
|
7
|
+
* - Per-component highlight state lives in ONE `R8UI` data texture indexed by
|
|
8
|
+
* `componentId` (the attribute already on the compact geometry). The texture is
|
|
9
|
+
* shared by every compact visual material via `onBeforeCompile`, so a state
|
|
10
|
+
* write is reflected by all materials with no recompile.
|
|
11
|
+
* - State is BIT FLAGS ({@link HighlightFlag}); SELECTED wins over HOVER.
|
|
12
|
+
* - `selectSolid` sets the flag for every registry component sharing a `solidPath`.
|
|
13
|
+
*
|
|
14
|
+
* Driven by the live event loop via `IdPicker.pickAt → registry → controller`.
|
|
15
|
+
*/
|
|
16
|
+
/** Highlight color for a selected component (was `ObjectGroup.HIGHLIGHT_COLOR_SELECTED`). */
|
|
17
|
+
export declare const HIGHLIGHT_COLOR_SELECTED = 5480675;
|
|
18
|
+
/** Highlight color for a hovered, not-selected component (was `HIGHLIGHT_COLOR_HOVER`). */
|
|
19
|
+
export declare const HIGHLIGHT_COLOR_HOVER = 9026019;
|
|
20
|
+
/**
|
|
21
|
+
* Per-topo FOCUS BASE sizes (was `ObjectGroup.vertexFocusSize` / `edgeFocusWidth`).
|
|
22
|
+
* These are injected PER-MATERIAL by the `patch*Material` methods (a `#define` /
|
|
23
|
+
* material-local uniform), NOT via the shared {@link HighlightUniforms} — edges
|
|
24
|
+
* (5) and vertices (8) need different values, and the hover-vs-selected `−2` delta
|
|
25
|
+
* (`objectgroup.ts:285-298 widen()`) is resolved in-shader from the {@link
|
|
26
|
+
* HighlightFlag} bits: `HOVER → base`, `SELECTED && !HOVER → base − 2`, else the
|
|
27
|
+
* material's authored size.
|
|
28
|
+
*/
|
|
29
|
+
export declare const VERTEX_FOCUS_SIZE = 6;
|
|
30
|
+
export declare const EDGE_FOCUS_WIDTH = 5;
|
|
31
|
+
/**
|
|
32
|
+
* Per-component highlight state, stored as bit flags in one texel of the state
|
|
33
|
+
* texture. SELECTED takes precedence over HOVER when both are set, so hovering an
|
|
34
|
+
* already-selected component keeps the selected color (matches the old
|
|
35
|
+
* `_getHighlightColor` / `unhighlight(true)`).
|
|
36
|
+
*/
|
|
37
|
+
export declare const HighlightFlag: {
|
|
38
|
+
readonly NONE: 0;
|
|
39
|
+
readonly SELECTED: number;
|
|
40
|
+
readonly HOVER: number;
|
|
41
|
+
};
|
|
42
|
+
export type HighlightFlagValue = (typeof HighlightFlag)[keyof typeof HighlightFlag];
|
|
43
|
+
/**
|
|
44
|
+
* Width of the highlight-state data texture in texels. Height grows with the
|
|
45
|
+
* component count: `height = ceil((maxId + 1) / WIDTH)`. A component's texel is at
|
|
46
|
+
* `(id % WIDTH, floor(id / WIDTH))` — the same mapping the shader recomputes.
|
|
47
|
+
*/
|
|
48
|
+
export declare const HIGHLIGHT_STATE_TEXTURE_WIDTH = 2048;
|
|
49
|
+
/** Uniform: the shared `usampler2D` highlight-state texture (R8UI). */
|
|
50
|
+
export declare const U_HIGHLIGHT_STATE = "uHighlightState";
|
|
51
|
+
/** Uniform: texture width, for `id -> ivec2` texel coordinates. */
|
|
52
|
+
export declare const U_HIGHLIGHT_TEX_WIDTH = "uHighlightTexWidth";
|
|
53
|
+
/** Uniform: selected color (linear RGB vec3). */
|
|
54
|
+
export declare const U_HIGHLIGHT_SELECTED_COLOR = "uHighlightSelectedColor";
|
|
55
|
+
/** Uniform: hover color (linear RGB vec3). */
|
|
56
|
+
export declare const U_HIGHLIGHT_HOVER_COLOR = "uHighlightHoverColor";
|
|
57
|
+
/**
|
|
58
|
+
* The shared `THREE.IUniform` set bound into every patched compact material. One
|
|
59
|
+
* instance per {@link HighlightController}; the same object is referenced by all
|
|
60
|
+
* materials so a single write updates them together. Property names MUST match the
|
|
61
|
+
* `U_HIGHLIGHT_*` GLSL identifier constants above.
|
|
62
|
+
*
|
|
63
|
+
* Intentionally TOPO-AGNOSTIC: focus sizes are per-topo and the original (authored)
|
|
64
|
+
* size is per-material, so those are injected by `patch*Material` per material —
|
|
65
|
+
* NOT here. See {@link VERTEX_FOCUS_SIZE} / {@link EDGE_FOCUS_WIDTH}.
|
|
66
|
+
*/
|
|
67
|
+
export interface HighlightUniforms {
|
|
68
|
+
uHighlightState: {
|
|
69
|
+
value: THREE.DataTexture | null;
|
|
70
|
+
};
|
|
71
|
+
uHighlightTexWidth: {
|
|
72
|
+
value: number;
|
|
73
|
+
};
|
|
74
|
+
uHighlightSelectedColor: {
|
|
75
|
+
value: THREE.Color;
|
|
76
|
+
};
|
|
77
|
+
uHighlightHoverColor: {
|
|
78
|
+
value: THREE.Color;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Minimal material surface the patch methods need. Declared structurally because
|
|
83
|
+
* three's examples-jsm `LineMaterial` has a `vertexColors: string | boolean` field
|
|
84
|
+
* that is not assignable to the nominal `THREE.Material` type.
|
|
85
|
+
*/
|
|
86
|
+
type PatchableMaterial = Pick<THREE.Material, "onBeforeCompile" | "customProgramCacheKey" | "needsUpdate" | "userData">;
|
|
87
|
+
/**
|
|
88
|
+
* Owns the per-component highlight state texture and patches compact visual
|
|
89
|
+
* materials to read it. Created by the compact `NestedGroup` alongside its
|
|
90
|
+
* `ComponentRegistry`.
|
|
91
|
+
*
|
|
92
|
+
* Lifecycle: construct → `patch*Material` on each compact face/edge/vertex visual
|
|
93
|
+
* material as it is built → `resize(registry.maxId)` once all components are
|
|
94
|
+
* registered → `setHover` / `setSelected` / `selectSolid` / `clear` to drive
|
|
95
|
+
* highlight → `dispose`.
|
|
96
|
+
*/
|
|
97
|
+
export declare class HighlightController {
|
|
98
|
+
/** The registry whose ids index the state texture. */
|
|
99
|
+
readonly registry: ComponentRegistry;
|
|
100
|
+
/** Shared uniforms bound into every patched material. */
|
|
101
|
+
readonly uniforms: HighlightUniforms;
|
|
102
|
+
/** Backing R8UI state texture (one byte = {@link HighlightFlag} bits per id). */
|
|
103
|
+
private texture;
|
|
104
|
+
/** CPU-side mirror of the texture data (length = `capacity`). */
|
|
105
|
+
private data;
|
|
106
|
+
/** Number of texels currently allocated (= width * height ≥ maxId + 1). */
|
|
107
|
+
private capacity;
|
|
108
|
+
/** Ids currently carrying the HOVER flag (one component, or a whole solid). */
|
|
109
|
+
private hoverIds;
|
|
110
|
+
/** Cheap identity of the current hover target so a repeat is a no-op. */
|
|
111
|
+
private hoverKey;
|
|
112
|
+
/**
|
|
113
|
+
* @param registry - the compact group's component registry; sizes the texture
|
|
114
|
+
* and resolves `solidPath` for {@link selectSolid}.
|
|
115
|
+
*/
|
|
116
|
+
constructor(registry: ComponentRegistry);
|
|
117
|
+
/**
|
|
118
|
+
* Allocate an R8UI data texture (+ CPU mirror) holding at least `texelCount`
|
|
119
|
+
* texels. `NearestFilter`, no mips — the shader reads exact integer flags via
|
|
120
|
+
* `texelFetch`. This is the FROZEN texture format.
|
|
121
|
+
*/
|
|
122
|
+
private _allocate;
|
|
123
|
+
/** The shared state texture (re-created by {@link resize}). */
|
|
124
|
+
get stateTexture(): THREE.DataTexture;
|
|
125
|
+
/**
|
|
126
|
+
* Set or clear `bit` on a component's texel and flag the texture for re-upload
|
|
127
|
+
* on change. The linear data index equals the id (row-major, width-W texels), so
|
|
128
|
+
* texel `(id % W, floor(id / W))` is `data[id]`. Ignores background (0) and ids
|
|
129
|
+
* past the current capacity (caller must {@link resize} first).
|
|
130
|
+
*/
|
|
131
|
+
private _setBit;
|
|
132
|
+
/**
|
|
133
|
+
* Move the HOVER flag onto exactly the given `ids` (clearing it from the
|
|
134
|
+
* previously hovered set). `key` is a cheap identity so a repeat target is a
|
|
135
|
+
* no-op — without it, re-hovering the same target every mouse-move would toggle
|
|
136
|
+
* the bits off→on and re-upload the texture each frame. Does not touch SELECTED.
|
|
137
|
+
*/
|
|
138
|
+
private _applyHover;
|
|
139
|
+
/**
|
|
140
|
+
* Move the HOVER flag onto a single component `id` (clearing the previous hover),
|
|
141
|
+
* or clear hover entirely when `id` is `null`/background.
|
|
142
|
+
*/
|
|
143
|
+
setHover(id: number | null): void;
|
|
144
|
+
/**
|
|
145
|
+
* HOVER a whole solid (its FACES only — see {@link selectSolid}), or clear hover
|
|
146
|
+
* when `null`. Mirrors {@link selectSolid} for the transient hover state.
|
|
147
|
+
*/
|
|
148
|
+
setHoverSolid(solidPath: string | null): void;
|
|
149
|
+
/** Set or clear the SELECTED flag for a single component id. */
|
|
150
|
+
setSelected(id: number, flag: boolean): void;
|
|
151
|
+
/** Whether a component currently carries the SELECTED flag. */
|
|
152
|
+
isSelected(id: number): boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Whether a solid is selected — every one of its faces carries SELECTED (false if it
|
|
155
|
+
* has no faces). Used for solid toggle, so a solid that is only partially selected
|
|
156
|
+
* (e.g. one face previously single-selected) is treated as not-selected and a click
|
|
157
|
+
* selects the whole solid rather than clearing it.
|
|
158
|
+
*/
|
|
159
|
+
isSolidSelected(solidPath: string): boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Set or clear SELECTED for a whole solid. Flags only the solid's **faces**
|
|
162
|
+
* (`topo === "face"`) — the body tints while edges keep their colour and corners
|
|
163
|
+
* stay hidden. Iterates {@link ComponentRegistry.entries}.
|
|
164
|
+
*/
|
|
165
|
+
selectSolid(solidPath: string, flag: boolean): void;
|
|
166
|
+
/** Clear all highlight state (hover + selection) for every component. */
|
|
167
|
+
clear(): void;
|
|
168
|
+
/**
|
|
169
|
+
* Grow the state texture to hold at least `maxId + 1` texels, preserving existing
|
|
170
|
+
* state, and re-bind the new texture object into {@link uniforms}. No-op when the
|
|
171
|
+
* current capacity already suffices.
|
|
172
|
+
*/
|
|
173
|
+
resize(maxId: number): void;
|
|
174
|
+
/**
|
|
175
|
+
* Shared `onBeforeCompile` installer: binds the shared uniforms, prepends the
|
|
176
|
+
* common vertex/fragment headers, forwards the component id, then runs the
|
|
177
|
+
* topo-specific `customize` (color override / widening). Idempotent per material.
|
|
178
|
+
*/
|
|
179
|
+
private _install;
|
|
180
|
+
/**
|
|
181
|
+
* Install `onBeforeCompile` on a face (`MeshStandardMaterial`) visual material.
|
|
182
|
+
* Overwrites `diffuseColor.rgb` with the highlight color BEFORE lighting (after
|
|
183
|
+
* the base color/map is applied) so a highlighted face is lit exactly as the old
|
|
184
|
+
* `material.color` swap was — selected wins over hover.
|
|
185
|
+
*/
|
|
186
|
+
patchFaceMaterial(material: PatchableMaterial): void;
|
|
187
|
+
/**
|
|
188
|
+
* Install `onBeforeCompile` on an edge (`LineMaterial`) visual material (Option A).
|
|
189
|
+
* Widens the screen-space half-width for flagged segments by patching the stock
|
|
190
|
+
* LineMaterial expansion (`offset *= linewidth;`, screen-space branch) and recolors
|
|
191
|
+
* via the shared fragment override. `vertexColors` axes/trihedron carry no
|
|
192
|
+
* registry id (state 0) so they stay inert.
|
|
193
|
+
*/
|
|
194
|
+
patchEdgeMaterial(material: PatchableMaterial): void;
|
|
195
|
+
/**
|
|
196
|
+
* Install `onBeforeCompile` on a vertex (`PointsMaterial`) visual material.
|
|
197
|
+
* Flagged points widen to the focus size and recolor. When `cullUnhighlighted`
|
|
198
|
+
* (the solid highlight-Points cloud, invisible until selected), non-flagged points
|
|
199
|
+
* are culled — `gl_PointSize = 0` AND a fragment `discard` (the discard is the
|
|
200
|
+
* real guard; size-0 rasterization is driver-defined). Standalone visible vertices
|
|
201
|
+
* (default) keep their authored `size` and color when unflagged.
|
|
202
|
+
*/
|
|
203
|
+
patchVertexMaterial(material: PatchableMaterial, options?: {
|
|
204
|
+
cullUnhighlighted?: boolean;
|
|
205
|
+
}): void;
|
|
206
|
+
/** Dispose the state texture and release tracking. */
|
|
207
|
+
dispose(): void;
|
|
208
|
+
}
|
|
209
|
+
export {};
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import type { Camera } from "../camera/camera.js";
|
|
3
|
+
/**
|
|
4
|
+
* Topology type of a pickable component. Mirrors the values of `TopoFilter`
|
|
5
|
+
* below (minus the `none` sentinel).
|
|
6
|
+
*/
|
|
7
|
+
export type TopoType = "face" | "edge" | "vertex" | "solid";
|
|
8
|
+
/**
|
|
9
|
+
* Filter types for topology-based picking. `none` is the "no filter" sentinel
|
|
10
|
+
* (all topos eligible).
|
|
11
|
+
*/
|
|
12
|
+
export declare const TopoFilter: {
|
|
13
|
+
none: null;
|
|
14
|
+
vertex: "vertex";
|
|
15
|
+
edge: "edge";
|
|
16
|
+
face: "face";
|
|
17
|
+
solid: "solid";
|
|
18
|
+
};
|
|
19
|
+
export type TopoFilterType = (typeof TopoFilter)[keyof typeof TopoFilter];
|
|
20
|
+
/** Reserved component id meaning "nothing under the cursor". */
|
|
21
|
+
export declare const BACKGROUND_ID = 0;
|
|
22
|
+
/**
|
|
23
|
+
* Name of the per-vertex integer buffer attribute carrying the component id on
|
|
24
|
+
* every pickable geometry (faces, edges and vertices). The geometry attaches a
|
|
25
|
+
* `Uint32BufferAttribute` under this name; the pick pass reads it as an **integer**
|
|
26
|
+
* attribute (`gpuType = THREE.IntType`, GLSL3 `in uint`) so ids stay exact past
|
|
27
|
+
* 2^24. The geometry side must set `gpuType = THREE.IntType` on the attribute for
|
|
28
|
+
* the pick shader to read it.
|
|
29
|
+
*/
|
|
30
|
+
export declare const COMPONENT_ID_ATTRIBUTE = "componentId";
|
|
31
|
+
/**
|
|
32
|
+
* three.js camera/object layers reserved for the pick passes.
|
|
33
|
+
*
|
|
34
|
+
* A pickable object lives on visual layer 0 (so the main camera draws it) **and**
|
|
35
|
+
* its topo's pick layer. The pick pass sets the (reused) main camera to a single
|
|
36
|
+
* pick layer so `overrideMaterial` only touches one topo at a time — a mesh shader
|
|
37
|
+
* can't render lines/points. `obj_vertices` points live on `VERTEX` *only* (off the
|
|
38
|
+
* visual pass).
|
|
39
|
+
*/
|
|
40
|
+
export declare const PICK_LAYER: {
|
|
41
|
+
readonly FACE: 1;
|
|
42
|
+
readonly EDGE: 2;
|
|
43
|
+
readonly VERTEX: 3;
|
|
44
|
+
};
|
|
45
|
+
export type PickLayer = (typeof PICK_LAYER)[keyof typeof PICK_LAYER];
|
|
46
|
+
/**
|
|
47
|
+
* Attach the per-vertex `componentId` integer attribute to a pickable geometry: the
|
|
48
|
+
* canonical way to bind {@link COMPONENT_ID_ATTRIBUTE} so the GLSL3 pick shader reads
|
|
49
|
+
* it as `in uint` (`gpuType = IntType` — without it three uploads floats and the bits
|
|
50
|
+
* are wrong). Use `instanced` for fat-line (per-segment) edge geometry.
|
|
51
|
+
*/
|
|
52
|
+
export declare function applyComponentIds(geometry: THREE.BufferGeometry, componentId: Uint32Array, instanced?: boolean): void;
|
|
53
|
+
/**
|
|
54
|
+
* Add `object` to its topo's pick layer additively — it stays on visual layer 0 (the
|
|
55
|
+
* main render pass) AND becomes pickable. For face/edge geometry and visible vertices.
|
|
56
|
+
*/
|
|
57
|
+
export declare function enablePickLayer(object: THREE.Object3D, topo: "face" | "edge" | "vertex"): void;
|
|
58
|
+
/**
|
|
59
|
+
* Put `object` on its topo's pick layer ONLY (off visual layer 0), so the main camera
|
|
60
|
+
* never draws it — for the pick-only `obj_vertices` Points cloud.
|
|
61
|
+
*/
|
|
62
|
+
export declare function setPickLayerExclusive(object: THREE.Object3D, topo: "face" | "edge" | "vertex"): void;
|
|
63
|
+
/**
|
|
64
|
+
* Metadata for one pickable component (face, edge, vertex, or solid). The id-buffer
|
|
65
|
+
* pass reads back an id, and this is what it resolves to.
|
|
66
|
+
*/
|
|
67
|
+
export interface ComponentInfo {
|
|
68
|
+
/** Unique id within the current model; encoded into the pick buffer. Never 0. */
|
|
69
|
+
id: number;
|
|
70
|
+
/** Full path, e.g. "/Assembly/Part1/faces/faces_3". */
|
|
71
|
+
path: string;
|
|
72
|
+
/** Leaf name, e.g. "faces_3". */
|
|
73
|
+
name: string;
|
|
74
|
+
/** Topology type. */
|
|
75
|
+
topo: TopoType;
|
|
76
|
+
/** Subtype of the owning shape (e.g. "solid"), or null. */
|
|
77
|
+
subtype: string | null;
|
|
78
|
+
/**
|
|
79
|
+
* Path of the owning solid when this component is a face/edge/vertex *inside* a
|
|
80
|
+
* solid — pick-only, no tree node. `null` for standalone faces/edges/vertices
|
|
81
|
+
* that are tree leaves in their own right.
|
|
82
|
+
*/
|
|
83
|
+
solidPath: string | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Map a dropdown topo filter to the picker's `TopoType[]`. `none`/`[null]` (the
|
|
87
|
+
* "no filter" sentinel) → `undefined` (all topos eligible).
|
|
88
|
+
*/
|
|
89
|
+
export declare function pickerTopoFilter(filter: TopoFilterType[]): TopoType[] | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* Tree-leaf path that owns a picked component: the owning solid's path for a
|
|
92
|
+
* sub-component, else the component's own path with the topo suffix stripped. This
|
|
93
|
+
* is the leaf the double-click pick and the visibility gate operate on.
|
|
94
|
+
*/
|
|
95
|
+
export declare function leafPath(info: ComponentInfo): string;
|
|
96
|
+
/**
|
|
97
|
+
* Signature of the live clip state, to detect actual clip changes for the picker
|
|
98
|
+
* (used by the render loop's dirty cadence).
|
|
99
|
+
*/
|
|
100
|
+
export declare function clipSignature(planes: THREE.Plane[] | null, intersection: boolean): string;
|
|
101
|
+
/**
|
|
102
|
+
* Flat `id -> ComponentInfo` registry. Replaces the `NestedGroup.groups[id]`
|
|
103
|
+
* explosion for pick lookups. Ids are allocated monotonically starting at 1;
|
|
104
|
+
* id 0 ({@link BACKGROUND_ID}) means "nothing".
|
|
105
|
+
*/
|
|
106
|
+
export declare class ComponentRegistry {
|
|
107
|
+
private byId;
|
|
108
|
+
private nextId;
|
|
109
|
+
constructor();
|
|
110
|
+
/**
|
|
111
|
+
* Register a component and return its freshly allocated id. The returned id is
|
|
112
|
+
* also written onto the stored record's `id` field.
|
|
113
|
+
*/
|
|
114
|
+
register(info: Omit<ComponentInfo, "id">): number;
|
|
115
|
+
/** Look up a component by id; `undefined` for unknown ids or background (0). */
|
|
116
|
+
get(id: number): ComponentInfo | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* Iterate every registered component in registration (id-ascending) order.
|
|
119
|
+
* Used by the highlight controller to resolve solid selection (all components
|
|
120
|
+
* sharing a `solidPath`) without a scene-graph walk.
|
|
121
|
+
*/
|
|
122
|
+
entries(): IterableIterator<ComponentInfo>;
|
|
123
|
+
/**
|
|
124
|
+
* Largest allocated id so far (0 when empty). Ids are allocated contiguously from
|
|
125
|
+
* 1 and NEVER recycled ({@link removeByPathPrefix} deletes records but keeps
|
|
126
|
+
* `nextId`), so this is the high-water mark used as the upper bound for sizing the
|
|
127
|
+
* highlight-state texture; it is `>=` {@link size} (equal until the first removal).
|
|
128
|
+
*/
|
|
129
|
+
get maxId(): number;
|
|
130
|
+
/** Number of registered components. */
|
|
131
|
+
get size(): number;
|
|
132
|
+
/**
|
|
133
|
+
* Drop every component whose path is `prefix` or lies under it (`prefix + "/"`),
|
|
134
|
+
* for {@link Viewer.removePart}. Does NOT recycle ids — surviving components keep
|
|
135
|
+
* their ids (and thus their highlight-state texel), and {@link maxId} stays put.
|
|
136
|
+
*/
|
|
137
|
+
removeByPathPrefix(prefix: string): void;
|
|
138
|
+
/** Drop all entries and reset id allocation. */
|
|
139
|
+
clear(): void;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Pack a component id into an RGBA8 byte tuple (full 32-bit range, low byte in R).
|
|
143
|
+
* The pick fragment shader must write the same layout. Inverse of {@link unpackId}.
|
|
144
|
+
*/
|
|
145
|
+
export declare function packId(id: number): [number, number, number, number];
|
|
146
|
+
/** Decode an RGBA8 tuple (as written by {@link packId}) back into a component id. */
|
|
147
|
+
export declare function unpackId(r: number, g: number, b: number, a: number): number;
|
|
148
|
+
/**
|
|
149
|
+
* Build the per-vertex face component-id attribute for a solid/standalone-face
|
|
150
|
+
* node's EXISTING indexed tessellation, registering one face component per face
|
|
151
|
+
* under `{path}/faces/faces_{i}` in array (backend-enumeration) order.
|
|
152
|
+
*
|
|
153
|
+
* No de-indexing: vertices are pooled per face, so each vertex maps to exactly one
|
|
154
|
+
* face. `collisions` counts vertices referenced by more than one face — it must be
|
|
155
|
+
* 0 (a non-zero value means cross-face sharing, i.e. de-indexing would be required).
|
|
156
|
+
*
|
|
157
|
+
* @param vertexCount - number of vertices in the position pool (`positions.length / 3`)
|
|
158
|
+
* @param triangles - flat index array (with `trianglesPerFace`) OR nested
|
|
159
|
+
* `number[][]` (one face's flat index list per entry)
|
|
160
|
+
* @param trianglesPerFace - per-face triangle counts when `triangles` is flat;
|
|
161
|
+
* `undefined` for the nested format
|
|
162
|
+
*/
|
|
163
|
+
export declare function buildFaceComponentIds(vertexCount: number, triangles: Uint32Array | number[] | number[][], trianglesPerFace: Uint32Array | number[] | undefined, path: string, subtype: string | null, registry: ComponentRegistry): {
|
|
164
|
+
componentId: Uint32Array;
|
|
165
|
+
collisions: number;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Build the per-segment (instanced) edge component-id attribute, registering one
|
|
169
|
+
* edge component per edge under `{path}/edges/edges_{i}` in array order. Produces
|
|
170
|
+
* one id per line segment (one `LineSegments2` instance).
|
|
171
|
+
*
|
|
172
|
+
* @param edges - flat segment endpoints (with `segmentsPerEdge`) OR nested
|
|
173
|
+
* `number[][]` (one edge's flat points per entry, 6 floats per segment)
|
|
174
|
+
* @param segmentsPerEdge - per-edge segment counts when `edges` is flat;
|
|
175
|
+
* `undefined` for the nested format
|
|
176
|
+
*/
|
|
177
|
+
export declare function buildEdgeComponentIds(edges: Float32Array | number[] | number[][], segmentsPerEdge: Uint32Array | number[] | undefined, path: string, subtype: string | null, registry: ComponentRegistry): {
|
|
178
|
+
componentId: Uint32Array;
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Build the per-point vertex component-id attribute for a node's `obj_vertices`
|
|
182
|
+
* (the real B-rep corners — the *selectable* vertices, NOT the triangle-mesh
|
|
183
|
+
* vertices), registering one vertex component per point under
|
|
184
|
+
* `{path}/vertices/vertices_{i}` in array (backend `get_vertices`) order.
|
|
185
|
+
*
|
|
186
|
+
* @param objVertices - flat xyz triples (3 floats per vertex)
|
|
187
|
+
*/
|
|
188
|
+
export declare function buildVertexComponentIds(objVertices: Float32Array | number[], path: string, subtype: string | null, registry: ComponentRegistry): {
|
|
189
|
+
componentId: Uint32Array;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Result of a successful pick: the component id read from the id buffer, its
|
|
193
|
+
* registry metadata, and the world-space hit point.
|
|
194
|
+
*/
|
|
195
|
+
export interface PickResult {
|
|
196
|
+
/** Picked component id (never {@link BACKGROUND_ID}). */
|
|
197
|
+
id: number;
|
|
198
|
+
/** Registry metadata for the picked component. */
|
|
199
|
+
info: ComponentInfo;
|
|
200
|
+
/**
|
|
201
|
+
* World-space hit point, sourced from the position-MRT attachment. `null` when
|
|
202
|
+
* the RGBA32F position attachment is unsupported (id-only pick).
|
|
203
|
+
*/
|
|
204
|
+
point: THREE.Vector3 | null;
|
|
205
|
+
}
|
|
206
|
+
/** Options for {@link IdPicker.pickAt}. */
|
|
207
|
+
export interface PickAtOptions {
|
|
208
|
+
/**
|
|
209
|
+
* Restrict which topo types may be resolved (mirrors the existing
|
|
210
|
+
* `topoFilter`). `undefined`/empty = all (priority vertex>edge>face applies).
|
|
211
|
+
* `"solid"` makes faces eligible (caller maps the face to its `solidPath`).
|
|
212
|
+
*/
|
|
213
|
+
topoFilter?: TopoType[];
|
|
214
|
+
/**
|
|
215
|
+
* Odd N for the N×N readback window (touch radius for thin edges / tiny
|
|
216
|
+
* vertices). Defaults to {@link IdPicker}'s window size (5).
|
|
217
|
+
*/
|
|
218
|
+
windowSize?: number;
|
|
219
|
+
}
|
|
220
|
+
/** Options for the per-topo pick materials. */
|
|
221
|
+
export interface PickMaterialOptions {
|
|
222
|
+
/** Live clipping planes the pick pass must honor (so clipped geometry is not picked). */
|
|
223
|
+
clippingPlanes?: THREE.Plane[] | null;
|
|
224
|
+
/** Intersection clipping (mirror the visual material's `clipIntersection`). */
|
|
225
|
+
clipIntersection?: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Forward depth bias in **view-space world units** (a positive nudge toward the
|
|
228
|
+
* camera) so a corner's higher-priority topo wins the shared-depth pick test over
|
|
229
|
+
* its coincident lower-priority topo. Applied to `mvPosition.z` BEFORE projection —
|
|
230
|
+
* i.e. a constant world distance, independent of the camera's near/far spread.
|
|
231
|
+
* (An earlier build biased a constant in NDC z, which under an orthographic camera
|
|
232
|
+
* on a large scene became a multi-mm world window that let geometry behind an
|
|
233
|
+
* occluding face get picked "through" it.) Set per-scene from the bounding radius.
|
|
234
|
+
*/
|
|
235
|
+
depthBias?: number;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* View-space depth-bias factors (× scene bounding radius) for the edge and vertex
|
|
239
|
+
* pick passes. Face = 0. Kept monotonic with the vertex > edge > face hover priority
|
|
240
|
+
* so a corner's vertex wins over its edges and its edges over their faces. The factor
|
|
241
|
+
* is a fraction of the bounding radius: large enough to clear 24-bit depth-buffer
|
|
242
|
+
* quantization (≈ (far−near)/2^24 with far/near ∝ radius), small enough that the
|
|
243
|
+
* world window stays sub-mm on typical models so geometry a real distance behind an
|
|
244
|
+
* occluder is NOT picked through it.
|
|
245
|
+
*/
|
|
246
|
+
export declare const EDGE_DEPTH_BIAS_FACTOR = 0.0001;
|
|
247
|
+
export declare const VERTEX_DEPTH_BIAS_FACTOR = 0.0002;
|
|
248
|
+
/**
|
|
249
|
+
* Pick size (framebuffer px in the half-res target) for vertex pick points. Kept
|
|
250
|
+
* SMALL: the N×N readback window provides the "touch radius" by reading neighbours,
|
|
251
|
+
* so fat rendering is unnecessary and would only overwrite neighbouring face pixels
|
|
252
|
+
* in the shared id buffer (hurting faces-only picks near corners). A few px keeps
|
|
253
|
+
* the corner reliably present without eating faces.
|
|
254
|
+
*/
|
|
255
|
+
export declare const PICK_POINT_SIZE = 3;
|
|
256
|
+
/**
|
|
257
|
+
* Build the **face** pick override-material: a GLSL3 `ShaderMaterial` that reads the
|
|
258
|
+
* integer {@link COMPONENT_ID_ATTRIBUTE} and writes `packId(componentId)` +
|
|
259
|
+
* world position to the MRT pick target. Honors clipping (planes + intersection).
|
|
260
|
+
*/
|
|
261
|
+
export declare function createFacePickMaterial(options?: PickMaterialOptions): THREE.ShaderMaterial;
|
|
262
|
+
/**
|
|
263
|
+
* Build the **vertex** pick override-material for an `obj_vertices` `THREE.Points`
|
|
264
|
+
* cloud: a GLSL3 `ShaderMaterial` that draws each point **fat** (`gl_PointSize` =
|
|
265
|
+
* {@link PICK_POINT_SIZE}, a touch radius independent of the visual point size),
|
|
266
|
+
* reads the integer `componentId`, and writes id + world position to the MRT. A
|
|
267
|
+
* tiny forward depth bias lets a corner vertex win the depth test over its
|
|
268
|
+
* coincident face (so it can be picked), while occluded vertices stay hidden.
|
|
269
|
+
* Honors clipping.
|
|
270
|
+
*/
|
|
271
|
+
export declare function createVertexPickMaterial(options?: PickMaterialOptions): THREE.ShaderMaterial;
|
|
272
|
+
/**
|
|
273
|
+
* Pick width (px in the half-res target) for edge pick lines. Kept THIN: edges run
|
|
274
|
+
* along face boundaries, so a fat band would overwrite face pixels on both sides and
|
|
275
|
+
* wreck faces-only picks. The N×N readback window supplies the touch radius instead.
|
|
276
|
+
*/
|
|
277
|
+
export declare const PICK_EDGE_WIDTH = 2;
|
|
278
|
+
/**
|
|
279
|
+
* Build the **edge** pick override-material for a `LineSegments2` /
|
|
280
|
+
* `LineSegmentsGeometry` fat line: a GLSL3 `ShaderMaterial` that ports three
|
|
281
|
+
* `LineMaterial`'s screen-space instanced fat-line expansion (instanceStart/End +
|
|
282
|
+
* the `position` quad template, `linewidth`/`resolution` uniforms), reads the
|
|
283
|
+
* **instanced** integer `componentId`, and writes id + world position to the MRT.
|
|
284
|
+
* Drawn fat ({@link PICK_EDGE_WIDTH}) for a touch radius independent of the visual
|
|
285
|
+
* line width. `resolution` MUST be set to the pick target size by the caller.
|
|
286
|
+
* Honors clipping. (World-units + dash + color paths are dropped — pick only.)
|
|
287
|
+
*/
|
|
288
|
+
export declare function createEdgePickMaterial(options?: PickMaterialOptions): THREE.ShaderMaterial;
|
|
289
|
+
/** MRT attachment index of the packed-id texture. */
|
|
290
|
+
export declare const PICK_ID_ATTACHMENT = 0;
|
|
291
|
+
/** MRT attachment index of the world-position texture. */
|
|
292
|
+
export declare const PICK_POS_ATTACHMENT = 1;
|
|
293
|
+
/**
|
|
294
|
+
* Create the MRT pick render target: a single `WebGLRenderTarget` with two color
|
|
295
|
+
* attachments —
|
|
296
|
+
* - {@link PICK_ID_ATTACHMENT} (0): RGBA8/UnsignedByte packed `componentId`
|
|
297
|
+
* (`packId`, low byte in R), and
|
|
298
|
+
* - {@link PICK_POS_ATTACHMENT} (1): RGBA32F world-space hit position,
|
|
299
|
+
* `xyz` = point, `w = 1` where a fragment was written (0 = background).
|
|
300
|
+
*
|
|
301
|
+
* `NearestFilter` on both — interpolated ids/positions are meaningless. A depth
|
|
302
|
+
* buffer keeps the nearest fragment per pixel; no depth *texture* is read.
|
|
303
|
+
* `NoColorSpace` on the id texture stops any sRGB transform corrupting the bytes.
|
|
304
|
+
*
|
|
305
|
+
* Plain factory: the caller ({@link IdPicker.pickAt}) owns sizing/resize/disposal.
|
|
306
|
+
* `width`/`height` are already the pick-buffer pixel size (caller applies half-DPR).
|
|
307
|
+
*
|
|
308
|
+
* @param withPosition - allocate the RGBA32F world-position attachment. Requires
|
|
309
|
+
* `EXT_color_buffer_float` (probed by {@link IdPicker}); when `false` the target
|
|
310
|
+
* carries the id attachment only and `pickAt` returns `point = null` (the fat
|
|
311
|
+
* `out` at location 1 in the pick shaders is simply dropped — writing to an
|
|
312
|
+
* unbound draw buffer is well-defined and discarded in WebGL2).
|
|
313
|
+
*/
|
|
314
|
+
export declare function createPickTargets(width: number, height: number, withPosition?: boolean): THREE.WebGLRenderTarget;
|
|
315
|
+
/**
|
|
316
|
+
* GPU id-based picker: cursor → component id → registry → backend path, plus the
|
|
317
|
+
* world-space hit point.
|
|
318
|
+
*
|
|
319
|
+
* Lifecycle the host (viewer) drives:
|
|
320
|
+
* - {@link attach} once the scene + camera exist (re-attach after `clear()`),
|
|
321
|
+
* - {@link setSize} on canvas resize, {@link setClippingPlanes} on clip change,
|
|
322
|
+
* - {@link setDirty} whenever the view/geometry changes (camera move, geometry
|
|
323
|
+
* rebuild) so the pick buffer is re-rendered lazily on the next {@link pickAt},
|
|
324
|
+
* - {@link pickAt} on hover/click to resolve the component under the cursor.
|
|
325
|
+
*
|
|
326
|
+
* Per-topo passes (FACE, then fat EDGE lines, then fat VERTEX points) accumulate
|
|
327
|
+
* into one MRT target; `pickAt` reads an N×N window and resolves **vertex > edge >
|
|
328
|
+
* face** priority via the registry's topo, honoring `topoFilter`, then reads the
|
|
329
|
+
* position attachment → `point`.
|
|
330
|
+
*/
|
|
331
|
+
export declare class IdPicker {
|
|
332
|
+
readonly registry: ComponentRegistry;
|
|
333
|
+
/**
|
|
334
|
+
* Default N for the N×N readback window — the pick "touch radius" applied to the
|
|
335
|
+
* (thin) geometry. 3 is odd, so the window is symmetric about the cursor pixel
|
|
336
|
+
* (half = 1 → cursor ±1 on each axis); an even size is asymmetric. Tuned
|
|
337
|
+
* interactively (pick-explore.html): 2 and 3 feel identical, much larger (e.g. 7)
|
|
338
|
+
* pulls edge/vertex priority too far off the actual line/point; 1 is too unforgiving.
|
|
339
|
+
*/
|
|
340
|
+
windowSize: number;
|
|
341
|
+
private renderer;
|
|
342
|
+
/** Render source — the scene + camera the pick pass renders. */
|
|
343
|
+
private scene;
|
|
344
|
+
private camera;
|
|
345
|
+
/** Live clipping planes mirrored onto the pick materials (`null` = clipping off). */
|
|
346
|
+
private clippingPlanes;
|
|
347
|
+
/** Canvas size in CSS px (the pick target is sized at half-DPR internally). */
|
|
348
|
+
private width;
|
|
349
|
+
private height;
|
|
350
|
+
/** Intersection clipping mode mirrored onto the pick materials. */
|
|
351
|
+
private clipIntersection;
|
|
352
|
+
/**
|
|
353
|
+
* Scene bounding radius, used to scale the edge/vertex view-space depth bias to a
|
|
354
|
+
* small constant world distance (see {@link setSceneRadius}). 0 until the host sets
|
|
355
|
+
* it — with 0, coincident corner topo would z-fight and be unpickable, so the host
|
|
356
|
+
* MUST call {@link setSceneRadius} once the model bounds are known.
|
|
357
|
+
*/
|
|
358
|
+
private sceneRadius;
|
|
359
|
+
/** Whether the pick buffer must be re-rendered before the next read. */
|
|
360
|
+
private dirty;
|
|
361
|
+
/** MRT pick target (id + world-position attachments). Allocated lazily in `pickAt`. */
|
|
362
|
+
private pickTarget;
|
|
363
|
+
/** Per-topo pick override-materials. Allocated lazily in `pickAt`. */
|
|
364
|
+
private faceMaterial;
|
|
365
|
+
private edgeMaterial;
|
|
366
|
+
private vertexMaterial;
|
|
367
|
+
/** Clip-plane count the pick materials were last compiled for (program key). */
|
|
368
|
+
private compiledPlaneCount;
|
|
369
|
+
/** Intersection mode the pick materials were last compiled for (program key). */
|
|
370
|
+
private compiledIntersection;
|
|
371
|
+
/** Scratch to save/restore the renderer clear color across a pick pass. */
|
|
372
|
+
private readonly savedClearColor;
|
|
373
|
+
/** Scratch to save/restore the renderer viewport across a pick pass. */
|
|
374
|
+
private readonly savedViewport;
|
|
375
|
+
/** Scratch readback buffer for the position attachment (one RGBA32F pixel). */
|
|
376
|
+
private readonly posPixel;
|
|
377
|
+
/** Scratch readback buffer for the N×N id window (grown as needed). */
|
|
378
|
+
private idWindow;
|
|
379
|
+
/**
|
|
380
|
+
* Whether the GPU can render to a float color attachment (`EXT_color_buffer_float`,
|
|
381
|
+
* probed at construction). When `false`, the pick target carries the id attachment
|
|
382
|
+
* only and {@link pickAt} returns `point = null` — id picking still works, just
|
|
383
|
+
* without the world-space hit point (degrades hover coords / double-click pivot to
|
|
384
|
+
* the bbox-center fallback). Universal in real WebGL2; the probe guards exotic
|
|
385
|
+
* contexts.
|
|
386
|
+
*/
|
|
387
|
+
readonly positionSupported: boolean;
|
|
388
|
+
constructor(renderer: THREE.WebGLRenderer, registry: ComponentRegistry);
|
|
389
|
+
/**
|
|
390
|
+
* Probe `EXT_color_buffer_float` — the capability needed to *render into* the
|
|
391
|
+
* RGBA32F world-position attachment. Getting the extension is the canonical
|
|
392
|
+
* feature test (no side effects) and is WebGL2-only: it does not exist on WebGL1
|
|
393
|
+
* (whose float-color extension is `WEBGL_color_buffer_float`), so a non-null result
|
|
394
|
+
* already implies a WebGL2 context. Near-universal on WebGL2 but spec-optional.
|
|
395
|
+
*/
|
|
396
|
+
private static _probeFloatColor;
|
|
397
|
+
/**
|
|
398
|
+
* Point the picker at the scene + camera it renders for picking. Call once
|
|
399
|
+
* the scene exists and again after the scene/camera are recreated (`clear()`).
|
|
400
|
+
*/
|
|
401
|
+
attach(scene: THREE.Scene, camera: Camera): void;
|
|
402
|
+
/**
|
|
403
|
+
* Mark the pick buffer stale so the next {@link pickAt} re-renders it. Call on any
|
|
404
|
+
* view change (camera move/zoom, geometry rebuild) — NOT on mouse-move.
|
|
405
|
+
*/
|
|
406
|
+
setDirty(): void;
|
|
407
|
+
/**
|
|
408
|
+
* Set the scene bounding radius so the edge/vertex pick passes bias depth by a
|
|
409
|
+
* small constant WORLD distance (radius × {@link EDGE_DEPTH_BIAS_FACTOR} /
|
|
410
|
+
* {@link VERTEX_DEPTH_BIAS_FACTOR}) rather than a constant in NDC z. The world
|
|
411
|
+
* window is then independent of the camera near/far spread — the fix for geometry
|
|
412
|
+
* behind an occluding face getting picked "through" it under an orthographic camera
|
|
413
|
+
* on a large scene. Call whenever the model bounds change (initial build, add/remove
|
|
414
|
+
* part). Updates existing materials in place and seeds lazily-created ones.
|
|
415
|
+
*/
|
|
416
|
+
setSceneRadius(radius: number): void;
|
|
417
|
+
/** View-space edge depth bias (world units) for the current scene radius. */
|
|
418
|
+
private _edgeDepthBias;
|
|
419
|
+
/** View-space vertex depth bias (world units) for the current scene radius. */
|
|
420
|
+
private _vertexDepthBias;
|
|
421
|
+
/** Resize the pick render target to match the canvas (CSS px; half-DPR internally). */
|
|
422
|
+
setSize(width: number, height: number): void;
|
|
423
|
+
/**
|
|
424
|
+
* Update the clipping the pick materials must honor — planes (`null` when the
|
|
425
|
+
* viewer's clipping is off) and intersection mode (mirror the visual material's
|
|
426
|
+
* `clipIntersection`). Recompiles the materials only when the plane *count* or the
|
|
427
|
+
* intersection *mode* changes (three keys the program on `NUM_CLIPPING_PLANES` /
|
|
428
|
+
* `UNION_CLIPPING_PLANES`); a value-only change just re-renders. Always
|
|
429
|
+
* re-renders next {@link pickAt}.
|
|
430
|
+
*/
|
|
431
|
+
setClippingPlanes(planes: THREE.Plane[] | null, intersection?: boolean): void;
|
|
432
|
+
/**
|
|
433
|
+
* Resolve the component under the cursor, with its world-space hit point.
|
|
434
|
+
*
|
|
435
|
+
* @param x - canvas-relative x in CSS px (`getBoundingClientRect`)
|
|
436
|
+
* @param y - canvas-relative y in CSS px (top-left origin; WebGL Y-flip is internal)
|
|
437
|
+
* @returns the picked component (`point` = world hit position), or `null` for
|
|
438
|
+
* background / no hit.
|
|
439
|
+
*
|
|
440
|
+
* Re-renders the MRT pick target if {@link dirty} (per-topo passes on the pick
|
|
441
|
+
* layers, honoring clipping), then reads an N×N window around the cursor pixel
|
|
442
|
+
* (canvas px → ×dpr×0.5 → Y-flip) and resolves **vertex > edge > face** priority
|
|
443
|
+
* via the registry's topo (nearest-to-center tie-break), restricted to
|
|
444
|
+
* `options.topoFilter`. The position attachment at the chosen pixel → `point`.
|
|
445
|
+
*/
|
|
446
|
+
pickAt(x: number, y: number, options?: PickAtOptions): PickResult | null;
|
|
447
|
+
/** Lazily allocate / resize the MRT target and the per-topo pick materials. */
|
|
448
|
+
private _ensureResources;
|
|
449
|
+
/**
|
|
450
|
+
* Render the per-topo pick passes into the MRT target, reusing the main camera.
|
|
451
|
+
* Clears once, then accumulates **FACE → EDGE → VERTEX** (each drawn fat
|
|
452
|
+
* and later, so higher-priority topos overwrite at coincident pixels). All
|
|
453
|
+
* renderer state touched here is saved and restored.
|
|
454
|
+
*/
|
|
455
|
+
private _renderPickBuffer;
|
|
456
|
+
/** One pick pass: render only `layer`'s objects with `material` (no clear).
|
|
457
|
+
* Clipping planes are kept in sync solely by {@link setClippingPlanes} (and the
|
|
458
|
+
* lazy material creation in {@link _ensureResources}); re-assigning them here per
|
|
459
|
+
* pass would mutate `clippingPlanes` without `needsUpdate`, risking a stale
|
|
460
|
+
* `NUM_CLIPPING_PLANES` if the count ever changed off that path. */
|
|
461
|
+
private _pass;
|
|
462
|
+
/** Release GPU resources (pick render target + materials). */
|
|
463
|
+
dispose(): void;
|
|
464
|
+
}
|