three-zoo 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { PerspectiveCamera, MathUtils, Vector3, Mesh, BufferAttribute, AnimationMixer, DirectionalLight, Box3, Spherical, RGBAFormat } from 'three';
1
+ import { PerspectiveCamera, MathUtils, Vector3, Mesh, BufferAttribute, AnimationMixer, MeshBasicMaterial, MeshLambertMaterial, DirectionalLight, Box3, Spherical, RGBAFormat } from 'three';
2
2
 
3
3
  /** Default horizontal field of view in degrees */
4
4
  const DEFAULT_HORIZONTAL_FOV = 90;
@@ -21,13 +21,13 @@ const MAX_FOV = 179;
21
21
  */
22
22
  class DualFovCamera extends PerspectiveCamera {
23
23
  /**
24
- * Creates a new DualFovCamera instance with independent horizontal and vertical FOV control.
24
+ * Creates a new DualFovCamera instance.
25
25
  *
26
- * @param horizontalFov - Horizontal field of view in degrees. Must be between 1° and 179°. Defaults to 90°.
27
- * @param verticalFov - Vertical field of view in degrees. Must be between 1° and 179°. Defaults to 90°.
28
- * @param aspect - Camera aspect ratio (width/height). Defaults to 1.
29
- * @param near - Near clipping plane distance. Must be greater than 0. Defaults to 1.
30
- * @param far - Far clipping plane distance. Must be greater than near plane. Defaults to 1000.
26
+ * @param horizontalFov - Horizontal field of view in degrees. Clamped between 1° and 179°.
27
+ * @param verticalFov - Vertical field of view in degrees. Clamped between 1° and 179°.
28
+ * @param aspect - Camera aspect ratio (width/height).
29
+ * @param near - Near clipping plane distance.
30
+ * @param far - Far clipping plane distance.
31
31
  */
32
32
  constructor(horizontalFov = DEFAULT_HORIZONTAL_FOV, verticalFov = DEFAULT_VERTICAL_FOV, aspect = DEFAULT_ASPECT, near = DEFAULT_NEAR, far = DEFAULT_FAR) {
33
33
  super(verticalFov, aspect, near, far);
@@ -36,17 +36,17 @@ class DualFovCamera extends PerspectiveCamera {
36
36
  this.updateProjectionMatrix();
37
37
  }
38
38
  /**
39
- * Gets the current horizontal field of view in degrees.
39
+ * Gets the horizontal field of view in degrees.
40
40
  *
41
- * @returns The horizontal FOV value between 1° and 179°
41
+ * @returns The horizontal FOV value
42
42
  */
43
43
  get horizontalFov() {
44
44
  return this._private_horizontalFovInternal;
45
45
  }
46
46
  /**
47
- * Gets the current vertical field of view in degrees.
47
+ * Gets the vertical field of view in degrees.
48
48
  *
49
- * @returns The vertical FOV value between 1° and 179°
49
+ * @returns The vertical FOV value
50
50
  */
51
51
  get verticalFov() {
52
52
  return this._private_verticalFovInternal;
@@ -54,7 +54,7 @@ class DualFovCamera extends PerspectiveCamera {
54
54
  /**
55
55
  * Sets the horizontal field of view in degrees.
56
56
  *
57
- * @param value - The horizontal FOV value in degrees. Will be clamped between 1° and 179°.
57
+ * @param value - The horizontal FOV value in degrees. Clamped between 1° and 179°.
58
58
  */
59
59
  set horizontalFov(value) {
60
60
  this._private_horizontalFovInternal = MathUtils.clamp(value, MIN_FOV, MAX_FOV);
@@ -63,17 +63,17 @@ class DualFovCamera extends PerspectiveCamera {
63
63
  /**
64
64
  * Sets the vertical field of view in degrees.
65
65
  *
66
- * @param value - The vertical FOV value in degrees. Will be clamped between 1° and 179°.
66
+ * @param value - The vertical FOV value in degrees. Clamped between 1° and 179°.
67
67
  */
68
68
  set verticalFov(value) {
69
69
  this._private_verticalFovInternal = MathUtils.clamp(value, MIN_FOV, MAX_FOV);
70
70
  this.updateProjectionMatrix();
71
71
  }
72
72
  /**
73
- * Updates both horizontal and vertical field of view values simultaneously.
73
+ * Sets both horizontal and vertical field of view values.
74
74
  *
75
- * @param horizontal - Horizontal FOV in degrees. Will be clamped between 1° and 179°.
76
- * @param vertical - Vertical FOV in degrees. Will be clamped between 1° and 179°.
75
+ * @param horizontal - Horizontal FOV in degrees. Clamped between 1° and 179°.
76
+ * @param vertical - Vertical FOV in degrees. Clamped between 1° and 179°.
77
77
  */
78
78
  setFov(horizontal, vertical) {
79
79
  this._private_horizontalFovInternal = MathUtils.clamp(horizontal, MIN_FOV, MAX_FOV);
@@ -81,9 +81,9 @@ class DualFovCamera extends PerspectiveCamera {
81
81
  this.updateProjectionMatrix();
82
82
  }
83
83
  /**
84
- * Copies the field of view settings from another DualFovCamera instance.
84
+ * Copies the field of view settings from another DualFovCamera.
85
85
  *
86
- * @param source - The DualFovCamera instance to copy FOV settings from.
86
+ * @param source - The DualFovCamera to copy FOV settings from.
87
87
  */
88
88
  copyFovSettings(source) {
89
89
  this._private_horizontalFovInternal = source.horizontalFov;
@@ -97,7 +97,7 @@ class DualFovCamera extends PerspectiveCamera {
97
97
  * - **Landscape mode (aspect > 1)**: Preserves horizontal FOV, calculates vertical FOV
98
98
  * - **Portrait mode (aspect ≤ 1)**: Preserves vertical FOV, calculates horizontal FOV
99
99
  *
100
- * This method is automatically called when FOV values or aspect ratio changes.
100
+ * Called when FOV values or aspect ratio changes.
101
101
  *
102
102
  * @override
103
103
  */
@@ -114,12 +114,12 @@ class DualFovCamera extends PerspectiveCamera {
114
114
  super.updateProjectionMatrix();
115
115
  }
116
116
  /**
117
- * Gets the actual horizontal field of view after aspect ratio adjustments.
117
+ * Gets the horizontal field of view after aspect ratio adjustments.
118
118
  *
119
- * In landscape mode, this returns the set horizontal FOV.
120
- * In portrait mode, this calculates the actual horizontal FOV based on the vertical FOV and aspect ratio.
119
+ * In landscape mode, returns the set horizontal FOV.
120
+ * In portrait mode, calculates the horizontal FOV from vertical FOV and aspect ratio.
121
121
  *
122
- * @returns The actual horizontal FOV in degrees
122
+ * @returns The horizontal FOV in degrees
123
123
  */
124
124
  getActualHorizontalFov() {
125
125
  if (this.aspect >= 1) {
@@ -129,12 +129,12 @@ class DualFovCamera extends PerspectiveCamera {
129
129
  return MathUtils.radToDeg(Math.atan(Math.tan(verticalRadians / 2) * this.aspect) * 2);
130
130
  }
131
131
  /**
132
- * Gets the actual vertical field of view after aspect ratio adjustments.
132
+ * Gets the vertical field of view after aspect ratio adjustments.
133
133
  *
134
- * In portrait mode, this returns the set vertical FOV.
135
- * In landscape mode, this calculates the actual vertical FOV based on the horizontal FOV and aspect ratio.
134
+ * In portrait mode, returns the set vertical FOV.
135
+ * In landscape mode, calculates the vertical FOV from horizontal FOV and aspect ratio.
136
136
  *
137
- * @returns The actual vertical FOV in degrees
137
+ * @returns The vertical FOV in degrees
138
138
  */
139
139
  getActualVerticalFov() {
140
140
  if (this.aspect < 1) {
@@ -144,12 +144,12 @@ class DualFovCamera extends PerspectiveCamera {
144
144
  return MathUtils.radToDeg(Math.atan(Math.tan(horizontalRadians / 2) / this.aspect) * 2);
145
145
  }
146
146
  /**
147
- * Adjusts the vertical field of view to fit all specified points within the camera's view.
147
+ * Adjusts the vertical field of view to fit specified points within the camera's view.
148
148
  *
149
- * This method calculates the required vertical FOV to ensure all provided vertices
149
+ * Calculates the required vertical FOV to ensure all provided vertices
150
150
  * are visible within the vertical bounds of the camera's frustum.
151
151
  *
152
- * @param vertices - Array of 3D points (in world coordinates) that should fit within the camera's vertical view
152
+ * @param vertices - Array of 3D points in world coordinates
153
153
  */
154
154
  fitVerticalFovToPoints(vertices) {
155
155
  const up = new Vector3(0, 1, 0).applyQuaternion(this.quaternion);
@@ -170,10 +170,10 @@ class DualFovCamera extends PerspectiveCamera {
170
170
  /**
171
171
  * Adjusts the vertical field of view to fit a bounding box within the camera's view.
172
172
  *
173
- * This method calculates the required vertical FOV to ensure the entire bounding box
173
+ * Calculates the required vertical FOV to ensure the bounding box
174
174
  * is visible within the vertical bounds of the camera's frustum.
175
175
  *
176
- * @param box - The 3D bounding box (in world coordinates) that should fit within the camera's vertical view
176
+ * @param box - The 3D bounding box in world coordinates
177
177
  */
178
178
  fitVerticalFovToBox(box) {
179
179
  this.fitVerticalFovToPoints([
@@ -190,11 +190,11 @@ class DualFovCamera extends PerspectiveCamera {
190
190
  /**
191
191
  * Adjusts the vertical field of view to fit a skinned mesh within the camera's view.
192
192
  *
193
- * This method updates the mesh's skeleton, applies bone transformations to all vertices,
194
- * and then calculates the required vertical FOV to ensure the entire deformed mesh
195
- * is visible within the vertical bounds of the camera's frustum.
193
+ * Updates the mesh's skeleton, applies bone transformations to vertices,
194
+ * and calculates the required vertical FOV to fit the deformed mesh
195
+ * within the vertical bounds of the camera's frustum.
196
196
  *
197
- * @param skinnedMesh - The skinned mesh (with active skeleton) that should fit within the camera's vertical view
197
+ * @param skinnedMesh - The skinned mesh with active skeleton
198
198
  */
199
199
  fitVerticalFovToMesh(skinnedMesh) {
200
200
  skinnedMesh.updateWorldMatrix(true, true);
@@ -213,15 +213,14 @@ class DualFovCamera extends PerspectiveCamera {
213
213
  /**
214
214
  * Points the camera to look at the center of mass of a skinned mesh.
215
215
  *
216
- * This method updates the mesh's skeleton, applies bone transformations to all vertices,
217
- * calculates the center of mass using a clustering algorithm, and then orients the camera
216
+ * Updates the mesh's skeleton, applies bone transformations to vertices,
217
+ * calculates the center of mass using a clustering algorithm, and orients the camera
218
218
  * to look at that point.
219
219
  *
220
- * The center of mass calculation uses an iterative clustering approach to find the
221
- * main concentration of vertices, which provides better results than a simple average
222
- * for complex meshes.
220
+ * The center of mass calculation uses iterative clustering to find the
221
+ * main concentration of vertices.
223
222
  *
224
- * @param skinnedMesh - The skinned mesh (with active skeleton) whose center of mass should be the camera's target
223
+ * @param skinnedMesh - The skinned mesh with active skeleton
225
224
  */
226
225
  lookAtMeshCenterOfMass(skinnedMesh) {
227
226
  skinnedMesh.updateWorldMatrix(true, true);
@@ -236,11 +235,11 @@ class DualFovCamera extends PerspectiveCamera {
236
235
  points.push(target.clone());
237
236
  }
238
237
  /**
239
- * Finds the main cluster center of a set of 3D points using iterative refinement.
238
+ * Finds the main cluster center of 3D points using iterative refinement.
240
239
  *
241
240
  * @param points - Array of 3D points to cluster
242
- * @param iterations - Number of refinement iterations to perform
243
- * @returns The calculated center point of the main cluster
241
+ * @param iterations - Number of refinement iterations
242
+ * @returns The center point of the main cluster
244
243
  */
245
244
  const findMainCluster = (points, iterations = 3) => {
246
245
  if (points.length === 0) {
@@ -267,12 +266,12 @@ class DualFovCamera extends PerspectiveCamera {
267
266
  this.lookAt(centerOfMass);
268
267
  }
269
268
  /**
270
- * Creates a deep copy of this DualFovCamera instance.
269
+ * Creates a copy of this DualFovCamera.
271
270
  *
272
- * The cloned camera will have identical FOV settings, position, rotation,
271
+ * The cloned camera has identical FOV settings, position, rotation,
273
272
  * and all other camera properties.
274
273
  *
275
- * @returns A new DualFovCamera instance that is an exact copy of this one
274
+ * @returns A new DualFovCamera instance
276
275
  * @override
277
276
  */
278
277
  clone() {
@@ -285,25 +284,23 @@ class DualFovCamera extends PerspectiveCamera {
285
284
  /**
286
285
  * Utility class for finding and modifying objects in a Three.js scene graph.
287
286
  *
288
- * This class provides static methods for traversing Three.js scene hierarchies,
289
- * searching for specific objects or materials, and performing batch operations
290
- * on collections of scene objects.
287
+ * Provides static methods for traversing Three.js scene hierarchies,
288
+ * searching for objects or materials, and performing batch operations.
291
289
  *
292
- * All methods perform depth-first traversal of the scene graph starting from
293
- * the provided root object and recursively processing all children.
290
+ * All methods perform depth-first traversal starting from the provided
291
+ * root object and recursively processing all children.
294
292
  */
295
293
  class SceneTraversal {
296
294
  /**
297
295
  * Finds the first object in the scene hierarchy with an exact name match.
298
296
  *
299
- * Performs a depth-first search through the scene graph starting from the provided
300
- * root object. Returns the first object encountered whose name property exactly
301
- * matches the search string.
297
+ * Performs depth-first search through the scene graph starting from the
298
+ * root object. Returns the first object whose name property matches
299
+ * the search string.
302
300
  *
303
301
  * @param object - The root Object3D to start searching from
304
302
  * @param name - The exact name to search for (case-sensitive)
305
303
  * @returns The first matching Object3D, or null if no match is found
306
-
307
304
  */
308
305
  static getObjectByName(object, name) {
309
306
  if (object.name === name) {
@@ -320,15 +317,13 @@ class SceneTraversal {
320
317
  /**
321
318
  * Finds the first material in the scene hierarchy with an exact name match.
322
319
  *
323
- * Performs a depth-first search through the scene graph, examining materials
320
+ * Performs depth-first search through the scene graph, examining materials
324
321
  * attached to Mesh objects. Handles both single materials and material arrays.
325
- * Returns the first material encountered whose name property exactly matches
326
- * the search string.
322
+ * Returns the first material whose name property matches the search string.
327
323
  *
328
324
  * @param object - The root Object3D to start searching from
329
325
  * @param name - The exact material name to search for (case-sensitive)
330
326
  * @returns The first matching Material, or null if no match is found
331
-
332
327
  */
333
328
  static getMaterialByName(object, name) {
334
329
  if (object instanceof Mesh) {
@@ -354,15 +349,13 @@ class SceneTraversal {
354
349
  /**
355
350
  * Processes all objects of a specific type in the scene hierarchy.
356
351
  *
357
- * Performs a depth-first traversal and executes the provided callback function
358
- * for every object that is an instance of the specified type. This is useful
359
- * for batch operations on specific object types (e.g., all lights, all meshes, etc.).
352
+ * Performs depth-first traversal and executes the callback function
353
+ * for every object that is an instance of the specified type.
360
354
  *
361
355
  * @template T - The type of objects to process
362
356
  * @param object - The root Object3D to start searching from
363
357
  * @param type - The constructor/class to filter by (e.g., DirectionalLight, Mesh)
364
358
  * @param callback - Function to execute for each matching object instance
365
-
366
359
  */
367
360
  static enumerateObjectsByType(object, type, callback) {
368
361
  if (object instanceof type) {
@@ -375,13 +368,12 @@ class SceneTraversal {
375
368
  /**
376
369
  * Processes all materials found in mesh objects within the scene hierarchy.
377
370
  *
378
- * Performs a depth-first traversal, finding all Mesh objects and executing
379
- * the provided callback function for each material. Handles both single
380
- * materials and material arrays properly.
371
+ * Performs depth-first traversal, finding all Mesh objects and executing
372
+ * the callback function for each material. Handles both single
373
+ * materials and material arrays.
381
374
  *
382
375
  * @param object - The root Object3D to start searching from
383
376
  * @param callback - Function to execute for each material found
384
-
385
377
  */
386
378
  static enumerateMaterials(object, callback) {
387
379
  if (object instanceof Mesh) {
@@ -399,16 +391,15 @@ class SceneTraversal {
399
391
  }
400
392
  }
401
393
  /**
402
- * Finds all objects in the scene hierarchy that match the specified filter criteria.
394
+ * Finds all objects in the scene hierarchy that match filter criteria.
403
395
  *
404
- * Performs a depth-first search and collects all objects that either match
405
- * a regular expression pattern (applied to the object's name) or satisfy
406
- * a custom predicate function.
396
+ * Performs depth-first search and collects all objects that either match
397
+ * a regular expression pattern (applied to object names) or satisfy
398
+ * a predicate function.
407
399
  *
408
400
  * @param object - The root Object3D to start searching from
409
401
  * @param filter - Either a RegExp to test against object names, or a predicate function
410
402
  * @returns Array of all matching Object3D instances
411
-
412
403
  */
413
404
  static filterObjects(object, filter) {
414
405
  let result = [];
@@ -428,29 +419,29 @@ class SceneTraversal {
428
419
  return result;
429
420
  }
430
421
  /**
431
- * Finds all materials in the scene hierarchy whose names match a regular expression pattern.
422
+ * Finds all materials in the scene hierarchy whose names match a pattern.
432
423
  *
433
- * Performs a depth-first search through all Mesh objects and collects materials
434
- * whose name property matches the provided regular expression. Handles both
435
- * single materials and material arrays properly.
424
+ * Performs depth-first search through all Mesh objects and collects materials
425
+ * whose name property matches the regular expression. Handles both
426
+ * single materials and material arrays.
436
427
  *
437
428
  * @param object - The root Object3D to start searching from
438
429
  * @param name - Regular expression pattern to test against material names
439
430
  * @returns Array of all matching Material instances
440
-
441
431
  */
442
432
  static filterMaterials(object, name) {
443
433
  let result = [];
444
434
  if (object instanceof Mesh) {
445
435
  if (Array.isArray(object.material)) {
446
436
  for (const material of object.material) {
447
- if (material.name && name.test(material.name)) {
437
+ if (material.name !== undefined && name.test(material.name)) {
448
438
  result.push(material);
449
439
  }
450
440
  }
451
441
  }
452
442
  else {
453
- if (object.material.name && name.test(object.material.name)) {
443
+ if (object.material.name !== undefined &&
444
+ name.test(object.material.name)) {
454
445
  result.push(object.material);
455
446
  }
456
447
  }
@@ -461,11 +452,10 @@ class SceneTraversal {
461
452
  return result;
462
453
  }
463
454
  /**
464
- * Finds all objects (mesh users) that use materials with names matching a regular expression pattern.
455
+ * Finds all mesh objects that use materials with names matching a pattern.
465
456
  *
466
- * Performs a depth-first search through all Mesh objects and collects the mesh objects
467
- * whose materials have names that match the provided regular expression. This is useful
468
- * for finding all objects that use specific material types or naming patterns.
457
+ * Performs depth-first search through all Mesh objects and collects mesh objects
458
+ * whose materials have names that match the regular expression.
469
459
  *
470
460
  * @param object - The root Object3D to start searching from
471
461
  * @param materialName - Regular expression pattern to test against material names
@@ -477,14 +467,15 @@ class SceneTraversal {
477
467
  let hasMatchingMaterial = false;
478
468
  if (Array.isArray(object.material)) {
479
469
  for (const material of object.material) {
480
- if (material.name && materialName.test(material.name)) {
470
+ if (material.name !== undefined && materialName.test(material.name)) {
481
471
  hasMatchingMaterial = true;
482
472
  break;
483
473
  }
484
474
  }
485
475
  }
486
476
  else {
487
- if (object.material.name && materialName.test(object.material.name)) {
477
+ if (object.material.name !== undefined &&
478
+ materialName.test(object.material.name)) {
488
479
  hasMatchingMaterial = true;
489
480
  }
490
481
  }
@@ -501,11 +492,11 @@ class SceneTraversal {
501
492
 
502
493
  /** Number of components per vertex */
503
494
  const COMPONENT_COUNT = 3;
504
- /** Convert skinned meshes to regular static meshes */
495
+ /** Converts skinned meshes to static meshes */
505
496
  class SkinnedMeshBaker {
506
497
  /**
507
- * Convert a skinned mesh to a regular mesh in its current pose.
508
- * The resulting mesh will have no bones but look identical.
498
+ * Converts a skinned mesh to a static mesh in its current pose.
499
+ * The resulting mesh has no bones but looks identical.
509
500
  *
510
501
  * @param skinnedMesh - Mesh to convert
511
502
  * @returns Static mesh with baked vertex positions
@@ -531,9 +522,9 @@ class SkinnedMeshBaker {
531
522
  return mesh;
532
523
  }
533
524
  /**
534
- * Bake a single frame from an animation into a static mesh.
525
+ * Bakes a single frame from an animation into a static mesh.
535
526
  *
536
- * @param armature - Root object with bones (usually from GLTF)
527
+ * @param armature - Root object with bones
537
528
  * @param skinnedMesh - Mesh to convert
538
529
  * @param timeOffset - Time in seconds within the animation
539
530
  * @param clip - Animation to get the pose from
@@ -550,6 +541,392 @@ class SkinnedMeshBaker {
550
541
  }
551
542
  }
552
543
 
544
+ /** Factor for metalness brightness adjustment */
545
+ const METALNESS_BRIGHTNESS_FACTOR = 0.3;
546
+ /** Factor for emissive color contribution when combining with base color */
547
+ const EMISSIVE_CONTRIBUTION_FACTOR = 0.5;
548
+ /**
549
+ * Converts Three.js MeshStandardMaterial to MeshBasicMaterial.
550
+ *
551
+ * Handles translation from PBR StandardMaterial to unlit BasicMaterial.
552
+ * Since BasicMaterial doesn't respond to lighting, applies compensation
553
+ * techniques including brightness adjustments and emissive color combination.
554
+ */
555
+ class StandardToBasicConverter {
556
+ /**
557
+ * Converts a MeshStandardMaterial to MeshBasicMaterial.
558
+ *
559
+ * Performs conversion from PBR StandardMaterial to unlit BasicMaterial
560
+ * with brightness compensation and property mapping.
561
+ *
562
+ * @param standardMaterial - The source MeshStandardMaterial to convert
563
+ * @param options - Configuration options for the conversion
564
+ * @returns A new MeshBasicMaterial with properties mapped from the standard material
565
+ *
566
+ * @example
567
+ * ```typescript
568
+ * const standardMaterial = new MeshStandardMaterial({
569
+ * color: 0x00ff00,
570
+ * metalness: 0.5,
571
+ * emissive: 0x111111,
572
+ * emissiveIntensity: 0.2
573
+ * });
574
+ *
575
+ * const basicMaterial = StandardToBasicConverter.convert(standardMaterial, {
576
+ * brightnessFactor: 1.4,
577
+ * combineEmissive: true
578
+ * });
579
+ * ```
580
+ */
581
+ static convert(standardMaterial, options = {}) {
582
+ const config = {
583
+ preserveName: true,
584
+ copyUserData: true,
585
+ disposeOriginal: false,
586
+ combineEmissive: true,
587
+ brightnessFactor: 1.3,
588
+ ...options,
589
+ };
590
+ // Create new Basic material
591
+ const basicMaterial = new MeshBasicMaterial();
592
+ // Copy basic material properties
593
+ this.copyBasicProperties(standardMaterial, basicMaterial, config);
594
+ // Handle color properties with lighting compensation
595
+ this.convertColorProperties(standardMaterial, basicMaterial, config);
596
+ // Handle texture maps
597
+ this.convertTextureMaps(standardMaterial, basicMaterial);
598
+ // Handle transparency and alpha
599
+ this.convertTransparencyProperties(standardMaterial, basicMaterial);
600
+ // Cleanup if requested
601
+ if (config.disposeOriginal) {
602
+ standardMaterial.dispose();
603
+ }
604
+ basicMaterial.needsUpdate = true;
605
+ return basicMaterial;
606
+ }
607
+ /**
608
+ * Copies basic material properties from source to target material.
609
+ *
610
+ * @param source - The source MeshStandardMaterial
611
+ * @param target - The target MeshBasicMaterial
612
+ * @param config - The resolved configuration options
613
+ * @internal
614
+ */
615
+ static copyBasicProperties(source, target, config) {
616
+ if (config.preserveName) {
617
+ target.name = source.name;
618
+ }
619
+ target.side = source.side;
620
+ target.visible = source.visible;
621
+ target.fog = source.fog;
622
+ target.wireframe = source.wireframe;
623
+ target.wireframeLinewidth = source.wireframeLinewidth;
624
+ target.vertexColors = source.vertexColors;
625
+ if (config.copyUserData) {
626
+ target.userData = { ...source.userData };
627
+ }
628
+ }
629
+ /**
630
+ * Converts color-related properties with lighting compensation.
631
+ *
632
+ * Applies brightness compensation and optional emissive color combination
633
+ * to account for BasicMaterial's lack of lighting response.
634
+ *
635
+ * @param source - The source MeshStandardMaterial
636
+ * @param target - The target MeshBasicMaterial
637
+ * @param config - The resolved configuration options
638
+ * @internal
639
+ */
640
+ static convertColorProperties(source, target, config) {
641
+ // Base color conversion with brightness compensation
642
+ target.color = source.color.clone();
643
+ // Apply brightness compensation since BasicMaterial doesn't respond to lighting
644
+ target.color.multiplyScalar(config.brightnessFactor);
645
+ // Adjust for metalness - metallic materials tend to be darker without lighting
646
+ if (source.metalness > 0) {
647
+ const metalnessBrightness = 1 + source.metalness * METALNESS_BRIGHTNESS_FACTOR;
648
+ target.color.multiplyScalar(metalnessBrightness);
649
+ }
650
+ // Combine emissive color if requested
651
+ if (config.combineEmissive) {
652
+ const emissiveContribution = source.emissive
653
+ .clone()
654
+ .multiplyScalar(source.emissiveIntensity * EMISSIVE_CONTRIBUTION_FACTOR);
655
+ target.color.add(emissiveContribution);
656
+ }
657
+ // Ensure color doesn't exceed valid range
658
+ target.color.r = Math.min(target.color.r, 1.0);
659
+ target.color.g = Math.min(target.color.g, 1.0);
660
+ target.color.b = Math.min(target.color.b, 1.0);
661
+ }
662
+ /**
663
+ * Converts and maps texture properties from Standard to Basic material.
664
+ *
665
+ * Transfers compatible texture maps including diffuse, alpha, environment,
666
+ * light, and AO maps.
667
+ *
668
+ * @param source - The source MeshStandardMaterial
669
+ * @param target - The target MeshBasicMaterial
670
+ * @internal
671
+ */
672
+ static convertTextureMaps(source, target) {
673
+ // Main diffuse/albedo map
674
+ if (source.map) {
675
+ target.map = source.map;
676
+ }
677
+ // Alpha map
678
+ if (source.alphaMap) {
679
+ target.alphaMap = source.alphaMap;
680
+ }
681
+ // Environment map (BasicMaterial supports this for reflections)
682
+ if (source.envMap) {
683
+ target.envMap = source.envMap;
684
+ // Use metalness to determine reflectivity
685
+ target.reflectivity = source.metalness;
686
+ }
687
+ // Light map (BasicMaterial supports this)
688
+ if (source.lightMap) {
689
+ target.lightMap = source.lightMap;
690
+ target.lightMapIntensity = source.lightMapIntensity;
691
+ }
692
+ // AO map (BasicMaterial supports this)
693
+ if (source.aoMap) {
694
+ target.aoMap = source.aoMap;
695
+ target.aoMapIntensity = source.aoMapIntensity;
696
+ }
697
+ // Specular map (BasicMaterial supports this)
698
+ if (source.metalnessMap) {
699
+ // Use metalness map as specular map for some reflective effect
700
+ target.specularMap = source.metalnessMap;
701
+ }
702
+ // Copy UV transforms
703
+ this.copyUVTransforms(source, target);
704
+ }
705
+ /**
706
+ * Copies UV transformation properties for texture maps.
707
+ *
708
+ * @param source - The source MeshStandardMaterial
709
+ * @param target - The target MeshBasicMaterial
710
+ * @internal
711
+ */
712
+ static copyUVTransforms(source, target) {
713
+ // Main texture UV transform
714
+ if (source.map && target.map) {
715
+ target.map.offset.copy(source.map.offset);
716
+ target.map.repeat.copy(source.map.repeat);
717
+ target.map.rotation = source.map.rotation;
718
+ target.map.center.copy(source.map.center);
719
+ }
720
+ }
721
+ /**
722
+ * Converts transparency and rendering properties.
723
+ *
724
+ * @param source - The source MeshStandardMaterial
725
+ * @param target - The target MeshBasicMaterial
726
+ * @internal
727
+ */
728
+ static convertTransparencyProperties(source, target) {
729
+ target.transparent = source.transparent;
730
+ target.opacity = source.opacity;
731
+ target.alphaTest = source.alphaTest;
732
+ target.depthTest = source.depthTest;
733
+ target.depthWrite = source.depthWrite;
734
+ target.blending = source.blending;
735
+ }
736
+ }
737
+
738
+ /** Factor for metalness darkness adjustment */
739
+ const METALNESS_DARKNESS_FACTOR = 0.3;
740
+ /** Roughness threshold for additional darkening */
741
+ const ROUGHNESS_THRESHOLD = 0.5;
742
+ /** Factor for roughness color adjustment */
743
+ const ROUGHNESS_COLOR_ADJUSTMENT = 0.2;
744
+ /** Minimum reflectivity boost for environment maps */
745
+ const REFLECTIVITY_BOOST = 0.1;
746
+ /**
747
+ * Converts Three.js MeshStandardMaterial to MeshLambertMaterial.
748
+ *
749
+ * Handles translation between PBR properties of StandardMaterial and
750
+ * the Lambertian reflectance model used by LambertMaterial. Applies
751
+ * color compensation based on metalness and roughness values.
752
+ */
753
+ class StandardToLambertConverter {
754
+ /**
755
+ * Converts a MeshStandardMaterial to MeshLambertMaterial.
756
+ *
757
+ * Performs conversion from PBR StandardMaterial to Lambert lighting model
758
+ * with color compensation based on metalness and roughness values.
759
+ *
760
+ * @param material - The source MeshStandardMaterial to convert
761
+ * @param options - Configuration options for the conversion
762
+ * @returns A new MeshLambertMaterial with properties mapped from the standard material
763
+ *
764
+ * @example
765
+ * ```typescript
766
+ * const standardMaterial = new MeshStandardMaterial({
767
+ * color: 0xff0000,
768
+ * metalness: 0.8,
769
+ * roughness: 0.2
770
+ * });
771
+ *
772
+ * const lambertMaterial = StandardToLambertConverter.convert(standardMaterial);
773
+ * ```
774
+ */
775
+ static convert(material, options = {}) {
776
+ const config = {
777
+ preserveName: true,
778
+ copyUserData: true,
779
+ disposeOriginal: false,
780
+ roughnessColorFactor: 0.8,
781
+ ...options,
782
+ };
783
+ // Create new Lambert material
784
+ const lambertMaterial = new MeshLambertMaterial();
785
+ // Copy basic material properties
786
+ this.copyBasicProperties(material, lambertMaterial, config);
787
+ // Handle color properties with roughness compensation
788
+ this.convertColorProperties(material, lambertMaterial, config);
789
+ // Handle texture maps
790
+ this.convertTextureMaps(material, lambertMaterial);
791
+ // Handle transparency and alpha
792
+ this.convertTransparencyProperties(material, lambertMaterial);
793
+ // Cleanup if requested
794
+ if (config.disposeOriginal) {
795
+ material.dispose();
796
+ }
797
+ lambertMaterial.needsUpdate = true;
798
+ return lambertMaterial;
799
+ }
800
+ /**
801
+ * Copies basic material properties from source to target material.
802
+ *
803
+ * @param source - The source MeshStandardMaterial
804
+ * @param target - The target MeshLambertMaterial
805
+ * @param config - The resolved configuration options
806
+ * @internal
807
+ */
808
+ static copyBasicProperties(source, target, config) {
809
+ if (config.preserveName) {
810
+ target.name = source.name;
811
+ }
812
+ target.side = source.side;
813
+ target.visible = source.visible;
814
+ target.fog = source.fog;
815
+ target.wireframe = source.wireframe;
816
+ target.wireframeLinewidth = source.wireframeLinewidth;
817
+ target.vertexColors = source.vertexColors;
818
+ target.flatShading = source.flatShading;
819
+ if (config.copyUserData) {
820
+ target.userData = { ...source.userData };
821
+ }
822
+ }
823
+ /**
824
+ * Converts color-related properties with PBR compensation.
825
+ *
826
+ * Applies color adjustments to compensate for the loss of metalness and
827
+ * roughness information when converting to Lambert material.
828
+ *
829
+ * @param source - The source MeshStandardMaterial
830
+ * @param target - The target MeshLambertMaterial
831
+ * @param config - The resolved configuration options
832
+ * @internal
833
+ */
834
+ static convertColorProperties(source, target, config) {
835
+ target.color = source.color.clone();
836
+ // Adjust color based on metalness and roughness for better visual match
837
+ if (source.metalness > 0) {
838
+ // Metallic materials tend to be darker in Lambert shading
839
+ const metalnessFactor = 1 - source.metalness * METALNESS_DARKNESS_FACTOR;
840
+ target.color.multiplyScalar(metalnessFactor);
841
+ }
842
+ if (source.roughness > ROUGHNESS_THRESHOLD) {
843
+ // Rough materials appear slightly darker
844
+ const roughnessFactor = config.roughnessColorFactor +
845
+ source.roughness * ROUGHNESS_COLOR_ADJUSTMENT;
846
+ target.color.multiplyScalar(roughnessFactor);
847
+ }
848
+ target.emissive = source.emissive.clone();
849
+ target.emissiveIntensity = source.emissiveIntensity;
850
+ }
851
+ /**
852
+ * Converts and maps texture properties from Standard to Lambert material.
853
+ *
854
+ * Transfers compatible texture maps including diffuse, normal, emissive,
855
+ * AO, light maps, and environment maps.
856
+ *
857
+ * @param source - The source MeshStandardMaterial
858
+ * @param target - The target MeshLambertMaterial
859
+ * @internal
860
+ */
861
+ static convertTextureMaps(source, target) {
862
+ // Diffuse/Albedo map
863
+ if (source.map) {
864
+ target.map = source.map;
865
+ }
866
+ // Emissive map
867
+ if (source.emissiveMap) {
868
+ target.emissiveMap = source.emissiveMap;
869
+ }
870
+ // Normal map (Lambert materials support normal mapping)
871
+ if (source.normalMap) {
872
+ target.normalMap = source.normalMap;
873
+ target.normalScale = source.normalScale.clone();
874
+ }
875
+ // Light map
876
+ if (source.lightMap) {
877
+ target.lightMap = source.lightMap;
878
+ target.lightMapIntensity = source.lightMapIntensity;
879
+ }
880
+ // AO map
881
+ if (source.aoMap) {
882
+ target.aoMap = source.aoMap;
883
+ target.aoMapIntensity = source.aoMapIntensity;
884
+ }
885
+ // Environment map (for reflections)
886
+ if (source.envMap) {
887
+ target.envMap = source.envMap;
888
+ target.reflectivity = Math.min(source.metalness + REFLECTIVITY_BOOST, 1.0);
889
+ }
890
+ // Alpha map
891
+ if (source.alphaMap) {
892
+ target.alphaMap = source.alphaMap;
893
+ }
894
+ // Copy UV transforms
895
+ this.copyUVTransforms(source, target);
896
+ }
897
+ /**
898
+ * Copies UV transformation properties for texture maps.
899
+ *
900
+ * @param source - The source MeshStandardMaterial
901
+ * @param target - The target MeshLambertMaterial
902
+ * @internal
903
+ */
904
+ static copyUVTransforms(source, target) {
905
+ // Main texture UV transform
906
+ if (source.map && target.map) {
907
+ target.map.offset.copy(source.map.offset);
908
+ target.map.repeat.copy(source.map.repeat);
909
+ target.map.rotation = source.map.rotation;
910
+ target.map.center.copy(source.map.center);
911
+ }
912
+ }
913
+ /**
914
+ * Converts transparency and rendering properties.
915
+ *
916
+ * @param source - The source MeshStandardMaterial
917
+ * @param target - The target MeshLambertMaterial
918
+ * @internal
919
+ */
920
+ static convertTransparencyProperties(source, target) {
921
+ target.transparent = source.transparent;
922
+ target.opacity = source.opacity;
923
+ target.alphaTest = source.alphaTest;
924
+ target.depthTest = source.depthTest;
925
+ target.depthWrite = source.depthWrite;
926
+ target.blending = source.blending;
927
+ }
928
+ }
929
+
553
930
  /** Number of color channels in RGBA format */
554
931
  const RGBA_CHANNEL_COUNT = 4;
555
932
  /** Number of color channels in RGB format */
@@ -561,11 +938,11 @@ const LUMINANCE_G = 0.7152;
561
938
  /** Blue channel weight for luminance calculation (ITU-R BT.709) */
562
939
  const LUMINANCE_B = 0.0722;
563
940
  /**
564
- * A directional light with spherical positioning controls and advanced shadow mapping.
941
+ * A directional light with spherical positioning controls.
565
942
  *
566
- * Extends Three.js DirectionalLight to provide intuitive spherical coordinate control
567
- * (distance, elevation, azimuth) and automatic shadow map configuration for bounding boxes.
568
- * Also supports automatic sun direction calculation from HDR environment maps.
943
+ * Extends Three.js DirectionalLight to provide spherical coordinate control
944
+ * (distance, elevation, azimuth) and shadow map configuration for bounding boxes.
945
+ * Also supports sun direction calculation from HDR environment maps.
569
946
  */
570
947
  class Sun extends DirectionalLight {
571
948
  constructor() {
@@ -583,7 +960,7 @@ class Sun extends DirectionalLight {
583
960
  this._private_tempSpherical = new Spherical();
584
961
  }
585
962
  /**
586
- * Gets the distance from the light to its target (origin).
963
+ * Gets the distance from the light's position to the origin.
587
964
  *
588
965
  * @returns The distance in world units
589
966
  */
@@ -591,17 +968,17 @@ class Sun extends DirectionalLight {
591
968
  return this.position.length();
592
969
  }
593
970
  /**
594
- * Gets the elevation angle (vertical angle from the horizontal plane).
971
+ * Gets the elevation angle from the spherical coordinates.
595
972
  *
596
- * @returns The elevation angle in radians (0 = horizontal, π/2 = directly above)
973
+ * @returns The elevation angle in radians (phi angle from Three.js Spherical coordinates)
597
974
  */
598
975
  get elevation() {
599
976
  return this._private_tempSpherical.setFromVector3(this.position).phi;
600
977
  }
601
978
  /**
602
- * Gets the azimuth angle (horizontal rotation around the target).
979
+ * Gets the azimuth angle from the spherical coordinates.
603
980
  *
604
- * @returns The azimuth angle in radians (0 = positive X axis, π/2 = positive Z axis)
981
+ * @returns The azimuth angle in radians (theta angle from Three.js Spherical coordinates)
605
982
  */
606
983
  get azimuth() {
607
984
  return this._private_tempSpherical.setFromVector3(this.position).theta;
@@ -618,7 +995,7 @@ class Sun extends DirectionalLight {
618
995
  /**
619
996
  * Sets the elevation angle while preserving current distance and azimuth.
620
997
  *
621
- * @param value - The new elevation angle in radians (0 = horizontal, π/2 = directly above)
998
+ * @param value - The new elevation angle in radians (phi angle for Three.js Spherical coordinates)
622
999
  */
623
1000
  set elevation(value) {
624
1001
  this._private_tempSpherical.setFromVector3(this.position);
@@ -627,18 +1004,18 @@ class Sun extends DirectionalLight {
627
1004
  /**
628
1005
  * Sets the azimuth angle while preserving current distance and elevation.
629
1006
  *
630
- * @param value - The new azimuth angle in radians (0 = positive X axis, π/2 = positive Z axis)
1007
+ * @param value - The new azimuth angle in radians (theta angle for Three.js Spherical coordinates)
631
1008
  */
632
1009
  set azimuth(value) {
633
1010
  this._private_tempSpherical.setFromVector3(this.position);
634
1011
  this.position.setFromSphericalCoords(this._private_tempSpherical.radius, this._private_tempSpherical.phi, value);
635
1012
  }
636
1013
  /**
637
- * Configures the shadow camera to optimally cover a bounding box.
1014
+ * Configures the shadow camera frustum to cover a bounding box.
638
1015
  *
639
- * This method automatically adjusts the directional light's shadow camera frustum
640
- * to perfectly encompass the provided bounding box, ensuring efficient shadow map
641
- * usage and eliminating shadow clipping issues.
1016
+ * Adjusts the directional light's shadow camera frustum to encompass the
1017
+ * provided bounding box by transforming box corners to light space and
1018
+ * setting camera bounds accordingly.
642
1019
  *
643
1020
  * @param box3 - The 3D bounding box to cover with shadows
644
1021
  */
@@ -674,12 +1051,11 @@ class Sun extends DirectionalLight {
674
1051
  /**
675
1052
  * Sets the sun's direction based on the brightest point in an HDR environment map.
676
1053
  *
677
- * This method analyzes an HDR texture to find the pixel with the highest luminance
678
- * value and positions the sun to shine from that direction. This is useful for
679
- * creating realistic lighting that matches HDR environment maps.
1054
+ * Analyzes an HDR texture to find the pixel with the highest luminance value
1055
+ * and positions the sun to shine from that direction using spherical coordinates.
680
1056
  *
681
1057
  * @param texture - The HDR texture to analyze (must have image data available)
682
- * @param distance - The distance to place the sun from the origin (defaults to 1)
1058
+ * @param distance - The distance to place the sun from the origin
683
1059
  */
684
1060
  setDirectionFromHDRTexture(texture, distance = 1) {
685
1061
  const data = texture.image.data;
@@ -711,5 +1087,5 @@ class Sun extends DirectionalLight {
711
1087
  }
712
1088
  }
713
1089
 
714
- export { DualFovCamera, SceneTraversal, SkinnedMeshBaker, Sun };
1090
+ export { DualFovCamera, SceneTraversal, SkinnedMeshBaker, StandardToBasicConverter, StandardToLambertConverter, Sun };
715
1091
  //# sourceMappingURL=index.js.map