1 | import { AnimationAction } from './AnimationAction.js';
|
2 | import { EventDispatcher } from '../core/EventDispatcher.js';
|
3 | import { LinearInterpolant } from '../math/interpolants/LinearInterpolant.js';
|
4 | import { PropertyBinding } from './PropertyBinding.js';
|
5 | import { PropertyMixer } from './PropertyMixer.js';
|
6 | import { AnimationClip } from './AnimationClip.js';
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | function AnimationMixer( root ) {
|
19 |
|
20 | this._root = root;
|
21 | this._initMemoryManager();
|
22 | this._accuIndex = 0;
|
23 |
|
24 | this.time = 0;
|
25 |
|
26 | this.timeScale = 1.0;
|
27 |
|
28 | }
|
29 |
|
30 | AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
|
31 |
|
32 | constructor: AnimationMixer,
|
33 |
|
34 | _bindAction: function ( action, prototypeAction ) {
|
35 |
|
36 | var root = action._localRoot || this._root,
|
37 | tracks = action._clip.tracks,
|
38 | nTracks = tracks.length,
|
39 | bindings = action._propertyBindings,
|
40 | interpolants = action._interpolants,
|
41 | rootUuid = root.uuid,
|
42 | bindingsByRoot = this._bindingsByRootAndName,
|
43 | bindingsByName = bindingsByRoot[ rootUuid ];
|
44 |
|
45 | if ( bindingsByName === undefined ) {
|
46 |
|
47 | bindingsByName = {};
|
48 | bindingsByRoot[ rootUuid ] = bindingsByName;
|
49 |
|
50 | }
|
51 |
|
52 | for ( var i = 0; i !== nTracks; ++ i ) {
|
53 |
|
54 | var track = tracks[ i ],
|
55 | trackName = track.name,
|
56 | binding = bindingsByName[ trackName ];
|
57 |
|
58 | if ( binding !== undefined ) {
|
59 |
|
60 | bindings[ i ] = binding;
|
61 |
|
62 | } else {
|
63 |
|
64 | binding = bindings[ i ];
|
65 |
|
66 | if ( binding !== undefined ) {
|
67 |
|
68 |
|
69 |
|
70 | if ( binding._cacheIndex === null ) {
|
71 |
|
72 | ++ binding.referenceCount;
|
73 | this._addInactiveBinding( binding, rootUuid, trackName );
|
74 |
|
75 | }
|
76 |
|
77 | continue;
|
78 |
|
79 | }
|
80 |
|
81 | var path = prototypeAction && prototypeAction.
|
82 | _propertyBindings[ i ].binding.parsedPath;
|
83 |
|
84 | binding = new PropertyMixer(
|
85 | PropertyBinding.create( root, trackName, path ),
|
86 | track.ValueTypeName, track.getValueSize() );
|
87 |
|
88 | ++ binding.referenceCount;
|
89 | this._addInactiveBinding( binding, rootUuid, trackName );
|
90 |
|
91 | bindings[ i ] = binding;
|
92 |
|
93 | }
|
94 |
|
95 | interpolants[ i ].resultBuffer = binding.buffer;
|
96 |
|
97 | }
|
98 |
|
99 | },
|
100 |
|
101 | _activateAction: function ( action ) {
|
102 |
|
103 | if ( ! this._isActiveAction( action ) ) {
|
104 |
|
105 | if ( action._cacheIndex === null ) {
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | var rootUuid = ( action._localRoot || this._root ).uuid,
|
111 | clipUuid = action._clip.uuid,
|
112 | actionsForClip = this._actionsByClip[ clipUuid ];
|
113 |
|
114 | this._bindAction( action,
|
115 | actionsForClip && actionsForClip.knownActions[ 0 ] );
|
116 |
|
117 | this._addInactiveAction( action, clipUuid, rootUuid );
|
118 |
|
119 | }
|
120 |
|
121 | var bindings = action._propertyBindings;
|
122 |
|
123 |
|
124 | for ( var i = 0, n = bindings.length; i !== n; ++ i ) {
|
125 |
|
126 | var binding = bindings[ i ];
|
127 |
|
128 | if ( binding.useCount ++ === 0 ) {
|
129 |
|
130 | this._lendBinding( binding );
|
131 | binding.saveOriginalState();
|
132 |
|
133 | }
|
134 |
|
135 | }
|
136 |
|
137 | this._lendAction( action );
|
138 |
|
139 | }
|
140 |
|
141 | },
|
142 |
|
143 | _deactivateAction: function ( action ) {
|
144 |
|
145 | if ( this._isActiveAction( action ) ) {
|
146 |
|
147 | var bindings = action._propertyBindings;
|
148 |
|
149 |
|
150 | for ( var i = 0, n = bindings.length; i !== n; ++ i ) {
|
151 |
|
152 | var binding = bindings[ i ];
|
153 |
|
154 | if ( -- binding.useCount === 0 ) {
|
155 |
|
156 | binding.restoreOriginalState();
|
157 | this._takeBackBinding( binding );
|
158 |
|
159 | }
|
160 |
|
161 | }
|
162 |
|
163 | this._takeBackAction( action );
|
164 |
|
165 | }
|
166 |
|
167 | },
|
168 |
|
169 |
|
170 |
|
171 | _initMemoryManager: function () {
|
172 |
|
173 | this._actions = [];
|
174 | this._nActiveActions = 0;
|
175 |
|
176 | this._actionsByClip = {};
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | this._bindings = [];
|
185 | this._nActiveBindings = 0;
|
186 |
|
187 | this._bindingsByRootAndName = {};
|
188 |
|
189 |
|
190 | this._controlInterpolants = [];
|
191 | this._nActiveControlInterpolants = 0;
|
192 |
|
193 | var scope = this;
|
194 |
|
195 | this.stats = {
|
196 |
|
197 | actions: {
|
198 | get total() {
|
199 |
|
200 | return scope._actions.length;
|
201 |
|
202 | },
|
203 | get inUse() {
|
204 |
|
205 | return scope._nActiveActions;
|
206 |
|
207 | }
|
208 | },
|
209 | bindings: {
|
210 | get total() {
|
211 |
|
212 | return scope._bindings.length;
|
213 |
|
214 | },
|
215 | get inUse() {
|
216 |
|
217 | return scope._nActiveBindings;
|
218 |
|
219 | }
|
220 | },
|
221 | controlInterpolants: {
|
222 | get total() {
|
223 |
|
224 | return scope._controlInterpolants.length;
|
225 |
|
226 | },
|
227 | get inUse() {
|
228 |
|
229 | return scope._nActiveControlInterpolants;
|
230 |
|
231 | }
|
232 | }
|
233 |
|
234 | };
|
235 |
|
236 | },
|
237 |
|
238 |
|
239 |
|
240 | _isActiveAction: function ( action ) {
|
241 |
|
242 | var index = action._cacheIndex;
|
243 | return index !== null && index < this._nActiveActions;
|
244 |
|
245 | },
|
246 |
|
247 | _addInactiveAction: function ( action, clipUuid, rootUuid ) {
|
248 |
|
249 | var actions = this._actions,
|
250 | actionsByClip = this._actionsByClip,
|
251 | actionsForClip = actionsByClip[ clipUuid ];
|
252 |
|
253 | if ( actionsForClip === undefined ) {
|
254 |
|
255 | actionsForClip = {
|
256 |
|
257 | knownActions: [ action ],
|
258 | actionByRoot: {}
|
259 |
|
260 | };
|
261 |
|
262 | action._byClipCacheIndex = 0;
|
263 |
|
264 | actionsByClip[ clipUuid ] = actionsForClip;
|
265 |
|
266 | } else {
|
267 |
|
268 | var knownActions = actionsForClip.knownActions;
|
269 |
|
270 | action._byClipCacheIndex = knownActions.length;
|
271 | knownActions.push( action );
|
272 |
|
273 | }
|
274 |
|
275 | action._cacheIndex = actions.length;
|
276 | actions.push( action );
|
277 |
|
278 | actionsForClip.actionByRoot[ rootUuid ] = action;
|
279 |
|
280 | },
|
281 |
|
282 | _removeInactiveAction: function ( action ) {
|
283 |
|
284 | var actions = this._actions,
|
285 | lastInactiveAction = actions[ actions.length - 1 ],
|
286 | cacheIndex = action._cacheIndex;
|
287 |
|
288 | lastInactiveAction._cacheIndex = cacheIndex;
|
289 | actions[ cacheIndex ] = lastInactiveAction;
|
290 | actions.pop();
|
291 |
|
292 | action._cacheIndex = null;
|
293 |
|
294 |
|
295 | var clipUuid = action._clip.uuid,
|
296 | actionsByClip = this._actionsByClip,
|
297 | actionsForClip = actionsByClip[ clipUuid ],
|
298 | knownActionsForClip = actionsForClip.knownActions,
|
299 |
|
300 | lastKnownAction =
|
301 | knownActionsForClip[ knownActionsForClip.length - 1 ],
|
302 |
|
303 | byClipCacheIndex = action._byClipCacheIndex;
|
304 |
|
305 | lastKnownAction._byClipCacheIndex = byClipCacheIndex;
|
306 | knownActionsForClip[ byClipCacheIndex ] = lastKnownAction;
|
307 | knownActionsForClip.pop();
|
308 |
|
309 | action._byClipCacheIndex = null;
|
310 |
|
311 |
|
312 | var actionByRoot = actionsForClip.actionByRoot,
|
313 | rootUuid = ( action._localRoot || this._root ).uuid;
|
314 |
|
315 | delete actionByRoot[ rootUuid ];
|
316 |
|
317 | if ( knownActionsForClip.length === 0 ) {
|
318 |
|
319 | delete actionsByClip[ clipUuid ];
|
320 |
|
321 | }
|
322 |
|
323 | this._removeInactiveBindingsForAction( action );
|
324 |
|
325 | },
|
326 |
|
327 | _removeInactiveBindingsForAction: function ( action ) {
|
328 |
|
329 | var bindings = action._propertyBindings;
|
330 | for ( var i = 0, n = bindings.length; i !== n; ++ i ) {
|
331 |
|
332 | var binding = bindings[ i ];
|
333 |
|
334 | if ( -- binding.referenceCount === 0 ) {
|
335 |
|
336 | this._removeInactiveBinding( binding );
|
337 |
|
338 | }
|
339 |
|
340 | }
|
341 |
|
342 | },
|
343 |
|
344 | _lendAction: function ( action ) {
|
345 |
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 | var actions = this._actions,
|
353 | prevIndex = action._cacheIndex,
|
354 |
|
355 | lastActiveIndex = this._nActiveActions ++,
|
356 |
|
357 | firstInactiveAction = actions[ lastActiveIndex ];
|
358 |
|
359 | action._cacheIndex = lastActiveIndex;
|
360 | actions[ lastActiveIndex ] = action;
|
361 |
|
362 | firstInactiveAction._cacheIndex = prevIndex;
|
363 | actions[ prevIndex ] = firstInactiveAction;
|
364 |
|
365 | },
|
366 |
|
367 | _takeBackAction: function ( action ) {
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 | var actions = this._actions,
|
376 | prevIndex = action._cacheIndex,
|
377 |
|
378 | firstInactiveIndex = -- this._nActiveActions,
|
379 |
|
380 | lastActiveAction = actions[ firstInactiveIndex ];
|
381 |
|
382 | action._cacheIndex = firstInactiveIndex;
|
383 | actions[ firstInactiveIndex ] = action;
|
384 |
|
385 | lastActiveAction._cacheIndex = prevIndex;
|
386 | actions[ prevIndex ] = lastActiveAction;
|
387 |
|
388 | },
|
389 |
|
390 |
|
391 |
|
392 | _addInactiveBinding: function ( binding, rootUuid, trackName ) {
|
393 |
|
394 | var bindingsByRoot = this._bindingsByRootAndName,
|
395 | bindingByName = bindingsByRoot[ rootUuid ],
|
396 |
|
397 | bindings = this._bindings;
|
398 |
|
399 | if ( bindingByName === undefined ) {
|
400 |
|
401 | bindingByName = {};
|
402 | bindingsByRoot[ rootUuid ] = bindingByName;
|
403 |
|
404 | }
|
405 |
|
406 | bindingByName[ trackName ] = binding;
|
407 |
|
408 | binding._cacheIndex = bindings.length;
|
409 | bindings.push( binding );
|
410 |
|
411 | },
|
412 |
|
413 | _removeInactiveBinding: function ( binding ) {
|
414 |
|
415 | var bindings = this._bindings,
|
416 | propBinding = binding.binding,
|
417 | rootUuid = propBinding.rootNode.uuid,
|
418 | trackName = propBinding.path,
|
419 | bindingsByRoot = this._bindingsByRootAndName,
|
420 | bindingByName = bindingsByRoot[ rootUuid ],
|
421 |
|
422 | lastInactiveBinding = bindings[ bindings.length - 1 ],
|
423 | cacheIndex = binding._cacheIndex;
|
424 |
|
425 | lastInactiveBinding._cacheIndex = cacheIndex;
|
426 | bindings[ cacheIndex ] = lastInactiveBinding;
|
427 | bindings.pop();
|
428 |
|
429 | delete bindingByName[ trackName ];
|
430 |
|
431 | remove_empty_map: {
|
432 |
|
433 | for ( var _ in bindingByName ) break remove_empty_map;
|
434 |
|
435 | delete bindingsByRoot[ rootUuid ];
|
436 |
|
437 | }
|
438 |
|
439 | },
|
440 |
|
441 | _lendBinding: function ( binding ) {
|
442 |
|
443 | var bindings = this._bindings,
|
444 | prevIndex = binding._cacheIndex,
|
445 |
|
446 | lastActiveIndex = this._nActiveBindings ++,
|
447 |
|
448 | firstInactiveBinding = bindings[ lastActiveIndex ];
|
449 |
|
450 | binding._cacheIndex = lastActiveIndex;
|
451 | bindings[ lastActiveIndex ] = binding;
|
452 |
|
453 | firstInactiveBinding._cacheIndex = prevIndex;
|
454 | bindings[ prevIndex ] = firstInactiveBinding;
|
455 |
|
456 | },
|
457 |
|
458 | _takeBackBinding: function ( binding ) {
|
459 |
|
460 | var bindings = this._bindings,
|
461 | prevIndex = binding._cacheIndex,
|
462 |
|
463 | firstInactiveIndex = -- this._nActiveBindings,
|
464 |
|
465 | lastActiveBinding = bindings[ firstInactiveIndex ];
|
466 |
|
467 | binding._cacheIndex = firstInactiveIndex;
|
468 | bindings[ firstInactiveIndex ] = binding;
|
469 |
|
470 | lastActiveBinding._cacheIndex = prevIndex;
|
471 | bindings[ prevIndex ] = lastActiveBinding;
|
472 |
|
473 | },
|
474 |
|
475 |
|
476 |
|
477 |
|
478 | _lendControlInterpolant: function () {
|
479 |
|
480 | var interpolants = this._controlInterpolants,
|
481 | lastActiveIndex = this._nActiveControlInterpolants ++,
|
482 | interpolant = interpolants[ lastActiveIndex ];
|
483 |
|
484 | if ( interpolant === undefined ) {
|
485 |
|
486 | interpolant = new LinearInterpolant(
|
487 | new Float32Array( 2 ), new Float32Array( 2 ),
|
488 | 1, this._controlInterpolantsResultBuffer );
|
489 |
|
490 | interpolant.__cacheIndex = lastActiveIndex;
|
491 | interpolants[ lastActiveIndex ] = interpolant;
|
492 |
|
493 | }
|
494 |
|
495 | return interpolant;
|
496 |
|
497 | },
|
498 |
|
499 | _takeBackControlInterpolant: function ( interpolant ) {
|
500 |
|
501 | var interpolants = this._controlInterpolants,
|
502 | prevIndex = interpolant.__cacheIndex,
|
503 |
|
504 | firstInactiveIndex = -- this._nActiveControlInterpolants,
|
505 |
|
506 | lastActiveInterpolant = interpolants[ firstInactiveIndex ];
|
507 |
|
508 | interpolant.__cacheIndex = firstInactiveIndex;
|
509 | interpolants[ firstInactiveIndex ] = interpolant;
|
510 |
|
511 | lastActiveInterpolant.__cacheIndex = prevIndex;
|
512 | interpolants[ prevIndex ] = lastActiveInterpolant;
|
513 |
|
514 | },
|
515 |
|
516 | _controlInterpolantsResultBuffer: new Float32Array( 1 ),
|
517 |
|
518 |
|
519 |
|
520 |
|
521 | clipAction: function ( clip, optionalRoot ) {
|
522 |
|
523 | var root = optionalRoot || this._root,
|
524 | rootUuid = root.uuid,
|
525 |
|
526 | clipObject = typeof clip === 'string' ?
|
527 | AnimationClip.findByName( root, clip ) : clip,
|
528 |
|
529 | clipUuid = clipObject !== null ? clipObject.uuid : clip,
|
530 |
|
531 | actionsForClip = this._actionsByClip[ clipUuid ],
|
532 | prototypeAction = null;
|
533 |
|
534 | if ( actionsForClip !== undefined ) {
|
535 |
|
536 | var existingAction =
|
537 | actionsForClip.actionByRoot[ rootUuid ];
|
538 |
|
539 | if ( existingAction !== undefined ) {
|
540 |
|
541 | return existingAction;
|
542 |
|
543 | }
|
544 |
|
545 |
|
546 |
|
547 | prototypeAction = actionsForClip.knownActions[ 0 ];
|
548 |
|
549 |
|
550 | if ( clipObject === null )
|
551 | clipObject = prototypeAction._clip;
|
552 |
|
553 | }
|
554 |
|
555 |
|
556 | if ( clipObject === null ) return null;
|
557 |
|
558 |
|
559 | var newAction = new AnimationAction( this, clipObject, optionalRoot );
|
560 |
|
561 | this._bindAction( newAction, prototypeAction );
|
562 |
|
563 |
|
564 | this._addInactiveAction( newAction, clipUuid, rootUuid );
|
565 |
|
566 | return newAction;
|
567 |
|
568 | },
|
569 |
|
570 |
|
571 | existingAction: function ( clip, optionalRoot ) {
|
572 |
|
573 | var root = optionalRoot || this._root,
|
574 | rootUuid = root.uuid,
|
575 |
|
576 | clipObject = typeof clip === 'string' ?
|
577 | AnimationClip.findByName( root, clip ) : clip,
|
578 |
|
579 | clipUuid = clipObject ? clipObject.uuid : clip,
|
580 |
|
581 | actionsForClip = this._actionsByClip[ clipUuid ];
|
582 |
|
583 | if ( actionsForClip !== undefined ) {
|
584 |
|
585 | return actionsForClip.actionByRoot[ rootUuid ] || null;
|
586 |
|
587 | }
|
588 |
|
589 | return null;
|
590 |
|
591 | },
|
592 |
|
593 |
|
594 | stopAllAction: function () {
|
595 |
|
596 | var actions = this._actions,
|
597 | nActions = this._nActiveActions,
|
598 | bindings = this._bindings,
|
599 | nBindings = this._nActiveBindings;
|
600 |
|
601 | this._nActiveActions = 0;
|
602 | this._nActiveBindings = 0;
|
603 |
|
604 | for ( var i = 0; i !== nActions; ++ i ) {
|
605 |
|
606 | actions[ i ].reset();
|
607 |
|
608 | }
|
609 |
|
610 | for ( var i = 0; i !== nBindings; ++ i ) {
|
611 |
|
612 | bindings[ i ].useCount = 0;
|
613 |
|
614 | }
|
615 |
|
616 | return this;
|
617 |
|
618 | },
|
619 |
|
620 |
|
621 | update: function ( deltaTime ) {
|
622 |
|
623 | deltaTime *= this.timeScale;
|
624 |
|
625 | var actions = this._actions,
|
626 | nActions = this._nActiveActions,
|
627 |
|
628 | time = this.time += deltaTime,
|
629 | timeDirection = Math.sign( deltaTime ),
|
630 |
|
631 | accuIndex = this._accuIndex ^= 1;
|
632 |
|
633 |
|
634 |
|
635 | for ( var i = 0; i !== nActions; ++ i ) {
|
636 |
|
637 | var action = actions[ i ];
|
638 |
|
639 | action._update( time, deltaTime, timeDirection, accuIndex );
|
640 |
|
641 | }
|
642 |
|
643 |
|
644 |
|
645 | var bindings = this._bindings,
|
646 | nBindings = this._nActiveBindings;
|
647 |
|
648 | for ( var i = 0; i !== nBindings; ++ i ) {
|
649 |
|
650 | bindings[ i ].apply( accuIndex );
|
651 |
|
652 | }
|
653 |
|
654 | return this;
|
655 |
|
656 | },
|
657 |
|
658 |
|
659 | getRoot: function () {
|
660 |
|
661 | return this._root;
|
662 |
|
663 | },
|
664 |
|
665 |
|
666 | uncacheClip: function ( clip ) {
|
667 |
|
668 | var actions = this._actions,
|
669 | clipUuid = clip.uuid,
|
670 | actionsByClip = this._actionsByClip,
|
671 | actionsForClip = actionsByClip[ clipUuid ];
|
672 |
|
673 | if ( actionsForClip !== undefined ) {
|
674 |
|
675 |
|
676 |
|
677 |
|
678 |
|
679 | var actionsToRemove = actionsForClip.knownActions;
|
680 |
|
681 | for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) {
|
682 |
|
683 | var action = actionsToRemove[ i ];
|
684 |
|
685 | this._deactivateAction( action );
|
686 |
|
687 | var cacheIndex = action._cacheIndex,
|
688 | lastInactiveAction = actions[ actions.length - 1 ];
|
689 |
|
690 | action._cacheIndex = null;
|
691 | action._byClipCacheIndex = null;
|
692 |
|
693 | lastInactiveAction._cacheIndex = cacheIndex;
|
694 | actions[ cacheIndex ] = lastInactiveAction;
|
695 | actions.pop();
|
696 |
|
697 | this._removeInactiveBindingsForAction( action );
|
698 |
|
699 | }
|
700 |
|
701 | delete actionsByClip[ clipUuid ];
|
702 |
|
703 | }
|
704 |
|
705 | },
|
706 |
|
707 |
|
708 | uncacheRoot: function ( root ) {
|
709 |
|
710 | var rootUuid = root.uuid,
|
711 | actionsByClip = this._actionsByClip;
|
712 |
|
713 | for ( var clipUuid in actionsByClip ) {
|
714 |
|
715 | var actionByRoot = actionsByClip[ clipUuid ].actionByRoot,
|
716 | action = actionByRoot[ rootUuid ];
|
717 |
|
718 | if ( action !== undefined ) {
|
719 |
|
720 | this._deactivateAction( action );
|
721 | this._removeInactiveAction( action );
|
722 |
|
723 | }
|
724 |
|
725 | }
|
726 |
|
727 | var bindingsByRoot = this._bindingsByRootAndName,
|
728 | bindingByName = bindingsByRoot[ rootUuid ];
|
729 |
|
730 | if ( bindingByName !== undefined ) {
|
731 |
|
732 | for ( var trackName in bindingByName ) {
|
733 |
|
734 | var binding = bindingByName[ trackName ];
|
735 | binding.restoreOriginalState();
|
736 | this._removeInactiveBinding( binding );
|
737 |
|
738 | }
|
739 |
|
740 | }
|
741 |
|
742 | },
|
743 |
|
744 |
|
745 | uncacheAction: function ( clip, optionalRoot ) {
|
746 |
|
747 | var action = this.existingAction( clip, optionalRoot );
|
748 |
|
749 | if ( action !== null ) {
|
750 |
|
751 | this._deactivateAction( action );
|
752 | this._removeInactiveAction( action );
|
753 |
|
754 | }
|
755 |
|
756 | }
|
757 |
|
758 | } );
|
759 |
|
760 |
|
761 | export { AnimationMixer };
|