three-cad-viewer 4.3.5 → 4.3.7

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/dist/three-cad-viewer.esm.js +8 -5
  2. package/dist/three-cad-viewer.esm.js.map +1 -1
  3. package/dist/three-cad-viewer.esm.min.js +1 -1
  4. package/dist/three-cad-viewer.js +8 -5
  5. package/dist/three-cad-viewer.min.js +1 -1
  6. package/package.json +2 -3
  7. package/src/_version.ts +0 -1
  8. package/src/camera/camera.ts +0 -445
  9. package/src/camera/controls/CADOrbitControls.ts +0 -241
  10. package/src/camera/controls/CADTrackballControls.ts +0 -598
  11. package/src/camera/controls.ts +0 -380
  12. package/src/core/patches.ts +0 -16
  13. package/src/core/studio-manager.ts +0 -652
  14. package/src/core/types.ts +0 -892
  15. package/src/core/viewer-state.ts +0 -784
  16. package/src/core/viewer.ts +0 -4821
  17. package/src/index.ts +0 -151
  18. package/src/rendering/environment.ts +0 -840
  19. package/src/rendering/light-detection.ts +0 -327
  20. package/src/rendering/material-factory.ts +0 -735
  21. package/src/rendering/material-presets.ts +0 -289
  22. package/src/rendering/raycast.ts +0 -291
  23. package/src/rendering/room-environment.ts +0 -192
  24. package/src/rendering/studio-composer.ts +0 -577
  25. package/src/rendering/studio-floor.ts +0 -108
  26. package/src/rendering/texture-cache.ts +0 -324
  27. package/src/rendering/tree-model.ts +0 -542
  28. package/src/rendering/triplanar.ts +0 -329
  29. package/src/scene/animation.ts +0 -343
  30. package/src/scene/axes.ts +0 -108
  31. package/src/scene/bbox.ts +0 -223
  32. package/src/scene/clipping.ts +0 -650
  33. package/src/scene/grid.ts +0 -864
  34. package/src/scene/nestedgroup.ts +0 -1448
  35. package/src/scene/objectgroup.ts +0 -866
  36. package/src/scene/orientation.ts +0 -259
  37. package/src/scene/render-shape.ts +0 -634
  38. package/src/tools/cad_tools/measure.ts +0 -811
  39. package/src/tools/cad_tools/select.ts +0 -100
  40. package/src/tools/cad_tools/tools.ts +0 -231
  41. package/src/tools/cad_tools/ui.ts +0 -454
  42. package/src/tools/cad_tools/zebra.ts +0 -369
  43. package/src/types/html.d.ts +0 -5
  44. package/src/types/n8ao.d.ts +0 -28
  45. package/src/types/three-augmentation.d.ts +0 -60
  46. package/src/ui/display.ts +0 -3295
  47. package/src/ui/index.html +0 -505
  48. package/src/ui/info.ts +0 -177
  49. package/src/ui/slider.ts +0 -206
  50. package/src/ui/toolbar.ts +0 -347
  51. package/src/ui/treeview.ts +0 -945
  52. package/src/utils/decode-instances.ts +0 -233
  53. package/src/utils/font.ts +0 -60
  54. package/src/utils/gpu-tracker.ts +0 -265
  55. package/src/utils/logger.ts +0 -92
  56. package/src/utils/sizeof.ts +0 -116
  57. package/src/utils/timer.ts +0 -69
  58. package/src/utils/utils.ts +0 -446
@@ -1,652 +0,0 @@
1
- /**
2
- * StudioManager — orchestrates Studio mode rendering.
3
- *
4
- * Extracted from viewer.ts to reduce its complexity. Owns the Studio-specific
5
- * resources (composer, floor, shadow lights, environment manager) and handles
6
- * all Studio subscriptions and mode enter/leave logic.
7
- *
8
- * Communicates with the Viewer via the StudioManagerContext interface to avoid
9
- * a circular dependency.
10
- */
11
- import * as THREE from "three";
12
- import type { ClippingState } from "../scene/clipping.js";
13
- import { EnvironmentManager } from "../rendering/environment.js";
14
- import { StudioFloor } from "../rendering/studio-floor.js";
15
- import { StudioComposer } from "../rendering/studio-composer.js";
16
- import { ViewerState } from "./viewer-state.js";
17
- import { logger } from "../utils/logger.js";
18
- import { scaleLight } from "../utils/utils.js";
19
- import type { NestedGroup, ObjectGroup } from "../scene/nestedgroup.js";
20
- import { isObjectGroup } from "../scene/nestedgroup.js";
21
- import type { Camera } from "../camera/camera.js";
22
- import type { Clipping } from "../scene/clipping.js";
23
- import type { BoundingBox } from "../scene/bbox.js";
24
-
25
- // ---------------------------------------------------------------------------
26
- // Context interface — what StudioManager needs from Viewer
27
- // ---------------------------------------------------------------------------
28
-
29
- /**
30
- * Abstraction over Viewer that StudioManager uses. Keeps the dependency
31
- * one-directional (StudioManager → context, never StudioManager → Viewer).
32
- */
33
- export interface StudioManagerContext {
34
- renderer: THREE.WebGLRenderer;
35
- state: ViewerState;
36
-
37
- /** Whether viewer has rendered content. */
38
- isRendered(): boolean;
39
-
40
- // Scene graph access (throw if not rendered)
41
- getScene(): THREE.Scene;
42
- getCamera(): Camera;
43
- getAmbientLight(): THREE.AmbientLight;
44
- getDirectLight(): THREE.DirectionalLight;
45
- getNestedGroup(): NestedGroup;
46
- getClipping(): Clipping;
47
- getBbox(): BoundingBox | null;
48
- getLastBboxId(): string | null;
49
-
50
- // Viewer actions
51
- setAxes(flag: boolean, notify?: boolean): void;
52
- setGrids(grids: [boolean, boolean, boolean], notify?: boolean): void;
53
- setOrtho(flag: boolean, notify?: boolean): void;
54
-
55
- // Callbacks
56
- update(updateMarker: boolean, notify?: boolean): void;
57
- dispatchEvent(event: Event): void;
58
- onSelectionChanged(id: string | null): void;
59
- }
60
-
61
- // ---------------------------------------------------------------------------
62
- // StudioManager
63
- // ---------------------------------------------------------------------------
64
-
65
- class StudioManager {
66
- readonly envManager: EnvironmentManager;
67
- readonly floor: StudioFloor;
68
-
69
- private _composer: StudioComposer | null = null;
70
- private _active: boolean = false;
71
- private _savedClippingState: ClippingState | null = null;
72
- private _shadowLights: THREE.DirectionalLight[] = [];
73
- private _ctx: StudioManagerContext;
74
-
75
- constructor(ctx: StudioManagerContext) {
76
- this._ctx = ctx;
77
- this.envManager = new EnvironmentManager();
78
- this.floor = new StudioFloor();
79
- this._setupSubscriptions();
80
- }
81
-
82
- // -------------------------------------------------------------------------
83
- // Public API
84
- // -------------------------------------------------------------------------
85
-
86
- get isActive(): boolean {
87
- return this._active && this._ctx.isRendered();
88
- }
89
-
90
- get hasComposer(): boolean {
91
- return this._composer !== null;
92
- }
93
-
94
- /** Render via composer (call from Viewer.update / _animate). */
95
- render(): void {
96
- this._composer?.render();
97
- }
98
-
99
- /** Update composer camera (call from Viewer.switchCamera). */
100
- setCamera(camera: THREE.Camera): void {
101
- this._composer?.setCamera(camera);
102
- }
103
-
104
- /** Resize composer (call from Viewer.resize). */
105
- setSize(width: number, height: number): void {
106
- this._composer?.setSize(width, height);
107
- }
108
-
109
- /** Whether env background needs per-frame update. */
110
- get isEnvBackgroundActive(): boolean {
111
- return this.envManager.isEnvBackgroundActive;
112
- }
113
-
114
- /** Update env background (ortho workaround, call per frame). */
115
- updateEnvBackground(renderer: THREE.WebGLRenderer, camera: THREE.Camera): void {
116
- this.envManager.updateEnvBackground(renderer, camera);
117
- }
118
-
119
- /** Check if an environment name is a Poly Haven preset. */
120
- isEnvPreset(name: string): boolean {
121
- return this.envManager.isPreset(name);
122
- }
123
-
124
- /**
125
- * Get the ObjectGroup and path for the currently selected object.
126
- * Returns null if nothing selected or Studio mode inactive.
127
- */
128
- getSelectedObjectGroup(): { object: ObjectGroup; path: string } | null {
129
- if (!this._active || this._ctx.getLastBboxId() == null) {
130
- return null;
131
- }
132
- const id = this._ctx.getLastBboxId()!;
133
- const entry = this._ctx.getNestedGroup().groups[id];
134
- if (!isObjectGroup(entry)) {
135
- return null;
136
- }
137
- return { object: entry, path: id };
138
- }
139
-
140
- // -------------------------------------------------------------------------
141
- // Mode enter/leave
142
- // -------------------------------------------------------------------------
143
-
144
- enterStudioMode = async (): Promise<void> => {
145
- if (!this._ctx.isRendered()) return;
146
- this._active = true;
147
-
148
- const { renderer, state } = this._ctx;
149
-
150
- try {
151
- // 1. Save and disable clipping
152
- const clipping = this._ctx.getClipping();
153
- this._savedClippingState = clipping.saveState();
154
- renderer.localClippingEnabled = false;
155
- clipping.setVisible(false);
156
- if (clipping.planeHelpers) {
157
- clipping.planeHelpers.visible = false;
158
- }
159
-
160
- // 2. Build/swap studio materials (async due to textures)
161
- const nestedGroup = this._ctx.getNestedGroup();
162
- const unresolvedTags = await nestedGroup.enterStudioMode(state.get("studioTextureMapping"));
163
- if (!this._active) return;
164
-
165
- // Surface unresolved material tags to the UI
166
- if (unresolvedTags.length > 0) {
167
- this._ctx.dispatchEvent(
168
- new CustomEvent("tcv-material-warnings", { detail: unresolvedTags }),
169
- );
170
- }
171
-
172
- // 3. Load environment map
173
- const envName = state.get("studioEnvironment");
174
- await this.envManager.loadEnvironment(envName, renderer);
175
- if (!this._active) return;
176
-
177
- // 4. Apply ALL rendering changes atomically
178
- const scene = this._ctx.getScene();
179
- const camera = this._ctx.getCamera();
180
- this.envManager.apply(
181
- scene,
182
- state.get("studioEnvIntensity"),
183
- state.get("studioBackground"),
184
- state.get("up") === "Z",
185
- camera.ortho,
186
- state.get("studioEnvRotation"),
187
- );
188
-
189
- // Lighting: disable CAD lights; environment IBL provides all illumination
190
- this._ctx.getAmbientLight().intensity = 0;
191
- this._ctx.getDirectLight().intensity = 0;
192
-
193
- // Floor
194
- this._configureFloor();
195
-
196
- // Create composer (must be before shadows)
197
- if (!this._composer) {
198
- this._composer = new StudioComposer(
199
- renderer,
200
- scene,
201
- camera.getCamera(),
202
- state.get("cadWidth"),
203
- state.get("height"),
204
- );
205
- }
206
-
207
- // Shadows (requires composer)
208
- if (state.get("studioShadowIntensity") > 0) {
209
- this._setShadowsEnabled(true);
210
- }
211
-
212
- // Tone mapping
213
- this._composer.setToneMapping(
214
- state.get("studioToneMapping"),
215
- state.get("studioExposure"),
216
- );
217
-
218
- // Background protection
219
- const bg = scene.background;
220
- this._composer.setBackgroundProtect(
221
- bg instanceof THREE.Color ? bg : null,
222
- );
223
-
224
- // Ambient Occlusion
225
- const aoIntensity = state.get("studioAOIntensity");
226
- this._composer.setAOIntensity(aoIntensity);
227
- this._composer.setAOEnabled(aoIntensity > 0);
228
-
229
- // Edges are always hidden in Studio mode
230
- nestedGroup.setStudioShowEdges(false);
231
-
232
- this._ctx.update(true, false);
233
- } catch (err) {
234
- if (this._composer) {
235
- this._composer.dispose();
236
- this._composer = null;
237
- }
238
- this._active = false;
239
- logger.error("Unexpected error entering studio mode", err);
240
- }
241
- };
242
-
243
- leaveStudioMode = (): void => {
244
- if (!this._ctx.isRendered()) return;
245
-
246
- const { renderer, state } = this._ctx;
247
- const nestedGroup = this._ctx.getNestedGroup();
248
-
249
- // 1. Restore materials
250
- nestedGroup.leaveStudioMode();
251
-
252
- // 2. Tear down composer
253
- if (this._composer) {
254
- this._composer.dispose();
255
- this._composer = null;
256
- }
257
-
258
- // 3. Remove environment, disable shadows
259
- this.envManager.remove(this._ctx.getScene());
260
- this._setShadowsEnabled(false);
261
-
262
- // 4. Restore lighting
263
- this._ctx.getAmbientLight().intensity = scaleLight(state.get("ambientIntensity"));
264
- this._ctx.getDirectLight().intensity = scaleLight(state.get("directIntensity"));
265
-
266
- // 5. Disable tone mapping
267
- renderer.toneMapping = THREE.NoToneMapping;
268
- renderer.toneMappingExposure = 1.0;
269
-
270
- // 6. Restore clipping state
271
- if (this._savedClippingState) {
272
- this._ctx.getClipping().restoreState(this._savedClippingState);
273
- this._savedClippingState = null;
274
- }
275
-
276
- // 7. Clear active flag; edges restored by ObjectGroup.leaveStudioMode()
277
- this._active = false;
278
-
279
- this._ctx.update(true, false);
280
- };
281
-
282
- resetStudio = (): void => {
283
- const defaults = ViewerState.STUDIO_MODE_DEFAULTS;
284
- const state = this._ctx.state;
285
- state.set("studioEnvironment", defaults.studioEnvironment);
286
- state.set("studioEnvIntensity", defaults.studioEnvIntensity);
287
- state.set("studioBackground", defaults.studioBackground);
288
- state.set("studioToneMapping", defaults.studioToneMapping);
289
- state.set("studioExposure", defaults.studioExposure);
290
- state.set("studio4kEnvMaps", defaults.studio4kEnvMaps);
291
- state.set("studioTextureMapping", defaults.studioTextureMapping);
292
- state.set("studioEnvRotation", defaults.studioEnvRotation);
293
- state.set("studioShadowIntensity", defaults.studioShadowIntensity);
294
- state.set("studioShadowSoftness", defaults.studioShadowSoftness);
295
- state.set("studioAOIntensity", defaults.studioAOIntensity);
296
- };
297
-
298
- /**
299
- * Dispose all Studio resources. Called from Viewer.dispose().
300
- * Must be called BEFORE renderer.dispose().
301
- */
302
- dispose(): void {
303
- if (this._composer) {
304
- this._composer.dispose();
305
- this._composer = null;
306
- }
307
- this._removeShadowLights();
308
- this.envManager.dispose();
309
- this.floor.dispose();
310
- this._active = false;
311
- this._savedClippingState = null;
312
- }
313
-
314
- // -------------------------------------------------------------------------
315
- // Private — subscriptions
316
- // -------------------------------------------------------------------------
317
-
318
- private _setupSubscriptions(): void {
319
- const isActive = (): boolean => {
320
- return this._active && this._ctx.isRendered();
321
- };
322
-
323
- const reapplyEnv = (orthoOverride?: boolean): void => {
324
- this.envManager.apply(
325
- this._ctx.getScene(),
326
- this._ctx.state.get("studioEnvIntensity"),
327
- this._ctx.state.get("studioBackground"),
328
- this._ctx.state.get("up") === "Z",
329
- orthoOverride ?? this._ctx.getCamera().ortho,
330
- this._ctx.state.get("studioEnvRotation"),
331
- );
332
- if (this._composer) {
333
- const bg = this._ctx.getScene().background;
334
- this._composer.setBackgroundProtect(
335
- bg instanceof THREE.Color ? bg : null,
336
- );
337
- }
338
- };
339
-
340
- const state = this._ctx.state;
341
-
342
- state.subscribe("studioEnvironment", (change) => {
343
- if (!isActive()) return;
344
- this.envManager.loadEnvironment(change.new, this._ctx.renderer).then(() => {
345
- if (!isActive()) return;
346
- reapplyEnv();
347
- if (state.get("studioShadowIntensity") > 0) {
348
- this._configureShadowLights();
349
- }
350
- this._ctx.update(true, false);
351
- this._ctx.dispatchEvent(new Event("tcv-studio-ready"));
352
- }).catch((err) => {
353
- logger.error("Unexpected error loading studio environment", err);
354
- this._ctx.dispatchEvent(new Event("tcv-studio-ready"));
355
- });
356
- });
357
-
358
- state.subscribe("studioEnvIntensity", () => {
359
- if (!isActive()) return;
360
- reapplyEnv();
361
- this._ctx.update(true, false);
362
- });
363
-
364
- state.subscribe("studioEnvRotation", () => {
365
- if (!isActive()) return;
366
- reapplyEnv();
367
- if (state.get("studioShadowIntensity") > 0) {
368
- this._configureShadowLights();
369
- }
370
- this._ctx.update(true, false);
371
- });
372
-
373
- state.subscribe("studioShadowIntensity", (change) => {
374
- if (!isActive()) return;
375
- const intensity = change.new;
376
- const wasEnabled = change.old != null && change.old > 0;
377
- const nowEnabled = intensity > 0;
378
-
379
- if (nowEnabled && !wasEnabled) {
380
- this._setShadowsEnabled(true);
381
- } else if (!nowEnabled && wasEnabled) {
382
- this._setShadowsEnabled(false);
383
- }
384
-
385
- if (nowEnabled) {
386
- for (const light of this._shadowLights) {
387
- light.shadow.intensity = intensity;
388
- }
389
- this.floor.setShadowIntensity(intensity);
390
- if (this._composer) {
391
- this._composer.setShadowMaskIntensity(intensity * 0.75);
392
- }
393
- }
394
- this._ctx.update(true, false);
395
- });
396
-
397
- state.subscribe("studioShadowSoftness", (change) => {
398
- if (!isActive()) return;
399
- if (state.get("studioShadowIntensity") <= 0) return;
400
- if (this._composer) {
401
- this._composer.setShadowSoftness(change.new);
402
- }
403
- this._ctx.update(true, false);
404
- });
405
-
406
- state.subscribe("studioAOIntensity", (change) => {
407
- if (!isActive()) return;
408
- if (this._composer) {
409
- const intensity = change.new;
410
- this._composer.setAOEnabled(intensity > 0);
411
- this._composer.setAOIntensity(intensity);
412
- }
413
- this._ctx.update(true, false);
414
- });
415
-
416
- state.subscribe("studioBackground", () => {
417
- if (!isActive()) return;
418
- reapplyEnv();
419
- this._ctx.update(true, false);
420
- });
421
-
422
- state.subscribe("ortho", (change) => {
423
- if (!isActive()) return;
424
- reapplyEnv(change.new as boolean);
425
- this._ctx.update(true, false);
426
- });
427
-
428
- state.subscribe("studioToneMapping", () => {
429
- if (!isActive()) return;
430
- this._applyToneMapping();
431
- this._ctx.update(true, false);
432
- });
433
-
434
- state.subscribe("studioExposure", () => {
435
- if (!isActive()) return;
436
- this._applyToneMapping();
437
- this._ctx.update(true, false);
438
- });
439
-
440
- state.subscribe("studio4kEnvMaps", (change) => {
441
- if (!isActive()) return;
442
- const envName = state.get("studioEnvironment");
443
- this.envManager.setUse4kEnvMaps(change.new, envName, this._ctx.renderer).then(() => {
444
- if (!isActive()) return;
445
- reapplyEnv();
446
- if (state.get("studioShadowIntensity") > 0) {
447
- this._configureShadowLights();
448
- }
449
- this._ctx.update(true, false);
450
- this._ctx.dispatchEvent(new Event("tcv-studio-ready"));
451
- });
452
- });
453
-
454
- state.subscribe("studioTextureMapping", () => {
455
- if (!isActive()) return;
456
- this._rebuildMaterials().finally(() => {
457
- this._ctx.dispatchEvent(new Event("tcv-studio-ready"));
458
- });
459
- });
460
- }
461
-
462
- // -------------------------------------------------------------------------
463
- // Private — floor, shadows, tone mapping, material rebuild
464
- // -------------------------------------------------------------------------
465
-
466
- private _configureFloor(): void {
467
- const bbox = this._ctx.getBbox();
468
- if (!bbox) return;
469
- const maxExtent = Math.max(
470
- bbox.max.x - bbox.min.x,
471
- bbox.max.y - bbox.min.y,
472
- bbox.max.z - bbox.min.z,
473
- );
474
- const zPosition = bbox.min.z - maxExtent * 0.001;
475
- this.floor.configure(zPosition, maxExtent);
476
- }
477
-
478
- private _configureShadowLights(): void {
479
- this._removeShadowLights();
480
-
481
- const bbox = this._ctx.getBbox();
482
- if (!bbox || !this._ctx.isRendered()) return;
483
-
484
- const state = this._ctx.state;
485
- const envName = state.get("studioEnvironment");
486
- const detection = this.envManager.getLightDetection(envName);
487
- if (!detection || detection.lights.length === 0) return;
488
-
489
- const bboxCenter = new THREE.Vector3(
490
- (bbox.min.x + bbox.max.x) / 2,
491
- (bbox.min.y + bbox.max.y) / 2,
492
- (bbox.min.z + bbox.max.z) / 2,
493
- );
494
- const maxExtent = Math.max(
495
- bbox.max.x - bbox.min.x,
496
- bbox.max.y - bbox.min.y,
497
- bbox.max.z - bbox.min.z,
498
- );
499
-
500
- const isZUp = state.get("up") === "Z";
501
- const envRotationRad = (state.get("studioEnvRotation") * Math.PI) / 180;
502
-
503
- this._ctx.renderer.shadowMap.enabled = true;
504
- this._ctx.renderer.shadowMap.type = THREE.PCFShadowMap;
505
-
506
- const scene = this._ctx.getScene();
507
-
508
- for (const detected of detection.lights) {
509
- let [dx, dy, dz] = detected.direction;
510
-
511
- if (isZUp) {
512
- const oy = dy;
513
- const oz = dz;
514
- dy = -oz;
515
- dz = oy;
516
- }
517
-
518
- if (envRotationRad !== 0) {
519
- if (isZUp) {
520
- const cosR = Math.cos(envRotationRad);
521
- const sinR = Math.sin(envRotationRad);
522
- const nx = dx * cosR - dy * sinR;
523
- const ny = dx * sinR + dy * cosR;
524
- dx = nx;
525
- dy = ny;
526
- } else {
527
- const cosR = Math.cos(envRotationRad);
528
- const sinR = Math.sin(envRotationRad);
529
- const nx = dx * cosR + dz * sinR;
530
- const nz = -dx * sinR + dz * cosR;
531
- dx = nx;
532
- dz = nz;
533
- }
534
- }
535
-
536
- const dir = new THREE.Vector3(dx, dy, dz).normalize();
537
- const light = new THREE.DirectionalLight(0xffffff, 0.01);
538
-
539
- light.position.copy(bboxCenter).addScaledVector(dir, maxExtent * 3);
540
- light.target.position.copy(bboxCenter);
541
-
542
- light.castShadow = true;
543
- const frustumSize = maxExtent * 6.0;
544
- light.shadow.camera.left = -frustumSize;
545
- light.shadow.camera.right = frustumSize;
546
- light.shadow.camera.top = frustumSize;
547
- light.shadow.camera.bottom = -frustumSize;
548
- light.shadow.camera.near = maxExtent * 0.1;
549
- light.shadow.camera.far = maxExtent * 7;
550
- light.shadow.mapSize.set(4096, 4096);
551
- light.shadow.bias = -0.001;
552
- light.shadow.intensity = state.get("studioShadowIntensity");
553
-
554
- scene.add(light);
555
- scene.add(light.target);
556
- this._shadowLights.push(light);
557
- }
558
-
559
- if (this._composer) {
560
- this._composer.setShadowMaskEnabled(true);
561
- this._composer.setShadowSoftness(state.get("studioShadowSoftness"));
562
- this._composer.setShadowMaskIntensity(state.get("studioShadowIntensity") * 0.75);
563
- }
564
- }
565
-
566
- private _removeShadowLights(): void {
567
- if (this._ctx.isRendered()) {
568
- const scene = this._ctx.getScene();
569
- for (const light of this._shadowLights) {
570
- scene.remove(light);
571
- scene.remove(light.target);
572
- light.shadow.map?.dispose();
573
- light.dispose();
574
- }
575
- } else {
576
- for (const light of this._shadowLights) {
577
- light.shadow.map?.dispose();
578
- light.dispose();
579
- }
580
- }
581
- this._shadowLights = [];
582
-
583
- if (this._composer) {
584
- this._composer.setShadowMaskEnabled(false);
585
- }
586
- }
587
-
588
- private _setShadowsEnabled(enabled: boolean): void {
589
- if (!this._ctx.isRendered()) return;
590
-
591
- const nestedGroup = this._ctx.getNestedGroup();
592
-
593
- if (enabled) {
594
- this._configureShadowLights();
595
-
596
- nestedGroup.rootGroup?.traverse((obj) => {
597
- if (obj instanceof THREE.Mesh) {
598
- obj.castShadow = true;
599
- }
600
- });
601
-
602
- this.floor.setShadowsEnabled(true);
603
-
604
- this._ctx.getScene().traverse((obj) => {
605
- if (obj instanceof THREE.Mesh && obj.material) {
606
- const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
607
- for (const m of mats) {
608
- m.needsUpdate = true;
609
- }
610
- }
611
- });
612
- } else {
613
- this._removeShadowLights();
614
- this._ctx.renderer.shadowMap.enabled = false;
615
-
616
- nestedGroup.rootGroup?.traverse((obj) => {
617
- if (obj instanceof THREE.Mesh) {
618
- obj.castShadow = false;
619
- }
620
- });
621
-
622
- this.floor.setShadowsEnabled(false);
623
- }
624
- }
625
-
626
- private _applyToneMapping(): void {
627
- if (this._composer) {
628
- this._composer.setToneMapping(
629
- this._ctx.state.get("studioToneMapping"),
630
- this._ctx.state.get("studioExposure"),
631
- );
632
- }
633
- }
634
-
635
- private _rebuildMaterials = async (): Promise<void> => {
636
- const nestedGroup = this._ctx.getNestedGroup();
637
- nestedGroup.leaveStudioMode();
638
- nestedGroup.clearStudioMaterialCache();
639
- const unresolvedTags = await nestedGroup.enterStudioMode(this._ctx.state.get("studioTextureMapping"));
640
- if (this._active) {
641
- nestedGroup.setStudioShowEdges(false);
642
- if (unresolvedTags.length > 0) {
643
- this._ctx.dispatchEvent(
644
- new CustomEvent("tcv-material-warnings", { detail: unresolvedTags }),
645
- );
646
- }
647
- this._ctx.update(true, false);
648
- }
649
- };
650
- }
651
-
652
- export { StudioManager };