Spaces:
Running
Running
| import { PropertyBinding } from './PropertyBinding.js'; | |
| import { _Math } from '../math/Math.js'; | |
| /** | |
| * | |
| * A group of objects that receives a shared animation state. | |
| * | |
| * Usage: | |
| * | |
| * - Add objects you would otherwise pass as 'root' to the | |
| * constructor or the .clipAction method of AnimationMixer. | |
| * | |
| * - Instead pass this object as 'root'. | |
| * | |
| * - You can also add and remove objects later when the mixer | |
| * is running. | |
| * | |
| * Note: | |
| * | |
| * Objects of this class appear as one object to the mixer, | |
| * so cache control of the individual objects must be done | |
| * on the group. | |
| * | |
| * Limitation: | |
| * | |
| * - The animated properties must be compatible among the | |
| * all objects in the group. | |
| * | |
| * - A single property can either be controlled through a | |
| * target group or directly, but not both. | |
| * | |
| * @author tschw | |
| */ | |
| function AnimationObjectGroup() { | |
| this.uuid = _Math.generateUUID(); | |
| // cached objects followed by the active ones | |
| this._objects = Array.prototype.slice.call( arguments ); | |
| this.nCachedObjects_ = 0; // threshold | |
| // note: read by PropertyBinding.Composite | |
| var indices = {}; | |
| this._indicesByUUID = indices; // for bookkeeping | |
| for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
| indices[ arguments[ i ].uuid ] = i; | |
| } | |
| this._paths = []; // inside: string | |
| this._parsedPaths = []; // inside: { we don't care, here } | |
| this._bindings = []; // inside: Array< PropertyBinding > | |
| this._bindingsIndicesByPath = {}; // inside: indices in these arrays | |
| var scope = this; | |
| this.stats = { | |
| objects: { | |
| get total() { | |
| return scope._objects.length; | |
| }, | |
| get inUse() { | |
| return this.total - scope.nCachedObjects_; | |
| } | |
| }, | |
| get bindingsPerObject() { | |
| return scope._bindings.length; | |
| } | |
| }; | |
| } | |
| Object.assign( AnimationObjectGroup.prototype, { | |
| isAnimationObjectGroup: true, | |
| add: function () { | |
| var objects = this._objects, | |
| nObjects = objects.length, | |
| nCachedObjects = this.nCachedObjects_, | |
| indicesByUUID = this._indicesByUUID, | |
| paths = this._paths, | |
| parsedPaths = this._parsedPaths, | |
| bindings = this._bindings, | |
| nBindings = bindings.length, | |
| knownObject = undefined; | |
| for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
| var object = arguments[ i ], | |
| uuid = object.uuid, | |
| index = indicesByUUID[ uuid ]; | |
| if ( index === undefined ) { | |
| // unknown object -> add it to the ACTIVE region | |
| index = nObjects ++; | |
| indicesByUUID[ uuid ] = index; | |
| objects.push( object ); | |
| // accounting is done, now do the same for all bindings | |
| for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
| bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); | |
| } | |
| } else if ( index < nCachedObjects ) { | |
| knownObject = objects[ index ]; | |
| // move existing object to the ACTIVE region | |
| var firstActiveIndex = -- nCachedObjects, | |
| lastCachedObject = objects[ firstActiveIndex ]; | |
| indicesByUUID[ lastCachedObject.uuid ] = index; | |
| objects[ index ] = lastCachedObject; | |
| indicesByUUID[ uuid ] = firstActiveIndex; | |
| objects[ firstActiveIndex ] = object; | |
| // accounting is done, now do the same for all bindings | |
| for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
| var bindingsForPath = bindings[ j ], | |
| lastCached = bindingsForPath[ firstActiveIndex ], | |
| binding = bindingsForPath[ index ]; | |
| bindingsForPath[ index ] = lastCached; | |
| if ( binding === undefined ) { | |
| // since we do not bother to create new bindings | |
| // for objects that are cached, the binding may | |
| // or may not exist | |
| binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); | |
| } | |
| bindingsForPath[ firstActiveIndex ] = binding; | |
| } | |
| } else if ( objects[ index ] !== knownObject ) { | |
| console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + | |
| 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); | |
| } // else the object is already where we want it to be | |
| } // for arguments | |
| this.nCachedObjects_ = nCachedObjects; | |
| }, | |
| remove: function () { | |
| var objects = this._objects, | |
| nCachedObjects = this.nCachedObjects_, | |
| indicesByUUID = this._indicesByUUID, | |
| bindings = this._bindings, | |
| nBindings = bindings.length; | |
| for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
| var object = arguments[ i ], | |
| uuid = object.uuid, | |
| index = indicesByUUID[ uuid ]; | |
| if ( index !== undefined && index >= nCachedObjects ) { | |
| // move existing object into the CACHED region | |
| var lastCachedIndex = nCachedObjects ++, | |
| firstActiveObject = objects[ lastCachedIndex ]; | |
| indicesByUUID[ firstActiveObject.uuid ] = index; | |
| objects[ index ] = firstActiveObject; | |
| indicesByUUID[ uuid ] = lastCachedIndex; | |
| objects[ lastCachedIndex ] = object; | |
| // accounting is done, now do the same for all bindings | |
| for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
| var bindingsForPath = bindings[ j ], | |
| firstActive = bindingsForPath[ lastCachedIndex ], | |
| binding = bindingsForPath[ index ]; | |
| bindingsForPath[ index ] = firstActive; | |
| bindingsForPath[ lastCachedIndex ] = binding; | |
| } | |
| } | |
| } // for arguments | |
| this.nCachedObjects_ = nCachedObjects; | |
| }, | |
| // remove & forget | |
| uncache: function () { | |
| var objects = this._objects, | |
| nObjects = objects.length, | |
| nCachedObjects = this.nCachedObjects_, | |
| indicesByUUID = this._indicesByUUID, | |
| bindings = this._bindings, | |
| nBindings = bindings.length; | |
| for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
| var object = arguments[ i ], | |
| uuid = object.uuid, | |
| index = indicesByUUID[ uuid ]; | |
| if ( index !== undefined ) { | |
| delete indicesByUUID[ uuid ]; | |
| if ( index < nCachedObjects ) { | |
| // object is cached, shrink the CACHED region | |
| var firstActiveIndex = -- nCachedObjects, | |
| lastCachedObject = objects[ firstActiveIndex ], | |
| lastIndex = -- nObjects, | |
| lastObject = objects[ lastIndex ]; | |
| // last cached object takes this object's place | |
| indicesByUUID[ lastCachedObject.uuid ] = index; | |
| objects[ index ] = lastCachedObject; | |
| // last object goes to the activated slot and pop | |
| indicesByUUID[ lastObject.uuid ] = firstActiveIndex; | |
| objects[ firstActiveIndex ] = lastObject; | |
| objects.pop(); | |
| // accounting is done, now do the same for all bindings | |
| for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
| var bindingsForPath = bindings[ j ], | |
| lastCached = bindingsForPath[ firstActiveIndex ], | |
| last = bindingsForPath[ lastIndex ]; | |
| bindingsForPath[ index ] = lastCached; | |
| bindingsForPath[ firstActiveIndex ] = last; | |
| bindingsForPath.pop(); | |
| } | |
| } else { | |
| // object is active, just swap with the last and pop | |
| var lastIndex = -- nObjects, | |
| lastObject = objects[ lastIndex ]; | |
| indicesByUUID[ lastObject.uuid ] = index; | |
| objects[ index ] = lastObject; | |
| objects.pop(); | |
| // accounting is done, now do the same for all bindings | |
| for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
| var bindingsForPath = bindings[ j ]; | |
| bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; | |
| bindingsForPath.pop(); | |
| } | |
| } // cached or active | |
| } // if object is known | |
| } // for arguments | |
| this.nCachedObjects_ = nCachedObjects; | |
| }, | |
| // Internal interface used by befriended PropertyBinding.Composite: | |
| subscribe_: function ( path, parsedPath ) { | |
| // returns an array of bindings for the given path that is changed | |
| // according to the contained objects in the group | |
| var indicesByPath = this._bindingsIndicesByPath, | |
| index = indicesByPath[ path ], | |
| bindings = this._bindings; | |
| if ( index !== undefined ) return bindings[ index ]; | |
| var paths = this._paths, | |
| parsedPaths = this._parsedPaths, | |
| objects = this._objects, | |
| nObjects = objects.length, | |
| nCachedObjects = this.nCachedObjects_, | |
| bindingsForPath = new Array( nObjects ); | |
| index = bindings.length; | |
| indicesByPath[ path ] = index; | |
| paths.push( path ); | |
| parsedPaths.push( parsedPath ); | |
| bindings.push( bindingsForPath ); | |
| for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) { | |
| var object = objects[ i ]; | |
| bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); | |
| } | |
| return bindingsForPath; | |
| }, | |
| unsubscribe_: function ( path ) { | |
| // tells the group to forget about a property path and no longer | |
| // update the array previously obtained with 'subscribe_' | |
| var indicesByPath = this._bindingsIndicesByPath, | |
| index = indicesByPath[ path ]; | |
| if ( index !== undefined ) { | |
| var paths = this._paths, | |
| parsedPaths = this._parsedPaths, | |
| bindings = this._bindings, | |
| lastBindingsIndex = bindings.length - 1, | |
| lastBindings = bindings[ lastBindingsIndex ], | |
| lastBindingsPath = path[ lastBindingsIndex ]; | |
| indicesByPath[ lastBindingsPath ] = index; | |
| bindings[ index ] = lastBindings; | |
| bindings.pop(); | |
| parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; | |
| parsedPaths.pop(); | |
| paths[ index ] = paths[ lastBindingsIndex ]; | |
| paths.pop(); | |
| } | |
| } | |
| } ); | |
| export { AnimationObjectGroup }; | |