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
package/Readme.md CHANGED
@@ -257,7 +257,7 @@ After the initial `viewer.render()`, parts can be added, removed, or updated wit
257
257
  `addPart(parentPath, partData)` creates new Three.js objects (meshes, edges, clipping stencils) from the part data and inserts them into the scene graph and navigation tree. `removePart(path)` disposes the Three.js objects and removes the part from the scene.
258
258
 
259
259
  ```js
260
- viewer.addPart("/Group", partData); // creates "/Group/PartName"
260
+ viewer.addPart("/Group", partData); // creates "/Group/PartName"
261
261
  viewer.removePart("/Group/PartName");
262
262
  ```
263
263
 
@@ -296,9 +296,12 @@ Add, remove, and update calls can be freely mixed within a single batch.
296
296
  viewer.render(shapes, renderOptions, viewerOptions);
297
297
 
298
298
  viewer.ensureStencilSize({
299
- xmin: -200, xmax: 200,
300
- ymin: -200, ymax: 200,
301
- zmin: 0, zmax: 300,
299
+ xmin: -200,
300
+ xmax: 200,
301
+ ymin: -200,
302
+ ymax: 200,
303
+ zmin: 0,
304
+ zmax: 300,
302
305
  });
303
306
 
304
307
  // All updates within these bounds are now stencil-rebuild-free
@@ -340,7 +343,7 @@ Pass a partial keymap to override individual bindings — unspecified keys keep
340
343
 
341
344
  ```javascript
342
345
  const displayOptions = {
343
- keymap: { axes: "q", reset: "!" }, // only these two change
346
+ keymap: { axes: "q", reset: "!" }, // only these two change
344
347
  };
345
348
  ```
346
349
 
@@ -427,3 +430,7 @@ For the deployment, see [Release.md](./Release.md)
427
430
  # Changes
428
431
 
429
432
  see [Changes.md](./Changes.md)
433
+
434
+ ## Credit
435
+
436
+ - The toycar example is downloaded from https://grabcad.com/library/toy-rider-car-1 and tessellated
@@ -22,6 +22,13 @@ type UpMode = "y_up" | "z_up" | "legacy";
22
22
  */
23
23
  declare class Camera {
24
24
  private static readonly DISTANCE_FACTOR;
25
+ /**
26
+ * Near plane factor: near = max(0.1, NEAR_FACTOR * distance).
27
+ * With far = 100 * distance, this gives a far/near ratio of 10,000:1,
28
+ * which is comfortable for 24-bit depth buffers and avoids z-fighting
29
+ * on large models (e.g. toycar at ~1100 unit bounding radius).
30
+ */
31
+ private static readonly NEAR_FACTOR;
25
32
  target: THREE.Vector3;
26
33
  ortho: boolean;
27
34
  up: UpMode;
@@ -31,6 +38,11 @@ declare class Camera {
31
38
  pCamera: THREE.PerspectiveCamera;
32
39
  oCamera: THREE.OrthographicCamera;
33
40
  camera: THREE.PerspectiveCamera | THREE.OrthographicCamera;
41
+ /**
42
+ * Compute the near clipping plane from the bounding radius.
43
+ * Keeps the far/near ratio bounded for depth buffer precision.
44
+ */
45
+ private static _computeNear;
34
46
  /**
35
47
  * Create a combined camera (orthographic and perspective).
36
48
  * @param width - canvas width.
@@ -42,8 +54,8 @@ declare class Camera {
42
54
  */
43
55
  constructor(width: number, height: number, distance: number, target: Vector3Tuple, ortho: boolean, up: UpDirection);
44
56
  /**
45
- * Update the far clipping plane for both cameras.
46
- * @param distance - The new bounding radius to base the far plane on.
57
+ * Update the near/far clipping planes for both cameras.
58
+ * @param distance - The new bounding radius to base the clipping planes on.
47
59
  */
48
60
  updateFarPlane(distance: number): void;
49
61
  /**
@@ -0,0 +1,91 @@
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 { EnvironmentManager } from "../rendering/environment.js";
13
+ import { StudioFloor } from "../rendering/studio-floor.js";
14
+ import { ViewerState } from "./viewer-state.js";
15
+ import type { NestedGroup, ObjectGroup } from "../scene/nestedgroup.js";
16
+ import type { Camera } from "../camera/camera.js";
17
+ import type { Clipping } from "../scene/clipping.js";
18
+ import type { BoundingBox } from "../scene/bbox.js";
19
+ /**
20
+ * Abstraction over Viewer that StudioManager uses. Keeps the dependency
21
+ * one-directional (StudioManager → context, never StudioManager → Viewer).
22
+ */
23
+ export interface StudioManagerContext {
24
+ renderer: THREE.WebGLRenderer;
25
+ state: ViewerState;
26
+ /** Whether viewer has rendered content. */
27
+ isRendered(): boolean;
28
+ getScene(): THREE.Scene;
29
+ getCamera(): Camera;
30
+ getAmbientLight(): THREE.AmbientLight;
31
+ getDirectLight(): THREE.DirectionalLight;
32
+ getNestedGroup(): NestedGroup;
33
+ getClipping(): Clipping;
34
+ getBbox(): BoundingBox | null;
35
+ getLastBboxId(): string | null;
36
+ setAxes(flag: boolean, notify?: boolean): void;
37
+ setGrids(grids: [boolean, boolean, boolean], notify?: boolean): void;
38
+ setOrtho(flag: boolean, notify?: boolean): void;
39
+ update(updateMarker: boolean, notify?: boolean): void;
40
+ dispatchEvent(event: Event): void;
41
+ onSelectionChanged(id: string | null): void;
42
+ }
43
+ declare class StudioManager {
44
+ readonly envManager: EnvironmentManager;
45
+ readonly floor: StudioFloor;
46
+ private _composer;
47
+ private _active;
48
+ private _savedClippingState;
49
+ private _savedViewState;
50
+ private _shadowLights;
51
+ private _ctx;
52
+ constructor(ctx: StudioManagerContext);
53
+ get isActive(): boolean;
54
+ get hasComposer(): boolean;
55
+ /** Render via composer (call from Viewer.update / _animate). */
56
+ render(): void;
57
+ /** Update composer camera (call from Viewer.switchCamera). */
58
+ setCamera(camera: THREE.Camera): void;
59
+ /** Resize composer (call from Viewer.resize). */
60
+ setSize(width: number, height: number): void;
61
+ /** Whether env background needs per-frame update. */
62
+ get isEnvBackgroundActive(): boolean;
63
+ /** Update env background (ortho workaround, call per frame). */
64
+ updateEnvBackground(renderer: THREE.WebGLRenderer, camera: THREE.Camera): void;
65
+ /** Check if an environment name is a Poly Haven preset. */
66
+ isEnvPreset(name: string): boolean;
67
+ /**
68
+ * Get the ObjectGroup and path for the currently selected object.
69
+ * Returns null if nothing selected or Studio mode inactive.
70
+ */
71
+ getSelectedObjectGroup(): {
72
+ object: ObjectGroup;
73
+ path: string;
74
+ } | null;
75
+ enterStudioMode: () => Promise<void>;
76
+ leaveStudioMode: () => void;
77
+ resetStudio: () => void;
78
+ /**
79
+ * Dispose all Studio resources. Called from Viewer.dispose().
80
+ * Must be called BEFORE renderer.dispose().
81
+ */
82
+ dispose(): void;
83
+ private _setupSubscriptions;
84
+ private _configureFloor;
85
+ private _configureShadowLights;
86
+ private _removeShadowLights;
87
+ private _setShadowsEnabled;
88
+ private _applyToneMapping;
89
+ private _rebuildMaterials;
90
+ }
91
+ export { StudioManager };
@@ -15,11 +15,24 @@ export type UpDirection = "Z" | "Y" | "legacy";
15
15
  /** Animation mode */
16
16
  export type AnimationMode = "none" | "animation" | "explode";
17
17
  /** Active sidebar tab */
18
- export type ActiveTab = "tree" | "clip" | "material" | "zebra";
18
+ export type ActiveTab = "tree" | "clip" | "material" | "zebra" | "studio";
19
19
  /** Zebra color scheme */
20
20
  export type ZebraColorScheme = "blackwhite" | "colorful" | "grayscale";
21
21
  /** Zebra mapping mode */
22
22
  export type ZebraMappingMode = "reflection" | "normal";
23
+ /**
24
+ * Studio environment preset name.
25
+ * - "studio": Built-in procedural RoomEnvironment (zero network)
26
+ * - "none": No environment map
27
+ * - Any other string: Poly Haven HDR preset slug or custom HDR URL
28
+ */
29
+ export type StudioEnvironment = string;
30
+ /** Studio tone mapping algorithm */
31
+ export type StudioToneMapping = "neutral" | "ACES" | "none";
32
+ /** Studio background mode */
33
+ export type StudioBackground = "grey" | "darkgrey" | "white" | "gradient" | "gradient-dark" | "environment" | "transparent";
34
+ /** Studio texture mapping mode */
35
+ export type StudioTextureMapping = "triplanar" | "parametric";
23
36
  /** Shape type */
24
37
  export type ShapeType = "shapes" | "edges" | "vertices";
25
38
  /** Shape subtype */
@@ -43,6 +56,8 @@ export declare enum CollapseState {
43
56
  export type ColorValue = number | string;
44
57
  /** RGB color as tuple [r, g, b] with values 0-1 */
45
58
  export type RGBColor = [number, number, number];
59
+ /** RGBA color as tuple [r, g, b, a] with values 0-1 */
60
+ export type RGBAColor = [number, number, number, number];
46
61
  /** Axis colors per theme - array of RGB colors for X, Y, Z axes */
47
62
  export type AxisColors = Record<Theme, RGBColor[]>;
48
63
  /** Flat axis colors per theme - all RGB values concatenated for line geometry */
@@ -126,7 +141,7 @@ export interface ChangeNotification {
126
141
  /** Callback for notifications */
127
142
  export type NotificationCallback = (change: ChangeNotification) => void;
128
143
  /** Action shortcut names for toolbar buttons and tabs */
129
- export type ActionShortcutName = "axes" | "axes0" | "grid" | "gridxy" | "perspective" | "transparent" | "blackedges" | "zscale" | "reset" | "resize" | "iso" | "front" | "rear" | "top" | "bottom" | "left" | "right" | "explode" | "distance" | "properties" | "select" | "help" | "play" | "stop" | "tree" | "clip" | "material" | "zebra";
144
+ export type ActionShortcutName = "axes" | "axes0" | "grid" | "gridxy" | "perspective" | "transparent" | "blackedges" | "zscale" | "reset" | "resize" | "iso" | "front" | "rear" | "top" | "bottom" | "left" | "right" | "explode" | "distance" | "properties" | "select" | "help" | "play" | "stop" | "tree" | "clip" | "material" | "zebra" | "studio";
130
145
  /** Action keymap: action name → key character */
131
146
  export type ActionKeymap = Partial<Record<ActionShortcutName, string>>;
132
147
  /** Combined keymap: modifier keys + action shortcuts */
@@ -168,28 +183,34 @@ export interface DisplayOptions {
168
183
  zscaleTool?: boolean;
169
184
  /** Show zebra tool (default: true) */
170
185
  zebraTool?: boolean;
186
+ /** Show studio tool (default: true) */
187
+ studioTool?: boolean;
171
188
  /** Enable measurement debug mode (default: false) */
172
189
  measurementDebug?: boolean;
190
+ /** External canvas element to use for the WebGL renderer, enabling shared WebGL context scenarios (default: undefined — renderer creates its own canvas) */
191
+ canvas?: HTMLCanvasElement;
192
+ /** External WebGL context to use for the renderer. When provided together with `canvas`, the renderer will use this context instead of creating a new one. Useful for sharing a context with other renderers like PixiJS. (default: undefined) */
193
+ gl?: WebGLRenderingContext | WebGL2RenderingContext;
173
194
  }
174
195
  /** Render options */
175
196
  export interface RenderOptions {
176
197
  /** Default edge color (default: 0x707070) */
177
198
  edgeColor?: number;
178
- /** Ambient light intensity (default: 0.5) */
199
+ /** Ambient light intensity (default: 1) */
179
200
  ambientIntensity?: number;
180
- /** Direct light intensity (default: 0.6) */
201
+ /** Direct light intensity (default: 1.1) */
181
202
  directIntensity?: number;
182
- /** Metalness (default: 0.7) */
203
+ /** Metalness (default: 0.3) */
183
204
  metalness?: number;
184
- /** Roughness (default: 0.7) */
205
+ /** Roughness (default: 0.65) */
185
206
  roughness?: number;
186
207
  /** Default opacity level for transparency (default: 0.5) */
187
208
  defaultOpacity?: number;
188
209
  /** Show triangle normals when normalLen > 0 (default: 0) */
189
210
  normalLen?: number;
190
211
  }
191
- /** Viewer options */
192
- export interface ViewerOptions {
212
+ /** Viewer options (includes studio mode configuration) */
213
+ export interface ViewerOptions extends StudioModeOptions {
193
214
  /** Use OrbitControls or TrackballControls (default: "orbit") */
194
215
  control?: ControlType;
195
216
  /** Show X-, Y-, Z-axes (default: false) */
@@ -264,8 +285,33 @@ export interface ZebraOptions {
264
285
  /** Zebra mapping mode (default: "reflection") */
265
286
  zebraMappingMode?: ZebraMappingMode;
266
287
  }
288
+ /** Studio mode options (environment, tone mapping, edges) */
289
+ export interface StudioModeOptions {
290
+ /** Environment preset or custom HDR URL (default: "studio") */
291
+ studioEnvironment?: string;
292
+ /** Environment map intensity, 0-1 (default: 0.5) */
293
+ studioEnvIntensity?: number;
294
+ /** Background mode (default: "environment") */
295
+ studioBackground?: StudioBackground;
296
+ /** Tone mapping algorithm (default: "neutral") */
297
+ studioToneMapping?: StudioToneMapping;
298
+ /** Tone mapping exposure, 0-2 (default: 1.0) */
299
+ studioExposure?: number;
300
+ /** Use 4K environment maps instead of 2K (default: false) */
301
+ studio4kEnvMaps?: boolean;
302
+ /** Texture mapping mode: triplanar projection or parametric UVs (default: "triplanar") */
303
+ studioTextureMapping?: StudioTextureMapping;
304
+ /** Environment map rotation in degrees, 0-360 (default: 0) */
305
+ studioEnvRotation?: number;
306
+ /** Shadow intensity, 0-1 (default: 0 = off) */
307
+ studioShadowIntensity?: number;
308
+ /** Shadow softness, 0-1 (default: 0.3) */
309
+ studioShadowSoftness?: number;
310
+ /** Ambient occlusion intensity, 0-3.0 (default: 0 = off) */
311
+ studioAOIntensity?: number;
312
+ }
267
313
  /** Combined options for initialization */
268
- export type CombinedOptions = DisplayOptions & RenderOptions & ViewerOptions & ZebraOptions;
314
+ export type CombinedOptions = DisplayOptions & RenderOptions & ViewerOptions & ZebraOptions & StudioModeOptions;
269
315
  /** Complete state shape with all properties */
270
316
  export interface ViewerStateShape {
271
317
  theme: Theme;
@@ -283,6 +329,7 @@ export interface ViewerStateShape {
283
329
  explodeTool: boolean;
284
330
  zscaleTool: boolean;
285
331
  zebraTool: boolean;
332
+ studioTool: boolean;
286
333
  measurementDebug: boolean;
287
334
  ambientIntensity: number;
288
335
  directIntensity: number;
@@ -326,6 +373,17 @@ export interface ViewerStateShape {
326
373
  zebraDirection: number;
327
374
  zebraColorScheme: ZebraColorScheme;
328
375
  zebraMappingMode: ZebraMappingMode;
376
+ studioEnvironment: string;
377
+ studioEnvIntensity: number;
378
+ studioBackground: StudioBackground;
379
+ studioToneMapping: StudioToneMapping;
380
+ studioExposure: number;
381
+ studio4kEnvMaps: boolean;
382
+ studioTextureMapping: StudioTextureMapping;
383
+ studioEnvRotation: number;
384
+ studioShadowIntensity: number;
385
+ studioShadowSoftness: number;
386
+ studioAOIntensity: number;
329
387
  activeTool: string | null;
330
388
  animationMode: AnimationMode;
331
389
  animationSliderValue: number;
@@ -335,6 +393,176 @@ export interface ViewerStateShape {
335
393
  }
336
394
  /** Keys of ViewerStateShape */
337
395
  export type StateKey = keyof ViewerStateShape;
396
+ /**
397
+ * Material appearance definition for Studio mode.
398
+ *
399
+ * All fields are optional. Only provided fields override defaults.
400
+ * In Studio mode the viewer uses MeshPhysicalMaterial; properties left
401
+ * unset default to their "off" values (transmission=0, clearcoat=0, etc.)
402
+ * which the shader skips at near-zero cost.
403
+ *
404
+ * This is a data-format interface (describes JSON input), not a Three.js material.
405
+ * Texture string fields reference either a key in the root-level `textures` table,
406
+ * a data URI, or a URL resolved against the HTML page.
407
+ */
408
+ export interface MaterialAppearance {
409
+ /** Display name */
410
+ name?: string;
411
+ /** Reference to a built-in preset (e.g., "stainless-steel", "car-paint") */
412
+ builtin?: string;
413
+ /** sRGB base color. Accepts RGBA tuple [r,g,b,a] (0-1) or CSS hex string "#rrggbb". */
414
+ color?: RGBAColor | string;
415
+ /** Texture reference for base color */
416
+ map?: string;
417
+ /** Metalness factor, 0-1 (default: 0.0) */
418
+ metalness?: number;
419
+ /** Roughness factor, 0-1 (default: 0.5) */
420
+ roughness?: number;
421
+ /** Normal map texture reference */
422
+ normalMap?: string;
423
+ /** Ambient occlusion texture reference */
424
+ aoMap?: string;
425
+ /** Metalness map texture reference */
426
+ metalnessMap?: string;
427
+ /** Roughness map texture reference */
428
+ roughnessMap?: string;
429
+ /** Emissive color, linear RGB */
430
+ emissive?: RGBColor;
431
+ /** Emissive map texture reference */
432
+ emissiveMap?: string;
433
+ /** Emissive intensity multiplier (default: 1.0) */
434
+ emissiveIntensity?: number;
435
+ /** Alpha blending mode */
436
+ alphaMode?: "OPAQUE" | "MASK" | "BLEND";
437
+ /** Alpha cutoff threshold for MASK mode (default: 0.5) */
438
+ alphaCutoff?: number;
439
+ /** Transmission factor, 0-1 */
440
+ transmission?: number;
441
+ /** Transmission map texture reference */
442
+ transmissionMap?: string;
443
+ /** Clearcoat intensity, 0-1 */
444
+ clearcoat?: number;
445
+ /** Clearcoat roughness */
446
+ clearcoatRoughness?: number;
447
+ /** Clearcoat intensity texture reference */
448
+ clearcoatMap?: string;
449
+ /** Clearcoat roughness texture reference */
450
+ clearcoatRoughnessMap?: string;
451
+ /** Clearcoat normal map texture reference */
452
+ clearcoatNormalMap?: string;
453
+ /** Thickness for volume effects */
454
+ thickness?: number;
455
+ /** Thickness map texture reference */
456
+ thicknessMap?: string;
457
+ /** Attenuation distance for volume absorption */
458
+ attenuationDistance?: number;
459
+ /** Attenuation color, linear RGB */
460
+ attenuationColor?: RGBColor;
461
+ /** Index of refraction (default: 1.5) */
462
+ ior?: number;
463
+ /** Specular intensity, 0-1 */
464
+ specularIntensity?: number;
465
+ /** Specular tint color, linear RGB */
466
+ specularColor?: RGBColor;
467
+ /** Specular intensity texture reference */
468
+ specularIntensityMap?: string;
469
+ /** Specular color texture reference */
470
+ specularColorMap?: string;
471
+ /** Sheen intensity, 0-1 (required to enable sheen layer) */
472
+ sheen?: number;
473
+ /** Sheen tint color, linear RGB */
474
+ sheenColor?: RGBColor;
475
+ /** Sheen roughness */
476
+ sheenRoughness?: number;
477
+ /** Sheen color texture reference */
478
+ sheenColorMap?: string;
479
+ /** Sheen roughness texture reference */
480
+ sheenRoughnessMap?: string;
481
+ /** Anisotropy strength, 0-1 */
482
+ anisotropy?: number;
483
+ /** Anisotropy rotation in radians */
484
+ anisotropyRotation?: number;
485
+ /** Anisotropy direction texture reference */
486
+ anisotropyMap?: string;
487
+ /** Use MeshBasicMaterial (unlit, no shading) */
488
+ unlit?: boolean;
489
+ /** Render both sides of faces (THREE.DoubleSide) */
490
+ doubleSided?: boolean;
491
+ }
492
+ /**
493
+ * Material definition from threejs-materials (Three.js MeshPhysicalMaterial-compatible).
494
+ *
495
+ * This format is produced by the threejs-materials Python library, which catalogs
496
+ * PBR materials from ambientCG, GPUOpen, PolyHaven, and PhysicallyBased.
497
+ * The `properties` dict uses simplified property names (e.g., "color", "roughness",
498
+ * "normal") where each entry has an optional `value` (scalar or color array in
499
+ * linear RGB) and/or `texture` (inline data URI).
500
+ *
501
+ * Detected by the presence of the `properties` key.
502
+ * Extra keys from threejs-materials (id, name, source, url, license) pass through
503
+ * harmlessly and are not part of this interface.
504
+ */
505
+ export interface MaterialXMaterial {
506
+ /** Material properties from threejs-materials. Each key maps to { value?, texture? } */
507
+ properties: Record<string, {
508
+ value?: unknown;
509
+ texture?: string;
510
+ }>;
511
+ /** Optional texture tiling [u, v], default [1, 1]. Applied to all textures. */
512
+ textureRepeat?: [number, number];
513
+ }
514
+ /**
515
+ * Type guard to check if a material entry is a threejs-materials format dict.
516
+ * Detected by the presence of the `properties` key.
517
+ */
518
+ export declare function isMaterialXMaterial(m: unknown): m is MaterialXMaterial;
519
+ /**
520
+ * Entry in the root-level `textures` table.
521
+ *
522
+ * Each entry is either embedded (base64-encoded image data) or a URL reference
523
+ * loaded on demand. At least one of `data`+`format` or `url` must be provided.
524
+ * An empty TextureEntry is invalid and will be ignored at runtime.
525
+ * Multiple builtin preset texture fields can reference the same key for
526
+ * deduplication. threejs-materials carry their own textures as inline data URIs.
527
+ */
528
+ export interface TextureEntry {
529
+ /** Base64-encoded image data (for embedded textures) */
530
+ data?: string;
531
+ /** Image format, e.g., "png", "jpg", "webp" (required when data is provided) */
532
+ format?: string;
533
+ /** URL to load the texture from (for URL-referenced textures) */
534
+ url?: string;
535
+ }
536
+ /**
537
+ * Root-level Studio mode configuration.
538
+ *
539
+ * Optional configuration for the rendering environment, only used when
540
+ * the Studio tab is active. Fields map directly to ViewerState keys on load.
541
+ */
542
+ export interface StudioOptions {
543
+ /** Environment preset slug, custom HDR URL, or "none" (default: "studio") */
544
+ environment?: StudioEnvironment;
545
+ /** Environment map intensity, 0-1 (default: 0.5) */
546
+ envIntensity?: number;
547
+ /** Background mode (default: "environment") */
548
+ background?: StudioBackground;
549
+ /** Tone mapping algorithm (default: "neutral") */
550
+ toneMapping?: StudioToneMapping;
551
+ /** Tone mapping exposure (default: 1.0) */
552
+ toneMappingExposure?: number;
553
+ /** Use 4K environment maps instead of 2K (default: false) */
554
+ use4kEnvMaps?: boolean;
555
+ /** Texture mapping mode (default: "triplanar") */
556
+ textureMapping?: StudioTextureMapping;
557
+ /** Environment map rotation in degrees (default: 0) */
558
+ envRotation?: number;
559
+ /** Shadow intensity, 0-1 (default: 0 = off) */
560
+ shadowIntensity?: number;
561
+ /** Shadow softness, 0-1 (default: 0.3) */
562
+ shadowSoftness?: number;
563
+ /** Ambient occlusion intensity, 0-3.0 (default: 0 = off) */
564
+ aoIntensity?: number;
565
+ }
338
566
  /** Encoded texture */
339
567
  export interface Texture {
340
568
  height: number;
@@ -370,6 +598,8 @@ export interface Shape {
370
598
  triangles_per_face?: number[] | Uint32Array;
371
599
  /** Number of segments per edge (when edges is flat) */
372
600
  segments_per_edge?: number[] | Uint32Array;
601
+ /** UV coordinates (2 floats per vertex, same indexing as vertices) */
602
+ uvs?: number[] | Float32Array;
373
603
  }
374
604
  /**
375
605
  * Shape with flat binary format (TypedArrays with per-face/per-edge counts).
@@ -397,6 +627,15 @@ export interface ShapeNested {
397
627
  edge_types: number[];
398
628
  face_types: number[];
399
629
  }
630
+ /**
631
+ * Shape reference for the instanced/compressed format.
632
+ * Before decoding, a part's shape field may be `{ ref: N }` referencing
633
+ * the Nth entry in the instances array. After decoding, all ShapeRefs
634
+ * are replaced with full Shape objects.
635
+ */
636
+ export interface ShapeRef {
637
+ ref: number;
638
+ }
400
639
  /**
401
640
  * Check if shape uses binary format (has triangles_per_face).
402
641
  */
@@ -471,6 +710,18 @@ export interface Shapes {
471
710
  width?: number | undefined;
472
711
  /** Vertex size in pixels (added during decomposition for vertex shapes) */
473
712
  size?: number | undefined;
713
+ /** Material tag referencing materials table or built-in preset (leaf nodes) */
714
+ material?: string | undefined;
715
+ /** User-defined material library (root node).
716
+ * Values can be:
717
+ * - string: builtin preset reference (e.g., "builtin:car-paint")
718
+ * - MaterialXMaterial: threejs-materials format (detected by `properties` key)
719
+ * - MaterialAppearance: preset with overrides (e.g., { builtin: "acrylic-clear", color: "#55a0e3" })
720
+ */
721
+ materials?: Record<string, string | MaterialXMaterial | MaterialAppearance> | undefined;
722
+ /** Shared texture table for builtin preset materials (root node).
723
+ * threejs-materials carry their own textures inline. */
724
+ textures?: Record<string, TextureEntry> | undefined;
474
725
  }
475
726
  /** Callback for DOM events */
476
727
  export type DomEventCallback = (event: Event) => void;
@@ -1,5 +1,5 @@
1
1
  import * as THREE from "three";
2
- import { CollapseState, type Theme, type ThemeInput, type ControlType, type UpDirection, type AnimationMode, type ActiveTab, type ZebraColorScheme, type ZebraMappingMode, type Keymap, type StateChange, type StateSubscriber, type GlobalStateSubscriber, type SubscribeOptions, type RenderOptions, type ViewerOptions } from "./types";
2
+ import { CollapseState, type Theme, type ThemeInput, type ControlType, type UpDirection, type AnimationMode, type ActiveTab, type ZebraColorScheme, type ZebraMappingMode, type StudioBackground, type StudioToneMapping, type StudioTextureMapping, type Keymap, type StateChange, type StateSubscriber, type GlobalStateSubscriber, type SubscribeOptions, type RenderOptions, type ViewerOptions, type StudioOptions } from "./types";
3
3
  /**
4
4
  * Display configuration defaults
5
5
  */
@@ -19,6 +19,7 @@ interface DisplayDefaults {
19
19
  explodeTool: boolean;
20
20
  zscaleTool: boolean;
21
21
  zebraTool: boolean;
22
+ studioTool: boolean;
22
23
  measurementDebug: boolean;
23
24
  }
24
25
  /**
@@ -78,6 +79,22 @@ interface ZebraDefaults {
78
79
  zebraColorScheme: ZebraColorScheme;
79
80
  zebraMappingMode: ZebraMappingMode;
80
81
  }
82
+ /**
83
+ * Studio mode defaults
84
+ */
85
+ interface StudioModeDefaults {
86
+ studioEnvironment: string;
87
+ studioEnvIntensity: number;
88
+ studioBackground: StudioBackground;
89
+ studioToneMapping: StudioToneMapping;
90
+ studioExposure: number;
91
+ studio4kEnvMaps: boolean;
92
+ studioTextureMapping: StudioTextureMapping;
93
+ studioEnvRotation: number;
94
+ studioShadowIntensity: number;
95
+ studioShadowSoftness: number;
96
+ studioAOIntensity: number;
97
+ }
81
98
  /**
82
99
  * Runtime state defaults
83
100
  */
@@ -92,7 +109,7 @@ interface RuntimeDefaults {
92
109
  /**
93
110
  * Complete state shape
94
111
  */
95
- type StateShape = DisplayDefaults & RenderDefaults & ViewerDefaults & ZebraDefaults & RuntimeDefaults;
112
+ type StateShape = DisplayDefaults & RenderDefaults & ViewerDefaults & ZebraDefaults & StudioModeDefaults & RuntimeDefaults;
96
113
  /**
97
114
  * Keys of the state shape
98
115
  */
@@ -160,6 +177,10 @@ declare class ViewerState {
160
177
  * Zebra tool settings
161
178
  */
162
179
  static ZEBRA_DEFAULTS: ZebraDefaults;
180
+ /**
181
+ * Studio mode settings
182
+ */
183
+ static STUDIO_MODE_DEFAULTS: StudioModeDefaults;
163
184
  /**
164
185
  * Runtime state (not from options, changes during execution)
165
186
  */
@@ -198,6 +219,11 @@ declare class ViewerState {
198
219
  * Converts Vector3Tuple/QuaternionTuple to THREE objects.
199
220
  */
200
221
  updateViewerState(options: ViewerOptions, notify?: boolean): void;
222
+ /**
223
+ * Update studio state from StudioOptions (shapes.studioOptions).
224
+ * Maps short field names to prefixed state keys.
225
+ */
226
+ updateStudioState(options: StudioOptions): void;
201
227
  /**
202
228
  * Get all state as a plain object (for serialization)
203
229
  */