@preference-sl/pref-viewer 2.11.0-beta.3 → 2.11.0-beta.4
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/package.json +1 -2
- package/src/babylonjs-animation-controller.js +51 -392
- package/src/babylonjs-animation-opening-menu.js +360 -0
- package/src/babylonjs-animation-opening.js +496 -0
- package/src/babylonjs-controller.js +11 -10
- package/src/pref-viewer-3d.js +1 -1
- package/src/images/icon-pause.svg +0 -1
- package/src/images/icon-play-backwards.svg +0 -1
- package/src/images/icon-play.svg +0 -1
- package/src/images/icon-skip-backward.svg +0 -1
- package/src/images/icon-skip-forward.svg +0 -1
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import { AdvancedDynamicTexture } from "@babylonjs/gui";
|
|
2
|
+
import { OpeningAnimationMenu } from "./babylonjs-animation-opening-menu.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* OpeningAnimation - Manages open/close animations for a model part (e.g., a door) in a Babylon.js scene.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Controls playback of opening and closing AnimationGroups.
|
|
9
|
+
* - Tracks animation state (paused, closed, opened, opening, closing).
|
|
10
|
+
* - Synchronizes animation progress and UI controls.
|
|
11
|
+
* - Handles loop mode and progress threshold logic.
|
|
12
|
+
* - Provides methods for play, pause, go to opened/closed, and progress control.
|
|
13
|
+
* - Manages the animation control menu (OpeningAnimationMenu) and its callbacks.
|
|
14
|
+
*
|
|
15
|
+
* Public Methods:
|
|
16
|
+
* - isAnimationForNode(node): Checks if the animation affects the given node.
|
|
17
|
+
* - playOpen(): Starts the opening animation.
|
|
18
|
+
* - playClose(): Starts the closing animation.
|
|
19
|
+
* - pause(): Pauses the current animation.
|
|
20
|
+
* - goToOpened(): Moves animation to the fully opened state.
|
|
21
|
+
* - goToClosed(): Moves animation to the fully closed state.
|
|
22
|
+
* - showControls(advancedDynamicTexture): Displays the animation control menu.
|
|
23
|
+
* - hideControls(): Hides the animation control menu.
|
|
24
|
+
* - isControlsVisible(): Returns true if the control menu is visible for this animation.
|
|
25
|
+
*
|
|
26
|
+
* Public Properties:
|
|
27
|
+
* - state: Returns the current animation state.
|
|
28
|
+
*
|
|
29
|
+
* Private Methods:
|
|
30
|
+
* - #getNodesFromAnimationGroups(): Collects node IDs affected by the animation groups.
|
|
31
|
+
* - #onOpened(): Handles end of opening animation.
|
|
32
|
+
* - #onClosed(): Handles end of closing animation.
|
|
33
|
+
* - #goToOpened(useLoop): Sets state to opened and optionally loops to close.
|
|
34
|
+
* - #goToClosed(useLoop): Sets state to closed and optionally loops to open.
|
|
35
|
+
* - #goToFrameAndPause(frame): Moves to a specific frame and pauses.
|
|
36
|
+
* - #getCurrentFrame(): Gets the current frame based on state.
|
|
37
|
+
* - #getFrameFromProgress(progress): Calculates frame from progress value.
|
|
38
|
+
* - #getProgress(): Calculates progress (0-1) from current frame.
|
|
39
|
+
* - #checkProgress(progress): Applies threshold logic to progress.
|
|
40
|
+
* - #updateControlsSlider(): Updates the slider in the control menu.
|
|
41
|
+
* - #updateControls(): Updates all controls in the menu.
|
|
42
|
+
*
|
|
43
|
+
* Usage Example:
|
|
44
|
+
* const anim = new OpeningAnimation("door", openGroup, closeGroup);
|
|
45
|
+
* anim.playOpen();
|
|
46
|
+
* anim.pause();
|
|
47
|
+
* anim.goToClosed();
|
|
48
|
+
* anim.showControls(adt);
|
|
49
|
+
* anim.hideControls();
|
|
50
|
+
*/
|
|
51
|
+
export class OpeningAnimation {
|
|
52
|
+
static states = {
|
|
53
|
+
paused: 0,
|
|
54
|
+
closed: 1,
|
|
55
|
+
opened: 2,
|
|
56
|
+
opening: 3,
|
|
57
|
+
closing: 4,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
#openAnimation = null;
|
|
61
|
+
#closeAnimation = null;
|
|
62
|
+
|
|
63
|
+
#nodes = [];
|
|
64
|
+
#state = OpeningAnimation.states.closed;
|
|
65
|
+
#lastPausedFrame = 0;
|
|
66
|
+
#startFrame = 0;
|
|
67
|
+
#endFrame = 0;
|
|
68
|
+
#speedRatio = 1.0;
|
|
69
|
+
#loop = false;
|
|
70
|
+
|
|
71
|
+
#advancedDynamicTexture = null;
|
|
72
|
+
#menu = null;
|
|
73
|
+
#progressThreshold = 0.025;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Creates a new OpeningAnimation instance for managing open/close animations of a model part.
|
|
77
|
+
* @param {string} name - The identifier for this animation (e.g., door name).
|
|
78
|
+
* @param {AnimationGroup} openAnimationGroup - Babylon.js AnimationGroup for the opening animation.
|
|
79
|
+
* @param {AnimationGroup} closeAnimationGroup - Babylon.js AnimationGroup for the closing animation.
|
|
80
|
+
* @description
|
|
81
|
+
* Initializes internal state, sets up frame ranges, collects affected nodes, and attaches end-of-animation observers.
|
|
82
|
+
*/
|
|
83
|
+
constructor(name, openAnimationGroup, closeAnimationGroup) {
|
|
84
|
+
this.name = name;
|
|
85
|
+
this.#openAnimation = openAnimationGroup;
|
|
86
|
+
this.#closeAnimation = closeAnimationGroup;
|
|
87
|
+
|
|
88
|
+
this.#openAnimation.stop();
|
|
89
|
+
this.#openAnimation._loopAnimation = false;
|
|
90
|
+
this.#closeAnimation.stop();
|
|
91
|
+
this.#closeAnimation._loopAnimation = false;
|
|
92
|
+
|
|
93
|
+
this.#startFrame = this.#openAnimation.from;
|
|
94
|
+
this.#endFrame = this.#openAnimation.to;
|
|
95
|
+
this.#speedRatio = this.#openAnimation.speedRatio || 1.0;
|
|
96
|
+
|
|
97
|
+
this.#getNodesFromAnimationGroups();
|
|
98
|
+
this.#openAnimation.onAnimationGroupEndObservable.add(this.#onOpened.bind(this));
|
|
99
|
+
this.#closeAnimation.onAnimationGroupEndObservable.add(this.#onClosed.bind(this));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Collects node IDs affected by the opening and closing animation groups.
|
|
104
|
+
* Populates the #nodes array with unique node identifiers.
|
|
105
|
+
* @private
|
|
106
|
+
*/
|
|
107
|
+
#getNodesFromAnimationGroups() {
|
|
108
|
+
[this.#openAnimation, this.#closeAnimation].forEach((animationGroup) => {
|
|
109
|
+
animationGroup._targetedAnimations.forEach((targetedAnimation) => {
|
|
110
|
+
if (!this.#nodes.includes(targetedAnimation.target.id)) {
|
|
111
|
+
this.#nodes.push(targetedAnimation.target.id);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handles the end of the opening animation group.
|
|
119
|
+
* @private
|
|
120
|
+
*/
|
|
121
|
+
#onOpened() {
|
|
122
|
+
this.#goToOpened(true);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Handles the end of the closing animation group.
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
#onClosed() {
|
|
130
|
+
this.#goToClosed(true);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Moves the animation to the fully opened state, optionally looping to close.
|
|
135
|
+
* @private
|
|
136
|
+
* @param {boolean} useLoop - If true, starts closing animation after opening.
|
|
137
|
+
*/
|
|
138
|
+
#goToOpened(useLoop = false) {
|
|
139
|
+
this.#lastPausedFrame = this.#endFrame;
|
|
140
|
+
|
|
141
|
+
if (this.#openAnimation._isStarted && !this.#openAnimation._isPaused) {
|
|
142
|
+
this.#openAnimation.pause();
|
|
143
|
+
}
|
|
144
|
+
this.#openAnimation.goToFrame(this.#endFrame);
|
|
145
|
+
|
|
146
|
+
this.#closeAnimation.pause();
|
|
147
|
+
this.#closeAnimation.goToFrame(this.#startFrame);
|
|
148
|
+
|
|
149
|
+
this.#state = OpeningAnimation.states.opened;
|
|
150
|
+
this.#updateControls();
|
|
151
|
+
|
|
152
|
+
if (this.#loop && useLoop) {
|
|
153
|
+
this.playClose();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Moves the animation to the fully closed state, optionally looping to open.
|
|
159
|
+
* @private
|
|
160
|
+
* @param {boolean} useLoop - If true, starts opening animation after closing.
|
|
161
|
+
*/
|
|
162
|
+
#goToClosed(useLoop = false) {
|
|
163
|
+
this.#lastPausedFrame = this.#startFrame;
|
|
164
|
+
|
|
165
|
+
if (this.#closeAnimation._isStarted && !this.#closeAnimation._isPaused) {
|
|
166
|
+
this.#closeAnimation.pause();
|
|
167
|
+
}
|
|
168
|
+
this.#closeAnimation.goToFrame(this.#endFrame - this.#startFrame);
|
|
169
|
+
|
|
170
|
+
this.#openAnimation.pause();
|
|
171
|
+
this.#openAnimation.goToFrame(this.#startFrame);
|
|
172
|
+
|
|
173
|
+
this.#state = OpeningAnimation.states.closed;
|
|
174
|
+
this.#updateControls();
|
|
175
|
+
|
|
176
|
+
if (this.#loop && useLoop) {
|
|
177
|
+
this.playOpen();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Moves the animation to a specific frame and pauses.
|
|
183
|
+
* @private
|
|
184
|
+
* @param {number} frame - The frame to go to and pause.
|
|
185
|
+
*/
|
|
186
|
+
#goToFrameAndPause(frame) {
|
|
187
|
+
if (!frame) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (this.#state === OpeningAnimation.states.opening) {
|
|
192
|
+
this.#openAnimation.pause();
|
|
193
|
+
}
|
|
194
|
+
if (this.#state === OpeningAnimation.states.closing) {
|
|
195
|
+
this.#closeAnimation.pause();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (this.#openAnimation._isStarted && this.#openAnimation._isPaused) {
|
|
199
|
+
this.#openAnimation.goToFrame(frame);
|
|
200
|
+
} else {
|
|
201
|
+
this.#openAnimation.start();
|
|
202
|
+
this.#openAnimation.pause();
|
|
203
|
+
this.#openAnimation.goToFrame(frame);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.#lastPausedFrame = frame;
|
|
207
|
+
this.#state = OpeningAnimation.states.paused;
|
|
208
|
+
this.#updateControls();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Gets the current frame of the animation based on its state.
|
|
213
|
+
* @private
|
|
214
|
+
* @returns {number} The current frame.
|
|
215
|
+
*/
|
|
216
|
+
#getCurrentFrame() {
|
|
217
|
+
let currentFrame;
|
|
218
|
+
if (this.#state === OpeningAnimation.states.opening) {
|
|
219
|
+
currentFrame = this.#openAnimation.getCurrentFrame();
|
|
220
|
+
} else if (this.#state === OpeningAnimation.states.closing) {
|
|
221
|
+
currentFrame = this.#endFrame - this.#closeAnimation.getCurrentFrame();
|
|
222
|
+
} else {
|
|
223
|
+
currentFrame = this.#lastPausedFrame;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Ensure currentFrame is within startFrame and endFrame
|
|
227
|
+
if (currentFrame < this.#startFrame) {
|
|
228
|
+
currentFrame = this.#startFrame;
|
|
229
|
+
} else if (currentFrame > this.#endFrame) {
|
|
230
|
+
currentFrame = this.#endFrame;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return currentFrame;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Calculates the frame number from a normalized progress value (0-1).
|
|
238
|
+
* @private
|
|
239
|
+
* @param {number} progress - Progress value between 0 and 1.
|
|
240
|
+
* @returns {number} The corresponding frame.
|
|
241
|
+
*/
|
|
242
|
+
#getFrameFromProgress(progress) {
|
|
243
|
+
const frame = this.#startFrame + (this.#endFrame - this.#startFrame) * progress;
|
|
244
|
+
return frame;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Calculates the normalized progress (0-1) from the current frame.
|
|
249
|
+
* @private
|
|
250
|
+
* @returns {number} Progress value.
|
|
251
|
+
*/
|
|
252
|
+
#getProgress() {
|
|
253
|
+
const currentFrame = this.#getCurrentFrame();
|
|
254
|
+
const progress = (currentFrame - this.#startFrame) / (this.#endFrame - this.#startFrame);
|
|
255
|
+
return progress;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Applies threshold logic to the progress value to snap to 0 or 1 if near the ends.
|
|
260
|
+
* Prevents floating point errors from leaving the animation in an in-between state.
|
|
261
|
+
* @private
|
|
262
|
+
* @param {number} progress - Progress value.
|
|
263
|
+
* @returns {number} Thresholded progress.
|
|
264
|
+
*/
|
|
265
|
+
#checkProgress(progress) {
|
|
266
|
+
if (progress <= this.#progressThreshold) {
|
|
267
|
+
progress = 0;
|
|
268
|
+
} else if (progress >= 1 - this.#progressThreshold) {
|
|
269
|
+
progress = 1;
|
|
270
|
+
}
|
|
271
|
+
return progress;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Updates the slider value in the animation control menu to match the current progress.
|
|
276
|
+
* @private
|
|
277
|
+
*/
|
|
278
|
+
#updateControlsSlider() {
|
|
279
|
+
if (!this.isControlsVisible()) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
this.#menu.animationProgress = this.#getProgress();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Updates all controls in the animation menu (buttons, slider) to reflect the current state and progress.
|
|
287
|
+
* @private
|
|
288
|
+
*/
|
|
289
|
+
#updateControls() {
|
|
290
|
+
if (!this.isControlsVisible()) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!this.#menu) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
this.#menu.animationState = this.#state;
|
|
297
|
+
this.#menu.animationProgress = this.#getProgress();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* ---------------------------
|
|
302
|
+
* Public methods
|
|
303
|
+
* ---------------------------
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Checks if the animation affects the given node.
|
|
308
|
+
* @param {string} node - Node identifier.
|
|
309
|
+
* @returns {boolean}
|
|
310
|
+
*/
|
|
311
|
+
isAnimationForNode(node) {
|
|
312
|
+
return this.#nodes.includes(node);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Starts the opening animation.
|
|
317
|
+
* @public
|
|
318
|
+
*/
|
|
319
|
+
playOpen() {
|
|
320
|
+
if (this.#state === OpeningAnimation.states.opening || this.#state === OpeningAnimation.states.opened) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (this.#state === OpeningAnimation.states.closing) {
|
|
324
|
+
this.#lastPausedFrame = this.#endFrame - this.#closeAnimation.getCurrentFrame();
|
|
325
|
+
this.#closeAnimation.pause();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (this.#openAnimation._isStarted && this.#openAnimation._isPaused) {
|
|
329
|
+
this.#openAnimation.goToFrame(this.#lastPausedFrame);
|
|
330
|
+
this.#openAnimation.restart();
|
|
331
|
+
} else {
|
|
332
|
+
this.#openAnimation.start(false, this.#speedRatio, this.#lastPausedFrame, this.#endFrame, undefined);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
this.#state = OpeningAnimation.states.opening;
|
|
336
|
+
this.#updateControls();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Starts the closing animation.
|
|
341
|
+
* @public
|
|
342
|
+
*/
|
|
343
|
+
playClose() {
|
|
344
|
+
if (this.#state === OpeningAnimation.states.closing || this.#state === OpeningAnimation.states.closed) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (this.#state === OpeningAnimation.states.opening) {
|
|
348
|
+
this.#lastPausedFrame = this.#openAnimation.getCurrentFrame();
|
|
349
|
+
this.#openAnimation.pause();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (this.#closeAnimation._isStarted && this.#closeAnimation._isPaused) {
|
|
353
|
+
this.#closeAnimation.goToFrame(this.#endFrame - this.#lastPausedFrame);
|
|
354
|
+
this.#closeAnimation.restart();
|
|
355
|
+
} else {
|
|
356
|
+
this.#closeAnimation.start(false, this.#speedRatio, this.#endFrame - this.#lastPausedFrame, this.#endFrame, undefined);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this.#state = OpeningAnimation.states.closing;
|
|
360
|
+
this.#updateControls();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Pauses the current animation.
|
|
365
|
+
* @public
|
|
366
|
+
*/
|
|
367
|
+
pause() {
|
|
368
|
+
if (this.#state === OpeningAnimation.states.opening) {
|
|
369
|
+
this.#lastPausedFrame = this.#openAnimation.getCurrentFrame();
|
|
370
|
+
this.#openAnimation.pause();
|
|
371
|
+
}
|
|
372
|
+
if (this.#state === OpeningAnimation.states.closing) {
|
|
373
|
+
this.#lastPausedFrame = this.#endFrame - this.#closeAnimation.getCurrentFrame();
|
|
374
|
+
this.#closeAnimation.pause();
|
|
375
|
+
}
|
|
376
|
+
this.#state = OpeningAnimation.states.paused;
|
|
377
|
+
this.#updateControls();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Moves animation to the fully opened state.
|
|
382
|
+
* @public
|
|
383
|
+
*/
|
|
384
|
+
goToOpened() {
|
|
385
|
+
this.#goToOpened(false);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Moves animation to the fully closed state.
|
|
390
|
+
* @public
|
|
391
|
+
*/
|
|
392
|
+
goToClosed() {
|
|
393
|
+
this.#goToClosed(false);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Displays the animation control menu and sets up callbacks.
|
|
398
|
+
* Synchronizes slider and button states with animation.
|
|
399
|
+
* @public
|
|
400
|
+
* @param {AdvancedDynamicTexture} advancedDynamicTexture - Babylon.js GUI texture.
|
|
401
|
+
*/
|
|
402
|
+
showControls(advancedDynamicTexture) {
|
|
403
|
+
this.#advancedDynamicTexture = advancedDynamicTexture;
|
|
404
|
+
this.#advancedDynamicTexture.metadata = { animationName: this.name };
|
|
405
|
+
const controlCallbacks = {
|
|
406
|
+
onGoToOpened: () => {
|
|
407
|
+
if (this.#state === OpeningAnimation.states.opened) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
this.goToOpened();
|
|
411
|
+
},
|
|
412
|
+
onOpen: () => {
|
|
413
|
+
if (this.#state === OpeningAnimation.states.opened || this.#state === OpeningAnimation.states.opening) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
this.playOpen();
|
|
417
|
+
},
|
|
418
|
+
onPause: () => {
|
|
419
|
+
if (this.#state === OpeningAnimation.states.paused || this.#state === OpeningAnimation.states.closed || this.#state === OpeningAnimation.states.opened) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
this.pause();
|
|
423
|
+
},
|
|
424
|
+
onClose: () => {
|
|
425
|
+
if (this.#state === OpeningAnimation.states.closed || this.#state === OpeningAnimation.states.closing) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
this.playClose();
|
|
429
|
+
},
|
|
430
|
+
onGoToClosed: () => {
|
|
431
|
+
if (this.#state === OpeningAnimation.states.closed) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
this.goToClosed();
|
|
435
|
+
},
|
|
436
|
+
onSetAnimationProgress: (progress) => {
|
|
437
|
+
progress = this.#checkProgress(progress);
|
|
438
|
+
if (progress === 0) {
|
|
439
|
+
this.goToClosed();
|
|
440
|
+
} else if (progress === 1) {
|
|
441
|
+
this.goToOpened();
|
|
442
|
+
} else {
|
|
443
|
+
const frame = this.#getFrameFromProgress(progress);
|
|
444
|
+
this.#goToFrameAndPause(frame);
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
onToggleLoop: () => {
|
|
448
|
+
this.#loop = !this.#loop;
|
|
449
|
+
this.#menu.animationLoop = this.#loop;
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
this.#menu = new OpeningAnimationMenu(this.#advancedDynamicTexture, this.#state, this.#getProgress(), this.#loop, controlCallbacks);
|
|
453
|
+
|
|
454
|
+
// Attach to Babylon.js scene render loop for real-time updates
|
|
455
|
+
this.#openAnimation._scene.onBeforeRenderObservable.add(this.#updateControlsSlider.bind(this));
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Hides the animation control menu and removes observers.
|
|
460
|
+
* @public
|
|
461
|
+
*/
|
|
462
|
+
hideControls() {
|
|
463
|
+
if (!this.isControlsVisible()) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
// Remove the observer when controls are hidden
|
|
467
|
+
this.#openAnimation._scene.onBeforeRenderObservable.removeCallback(this.#updateControlsSlider.bind(this));
|
|
468
|
+
this.#advancedDynamicTexture.dispose();
|
|
469
|
+
this.#advancedDynamicTexture = null;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Checks if the animation controls menu is currently visible for this animation instance.
|
|
474
|
+
* @public
|
|
475
|
+
* @returns {boolean} True if controls are visible for this animation; otherwise, false.
|
|
476
|
+
*/
|
|
477
|
+
isControlsVisible() {
|
|
478
|
+
return !!(this.#advancedDynamicTexture && this.#advancedDynamicTexture.metadata?.animationName === this.name && this.#menu);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* ---------------------------
|
|
483
|
+
* Public properties
|
|
484
|
+
* ---------------------------
|
|
485
|
+
*/
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Returns the current animation state.
|
|
489
|
+
* @public
|
|
490
|
+
* @returns {number}
|
|
491
|
+
*/
|
|
492
|
+
|
|
493
|
+
get state() {
|
|
494
|
+
return this.#state;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ArcRotateCamera, AssetContainer, Color4, DirectionalLight, Engine, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, LoadAssetContainerAsync, MeshBuilder, PointLight, Scene, ShadowGenerator, Tools, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager } from "@babylonjs/core";
|
|
2
2
|
import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompression";
|
|
3
3
|
import "@babylonjs/loaders";
|
|
4
4
|
import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
|
|
5
5
|
import { USDZExportAsync, GLTF2Export } from "@babylonjs/serializers";
|
|
6
6
|
|
|
7
7
|
import GLTFResolver from "./gltf-resolver.js";
|
|
8
|
+
import { ContainerData, MaterialData } from "./pref-viewer-3d-data.js";
|
|
8
9
|
import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -417,7 +418,7 @@ export default class BabylonJSController {
|
|
|
417
418
|
/**
|
|
418
419
|
* Applies material options from the configuration to the relevant meshes.
|
|
419
420
|
* @private
|
|
420
|
-
* @param {
|
|
421
|
+
* @param {MaterialData} optionMaterial - Material option containing value, nodePrefixes, nodeNames, and state.
|
|
421
422
|
* @returns {boolean} True if any mesh material was set, false otherwise.
|
|
422
423
|
*/
|
|
423
424
|
#setOptionsMaterial(optionMaterial) {
|
|
@@ -545,7 +546,7 @@ export default class BabylonJSController {
|
|
|
545
546
|
* Finds and returns the asset container object by its name.
|
|
546
547
|
* @private
|
|
547
548
|
* @param {string} name - The name of the container to find.
|
|
548
|
-
* @returns {
|
|
549
|
+
* @returns {ContainerData|null} The matching container object, or null if not found.
|
|
549
550
|
*/
|
|
550
551
|
#findContainerByName(name) {
|
|
551
552
|
return Object.values(this.#containers).find((container) => container.state.name === name) || null;
|
|
@@ -598,7 +599,7 @@ export default class BabylonJSController {
|
|
|
598
599
|
/**
|
|
599
600
|
* Adds the asset container to the Babylon.js scene if it should be shown and is not already visible.
|
|
600
601
|
* @private
|
|
601
|
-
* @param {
|
|
602
|
+
* @param {ContainerData} container - The container object containing asset state and metadata.
|
|
602
603
|
* @returns {boolean} True if the container was added, false otherwise.
|
|
603
604
|
*/
|
|
604
605
|
#addContainer(container) {
|
|
@@ -614,7 +615,7 @@ export default class BabylonJSController {
|
|
|
614
615
|
/**
|
|
615
616
|
* Removes the asset container from the Babylon.js scene if it is currently visible.
|
|
616
617
|
* @private
|
|
617
|
-
* @param {
|
|
618
|
+
* @param {ContainerData} container - The container object containing asset state and metadata.
|
|
618
619
|
* @returns {boolean} True if the container was removed, false otherwise.
|
|
619
620
|
*/
|
|
620
621
|
#removeContainer(container) {
|
|
@@ -630,8 +631,8 @@ export default class BabylonJSController {
|
|
|
630
631
|
/**
|
|
631
632
|
* Replaces the asset container in the Babylon.js scene with a new one.
|
|
632
633
|
* @private
|
|
633
|
-
* @param {
|
|
634
|
-
* @param {
|
|
634
|
+
* @param {ContainerData} container - The container object containing asset state and metadata.
|
|
635
|
+
* @param {AssetContainer} newAssetContainer - The new asset container to add to the scene.
|
|
635
636
|
* @returns {boolean} True if the container was replaced and added, false otherwise.
|
|
636
637
|
*/
|
|
637
638
|
#replaceContainer(container, newAssetContainer) {
|
|
@@ -682,8 +683,8 @@ export default class BabylonJSController {
|
|
|
682
683
|
/**
|
|
683
684
|
* Loads an asset container (model, environment, materials, etc.) using the provided container state.
|
|
684
685
|
* @private
|
|
685
|
-
* @param {
|
|
686
|
-
* @returns {Promise<[
|
|
686
|
+
* @param {ContainerData} container - The container object containing asset state and metadata.
|
|
687
|
+
* @returns {Promise<[ContainerData, AssetContainer|boolean]>} Resolves to an array with the container and the loaded asset container, or false if loading fails.
|
|
687
688
|
* @description
|
|
688
689
|
* Resolves the asset source using GLTFResolver, prepares plugin options, and loads the asset into the Babylon.js scene.
|
|
689
690
|
* Updates the container's cache data and returns the container along with the loaded asset container or false if loading fails.
|
|
@@ -756,7 +757,7 @@ export default class BabylonJSController {
|
|
|
756
757
|
}
|
|
757
758
|
const replacedAndAdded = this.#replaceContainer(container, assetContainer);
|
|
758
759
|
if (replacedAndAdded && container.state.name === "model") {
|
|
759
|
-
this.#babylonJSAnimationController = new BabylonJSAnimationController(this.#scene
|
|
760
|
+
this.#babylonJSAnimationController = new BabylonJSAnimationController(this.#scene);
|
|
760
761
|
}
|
|
761
762
|
container.state.setSuccess(true);
|
|
762
763
|
} else {
|
package/src/pref-viewer-3d.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg id="pause" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ffffff"><path d="M14,19H18V5H14M6,19H10V5H6V19Z" /></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg id="play_backwards" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ffffff"><path d="M16,18.86V4.86l-11,7,11,7Z" /></svg>
|
package/src/images/icon-play.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg id="play" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ffffff"><path d="M8,5.14v14l11-7-11-7Z" /></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg id="skip-backward" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ffffff"><path d="M20,5V19L13,12M6,5V19H4V5M13,5V19L6,12" /></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg id="skip-forward" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ffffff"><path d="M4,5V19L11,12M18,5V19H20V5M11,5V19L18,12" /></svg>
|