@needle-tools/engine 3.6.3 → 3.6.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/needle-engine.js +10589 -10562
  3. package/dist/needle-engine.light.js +10535 -10508
  4. package/dist/needle-engine.light.min.js +285 -286
  5. package/dist/needle-engine.light.umd.cjs +285 -286
  6. package/dist/needle-engine.min.js +285 -286
  7. package/dist/needle-engine.umd.cjs +286 -287
  8. package/lib/engine/api.d.ts +1 -0
  9. package/lib/engine/api.js +1 -0
  10. package/lib/engine/api.js.map +1 -1
  11. package/lib/engine/engine_context.d.ts +12 -1
  12. package/lib/engine/engine_context.js +13 -2
  13. package/lib/engine/engine_context.js.map +1 -1
  14. package/lib/engine/engine_create_objects.d.ts +3 -1
  15. package/lib/engine/engine_create_objects.js +17 -4
  16. package/lib/engine/engine_create_objects.js.map +1 -1
  17. package/lib/engine/engine_element.js +11 -4
  18. package/lib/engine/engine_element.js.map +1 -1
  19. package/lib/engine/engine_element_loading.js +3 -3
  20. package/lib/engine/engine_element_loading.js.map +1 -1
  21. package/lib/engine/engine_license.js +4 -6
  22. package/lib/engine/engine_license.js.map +1 -1
  23. package/lib/engine/engine_scenetools.d.ts +5 -4
  24. package/lib/engine/engine_scenetools.js +2 -67
  25. package/lib/engine/engine_scenetools.js.map +1 -1
  26. package/lib/engine/extensions/NEEDLE_components.js +5 -2
  27. package/lib/engine/extensions/NEEDLE_components.js.map +1 -1
  28. package/lib/engine-components/AnimationCurve.d.ts +1 -0
  29. package/lib/engine-components/AnimationCurve.js +24 -3
  30. package/lib/engine-components/AnimationCurve.js.map +1 -1
  31. package/lib/engine-components/AnimationUtils.js +69 -3
  32. package/lib/engine-components/AnimationUtils.js.map +1 -1
  33. package/lib/engine-components/Component.d.ts +2 -1
  34. package/lib/engine-components/Component.js +2 -1
  35. package/lib/engine-components/Component.js.map +1 -1
  36. package/lib/engine-components/Gizmos.js +1 -1
  37. package/lib/engine-components/Gizmos.js.map +1 -1
  38. package/lib/engine-components/NestedGltf.js +1 -0
  39. package/lib/engine-components/NestedGltf.js.map +1 -1
  40. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +2 -2
  41. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  42. package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js +2 -1
  43. package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js.map +1 -1
  44. package/package.json +1 -1
  45. package/plugins/vite/copyfiles.js +2 -0
  46. package/plugins/vite/transform-codegen.js +10 -1
  47. package/src/engine/api.ts +12 -11
  48. package/src/engine/engine_context.ts +15 -3
  49. package/src/engine/engine_create_objects.ts +18 -5
  50. package/src/engine/engine_element.ts +12 -6
  51. package/src/engine/engine_element_loading.ts +3 -3
  52. package/src/engine/engine_license.ts +4 -6
  53. package/src/engine/engine_scenetools.ts +9 -72
  54. package/src/engine/extensions/NEEDLE_components.ts +7 -2
  55. package/src/engine-components/AnimationCurve.ts +31 -3
  56. package/src/engine-components/AnimationUtils.ts +70 -5
  57. package/src/engine-components/Component.ts +2 -1
  58. package/src/engine-components/Gizmos.ts +1 -1
  59. package/src/engine-components/NestedGltf.ts +2 -0
  60. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +2 -2
  61. package/src/engine-components/postprocessing/Effects/ColorAdjustments.ts +2 -1
@@ -42,9 +42,10 @@ export class AnimationCurve {
42
42
  if (hasNextKeyframe) {
43
43
  const nextKf = this.keys[i+1];
44
44
  // if the next
45
- if(nextKf.time < time) continue;
46
- const t = Mathf.remap(time, kf.time, nextKf.time, 0, 1);
47
- return Mathf.lerp(kf.value, nextKf.value, t);
45
+ if (nextKf.time < time) continue;
46
+ // tangents are set to Infinity if interpolation is set to constant - in that case we should always return the floored value
47
+ if (!isFinite(kf.outTangent) || !isFinite(nextKf.inTangent)) return kf.value;
48
+ return AnimationCurve.interpolateValue(time, kf, nextKf);
48
49
  }
49
50
  else {
50
51
  return kf.value;
@@ -53,4 +54,31 @@ export class AnimationCurve {
53
54
  }
54
55
  return this.keys[this.keys.length - 1].value;
55
56
  }
57
+
58
+ static interpolateValue(time: number, keyframe1: Keyframe, keyframe2: Keyframe): number {
59
+ const startTime1 = keyframe1.time;
60
+ const startValue1 = keyframe1.value;
61
+ const outTangent1 = keyframe1.outTangent;
62
+
63
+ const startTime2 = keyframe2.time;
64
+ const startValue2 = keyframe2.value;
65
+ const inTangent2 = keyframe2.inTangent;
66
+
67
+ // could be precomputed and stored in the keyframes maybe
68
+ const timeDifference = startTime2 - startTime1;
69
+ const timeDifferenceSquared = timeDifference * timeDifference;
70
+ const timeDifferenceCubed = timeDifferenceSquared * timeDifference;
71
+
72
+ const a = ((outTangent1 + inTangent2) * timeDifference - 2 * (startValue2 - startValue1)) / timeDifferenceCubed;
73
+ const b = (3 * (startValue2 - startValue1) - (inTangent2 + 2 * outTangent1) * timeDifference) / timeDifferenceSquared;
74
+ const c = outTangent1;
75
+ const d = startValue1;
76
+
77
+ const timeDelta = time - startTime1;
78
+ const timeDeltaSquared = timeDelta * timeDelta;
79
+ const timeDeltaCubed = timeDeltaSquared * timeDelta;
80
+
81
+ return a * timeDeltaCubed + b * timeDeltaSquared + c * timeDelta + d;
82
+ }
83
+
56
84
  }
@@ -1,14 +1,79 @@
1
1
  import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
2
2
  import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry";
3
- import { findAnimations } from "../engine/engine_scenetools";
4
-
3
+ import { addNewComponent } from "../engine/engine_components";
4
+ import { Animator } from "./Animator";
5
+ import { Animation } from "./Animation";
6
+ import { GameObject } from "./Component";
7
+ import { PlayableDirector } from "./api";
5
8
 
6
9
  ContextRegistry.registerCallback(ContextEvent.ContextCreated, args => {
7
10
  const autoplay = args.context.domElement.getAttribute("autoplay");
8
11
  if (autoplay !== undefined && (autoplay === "" || autoplay === "true")) {
9
12
  if (args.files) {
10
- for (const file of args.files)
11
- findAnimations(file.file as GLTF, true);
13
+ for (const file of args.files) {
14
+ const hasAnimation = GameObject.foreachComponent(file.file.scene, comp => {
15
+ if (comp.enabled === false) return undefined;
16
+ if (comp instanceof Animation && comp.playAutomatically || comp instanceof Animator || comp instanceof PlayableDirector && comp.playOnAwake === true) {
17
+ return true;
18
+ }
19
+ else if (comp instanceof Animation) {
20
+ comp.playAutomatically = true;
21
+ return true;
22
+ }
23
+ else if (comp instanceof PlayableDirector) {
24
+ comp.playOnAwake = true;
25
+ return true;
26
+ }
27
+ return undefined;
28
+ }, true);
29
+ if (hasAnimation !== true)
30
+ findAnimations(file.file as GLTF);
31
+ }
32
+ }
33
+ }
34
+ });
35
+
36
+
37
+
38
+ function findAnimations(gltf: GLTF) {
39
+ if (!gltf || !gltf.animations) return;
40
+
41
+
42
+ for (let i = 0; i < gltf.animations.length; i++) {
43
+ const animation = gltf.animations[i];
44
+ if (!animation.tracks || animation.tracks.length <= 0) continue;
45
+ for (const t in animation.tracks) {
46
+ const track = animation.tracks[t];
47
+ const objectName = track["__objectName"] ?? track.name.substring(0, track.name.indexOf("."));
48
+ const obj = gltf.scene.getObjectByName(objectName);
49
+ if (!obj) {
50
+ // console.warn("could not find " + objectName, animation, gltf.scene);
51
+ continue;
52
+ }
53
+ let animationComponent = findAnimationGameObjectInParent(obj);
54
+ if (!animationComponent) {
55
+ animationComponent = addNewComponent(gltf.scene, new Animation());
56
+ }
57
+ const animations = animationComponent.animations = animationComponent.animations || [];
58
+ animation["name_animator"] = animationComponent.name;
59
+ // console.log(objectName, obj, animator.name, animations.length);
60
+ if (animations.indexOf(animation) < 0) {
61
+ animations.push(animation);
62
+ }
63
+ }
64
+ }
65
+ function findAnimationGameObjectInParent(obj) {
66
+ if (!obj) return;
67
+ const components = obj.userData?.components;
68
+ if (components && components.length > 0) {
69
+ for (let i = 0; i < components.length; i++) {
70
+ const component = components[i];
71
+ // console.log(component);
72
+ if (component instanceof Animator || component instanceof Animation) {
73
+ return obj;;
74
+ }
75
+ }
12
76
  }
77
+ return findAnimationGameObjectInParent(obj.parent);
13
78
  }
14
- });
79
+ }
@@ -63,8 +63,9 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
63
63
 
64
64
  /** Run a callback for all components of the provided type on the provided object and its children (if recursive is true)
65
65
  * @param instance object to run the method on
66
- * @param cb callback to run on each component
66
+ * @param cb callback to run on each component, "return undefined;" to continue and "return <anything>;" to break the loop
67
67
  * @param recursive if true, the method will be run on all children as well
68
+ * @returns the last return value of the callback
68
69
  */
69
70
  public static foreachComponent(instance: Object3D, cb: (comp: Component) => any, recursive: boolean = true): any {
70
71
  return foreachComponent(instance, cb as (comp: IComponent) => any, recursive);
@@ -21,7 +21,7 @@ export class BoxGizmo extends Behaviour {
21
21
  onEnable(): void {
22
22
  if (this.isGizmo && !params.showGizmos) return;
23
23
  if (!this._gizmoObject) {
24
- if (this.objectBounds && this.gameObject["isMesh"] === true) {
24
+ if (this.objectBounds) {
25
25
  this._gizmoObject = new THREE.BoxHelper(this.gameObject, this.color ?? 0xffff00);
26
26
  }
27
27
  else {
@@ -43,6 +43,8 @@ export class NestedGltf extends Behaviour {
43
43
  res.matrixAutoUpdate = true;
44
44
  res.layers.disableAll();
45
45
  res.layers.set(this.layer);
46
+
47
+ this.dispatchEvent(new CustomEvent("loaded", { detail: {instance: res, assetReference: this.filePath }}));
46
48
  }
47
49
  if (debug) console.log("Nested loading done:", this.filePath?.uri ?? this.filePath, res);
48
50
  }
@@ -1299,7 +1299,7 @@ function buildMaterial( material: MeshStandardMaterial, textures, quickLookCompa
1299
1299
 
1300
1300
  } else {
1301
1301
 
1302
- inputs.push( `${pad}float inputs:roughness = ${material.roughness}` );
1302
+ inputs.push( `${pad}float inputs:roughness = ${material.roughness !== undefined ? material.roughness : 1 }` );
1303
1303
 
1304
1304
  }
1305
1305
 
@@ -1311,7 +1311,7 @@ function buildMaterial( material: MeshStandardMaterial, textures, quickLookCompa
1311
1311
 
1312
1312
  } else {
1313
1313
 
1314
- inputs.push( `${pad}float inputs:metallic = ${material.metalness}` );
1314
+ inputs.push( `${pad}float inputs:metallic = ${material.metalness !== undefined ? material.metalness : 0 }` );
1315
1315
 
1316
1316
  }
1317
1317
 
@@ -63,7 +63,8 @@ export class ColorAdjustments extends PostProcessingEffect {
63
63
  this.postExposure!.onValueChanged = v => {
64
64
  if (this.context.renderer.toneMapping === NoToneMapping)
65
65
  this.context.renderer.toneMapping = LinearToneMapping;
66
- this.context.renderer.toneMappingExposure = v;
66
+ if (this.postExposure.overrideState)
67
+ this.context.renderer.toneMappingExposure = v;
67
68
  }
68
69
  this.contrast!.onValueChanged = v => {
69
70
  brightnesscontrast.contrast = v;