Needle Engine

Changes between version 3.11.3-beta and 3.11.4-beta
Files changed (6) hide show
  1. src/engine-components/Animator.ts +16 -2
  2. src/engine/engine_element.ts +11 -8
  3. src/engine-components/timeline/PlayableDirector.ts +11 -2
  4. src/engine/codegen/register_types.ts +2 -2
  5. src/engine-components/timeline/TimelineTracks.ts +1 -7
  6. src/engine-components/export/usdz/USDZExporter.ts +2 -0
src/engine-components/Animator.ts CHANGED
@@ -50,24 +50,33 @@
50
50
  return this._animatorController;
51
51
  }
52
52
 
53
+
54
+ get parametersAreDirty() { return this._parametersAreDirty; }
55
+ get isDirty() { return this._isDirty; }
56
+ private _parametersAreDirty: boolean = false;
57
+ private _isDirty: boolean = false;
58
+
53
59
  // NOTE: the uppercase events have been deprecated because UnityEvent methods are all exported with lowercase first letter
54
60
 
55
61
  /**@deprecated use play */
56
62
  Play(name: string | number, layer: number = -1, normalizedTime: number = Number.NEGATIVE_INFINITY, transitionDurationInSec: number = 0) { this.play(name, layer, normalizedTime, transitionDurationInSec); }
57
63
  play(name: string | number, layer: number = -1, normalizedTime: number = Number.NEGATIVE_INFINITY, transitionDurationInSec: number = 0) {
58
64
  this.runtimeAnimatorController?.play(name, layer, normalizedTime, transitionDurationInSec);
65
+ this._isDirty = true;
59
66
  }
60
67
 
61
68
  /**@deprecated use reset */
62
69
  Reset() { this.reset(); }
63
70
  reset() {
64
71
  this._animatorController?.reset();
72
+ this._isDirty = true;
65
73
  }
66
74
 
67
75
  /**@deprecated use setBool */
68
76
  SetBool(name: string | number, val: boolean) { this.setBool(name, val); }
69
77
  setBool(name: string | number, value: boolean) {
70
78
  this.runtimeAnimatorController?.setBool(name, value);
79
+ this._parametersAreDirty = true;
71
80
  }
72
81
 
73
82
  /**@deprecated use getBool */
@@ -80,6 +89,7 @@
80
89
  SetFloat(name: string | number, val: number) { this.setFloat(name, val); }
81
90
  setFloat(name: string | number, val: number) {
82
91
  this.runtimeAnimatorController?.setFloat(name, val);
92
+ this._parametersAreDirty = true;
83
93
  }
84
94
 
85
95
  /**@deprecated use getFloat */
@@ -92,6 +102,7 @@
92
102
  SetInteger(name: string | number, val: number) { this.setInteger(name, val); }
93
103
  setInteger(name: string | number, val: number) {
94
104
  this.runtimeAnimatorController?.setInteger(name, val);
105
+ this._parametersAreDirty = true;
95
106
  }
96
107
 
97
108
  /**@deprecated use getInteger */
@@ -103,20 +114,20 @@
103
114
  /**@deprecated use setTrigger */
104
115
  SetTrigger(name: string | number) { this.setTrigger(name); }
105
116
  setTrigger(name: string | number) {
106
- if (debug) console.log("SetTrigger", name);
107
117
  this.runtimeAnimatorController?.setTrigger(name);
118
+ this._parametersAreDirty = true;
108
119
  }
109
120
 
110
121
  /**@deprecated use resetTrigger */
111
122
  ResetTrigger(name: string | number) { this.resetTrigger(name); }
112
123
  resetTrigger(name: string | number) {
113
124
  this.runtimeAnimatorController?.resetTrigger(name);
125
+ this._parametersAreDirty = true;
114
126
  }
115
127
 
116
128
  /**@deprecated use getTrigger */
117
129
  GetTrigger(name: string | number) { this.getTrigger(name); }
118
130
  getTrigger(name: string | number) {
119
- if (debug) console.log("GetTrigger", name);
120
131
  this.runtimeAnimatorController?.getTrigger(name);
121
132
  }
122
133
 
@@ -187,6 +198,9 @@
187
198
  }
188
199
 
189
200
  onBeforeRender() {
201
+ this._isDirty = false;
202
+ this._parametersAreDirty = false;
203
+
190
204
  const isAnimatedExternally = getObjectAnimated(this.gameObject);
191
205
  if (isAnimatedExternally) return;
192
206
 
src/engine/engine_element.ts CHANGED
@@ -96,22 +96,25 @@
96
96
  // TODO: do we want to rename this event?
97
97
  this.addEventListener("ready", this.onReady);
98
98
 
99
- this.attachShadow({mode: 'open'});
99
+ this.attachShadow({ mode: 'open' });
100
100
  const template = document.createElement('template');
101
- template.innerHTML =
102
- `<style>
101
+ template.innerHTML = `<style>
103
102
  :host {
104
- position: relative;
103
+ position: absolute;
105
104
  display: block;
106
- width: max(600px, 100vw);
107
- height: max(300px, 100vh);
105
+ width: max(600px, 100%);
106
+ height: max(300px, 100%);
108
107
  }
109
108
  @media (max-width: 600px) {
110
109
  :host {
111
- width: 100vw;
112
- height: 100vh;
110
+ width: 100%;
113
111
  }
114
112
  }
113
+ @media (max-height: 300px) {
114
+ :host {
115
+ height: 100%;
116
+ }
117
+ }
115
118
  :host canvas {
116
119
  position: absolute;
117
120
  user-select: none;
src/engine-components/timeline/PlayableDirector.ts CHANGED
@@ -197,6 +197,7 @@
197
197
  }
198
198
 
199
199
  stop() {
200
+ this._isStopping = true;
200
201
  for (const track of this._audioTracks) track.stop();
201
202
  const pauseChanged = this._isPaused == true;
202
203
  const wasPlaying = this._isPlaying;
@@ -214,6 +215,7 @@
214
215
  if (this._internalUpdateRoutine)
215
216
  this.stopCoroutine(this._internalUpdateRoutine);
216
217
  this._internalUpdateRoutine = null;
218
+ this._isStopping = false;
217
219
  }
218
220
 
219
221
  evaluate() {
@@ -266,6 +268,8 @@
266
268
  private _isPlaying: boolean = false;
267
269
  private _internalUpdateRoutine: any;
268
270
  private _isPaused: boolean = false;
271
+ /** internal, true during the time stop() is being processed */
272
+ private _isStopping: boolean = false;
269
273
  private _time: number = 0;
270
274
  private _duration: number = 0;
271
275
  private _weight: number = 1;
@@ -341,8 +345,13 @@
341
345
  }
342
346
  }
343
347
 
344
- for (const handler of this._animationTracks) {
345
- handler.evaluate(time);
348
+ // When timeline reaches the end "stop()" is called which is evaluating with time 0
349
+ // We don't want to re-evaluate the animation then in case the timeline is blended with the Animator
350
+ // e.g then the timeline animation at time 0 is 100% applied on top of the animator animation
351
+ if (!this._isStopping) {
352
+ for (const handler of this._animationTracks) {
353
+ handler.evaluate(time);
354
+ }
346
355
  }
347
356
  for (const handler of this._audioTracks) {
348
357
  handler.evaluate(time);
src/engine/codegen/register_types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { TypeStore } from "./../engine_typestore.js"
2
-
2
+
3
3
  // Import types
4
4
  import { __Ignore } from "../../engine-components/codegen/components.js";
5
5
  import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js";
@@ -217,7 +217,7 @@
217
217
  import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering.js";
218
218
  import { XRRig } from "../../engine-components/webxr/WebXRRig.js";
219
219
  import { XRState } from "../../engine-components/XRFlag.js";
220
-
220
+
221
221
  // Register types
222
222
  TypeStore.add("__Ignore", __Ignore);
223
223
  TypeStore.add("ActionBuilder", ActionBuilder);
src/engine-components/timeline/TimelineTracks.ts CHANGED
@@ -287,13 +287,7 @@
287
287
  evaluate(time: number) {
288
288
  if (this.track.muted) return;
289
289
  if (!this.mixer) return;
290
- // Don't evaluate the timeline if the director is not playing animator
291
- // consider the case where we want to blend into an Animator (e.g. last clip is faded out)
292
- // and currently evaluate is being called when the director is stopped which happens for example
293
- // if Wrap mode is set to None. I think it makes sense to NOT evaluate anymore when stopping (not sure why we did it in the first place)
294
- // I won't change the root behaviour on the other tracks for now to avoid side-effects
295
- if (!this.director.isPlaying) return;
296
-
290
+
297
291
  this.bind();
298
292
 
299
293
  // if (this._animator && this.director.isPlaying && this.director.weight > 0) this._animator.enabled = false;
src/engine-components/export/usdz/USDZExporter.ts CHANGED
@@ -142,6 +142,8 @@
142
142
  if (!hasProLicense()) name += "-MadeWithNeedle";
143
143
  name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
144
144
 
145
+ if (!this.link) this.link = ensureQuicklookLinkIsCreated(this.context);
146
+
145
147
  // ability to specify a custom USDZ file to be used instead of a dynamic one
146
148
  if (this.customUsdzFile) {
147
149
  if(debug) console.log("Exporting custom usdz", this.customUsdzFile)