three-cad-viewer 4.3.4 → 4.3.6

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 (59) hide show
  1. package/dist/scene/clipping.d.ts +6 -0
  2. package/dist/three-cad-viewer.esm.js +20 -5
  3. package/dist/three-cad-viewer.esm.js.map +1 -1
  4. package/dist/three-cad-viewer.esm.min.js +1 -1
  5. package/dist/three-cad-viewer.js +20 -5
  6. package/dist/three-cad-viewer.min.js +1 -1
  7. package/package.json +2 -3
  8. package/src/_version.ts +0 -1
  9. package/src/camera/camera.ts +0 -445
  10. package/src/camera/controls/CADOrbitControls.ts +0 -241
  11. package/src/camera/controls/CADTrackballControls.ts +0 -598
  12. package/src/camera/controls.ts +0 -380
  13. package/src/core/patches.ts +0 -16
  14. package/src/core/studio-manager.ts +0 -652
  15. package/src/core/types.ts +0 -892
  16. package/src/core/viewer-state.ts +0 -784
  17. package/src/core/viewer.ts +0 -4821
  18. package/src/index.ts +0 -151
  19. package/src/rendering/environment.ts +0 -840
  20. package/src/rendering/light-detection.ts +0 -327
  21. package/src/rendering/material-factory.ts +0 -735
  22. package/src/rendering/material-presets.ts +0 -289
  23. package/src/rendering/raycast.ts +0 -291
  24. package/src/rendering/room-environment.ts +0 -192
  25. package/src/rendering/studio-composer.ts +0 -577
  26. package/src/rendering/studio-floor.ts +0 -108
  27. package/src/rendering/texture-cache.ts +0 -324
  28. package/src/rendering/tree-model.ts +0 -542
  29. package/src/rendering/triplanar.ts +0 -329
  30. package/src/scene/animation.ts +0 -343
  31. package/src/scene/axes.ts +0 -108
  32. package/src/scene/bbox.ts +0 -223
  33. package/src/scene/clipping.ts +0 -640
  34. package/src/scene/grid.ts +0 -864
  35. package/src/scene/nestedgroup.ts +0 -1444
  36. package/src/scene/objectgroup.ts +0 -866
  37. package/src/scene/orientation.ts +0 -259
  38. package/src/scene/render-shape.ts +0 -634
  39. package/src/tools/cad_tools/measure.ts +0 -811
  40. package/src/tools/cad_tools/select.ts +0 -100
  41. package/src/tools/cad_tools/tools.ts +0 -231
  42. package/src/tools/cad_tools/ui.ts +0 -454
  43. package/src/tools/cad_tools/zebra.ts +0 -369
  44. package/src/types/html.d.ts +0 -5
  45. package/src/types/n8ao.d.ts +0 -28
  46. package/src/types/three-augmentation.d.ts +0 -60
  47. package/src/ui/display.ts +0 -3295
  48. package/src/ui/index.html +0 -505
  49. package/src/ui/info.ts +0 -177
  50. package/src/ui/slider.ts +0 -206
  51. package/src/ui/toolbar.ts +0 -347
  52. package/src/ui/treeview.ts +0 -945
  53. package/src/utils/decode-instances.ts +0 -233
  54. package/src/utils/font.ts +0 -60
  55. package/src/utils/gpu-tracker.ts +0 -265
  56. package/src/utils/logger.ts +0 -92
  57. package/src/utils/sizeof.ts +0 -116
  58. package/src/utils/timer.ts +0 -69
  59. 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 };