@needle-tools/engine 4.11.3 → 4.11.4-next.a568de7

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 (122) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/generateMeshBVH.worker-B9bjdr6J.js +25 -0
  3. package/dist/{needle-engine.bundle-B_aGaFmh.js → needle-engine.bundle-DpWrB4yf.js} +5306 -5268
  4. package/dist/{needle-engine.bundle-TscA1IdQ.min.js → needle-engine.bundle-lEVjhicZ.min.js} +133 -133
  5. package/dist/{needle-engine.bundle-YQ3I1QD8.umd.cjs → needle-engine.bundle-nX51yB5a.umd.cjs} +132 -132
  6. package/dist/needle-engine.js +2 -2
  7. package/dist/needle-engine.min.js +1 -1
  8. package/dist/needle-engine.umd.cjs +1 -1
  9. package/dist/{vendor-H-9KkM5B.umd.cjs → vendor-6kAXU6fm.umd.cjs} +39 -39
  10. package/dist/{vendor-BahM12Xj.min.js → vendor-CsyK1CFs.min.js} +46 -46
  11. package/dist/{vendor-Becub4o1.js → vendor-petGQl0N.js} +3130 -3069
  12. package/lib/engine/api.d.ts +1 -0
  13. package/lib/engine/api.js +1 -0
  14. package/lib/engine/api.js.map +1 -1
  15. package/lib/engine/engine_addressables.d.ts +74 -11
  16. package/lib/engine/engine_addressables.js +74 -11
  17. package/lib/engine/engine_addressables.js.map +1 -1
  18. package/lib/engine/engine_camera.fit.d.ts +48 -3
  19. package/lib/engine/engine_camera.fit.js +29 -0
  20. package/lib/engine/engine_camera.fit.js.map +1 -1
  21. package/lib/engine/engine_context.d.ts +18 -3
  22. package/lib/engine/engine_context.js +18 -3
  23. package/lib/engine/engine_context.js.map +1 -1
  24. package/lib/engine/engine_utils.d.ts +0 -23
  25. package/lib/engine/engine_utils.js +0 -202
  26. package/lib/engine/engine_utils.js.map +1 -1
  27. package/lib/engine/engine_utils_attributes.d.ts +48 -0
  28. package/lib/engine/engine_utils_attributes.js +70 -0
  29. package/lib/engine/engine_utils_attributes.js.map +1 -0
  30. package/lib/engine/engine_utils_qrcode.d.ts +23 -0
  31. package/lib/engine/engine_utils_qrcode.js +234 -0
  32. package/lib/engine/engine_utils_qrcode.js.map +1 -0
  33. package/lib/engine/extensions/NEEDLE_components.d.ts +4 -4
  34. package/lib/engine/extensions/NEEDLE_components.js +36 -17
  35. package/lib/engine/extensions/NEEDLE_components.js.map +1 -1
  36. package/lib/engine/webcomponents/buttons.js +1 -1
  37. package/lib/engine/webcomponents/buttons.js.map +1 -1
  38. package/lib/engine-components/Animation.d.ts +1 -1
  39. package/lib/engine-components/Animation.js +1 -1
  40. package/lib/engine-components/AnimationCurve.d.ts +3 -0
  41. package/lib/engine-components/AnimationCurve.js +3 -0
  42. package/lib/engine-components/AnimationCurve.js.map +1 -1
  43. package/lib/engine-components/Animator.d.ts +2 -1
  44. package/lib/engine-components/Animator.js +2 -1
  45. package/lib/engine-components/Animator.js.map +1 -1
  46. package/lib/engine-components/AnimatorController.d.ts +3 -0
  47. package/lib/engine-components/AnimatorController.js +3 -0
  48. package/lib/engine-components/AnimatorController.js.map +1 -1
  49. package/lib/engine-components/LookAtConstraint.d.ts +4 -0
  50. package/lib/engine-components/LookAtConstraint.js +4 -0
  51. package/lib/engine-components/LookAtConstraint.js.map +1 -1
  52. package/lib/engine-components/NeedleMenu.d.ts +1 -0
  53. package/lib/engine-components/NeedleMenu.js +1 -0
  54. package/lib/engine-components/NeedleMenu.js.map +1 -1
  55. package/lib/engine-components/NestedGltf.d.ts +3 -0
  56. package/lib/engine-components/NestedGltf.js +3 -0
  57. package/lib/engine-components/NestedGltf.js.map +1 -1
  58. package/lib/engine-components/ReflectionProbe.d.ts +4 -0
  59. package/lib/engine-components/ReflectionProbe.js +4 -0
  60. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  61. package/lib/engine-components/Renderer.js +14 -40
  62. package/lib/engine-components/Renderer.js.map +1 -1
  63. package/lib/engine-components/RendererLightmap.d.ts +7 -0
  64. package/lib/engine-components/RendererLightmap.js +30 -21
  65. package/lib/engine-components/RendererLightmap.js.map +1 -1
  66. package/lib/engine-components/SeeThrough.d.ts +3 -0
  67. package/lib/engine-components/SeeThrough.js +3 -0
  68. package/lib/engine-components/SeeThrough.js.map +1 -1
  69. package/lib/engine-components/Skybox.d.ts +3 -0
  70. package/lib/engine-components/Skybox.js +3 -0
  71. package/lib/engine-components/Skybox.js.map +1 -1
  72. package/lib/engine-components/SpriteRenderer.d.ts +14 -1
  73. package/lib/engine-components/SpriteRenderer.js +17 -1
  74. package/lib/engine-components/SpriteRenderer.js.map +1 -1
  75. package/lib/engine-components/splines/Spline.d.ts +3 -0
  76. package/lib/engine-components/splines/Spline.js +3 -0
  77. package/lib/engine-components/splines/Spline.js.map +1 -1
  78. package/lib/engine-components/splines/SplineUtils.d.ts +3 -0
  79. package/lib/engine-components/splines/SplineUtils.js +3 -0
  80. package/lib/engine-components/splines/SplineUtils.js.map +1 -1
  81. package/lib/engine-components/splines/SplineWalker.d.ts +3 -0
  82. package/lib/engine-components/splines/SplineWalker.js +3 -0
  83. package/lib/engine-components/splines/SplineWalker.js.map +1 -1
  84. package/lib/engine-components/timeline/PlayableDirector.d.ts +2 -1
  85. package/lib/engine-components/timeline/PlayableDirector.js +16 -9
  86. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  87. package/lib/engine-components/timeline/SignalAsset.d.ts +11 -1
  88. package/lib/engine-components/timeline/SignalAsset.js +11 -1
  89. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  90. package/lib/engine-components/ui/Raycaster.d.ts +18 -0
  91. package/lib/engine-components/ui/Raycaster.js +18 -0
  92. package/lib/engine-components/ui/Raycaster.js.map +1 -1
  93. package/package.json +2 -2
  94. package/src/engine/api.ts +1 -0
  95. package/src/engine/engine_addressables.ts +76 -11
  96. package/src/engine/engine_camera.fit.ts +50 -4
  97. package/src/engine/engine_context.ts +18 -3
  98. package/src/engine/engine_utils.ts +0 -229
  99. package/src/engine/engine_utils_attributes.ts +72 -0
  100. package/src/engine/engine_utils_qrcode.ts +266 -0
  101. package/src/engine/extensions/NEEDLE_components.ts +47 -26
  102. package/src/engine/webcomponents/buttons.ts +1 -1
  103. package/src/engine-components/Animation.ts +1 -1
  104. package/src/engine-components/AnimationCurve.ts +4 -1
  105. package/src/engine-components/Animator.ts +3 -2
  106. package/src/engine-components/AnimatorController.ts +3 -0
  107. package/src/engine-components/LookAtConstraint.ts +6 -1
  108. package/src/engine-components/NeedleMenu.ts +1 -0
  109. package/src/engine-components/NestedGltf.ts +3 -0
  110. package/src/engine-components/ReflectionProbe.ts +4 -0
  111. package/src/engine-components/Renderer.ts +14 -42
  112. package/src/engine-components/RendererLightmap.ts +34 -20
  113. package/src/engine-components/SeeThrough.ts +3 -0
  114. package/src/engine-components/Skybox.ts +3 -0
  115. package/src/engine-components/SpriteRenderer.ts +18 -2
  116. package/src/engine-components/splines/Spline.ts +3 -0
  117. package/src/engine-components/splines/SplineUtils.ts +3 -1
  118. package/src/engine-components/splines/SplineWalker.ts +3 -0
  119. package/src/engine-components/timeline/PlayableDirector.ts +16 -10
  120. package/src/engine-components/timeline/SignalAsset.ts +13 -2
  121. package/src/engine-components/ui/Raycaster.ts +19 -0
  122. package/dist/generateMeshBVH.worker-2qGLkQjg.js +0 -25
@@ -1,13 +1,14 @@
1
1
  import { Object3D } from "three";
2
2
  import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
3
3
  import { type GLTF, type GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
4
+ import { isDevEnvironment } from "../debug/debug.js";
4
5
 
5
6
  import { builtinComponentKeyName } from "../engine_constants.js";
6
7
  import { debugExtension } from "../engine_default_parameters.js";
7
8
  import { getLoader } from "../engine_gltf.js";
8
9
  import { type NodeToObjectMap, type ObjectToNodeMap, SerializationContext } from "../engine_serialization_core.js";
9
10
  import { apply } from "../js-extensions/index.js";
10
- import { maskGltfAssociation,resolveReferences } from "./extension_utils.js";
11
+ import { maskGltfAssociation, resolveReferences } from "./extension_utils.js";
11
12
 
12
13
  export const debug = debugExtension
13
14
  const componentsArrayExportKey = "$___Export_Components";
@@ -15,7 +16,7 @@ const componentsArrayExportKey = "$___Export_Components";
15
16
  export const EXTENSION_NAME = "NEEDLE_components";
16
17
 
17
18
  class ExtensionData {
18
- [builtinComponentKeyName]?: Array<object | null>
19
+ [builtinComponentKeyName]?: Array<Record<string, any> | null>
19
20
  }
20
21
 
21
22
  class ExportData {
@@ -35,14 +36,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
35
36
  get name(): string {
36
37
  return EXTENSION_NAME;
37
38
  }
38
-
39
- // import
40
- parser?: GLTFParser;
41
- nodeToObjectMap: NodeToObjectMap = {};
42
- /** The loaded gltf */
43
- gltf: GLTF | null = null;
44
-
45
- // export
39
+ // #region export
46
40
  exportContext!: { [nodeIndex: number]: ExportData };
47
41
  objectToNodeMap: ObjectToNodeMap = {};
48
42
  context!: SerializationContext;
@@ -112,7 +106,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
112
106
 
113
107
  writeNode(node: Object3D, nodeDef) {
114
108
  const nodeIndex = this.writer.json.nodes.length;
115
- if (debug)
109
+ if (debug)
116
110
  console.log(node.name, nodeIndex, node.uuid);
117
111
  const context = new ExportData(node, nodeIndex, nodeDef);
118
112
  this.exportContext[nodeIndex] = context;
@@ -158,8 +152,13 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
158
152
 
159
153
 
160
154
  // -------------------------------------
161
- // LOADING
162
- // called by GLTFLoader
155
+ // #region import
156
+ parser?: GLTFParser;
157
+ nodeToObjectMap: NodeToObjectMap = {};
158
+ /** The loaded gltf */
159
+ gltf: GLTF | null = null;
160
+
161
+
163
162
  beforeRoot() {
164
163
  if (debug)
165
164
  console.log("BEGIN LOAD");
@@ -167,7 +166,6 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
167
166
  return null;
168
167
  }
169
168
 
170
- // called by GLTFLoader
171
169
  async afterRoot(result: GLTF): Promise<void> {
172
170
  this.gltf = result;
173
171
 
@@ -175,8 +173,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
175
173
  const ext = parser?.extensions;
176
174
  if (!ext) return;
177
175
  const hasExtension = ext[this.name];
178
- if (debug)
179
- console.log("After root", result, this.parser, ext);
176
+ if (debug) console.log("After root", result, this.parser, ext);
180
177
 
181
178
  const loadComponents: Array<Promise<void>> = [];
182
179
  if (hasExtension === true) {
@@ -204,7 +201,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
204
201
 
205
202
  apply(obj);
206
203
 
207
- loadComponents.push(this.createComponents(obj, data));
204
+ loadComponents.push(this.createComponents(result, node, obj, data));
208
205
  }
209
206
  }
210
207
  }
@@ -220,7 +217,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
220
217
  }
221
218
  }
222
219
 
223
- private async createComponents(obj: Object3D, data: ExtensionData) {
220
+ private async createComponents(result: GLTF, node: Node, obj: Object3D, data: ExtensionData) {
224
221
  if (!data) return;
225
222
  const componentData = data[builtinComponentKeyName];
226
223
  if (componentData) {
@@ -228,20 +225,42 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
228
225
  if (debug)
229
226
  console.log(obj.name, componentData);
230
227
  for (const i in componentData) {
231
- const serializedData = componentData[i];
232
- if (debug)
233
- console.log("Serialized data", JSON.parse(JSON.stringify(serializedData)));
228
+ const data = componentData[i];
229
+
230
+ if (debug) console.log("Serialized data", JSON.parse(JSON.stringify(data)));
231
+
232
+ // Fix for https://linear.app/needle/issue/NE-6779/blender-export-has-missing-sharedmaterials
233
+ if (data?.name === "MeshRenderer" || data?.name === "SkinnedMeshRenderer") {
234
+ if (!data.sharedMaterials) {
235
+ let success = false;
236
+ if ("mesh" in node) {
237
+ const meshIndex = node.mesh;
238
+ if (typeof meshIndex === "number" && result.parser) {
239
+ const meshDef = result.parser.json.meshes?.[meshIndex];
240
+ if (meshDef?.primitives) {
241
+ data.sharedMaterials = meshDef.primitives.map(prim => {
242
+ return "/materials/" + (prim.material ?? 0);
243
+ });
244
+ success = true;
245
+ }
246
+ }
247
+ }
248
+ if(!success && (debug || isDevEnvironment())) {
249
+ console.warn(`[NEEDLE_components] Component '${data.name}' on object '${obj.name}' is not added to a mesh or failed to retrieve materials from glTF.`);
250
+ }
251
+ }
252
+ }
234
253
 
235
- if (serializedData && this.parser) {
254
+ if (data && this.parser) {
236
255
  tasks.push(
237
- resolveReferences(this.parser, serializedData)
238
- .catch(e => console.error(`Error while resolving references (see console for details)\n`, e, obj, serializedData))
256
+ resolveReferences(this.parser, data)
257
+ .catch(e => console.error(`Error while resolving references (see console for details)\n`, e, obj, data))
239
258
  );
240
259
  }
241
260
 
242
261
  obj.userData = obj.userData || {};
243
262
  obj.userData[builtinComponentKeyName] = obj.userData[builtinComponentKeyName] || [];
244
- obj.userData[builtinComponentKeyName].push(serializedData);
263
+ obj.userData[builtinComponentKeyName].push(data);
245
264
  }
246
265
  await Promise.all(tasks).catch((e) => {
247
266
  console.error("Error while loading components", e);
@@ -266,4 +285,6 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
266
285
  // // console.log(components);
267
286
  // return null;
268
287
  // }
269
- }
288
+ }
289
+
290
+
@@ -1,6 +1,6 @@
1
1
  import { isDevEnvironment, showBalloonWarning } from "../debug/debug.js";
2
2
  import { IContext } from "../engine_types.js";
3
- import { generateQRCode, isMobileDevice } from "../engine_utils.js";
3
+ import { generateQRCode } from "../engine_utils_qrcode.js";
4
4
  import { onXRSessionEnd, onXRSessionStart } from "../xr/events.js";
5
5
  import { getIconElement } from "./icons.js";
6
6
 
@@ -58,7 +58,7 @@ declare type AnimationIdentifier = AnimationClip | number | string | undefined;
58
58
  class Vec2 { x!: number; y!: number }
59
59
 
60
60
  /**
61
- * Animation component to play animations on a GameObject
61
+ * Animation component to play animations on a GameObject.
62
62
  * @category Animation and Sequencing
63
63
  * @group Components
64
64
  */
@@ -27,7 +27,10 @@ export class Keyframe {
27
27
  }
28
28
 
29
29
  /**
30
- * AnimationCurve is a representation of a curve that can be used to animate values over time.
30
+ * AnimationCurve is a representation of a curve that can be used to animate values over time.
31
+ *
32
+ * @category Animation
33
+ * @group Utilities
31
34
  */
32
35
  export class AnimationCurve {
33
36
 
@@ -39,8 +39,9 @@ export declare class PlayOptions {
39
39
 
40
40
  /**
41
41
  * The Animator component plays and manages animations on a GameObject.
42
- * It works with an AnimatorController to handle state transitions and animation blending.
43
- * A new AnimatorController can be created from code via `AnimatorController.createFromClips`.
42
+ * It works with an {@link AnimatorController} to handle state transitions and animation blending.
43
+ * A new AnimatorController can be created from code via `AnimatorController.createFromClips`.
44
+ *
44
45
  * @category Animation and Sequencing
45
46
  * @group Components
46
47
  */
@@ -51,6 +51,9 @@ declare type CreateAnimatorControllerOptions = {
51
51
  *
52
52
  * Use the static method {@link AnimatorController.createFromClips} to create
53
53
  * an animator controller from a set of animation clips.
54
+ *
55
+ * @category Animation and Sequencing
56
+ * @group Utilities
54
57
  */
55
58
  export class AnimatorController {
56
59
 
@@ -2,9 +2,14 @@ import { Object3D, Vector3 } from "three";
2
2
 
3
3
  import { serializable } from "../engine/engine_serialization_decorator.js";
4
4
  import { Behaviour } from "./Component.js";
5
+ import type { OrbitControls } from "./OrbitControls.js";
5
6
 
6
7
  /**
7
- * A LookAtConstraint is used by OrbitControls to make the camera look at a target.
8
+ * A LookAtConstraint is used by OrbitControls to make the camera look at a target.
9
+ * This component is used by {@link OrbitControls} internally.
10
+ *
11
+ * @category Camera Controls
12
+ * @group Components
8
13
  */
9
14
  export class LookAtConstraint extends Behaviour {
10
15
 
@@ -9,6 +9,7 @@ import { Behaviour } from './Component.js';
9
9
  *
10
10
  * Controls display options, button visibility, and menu positioning.
11
11
  * From code, you can access the menu via {@link Context.menu}.
12
+ *
12
13
  * @category User Interface
13
14
  * @group Components
14
15
  **/
@@ -10,6 +10,9 @@ const debug = getParam("debugnestedgltf");
10
10
 
11
11
  /** The nested gltf is a component that is used to load a gltf file when the component becomes active (start)
12
12
  * It will load the gltf file and instantiate it as a child of the parent of the GameObject that has this component
13
+ *
14
+ * @category Asset Management
15
+ * @group Components
13
16
  */
14
17
  export class NestedGltf extends Behaviour {
15
18
 
@@ -15,6 +15,10 @@ const $reflectionProbeKey = Symbol("reflectionProbeKey");
15
15
  const $originalMaterial = Symbol("original material");
16
16
 
17
17
  /**
18
+ * A ReflectionProbe provides reflection data to materials within its defined area.
19
+ *
20
+ * - Sample: http://samples.needle.tools/reflection-probes
21
+ *
18
22
  * @category Rendering
19
23
  * @group Components
20
24
  */
@@ -69,7 +69,7 @@ class SharedMaterialArray implements ISharedMaterials {
69
69
  set changed(value: boolean) {
70
70
  if (value === true) {
71
71
  if (debugRenderer)
72
- console.warn("SharedMaterials have changed: " + this._renderer.name, this);
72
+ console.warn("SharedMaterials have changed: " + this._renderer.name);
73
73
  }
74
74
  this._changed = value;
75
75
  }
@@ -358,11 +358,15 @@ export class Renderer extends Behaviour implements IRenderer {
358
358
  //@ts-ignore
359
359
  get sharedMaterials(): SharedMaterialArray {
360
360
 
361
- // @ts-ignore (original materials will be set during deserialization)
362
- if (this._originalMaterials === undefined) return null;
363
-
364
- // @ts-ignore during deserialization code might access this property *before* the setter and then create an empty array
365
- if (this.__isDeserializing === true) return null;
361
+ if (this._originalMaterials === undefined) {
362
+ if (!this.__didAwake) {
363
+ // @ts-ignore (original materials will be set during deserialization)
364
+ return null;
365
+ }
366
+ else {
367
+ this._originalMaterials = [];
368
+ }
369
+ }
366
370
 
367
371
  if (!this._sharedMaterials || !this._sharedMaterials.is(this)) {
368
372
  if (!this._originalMaterials) this._originalMaterials = [];
@@ -454,6 +458,7 @@ export class Renderer extends Behaviour implements IRenderer {
454
458
  this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
455
459
  }
456
460
 
461
+ this._lightmaps = undefined;
457
462
  this.applyLightmapping();
458
463
 
459
464
  if (showWireframe) {
@@ -468,8 +473,8 @@ export class Renderer extends Behaviour implements IRenderer {
468
473
  }
469
474
 
470
475
  private applyLightmapping() {
471
- if (this.lightmapIndex >= 0) {
472
- const type = this.gameObject.type;
476
+ if (this.lightmapIndex >= 0 && !this._lightmaps) {
477
+ // const type = this.gameObject.type;
473
478
 
474
479
  // use the override lightmap if its not undefined
475
480
  const tex = this._lightmapTextureOverride !== undefined
@@ -477,45 +482,12 @@ export class Renderer extends Behaviour implements IRenderer {
477
482
  : this.context.lightmaps.tryGetLightmap(this.sourceId, this.lightmapIndex);
478
483
  if (tex) {
479
484
  if (!this._lightmaps) this._lightmaps = [];
480
-
481
-
482
485
  const rm = new RendererLightmap(this);
483
486
  rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
484
487
  this._lightmaps.push(rm);
485
-
486
- // if (type === "Mesh") {
487
- // const mat = this.gameObject["material"];
488
- // if (!mat?.isMeshBasicMaterial) {
489
- // if (this._lightmaps.length <= 0) {
490
- // }
491
- // const rm = this._lightmaps[0];
492
- // rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
493
- // }
494
- // else {
495
- // if (mat)
496
- // console.warn("Lightmapping is not supported on MeshBasicMaterial", mat.name)
497
- // }
498
- // }
499
- // // for multi materials we need to loop through children
500
- // // and then we add a lightmap renderer component to each of them
501
- // else if (this.isMultiMaterialObject(this.gameObject) && this.sharedMaterials.length > 0) {
502
- // for (let i = 0; i < this.gameObject.children.length; i++) {
503
- // const child = this.gameObject.children[i];
504
- // if (!child["material"]?.isMeshBasicMaterial) {
505
- // let rm: RendererLightmap | undefined = undefined;
506
- // if (i >= this._lightmaps.length) {
507
- // rm = new RendererLightmap(child as Mesh, this.context);
508
- // this._lightmaps.push(rm);
509
- // }
510
- // else
511
- // rm = this._lightmaps[i];
512
- // rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
513
- // }
514
- // }
515
- // }
516
488
  }
517
489
  else {
518
- if (debugRenderer) console.warn("Lightmap not found", this.sourceId, this.lightmapIndex);
490
+ if (debugRenderer) console.warn(`[Renderer] No lightmaps found ${this.name} (${this.sourceId}, ${this.lightmapIndex})`);
519
491
  }
520
492
  }
521
493
 
@@ -9,9 +9,17 @@ const debug = getParam("debuglightmaps");
9
9
 
10
10
  declare type MaterialWithLightmap = Material & { lightMap?: Texture | null };
11
11
 
12
- // this component is automatically added by the Renderer if the object has lightmap uvs AND we have a lightmap
13
- // for multimaterial objects GLTF exports a "Group" with the renderer component
14
- // and every child mesh is a material from unity
12
+ let cloningCounter = 0;
13
+
14
+ const $lightmapVersion = Symbol("lightmap-material-version");
15
+
16
+
17
+ /**
18
+ * This component is automatically added by the {@link Renderer} component if the object has lightmap uvs AND we have a lightmap.
19
+ *
20
+ * @category Rendering
21
+ * @group Components
22
+ */
15
23
  export class RendererLightmap {
16
24
 
17
25
  get lightmap(): Texture | null {
@@ -33,6 +41,8 @@ export class RendererLightmap {
33
41
  private lightmapScaleOffset: Vector4 = new Vector4(1, 1, 0, 0);
34
42
 
35
43
  private readonly renderer: Renderer;
44
+ private readonly clonedMaterials = new Array<Material>();
45
+
36
46
  private get context(): Context { return this.renderer.context; }
37
47
  private get gameObject() { return this.renderer.gameObject; }
38
48
  private lightmapTexture: Texture | null = null;
@@ -85,8 +95,7 @@ export class RendererLightmap {
85
95
 
86
96
  const mat = this.renderer.sharedMaterials[i];
87
97
  if (!mat) continue;
88
-
89
- const newMat = this.ensureLightmapMaterial(mat);
98
+ const newMat = this.ensureLightmapMaterial(mat, i);
90
99
  if (mat !== newMat) {
91
100
  this.renderer.sharedMaterials[i] = newMat;
92
101
  }
@@ -114,23 +123,28 @@ export class RendererLightmap {
114
123
  }
115
124
  }
116
125
 
117
- private ensureLightmapMaterial(material: Material) {
126
+ private ensureLightmapMaterial(material: Material, index: number) {
118
127
  if (!material.userData) material.userData = {};
119
128
  // if (material instanceof MeshPhysicalMaterial) {
120
129
  // return material;
121
130
  // }
122
131
  // check if the material version has changed and only then clone the material
123
- if (material["NEEDLE:lightmap-material-version"] != material.version) {
124
- if (material["NEEDLE:lightmap-material-version"] == undefined) {
125
- if (debug) console.warn("Cloning material for lightmap " + material.name);
126
- const mat: Material = material.clone();
127
- if (!mat.name?.includes("(lightmap)")) mat.name = material.name + " (lightmap)";
128
- material = mat;
129
- material.onBeforeCompile = this.onBeforeCompile;
130
- }
131
- else {
132
- // we need to clone the material
132
+ if (this.clonedMaterials[index] !== material) {
133
+ if (debug) {
134
+ ++cloningCounter;
135
+ if (cloningCounter++ < 1000) {
136
+ console.warn(`Cloning material for lightmap ${this.renderer.name}: '${material.name}'`);
137
+ }
138
+ else if (cloningCounter === 1000) {
139
+ console.warn(`Further material cloning for lightmaps suppressed to avoid flooding the console.`);
140
+ }
133
141
  }
142
+ const mat: Material = material.clone();
143
+ if (!mat.name?.includes("(lightmap)")) mat.name = material.name + " (lightmap)";
144
+ material = mat;
145
+ material.onBeforeCompile = this.onBeforeCompile;
146
+ this.clonedMaterials[index] = material;
147
+
134
148
  }
135
149
  return material;
136
150
  }
@@ -140,22 +154,22 @@ export class RendererLightmap {
140
154
  if (material instanceof MeshPhysicalMaterial && material.transmission > 0) {
141
155
  return;
142
156
  }
143
- const hasChanged = material.lightMap !== this.lightmapTexture || material["NEEDLE:lightmap-material-version"] !== material.version;
157
+ const hasChanged = material.lightMap !== this.lightmapTexture || material[$lightmapVersion] !== material.version;
144
158
  if (!hasChanged) {
145
159
  return;
146
160
  }
147
161
 
148
- if (debug) console.log("Assigning lightmap", material.name, material.version, material);
162
+ if (debug) console.log(`Assigning lightmap texture ${this.renderer.name}: '${material.name}' (${material.version} ${material[$lightmapVersion]})`);
149
163
 
150
164
  // assign the lightmap
151
165
  material.lightMap = this.lightmapTexture;
152
166
  material.needsUpdate = true;
153
167
  // store the version of the material
154
- material["NEEDLE:lightmap-material-version"] = material.version;
168
+ material[$lightmapVersion] = material.version;
155
169
  }
156
170
 
157
171
  private onBeforeCompile = (shader: WebGLProgramParametersWithUniforms, _) => {
158
- if (debug) console.log("Lightmaps, before compile\n", shader)
172
+ if (debug === "verbose") console.log("Lightmaps, before compile\n", shader)
159
173
  this.lightmapScaleOffsetUniform.value = this.lightmapScaleOffset;
160
174
  this.lightmapUniform.value = this.lightmapTexture;
161
175
  shader.uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
@@ -39,6 +39,9 @@ let i = 0;
39
39
  * Requires a Renderer component on the same object or a child object.
40
40
  *
41
41
  * - Example https://see-through-walls-z23hmxbz1kjfjn.needle.run/
42
+ *
43
+ * @category Rendering
44
+ * @group Components
42
45
  */
43
46
  export class SeeThrough extends Behaviour {
44
47
 
@@ -120,6 +120,9 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, () => {
120
120
  * ```ts
121
121
  * skybox.setSkybox("https://example.com/skybox.hdr");
122
122
  * ```
123
+ *
124
+ * @category Rendering
125
+ * @group Components
123
126
  */
124
127
  export class RemoteSkybox extends Behaviour {
125
128
 
@@ -9,6 +9,9 @@ import { Behaviour } from "./Component.js";
9
9
  const debug = getParam("debugspriterenderer");
10
10
  const showWireframe = getParam("wireframe");
11
11
 
12
+ /**
13
+ * @internal
14
+ */
12
15
  class SpriteUtils {
13
16
 
14
17
  static cache: { [key: string]: BufferGeometry } = {};
@@ -82,7 +85,7 @@ function updateTextureIfNecessary(tex: Texture) {
82
85
  }
83
86
 
84
87
  /**
85
- * A sprite is a mesh that represents a 2D image
88
+ * A sprite is a mesh that represents a 2D image. Used by the {@link SpriteRenderer} to render 2D images in the scene.
86
89
  * @category Rendering
87
90
  * @group Components
88
91
  */
@@ -152,6 +155,9 @@ export class Sprite {
152
155
 
153
156
  const $spriteTexOwner = Symbol("spriteOwner");
154
157
 
158
+ /**
159
+ * @category Sprites
160
+ */
155
161
  export class SpriteSheet {
156
162
 
157
163
  @serializable(Sprite)
@@ -162,6 +168,11 @@ export class SpriteSheet {
162
168
  }
163
169
  }
164
170
 
171
+ /**
172
+ * Used by the {@link SpriteRenderer} to hold the sprite sheet and the currently active sprite index.
173
+ *
174
+ * @category Sprites
175
+ */
165
176
  export class SpriteData {
166
177
 
167
178
  static create() {
@@ -246,7 +257,12 @@ export class SpriteData {
246
257
  }
247
258
 
248
259
  /**
249
- * The sprite renderer renders a sprite on a GameObject using an assigned spritesheet ({@link SpriteData}).
260
+ * The sprite renderer renders a sprite on a GameObject using an assigned spritesheet ({@link SpriteData}).
261
+ *
262
+ * - Example: https://engine.needle.tools/samples/spritesheet-animation
263
+ *
264
+ * @category Sprites
265
+ * @group Components
250
266
  */
251
267
  export class SpriteRenderer extends Behaviour {
252
268
 
@@ -36,6 +36,9 @@ export class SplineData {
36
36
  * The spline is defined by an array of knots (SplineData) which define position, rotation and tangents.
37
37
  *
38
38
  * You can create a SplineContainer from an array of points using the static method 'createFromPoints'.
39
+ *
40
+ * @category Splines
41
+ * @group Components
39
42
  */
40
43
  export class SplineContainer extends Behaviour {
41
44
 
@@ -3,7 +3,9 @@ import { Vector3 } from "three";
3
3
  import { Mathf } from "../../engine/engine_math.js";
4
4
  import { SplineContainer, SplineData } from "./Spline.js";
5
5
 
6
-
6
+ /**
7
+ * @category Splines
8
+ */
7
9
  export namespace SplineUtils {
8
10
 
9
11
  /**
@@ -11,6 +11,9 @@ import { SplineContainer } from "./Spline.js";
11
11
  * Use this with a SplineContainer component.
12
12
  *
13
13
  * - Example http://samples.needle.tools/splines
14
+ *
15
+ * @category Splines
16
+ * @group Components
14
17
  */
15
18
  export class SplineWalker extends Behaviour {
16
19
 
@@ -114,8 +114,7 @@ export class PlayableDirector extends Behaviour {
114
114
 
115
115
  /** @internal */
116
116
  awake(): void {
117
- if (debug)
118
- console.log(this, this.playableAsset);
117
+ if (debug) console.log(`[Timeline] Awake '${this.name}'`, this);
119
118
 
120
119
  this.rebuildGraph();
121
120
 
@@ -134,6 +133,8 @@ export class PlayableDirector extends Behaviour {
134
133
 
135
134
  /** @internal */
136
135
  onEnable() {
136
+ if (debug) console.log("[Timeline] OnEnable", this.name, this.playOnAwake);
137
+
137
138
  for (const track of this._audioTracks) {
138
139
  track.onEnable?.();
139
140
  }
@@ -162,6 +163,8 @@ export class PlayableDirector extends Behaviour {
162
163
 
163
164
  /** @internal */
164
165
  onDisable(): void {
166
+ if (debug) console.log("[Timeline] OnDisable", this.name);
167
+
165
168
  this.stop();
166
169
  for (const track of this._audioTracks) {
167
170
  track.onDisable?.();
@@ -360,14 +363,17 @@ export class PlayableDirector extends Behaviour {
360
363
  private readonly _controlTracks: Array<Tracks.ControlTrackHandler> = [];
361
364
  private readonly _customTracks: Array<Tracks.TrackHandler> = [];
362
365
 
363
- private readonly _allTracks: Array<Array<Tracks.TrackHandler>> = [
364
- this._animationTracks,
365
- this._audioTracks,
366
- this._signalTracks,
367
- this._markerTracks,
368
- this._controlTracks,
369
- this._customTracks
370
- ];
366
+ private readonly _tracksArray: Array<Array<Tracks.TrackHandler>> = [];
367
+ private get _allTracks(): Array<Array<Tracks.TrackHandler>> {
368
+ this._tracksArray.length = 0;
369
+ this._tracksArray.push(this._animationTracks);
370
+ this._tracksArray.push(this._audioTracks);
371
+ this._tracksArray.push(this._signalTracks);
372
+ this._tracksArray.push(this._markerTracks);
373
+ this._tracksArray.push(this._controlTracks);
374
+ this._tracksArray.push(this._customTracks);
375
+ return this._tracksArray;
376
+ }
371
377
 
372
378
  /** should be called after evaluate if the director was playing */
373
379
  private invokePauseChangedMethodsOnTracks() {