three-cad-viewer 4.1.2 → 4.2.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.
Files changed (58) hide show
  1. package/Readme.md +12 -5
  2. package/dist/camera/camera.d.ts +14 -2
  3. package/dist/core/studio-manager.d.ts +91 -0
  4. package/dist/core/types.d.ts +260 -9
  5. package/dist/core/viewer-state.d.ts +28 -2
  6. package/dist/core/viewer.d.ts +200 -6
  7. package/dist/index.d.ts +7 -2
  8. package/dist/rendering/environment.d.ts +239 -0
  9. package/dist/rendering/light-detection.d.ts +44 -0
  10. package/dist/rendering/material-factory.d.ts +77 -2
  11. package/dist/rendering/material-presets.d.ts +32 -0
  12. package/dist/rendering/room-environment.d.ts +13 -0
  13. package/dist/rendering/studio-composer.d.ts +130 -0
  14. package/dist/rendering/studio-floor.d.ts +53 -0
  15. package/dist/rendering/texture-cache.d.ts +142 -0
  16. package/dist/rendering/triplanar.d.ts +37 -0
  17. package/dist/scene/animation.d.ts +1 -1
  18. package/dist/scene/clipping.d.ts +31 -0
  19. package/dist/scene/nestedgroup.d.ts +64 -27
  20. package/dist/scene/objectgroup.d.ts +47 -0
  21. package/dist/three-cad-viewer.css +339 -29
  22. package/dist/three-cad-viewer.esm.js +27567 -11874
  23. package/dist/three-cad-viewer.esm.js.map +1 -1
  24. package/dist/three-cad-viewer.esm.min.js +10 -4
  25. package/dist/three-cad-viewer.js +27486 -11787
  26. package/dist/three-cad-viewer.min.js +10 -4
  27. package/dist/ui/display.d.ts +147 -0
  28. package/dist/utils/decode-instances.d.ts +60 -0
  29. package/dist/utils/utils.d.ts +10 -0
  30. package/package.json +4 -2
  31. package/src/_version.ts +1 -1
  32. package/src/camera/camera.ts +27 -10
  33. package/src/core/studio-manager.ts +682 -0
  34. package/src/core/types.ts +328 -9
  35. package/src/core/viewer-state.ts +84 -4
  36. package/src/core/viewer.ts +453 -22
  37. package/src/index.ts +25 -1
  38. package/src/rendering/environment.ts +840 -0
  39. package/src/rendering/light-detection.ts +327 -0
  40. package/src/rendering/material-factory.ts +456 -2
  41. package/src/rendering/material-presets.ts +303 -0
  42. package/src/rendering/raycast.ts +2 -2
  43. package/src/rendering/room-environment.ts +192 -0
  44. package/src/rendering/studio-composer.ts +577 -0
  45. package/src/rendering/studio-floor.ts +108 -0
  46. package/src/rendering/texture-cache.ts +1020 -0
  47. package/src/rendering/triplanar.ts +329 -0
  48. package/src/scene/animation.ts +3 -2
  49. package/src/scene/clipping.ts +59 -0
  50. package/src/scene/nestedgroup.ts +399 -0
  51. package/src/scene/objectgroup.ts +186 -11
  52. package/src/scene/orientation.ts +12 -0
  53. package/src/scene/render-shape.ts +55 -21
  54. package/src/types/n8ao.d.ts +28 -0
  55. package/src/ui/display.ts +1032 -27
  56. package/src/ui/index.html +181 -44
  57. package/src/utils/decode-instances.ts +233 -0
  58. package/src/utils/utils.ts +33 -20
@@ -4,7 +4,7 @@ declare global {
4
4
  THREE?: typeof THREE;
5
5
  }
6
6
  }
7
- import { NestedGroup } from "../scene/nestedgroup.js";
7
+ import { NestedGroup, ObjectGroup } from "../scene/nestedgroup.js";
8
8
  import { Grid } from "../scene/grid.js";
9
9
  import { AxesHelper } from "../scene/axes.js";
10
10
  import { OrientationMarker } from "../scene/orientation.js";
@@ -23,7 +23,7 @@ import { PickedObject, Raycaster } from "../rendering/raycast.js";
23
23
  import { ViewerState } from "./viewer-state.js";
24
24
  import type { Display } from "../ui/display.js";
25
25
  import type { Vector3Tuple, QuaternionTuple } from "three";
26
- import { CollapseState, type ZebraColorScheme, type ZebraMappingMode, type NotificationCallback, type RenderOptions, type ViewerOptions, type Shapes, type VisibilityState, type ActiveTab, type Axis, type ClipIndex, type ThemeInput, type BoundingBoxFlat, type Keymap } from "./types.js";
26
+ import { CollapseState, type ZebraColorScheme, type ZebraMappingMode, type StudioToneMapping, type StudioTextureMapping, type StudioBackground, type NotificationCallback, type RenderOptions, type ViewerOptions, type Shapes, type VisibilityState, type ActiveTab, type Axis, type ClipIndex, type ThemeInput, type BoundingBoxFlat, type Keymap } from "./types.js";
27
27
  /**
28
28
  * Material settings for the viewer.
29
29
  */
@@ -97,6 +97,8 @@ interface DisplayOptionsInternal {
97
97
  zebraTool?: boolean;
98
98
  glass?: boolean;
99
99
  tools?: boolean;
100
+ canvas?: HTMLCanvasElement;
101
+ gl?: WebGLRenderingContext | WebGL2RenderingContext;
100
102
  keymap?: KeymapConfig;
101
103
  [key: string]: unknown;
102
104
  }
@@ -161,6 +163,8 @@ declare class Viewer {
161
163
  ready: boolean;
162
164
  display: Display;
163
165
  renderer: THREE.WebGLRenderer;
166
+ private _externalGl;
167
+ onAfterRender: (() => void) | null;
164
168
  mouse: THREE.Vector2;
165
169
  cadTools: Tools;
166
170
  animation: Animation;
@@ -199,6 +203,9 @@ declare class Viewer {
199
203
  expandedNestedGroup: NestedGroup | null;
200
204
  compactNestedGroup: NestedGroup | null;
201
205
  raycaster: Raycaster | null;
206
+ private _studioManager;
207
+ /** Environment manager — proxied from StudioManager for display.ts access. */
208
+ get envManager(): import("../index.js").EnvironmentManager;
202
209
  zScale: number;
203
210
  clipNormal0: Vector3Tuple | null;
204
211
  clipNormal1: Vector3Tuple | null;
@@ -379,7 +386,7 @@ declare class Viewer {
379
386
  toggleGroup(expanded: boolean): void;
380
387
  /**
381
388
  * Set the active sidebar tab.
382
- * @param tabName - Tab name: "tree", "clip", "material", or "zebra"
389
+ * @param tabName - Tab name: "tree", "clip", "material", "zebra", or "studio"
383
390
  * @param notify - whether to send notification or not.
384
391
  */
385
392
  setActiveTab(tabName: ActiveTab, notify?: boolean): void;
@@ -525,7 +532,7 @@ declare class Viewer {
525
532
  * @param nodeType - node type
526
533
  * @param tree - whether from tree
527
534
  */
528
- handlePick: (path: string, name: string, meta: boolean, shift: boolean, _alt: boolean, point: THREE.Vector3 | null, nodeType?: string | null, tree?: boolean) => void;
535
+ handlePick: (path: string, name: string, meta: boolean, shift: boolean, alt: boolean, point: THREE.Vector3 | null, nodeType?: string | null, tree?: boolean) => void;
529
536
  setPickHandler(flag: boolean): void;
530
537
  /**
531
538
  * Find the shape that was double clicked and send notification
@@ -749,6 +756,11 @@ declare class Viewer {
749
756
  * @param value - The mapping mode ("reflection", "normal").
750
757
  */
751
758
  setZebraMappingMode: (value: ZebraMappingMode) => void;
759
+ /**
760
+ * Resets zebra tool settings to defaults: count=9, opacity=1, direction=0,
761
+ * colorScheme=blackwhite, mappingMode=reflection.
762
+ */
763
+ resetZebra: () => void;
752
764
  /**
753
765
  * Gets the current stripe count value.
754
766
  * @returns The stripe count (2-50).
@@ -774,6 +786,173 @@ declare class Viewer {
774
786
  * @returns The mapping mode ("reflection", "normal").
775
787
  */
776
788
  getZebraMappingMode: () => ZebraMappingMode;
789
+ /**
790
+ * Sets the studio environment preset.
791
+ * @param value - The environment name ("studio", "neutral", "outdoor", "none", or custom HDR URL).
792
+ * @param notify - Whether to notify about the changes.
793
+ * @public
794
+ */
795
+ setStudioEnvironment: (value: string, notify?: boolean) => void;
796
+ /**
797
+ * Sets the studio environment intensity.
798
+ * @param value - The environment intensity (0-3).
799
+ * @param notify - Whether to notify about the changes.
800
+ * @public
801
+ */
802
+ setStudioEnvIntensity: (value: number, notify?: boolean) => void;
803
+ /**
804
+ * Sets the background mode for Studio mode.
805
+ * @param value - The background mode ("grey", "white", "gradient", "environment", or "transparent").
806
+ * @param notify - Whether to notify about the changes.
807
+ * @public
808
+ */
809
+ setStudioBackground: (value: StudioBackground, notify?: boolean) => void;
810
+ /**
811
+ * Sets the tone mapping mode for Studio mode.
812
+ * @param value - The tone mapping mode ("neutral", "ACES", or "none").
813
+ * @param notify - Whether to notify about the changes.
814
+ * @public
815
+ */
816
+ setStudioToneMapping: (value: StudioToneMapping, notify?: boolean) => void;
817
+ /**
818
+ * Sets the exposure value for Studio mode.
819
+ * @param value - The exposure value (0-2).
820
+ * @param notify - Whether to notify about the changes.
821
+ * @public
822
+ */
823
+ setStudioExposure: (value: number, notify?: boolean) => void;
824
+ /**
825
+ * Sets whether 4K environment maps are used (default: 2K).
826
+ * @param value - True for 4K, false for 2K.
827
+ * @param notify - Whether to notify about the changes.
828
+ * @public
829
+ */
830
+ setStudio4kEnvMaps: (value: boolean, notify?: boolean) => void;
831
+ /**
832
+ * Gets whether 4K environment maps are enabled.
833
+ * @returns True for 4K, false for 2K.
834
+ * @public
835
+ */
836
+ getStudio4kEnvMaps: () => boolean;
837
+ /**
838
+ * Sets the environment rotation for Studio mode.
839
+ * @param value - The rotation in degrees (0-360).
840
+ * @param notify - Whether to notify about the changes.
841
+ * @public
842
+ */
843
+ setStudioEnvRotation: (value: number, notify?: boolean) => void;
844
+ /**
845
+ * Gets the current environment rotation for Studio mode.
846
+ * @returns The rotation in degrees (0-360).
847
+ * @public
848
+ */
849
+ getStudioEnvRotation: () => number;
850
+ /**
851
+ * Sets the texture mapping mode for Studio mode.
852
+ * @param value - The texture mapping mode ("triplanar" or "parametric").
853
+ * @param notify - Whether to notify about the changes.
854
+ * @public
855
+ */
856
+ setStudioTextureMapping: (value: StudioTextureMapping, notify?: boolean) => void;
857
+ /**
858
+ * Gets the current texture mapping mode for Studio mode.
859
+ * @returns The texture mapping mode ("triplanar" or "parametric").
860
+ * @public
861
+ */
862
+ getStudioTextureMapping: () => StudioTextureMapping;
863
+ /**
864
+ * Gets the current studio environment preset.
865
+ * @returns The environment name ("studio", "neutral", "outdoor", "none", or custom HDR URL).
866
+ * @public
867
+ */
868
+ getStudioEnvironment: () => string;
869
+ /**
870
+ * Gets the current studio environment intensity.
871
+ * @returns The environment intensity (0-3).
872
+ * @public
873
+ */
874
+ getStudioEnvIntensity: () => number;
875
+ /**
876
+ * Gets the current background mode for Studio mode.
877
+ * @returns The background mode ("grey", "white", "gradient", "environment", or "transparent").
878
+ * @public
879
+ */
880
+ getStudioBackground: () => StudioBackground;
881
+ /**
882
+ * Gets the current tone mapping mode for Studio mode.
883
+ * @returns The tone mapping mode ("neutral", "ACES", or "none").
884
+ * @public
885
+ */
886
+ getStudioToneMapping: () => StudioToneMapping;
887
+ /**
888
+ * Gets the current exposure value for Studio mode.
889
+ * @returns The exposure value (0-3).
890
+ * @public
891
+ */
892
+ getStudioExposure: () => number;
893
+ /**
894
+ * Sets the shadow intensity in Studio mode.
895
+ * A value of 0 disables shadows; values > 0 enable them at that darkness.
896
+ * @param value - The shadow intensity (0-1).
897
+ * @param notify - Whether to notify about the changes.
898
+ * @public
899
+ */
900
+ setStudioShadowIntensity: (value: number, notify?: boolean) => void;
901
+ /**
902
+ * Gets the current shadow intensity in Studio mode.
903
+ * @returns The shadow intensity (0-1). 0 means shadows are off.
904
+ * @public
905
+ */
906
+ getStudioShadowIntensity: () => number;
907
+ /**
908
+ * Sets the shadow softness in Studio mode.
909
+ * Controls PCSS penumbra width (virtual light source size).
910
+ * @param value - The shadow softness (0-1).
911
+ * @param notify - Whether to notify about the changes.
912
+ * @public
913
+ */
914
+ setStudioShadowSoftness: (value: number, notify?: boolean) => void;
915
+ /**
916
+ * Gets the current shadow softness in Studio mode.
917
+ * @returns The shadow softness (0-1).
918
+ * @public
919
+ */
920
+ getStudioShadowSoftness: () => number;
921
+ /**
922
+ * Sets the ambient occlusion intensity in Studio mode.
923
+ * A value of 0 disables AO; values > 0 enable it at that intensity.
924
+ * @param value - The AO intensity (0-3.0).
925
+ * @param notify - Whether to notify about the changes.
926
+ * @public
927
+ */
928
+ setStudioAOIntensity: (value: number, notify?: boolean) => void;
929
+ /**
930
+ * Gets the current ambient occlusion intensity in Studio mode.
931
+ * @returns The AO intensity value (0.5-3.0).
932
+ * @public
933
+ */
934
+ getStudioAOIntensity: () => number;
935
+ /**
936
+ * Returns whether Studio mode is currently active.
937
+ * @returns True if Studio mode is active and the viewer has rendered content.
938
+ * @public
939
+ */
940
+ get isStudioActive(): boolean;
941
+ /**
942
+ * Get the ObjectGroup and path for the currently selected object in Studio mode.
943
+ * Returns null if nothing is selected, Studio mode is inactive, or the
944
+ * selection is a CompoundGroup (assembly node) rather than a leaf object.
945
+ */
946
+ getSelectedObjectGroup(): {
947
+ object: ObjectGroup;
948
+ path: string;
949
+ } | null;
950
+ /** Enter Studio mode. Called by display.ts switchToTab(). @internal */
951
+ enterStudioMode: () => Promise<void>;
952
+ /** Leave Studio mode. Called by display.ts switchToTab(). @internal */
953
+ leaveStudioMode: () => void;
954
+ /** Reset Studio settings to defaults. @public */
955
+ resetStudio: () => void;
777
956
  /**
778
957
  * Get ortho value as property (for ViewerLike interface compatibility).
779
958
  */
@@ -1157,6 +1336,11 @@ declare class Viewer {
1157
1336
  * @param notify - whether to send notification or not.
1158
1337
  */
1159
1338
  setClipSlider: (index: 0 | 1 | 2, value: number, notify?: boolean) => void;
1339
+ /**
1340
+ * Resets clip planes to default normals and slider positions.
1341
+ * Normals reset to -X, -Y, -Z; sliders to gridSize/2; checkboxes unchecked.
1342
+ */
1343
+ resetClip: () => void;
1160
1344
  /**
1161
1345
  * Replace CadView with an inline png image of the canvas.
1162
1346
  *
@@ -1266,11 +1450,21 @@ declare class Viewer {
1266
1450
  */
1267
1451
  showHelp: (flag: boolean) => void;
1268
1452
  /**
1269
- * Show/hide the info panel.
1270
- * @param flag - whether to show the info panel
1453
+ * Collapse or expand the info panel in glass mode.
1454
+ * @param flag - true to show, false to collapse
1271
1455
  * @public
1272
1456
  */
1457
+ showInfoPanel: (flag: boolean) => void;
1458
+ /**
1459
+ * @deprecated Use showInfoPanel() instead.
1460
+ */
1273
1461
  showInfo: (flag: boolean) => void;
1462
+ /**
1463
+ * Collapse or expand the tools panel (tabs + content) in glass mode.
1464
+ * @param flag - true to show, false to collapse
1465
+ * @public
1466
+ */
1467
+ showToolsPanel: (flag: boolean) => void;
1274
1468
  /**
1275
1469
  * Show/hide the pinning button.
1276
1470
  * @param flag - whether to show the pinning button
package/dist/index.d.ts CHANGED
@@ -10,15 +10,17 @@ import "../css/treeview.css";
10
10
  import "../css/tools.css";
11
11
  import { Viewer } from "./core/viewer.js";
12
12
  import { Display } from "./ui/display.js";
13
+ import { EnvironmentManager } from "./rendering/environment.js";
13
14
  import { Timer } from "./utils/timer.js";
14
15
  import { logger } from "./utils/logger.js";
15
16
  import { gpuTracker } from "./utils/gpu-tracker.js";
16
17
  import { version } from "./_version.js";
17
- export { Viewer, Display, Timer, logger, gpuTracker, version };
18
+ export { Viewer, Display, EnvironmentManager, Timer, logger, gpuTracker, version };
19
+ export { MATERIAL_PRESETS, MATERIAL_PRESET_NAMES } from "./rendering/material-presets.js";
18
20
  export type { LogLevel } from "./utils/logger.js";
19
21
  export type { ResourceType, TrackedResource, ResourceSummary } from "./utils/gpu-tracker.js";
20
22
  export type { Vector3Tuple, QuaternionTuple } from "three";
21
- export type { ThemeInput, Theme, ControlType, UpDirection, AnimationMode, ActiveTab, ZebraColorScheme, ZebraMappingMode, ShapeType, ShapeSubtype, Axis, ClipIndex, ColorValue, RGBColor, AxisColors, AxisColorsFlatArray, } from "./core/types.js";
23
+ export type { ThemeInput, Theme, ControlType, UpDirection, AnimationMode, ActiveTab, ZebraColorScheme, ZebraMappingMode, ShapeType, ShapeSubtype, Axis, ClipIndex, ColorValue, RGBColor, RGBAColor, AxisColors, AxisColorsFlatArray, } from "./core/types.js";
22
24
  export { CLIP_INDICES, isClipIndex, CollapseState } from "./core/types.js";
23
25
  export type { StateChange, StateSubscriber, GlobalStateSubscriber, } from "./core/types.js";
24
26
  export type { BoundingBox, BoundingSphere, BoundingBoxFlat, } from "./core/types.js";
@@ -30,4 +32,7 @@ export type { Texture, Shape, ShapeBinary, ShapeNested, Location, VisibilityValu
30
32
  export { isShapeBinaryFormat, hasTrianglesPerFace, hasSegmentsPerEdge, } from "./core/types.js";
31
33
  export type { DomEventCallback } from "./core/types.js";
32
34
  export type { ColoredMaterial } from "./core/types.js";
35
+ export type { MaterialAppearance, MaterialXMaterial, TextureEntry, StudioOptions, StudioBackground, StudioModeOptions, StudioEnvironment, StudioToneMapping, StudioTextureMapping, } from "./core/types.js";
36
+ export { isMaterialXMaterial } from "./core/types.js";
37
+ export { isInstancedFormat, decodeInstancedFormat } from "./utils/decode-instances.js";
33
38
  export type { SubscribeOptions } from "./core/types.js";
@@ -0,0 +1,239 @@
1
+ import * as THREE from "three";
2
+ import type { StudioEnvironment, StudioBackground } from "../core/types.js";
3
+ import { type LightDetectionResult } from "./light-detection.js";
4
+ /**
5
+ * Configuration options for EnvironmentManager.
6
+ */
7
+ interface EnvironmentManagerOptions {
8
+ /** Override URLs for the "neutral" and "outdoor" HDR presets */
9
+ presetUrls?: Partial<Record<string, string>>;
10
+ }
11
+ /**
12
+ * Manages environment maps for Studio mode.
13
+ *
14
+ * Handles three tiers of environment sources:
15
+ * - **Tier 1 "studio"**: Procedural RoomEnvironment (bundled, zero network)
16
+ * - **Tier 2 "neutral"/"outdoor"**: HDR presets loaded from configurable CDN URLs
17
+ * - **Tier 3 custom URL**: User-provided HDR URL (same loading path as Tier 2)
18
+ *
19
+ * The environment map is used for IBL (image-based lighting) via
20
+ * `scene.environment`. The scene background is configurable via the
21
+ * `backgroundMode` parameter in `apply()` (grey, white, gradient,
22
+ * blurred environment, or transparent).
23
+ *
24
+ * Features:
25
+ * - PMREM generation and caching for all tiers
26
+ * - In-flight promise deduplication (prevents duplicate loads on rapid switching)
27
+ * - Lazy PMREMGenerator creation
28
+ * - Fallback to "studio" on HDR load failure
29
+ * - GPU resource tracking via gpuTracker
30
+ */
31
+ declare class EnvironmentManager {
32
+ /** Cached PMREM render targets keyed by environment name or URL */
33
+ private _cache;
34
+ /** Cached light detection results keyed by environment name or URL */
35
+ private _lightDetectionCache;
36
+ /** In-flight load promises keyed by environment name or URL */
37
+ private _inflight;
38
+ /** Lazily-created PMREMGenerator instance */
39
+ private _pmremGenerator;
40
+ /** Resolved preset URLs (defaults merged with user overrides) */
41
+ private _presetUrls;
42
+ /** Whether 4K env maps are enabled (default false = 2K) */
43
+ private _use4k;
44
+ /** User-provided URL overrides from constructor */
45
+ private _userOverrides;
46
+ /** HDRLoader instance (created lazily on first HDR load) */
47
+ private _hdrLoader;
48
+ /** The last loaded PMREM texture (stateful — used by apply() for IBL) */
49
+ private _currentTexture;
50
+ /** Whether this manager has been disposed */
51
+ private _disposed;
52
+ /**
53
+ * Ortho env background workaround.
54
+ *
55
+ * Three.js cannot render PMREM/cubemap textures as scene.background with
56
+ * orthographic cameras (renders as a tiny rectangle). We work around this by
57
+ * rendering the env map to a render target using a virtual perspective camera,
58
+ * then setting that 2D texture as scene.background. A 2D texture background
59
+ * renders as a fullscreen quad regardless of camera projection, and the
60
+ * transmission pass (glass refraction) also sees it correctly.
61
+ */
62
+ private _bgScene;
63
+ private _bgCamera;
64
+ private _bgRenderTarget;
65
+ private _orthoEnvMainScene;
66
+ /** Whether the env background feature is active (ortho + environment background). */
67
+ private _envBackgroundActive;
68
+ /**
69
+ * Deferred-apply state: if apply() was called with backgroundMode "environment"
70
+ * while _currentTexture was null, store the arguments so loadEnvironment() can
71
+ * re-apply once the texture is ready.
72
+ */
73
+ private _deferredApply;
74
+ constructor(options?: EnvironmentManagerOptions);
75
+ /**
76
+ * Load or retrieve an environment map.
77
+ *
78
+ * Resolves the environment name to a loading strategy:
79
+ * - `"studio"` -- procedural RoomEnvironment via PMREMGenerator.fromScene()
80
+ * - `"neutral"` / `"outdoor"` -- HDR preset from configured CDN URL
81
+ * - `"none"` -- returns null (caller should call `remove()` instead)
82
+ * - Any other string -- treated as a custom HDR URL
83
+ *
84
+ * Results are cached. If a load is already in flight for the same key,
85
+ * the existing promise is returned (no duplicate loads).
86
+ *
87
+ * @param name - Environment preset name or custom HDR URL
88
+ * @param renderer - WebGL renderer (needed for PMREMGenerator)
89
+ * @returns PMREM texture, or null for "none"
90
+ */
91
+ loadEnvironment(name: StudioEnvironment | string, renderer: THREE.WebGLRenderer): Promise<THREE.Texture | null>;
92
+ /**
93
+ * Apply the current environment map to the scene.
94
+ *
95
+ * Sets `scene.environment` for PBR/IBL reflections and configures
96
+ * `scene.background` according to the selected background mode:
97
+ * - `"grey"`: Neutral grey color (default, clean product-shot look)
98
+ * - `"white"`: Pure white background (e-commerce / documentation style)
99
+ * - `"gradient"`: Radial vignette gradient (light grey center → darker edges)
100
+ * - `"environment"`: Blurred, dimmed PMREM environment as backdrop
101
+ * (color-matched to IBL, eliminates edge-glow artifacts on reflective objects)
102
+ * - `"transparent"`: No background (canvas alpha shows through)
103
+ *
104
+ * @param scene - The Three.js scene to apply the environment to
105
+ * @param envIntensity - Environment intensity multiplier (0-3, default 1.0)
106
+ * @param backgroundMode - Background mode
107
+ * @param upIsZ - Whether the scene uses Z-up coordinates (default true)
108
+ * @param ortho - Whether the camera is orthographic (env background falls back to gradient)
109
+ * @param envRotationDeg - Environment map rotation in degrees (default 0)
110
+ */
111
+ apply(scene: THREE.Scene, envIntensity: number, backgroundMode?: StudioBackground, upIsZ?: boolean, ortho?: boolean, envRotationDeg?: number): void;
112
+ /**
113
+ * Remove environment map from the scene.
114
+ *
115
+ * Clears `scene.environment`, `scene.background`, and resets
116
+ * environment/background properties to defaults.
117
+ *
118
+ * @param scene - The Three.js scene to clear
119
+ */
120
+ remove(scene: THREE.Scene): void;
121
+ /**
122
+ * Switch between 2K and 4K environment map resolution.
123
+ *
124
+ * Rebuilds preset URLs, evicts cached HDR presets (so they reload at
125
+ * the new resolution), and reloads the current environment if one is
126
+ * active.
127
+ *
128
+ * @param use4k - True for 4K, false for 2K
129
+ * @param currentEnvName - The currently active environment name (to reload)
130
+ * @param renderer - WebGL renderer (needed for reload)
131
+ * @returns Promise that resolves when the new texture is ready
132
+ */
133
+ setUse4kEnvMaps(use4k: boolean, currentEnvName: string, renderer: THREE.WebGLRenderer): Promise<THREE.Texture | null>;
134
+ /** Whether 4K env maps are currently enabled. */
135
+ get use4kEnvMaps(): boolean;
136
+ /**
137
+ * Whether an environment name is a Poly Haven preset (resolution-switchable).
138
+ * Returns false for "studio", "none", and custom URLs.
139
+ */
140
+ isPreset(name: string): boolean;
141
+ /**
142
+ * Whether the render-to-texture env background path is currently active.
143
+ * When true, the caller must call updateEnvBackground() each frame.
144
+ */
145
+ get isEnvBackgroundActive(): boolean;
146
+ /**
147
+ * Get cached light detection result for an environment.
148
+ *
149
+ * @param envName - Environment name or URL (same key used in loadEnvironment)
150
+ * @returns Detection result, or null if not yet analyzed
151
+ */
152
+ getLightDetection(envName: string): LightDetectionResult | null;
153
+ /**
154
+ * Update the env background render target (ortho camera workaround).
155
+ *
156
+ * Renders the PMREM env map to a 2D render target using a fixed-FOV virtual
157
+ * perspective camera whose quaternion is synced with the main camera. The
158
+ * resulting 2D texture is set as the main scene's background, giving a
159
+ * world-space environment that tracks camera orbit — matching how
160
+ * scene.environment (IBL reflections) already behaves.
161
+ *
162
+ * Called every frame from the render loop when isEnvBackgroundActive is true.
163
+ * Only active in ortho mode (perspective uses native cubemap background).
164
+ *
165
+ * @param renderer - WebGL renderer
166
+ * @param mainCamera - The active camera whose orientation to match
167
+ */
168
+ updateEnvBackground(renderer: THREE.WebGLRenderer, mainCamera?: THREE.Camera): void;
169
+ /**
170
+ * Dispose all cached resources.
171
+ *
172
+ * Disposes all cached PMREM render targets (and their textures) and
173
+ * the PMREMGenerator. After disposal, this manager cannot be used again.
174
+ *
175
+ * Call this on `viewer.dispose()`, NOT on `viewer.clear()` --
176
+ * the EnvironmentManager survives shape data clearing because
177
+ * environments are independent of shape data.
178
+ */
179
+ dispose(): void;
180
+ /**
181
+ * Set up the env background: a separate scene with the PMREM texture
182
+ * as background and a fixed-FOV virtual perspective camera for rendering
183
+ * to a 2D target. Used only for ortho cameras (perspective uses native
184
+ * cubemap background).
185
+ */
186
+ private _setupEnvBackground;
187
+ /**
188
+ * Tear down the env background state.
189
+ */
190
+ private _teardownEnvBackground;
191
+ /**
192
+ * Resolve environment name to an HDR URL, if applicable.
193
+ *
194
+ * Returns null for "studio" (uses RoomEnvironment, no URL).
195
+ * Returns the preset URL for "neutral"/"outdoor".
196
+ * Returns the name itself for custom URLs.
197
+ */
198
+ private _resolveUrl;
199
+ /**
200
+ * Get or create the PMREMGenerator (lazy initialization).
201
+ */
202
+ private _ensurePmremGenerator;
203
+ /**
204
+ * Get or create the HDRLoader (lazy initialization).
205
+ */
206
+ private _ensureHdrLoader;
207
+ /**
208
+ * Internal load dispatcher.
209
+ *
210
+ * Routes to RoomEnvironment generation or HDR loading based on the name.
211
+ * On HDR failure, falls back to "studio" (RoomEnvironment).
212
+ */
213
+ private _load;
214
+ /**
215
+ * Generate PMREM texture from the procedural RoomEnvironment.
216
+ *
217
+ * This is synchronous (no network), fast (~70ms), and always available.
218
+ */
219
+ private _loadRoomEnvironment;
220
+ /**
221
+ * Load an HDR file and generate a PMREM texture from it.
222
+ *
223
+ * Uses HDRLoader to fetch the .hdr file, then PMREMGenerator.fromEquirectangular()
224
+ * to create the PMREM cubemap. The source equirectangular texture is disposed
225
+ * after PMREM generation. The PMREM texture itself serves as both the IBL
226
+ * environment and the background (in "environment" mode).
227
+ *
228
+ * @param url - URL of the .hdr file
229
+ * @param cacheKey - Cache key for the resulting PMREM render target
230
+ * @param renderer - WebGL renderer for PMREMGenerator
231
+ * @returns PMREM texture
232
+ * @throws If the HDR file cannot be loaded
233
+ */
234
+ private _loadHdr;
235
+ }
236
+ /** Dispose lazy-cached gradient textures (called from EnvironmentManager.dispose). */
237
+ declare function disposeGradientTextures(): void;
238
+ export { EnvironmentManager, disposeGradientTextures };
239
+ export type { EnvironmentManagerOptions };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * HDR environment map light source detection.
3
+ *
4
+ * Analyzes equirectangular HDR pixel data to find dominant light sources
5
+ * (softboxes in studio HDRs, sun in outdoor HDRs). Returns direction,
6
+ * intensity, and color for up to 2 lights, used to create shadow-casting
7
+ * DirectionalLights in Studio mode.
8
+ *
9
+ * Algorithm: downsample to 128x64 luminance grid → threshold at 10x median
10
+ * → flood-fill cluster → convert centroids to 3D direction vectors.
11
+ * Runs ~5-10ms on CPU, no GPU readback needed.
12
+ */
13
+ /** A detected dominant light source from HDR analysis. */
14
+ export interface DetectedLight {
15
+ /** Unit direction vector toward the light source (Y-up, before Z-up rotation). */
16
+ direction: [number, number, number];
17
+ /** Relative intensity (normalized, 0-1 range). */
18
+ intensity: number;
19
+ /** Linear RGB color of the light source (0-1 range). */
20
+ color: [number, number, number];
21
+ }
22
+ /** Result of light detection analysis. */
23
+ export interface LightDetectionResult {
24
+ /** Detected light sources (0-2 entries). */
25
+ lights: DetectedLight[];
26
+ /** Whether the result came from actual HDR analysis (true) or a fallback (false). */
27
+ wasAnalyzed: boolean;
28
+ }
29
+ /**
30
+ * Detect dominant light sources from equirectangular HDR pixel data.
31
+ *
32
+ * @param data - Raw pixel data (Uint16Array for HalfFloat, or Float32Array)
33
+ * @param width - HDR image width in pixels
34
+ * @param height - HDR image height in pixels
35
+ * @returns Detection result with up to 2 lights
36
+ */
37
+ export declare function detectDominantLights(data: Uint16Array | Float32Array, width: number, height: number): LightDetectionResult;
38
+ /**
39
+ * Return hardcoded fallback lights for procedural RoomEnvironment.
40
+ *
41
+ * The RoomEnvironment has no raw HDR data to analyze. A single top-front
42
+ * light direction approximates its primary illumination.
43
+ */
44
+ export declare function getDefaultLights(): LightDetectionResult;