vendor: 8,882 bytes, lines 1-347
1import { PropertyBinding } from './PropertyBinding.js'; 2import { generateUUID } from '../math/MathUtils.js'; 3import { error } from '../utils.js'; 4 5/** 6 * A group of objects that receives a shared animation state. 7 * 8 * Usage: 9 * 10 * - Add objects you would otherwise pass as 'root' to the 11 * constructor or the .clipAction method of AnimationMixer. 12 * - Instead pass this object as 'root'. 13 * - You can also add and remove objects later when the mixer is running. 14 * 15 * Note: 16 * 17 * - Objects of this class appear as one object to the mixer, 18 * so cache control of the individual objects must be done on the group. 19 * 20 * Limitation: 21 * 22 * - The animated properties must be compatible among the all objects in the group. 23 * - A single property can either be controlled through a target group or directly, but not both. 24 */ 25class AnimationObjectGroup { 26 27 /** 28 * Constructs a new animation group. 29 * 30 * @param {...Object3D} arguments - An arbitrary number of 3D objects that share the same animation state. 31 */ 32 constructor() { 33 34 /** 35 * This flag can be used for type testing. 36 * 37 * @type {boolean} 38 * @readonly 39 * @default true 40 */ 41 this.isAnimationObjectGroup = true; 42 43 /** 44 * The UUID of the 3D object. 45 * 46 * @type {string} 47 * @readonly 48 */ 49 this.uuid = generateUUID(); 50 51 // cached objects followed by the active ones 52 this._objects = Array.prototype.slice.call( arguments ); 53 54 this.nCachedObjects_ = 0; // threshold 55 // note: read by PropertyBinding.Composite 56 57 const indices = {}; 58 this._indicesByUUID = indices; // for bookkeeping 59 60 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 61 62 indices[ arguments[ i ].uuid ] = i; 63 64 } 65 66 this._paths = []; // inside: string 67 this._parsedPaths = []; // inside: { we don't care, here } 68 this._bindings = []; // inside: Array< PropertyBinding > 69 this._bindingsIndicesByPath = {}; // inside: indices in these arrays 70 71 const scope = this; 72 73 this.stats = { 74 75 objects: { 76 get total() { 77 78 return scope._objects.length; 79 80 }, 81 get inUse() { 82 83 return this.total - scope.nCachedObjects_; 84 85 } 86 }, 87 get bindingsPerObject() { 88 89 return scope._bindings.length; 90 91 } 92 93 }; 94 95 } 96 97 /** 98 * Adds an arbitrary number of objects to this animation group. 99 * 100 * @param {...Object3D} arguments - The 3D objects to add. 101 */ 102 add() { 103 104 const objects = this._objects, 105 indicesByUUID = this._indicesByUUID, 106 paths = this._paths, 107 parsedPaths = this._parsedPaths, 108 bindings = this._bindings, 109 nBindings = bindings.length; 110 111 let knownObject = undefined, 112 nObjects = objects.length, 113 nCachedObjects = this.nCachedObjects_; 114 115 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 116 117 const object = arguments[ i ], 118 uuid = object.uuid; 119 let index = indicesByUUID[ uuid ]; 120 121 if ( index === undefined ) { 122 123 // unknown object -> add it to the ACTIVE region 124 125 index = nObjects ++; 126 indicesByUUID[ uuid ] = index; 127 objects.push( object ); 128 129 // accounting is done, now do the same for all bindings 130 131 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 132 133 bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); 134 135 } 136 137 } else if ( index < nCachedObjects ) { 138 139 knownObject = objects[ index ]; 140 141 // move existing object to the ACTIVE region 142 143 const firstActiveIndex = -- nCachedObjects, 144 lastCachedObject = objects[ firstActiveIndex ]; 145 146 indicesByUUID[ lastCachedObject.uuid ] = index; 147 objects[ index ] = lastCachedObject; 148 149 indicesByUUID[ uuid ] = firstActiveIndex; 150 objects[ firstActiveIndex ] = object; 151 152 // accounting is done, now do the same for all bindings 153 154 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 155 156 const bindingsForPath = bindings[ j ], 157 lastCached = bindingsForPath[ firstActiveIndex ]; 158 159 let binding = bindingsForPath[ index ]; 160 161 bindingsForPath[ index ] = lastCached; 162 163 if ( binding === undefined ) { 164 165 // since we do not bother to create new bindings 166 // for objects that are cached, the binding may 167 // or may not exist 168 169 binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); 170 171 } 172 173 bindingsForPath[ firstActiveIndex ] = binding; 174 175 } 176 177 } else if ( objects[ index ] !== knownObject ) { 178 179 error( 'AnimationObjectGroup: Different objects with the same UUID ' + 180 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); 181 182 } // else the object is already where we want it to be 183 184 } // for arguments 185 186 this.nCachedObjects_ = nCachedObjects; 187 188 } 189 190 /** 191 * Removes an arbitrary number of objects to this animation group 192 * 193 * @param {...Object3D} arguments - The 3D objects to remove. 194 */ 195 remove() { 196 197 const objects = this._objects, 198 indicesByUUID = this._indicesByUUID, 199 bindings = this._bindings, 200 nBindings = bindings.length; 201 202 let nCachedObjects = this.nCachedObjects_; 203 204 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 205 206 const object = arguments[ i ], 207 uuid = object.uuid, 208 index = indicesByUUID[ uuid ]; 209 210 if ( index !== undefined && index >= nCachedObjects ) { 211 212 // move existing object into the CACHED region 213 214 const lastCachedIndex = nCachedObjects ++, 215 firstActiveObject = objects[ lastCachedIndex ]; 216 217 indicesByUUID[ firstActiveObject.uuid ] = index; 218 objects[ index ] = firstActiveObject; 219 220 indicesByUUID[ uuid ] = lastCachedIndex; 221 objects[ lastCachedIndex ] = object; 222 223 // accounting is done, now do the same for all bindings 224 225 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 226 227 const bindingsForPath = bindings[ j ], 228 firstActive = bindingsForPath[ lastCachedIndex ], 229 binding = bindingsForPath[ index ]; 230 231 bindingsForPath[ index ] = firstActive; 232 bindingsForPath[ lastCachedIndex ] = binding; 233 234 } 235 236 } 237 238 } // for arguments 239 240 this.nCachedObjects_ = nCachedObjects; 241 242 } 243 244 /** 245 * Deallocates all memory resources for the passed 3D objects of this animation group. 246 * 247 * @param {...Object3D} arguments - The 3D objects to uncache. 248 */ 249 uncache() { 250 251 const objects = this._objects, 252 indicesByUUID = this._indicesByUUID, 253 bindings = this._bindings, 254 nBindings = bindings.length; 255 256 let nCachedObjects = this.nCachedObjects_, 257 nObjects = objects.length; 258 259 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 260 261 const object = arguments[ i ], 262 uuid = object.uuid, 263 index = indicesByUUID[ uuid ]; 264 265 if ( index !== undefined ) { 266 267 delete indicesByUUID[ uuid ]; 268 269 if ( index < nCachedObjects ) { 270 271 // object is cached, shrink the CACHED region 272 273 const firstActiveIndex = -- nCachedObjects, 274 lastCachedObject = objects[ firstActiveIndex ], 275 lastIndex = -- nObjects, 276 lastObject = objects[ lastIndex ]; 277 278 // last cached object takes this object's place 279 indicesByUUID[ lastCachedObject.uuid ] = index; 280 objects[ index ] = lastCachedObject; 281 282 // last object goes to the activated slot and pop 283 indicesByUUID[ lastObject.uuid ] = firstActiveIndex; 284 objects[ firstActiveIndex ] = lastObject; 285 objects.pop(); 286 287 // accounting is done, now do the same for all bindings 288 289 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 290 291 const bindingsForPath = bindings[ j ], 292 lastCached = bindingsForPath[ firstActiveIndex ], 293 last = bindingsForPath[ lastIndex ]; 294 295 bindingsForPath[ index ] = lastCached; 296 bindingsForPath[ firstActiveIndex ] = last; 297 bindingsForPath.pop(); 298 299 } 300 301 } else { 302 303 // object is active, just swap with the last and pop 304 305 const lastIndex = -- nObjects, 306 lastObject = objects[ lastIndex ]; 307 308 if ( lastIndex > 0 ) { 309 310 indicesByUUID[ lastObject.uuid ] = index; 311 312 } 313 314 objects[ index ] = lastObject; 315 objects.pop(); 316 317 // accounting is done, now do the same for all bindings 318 319 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 320 321 const bindingsForPath = bindings[ j ]; 322 323 bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; 324 bindingsForPath.pop(); 325 326 } 327 328 } // cached or active 329 330 } // if object is known 331 332 } // for arguments 333 334 this.nCachedObjects_ = nCachedObjects; 335 336 } 337 338 // Internal interface used by befriended PropertyBinding.Composite: 339 340 subscribe_( path, parsedPath ) { 341 342 // returns an array of bindings for the given path that is changed 343 // according to the contained objects in the group 344 345 const indicesByPath = this._bindingsIndicesByPath; 346 let index = indicesByPath[ path ]; 347 const bindings = this._bindings;
vendor: 1,515 bytes, lines 348-411
348 349 if ( index !== undefined ) return bindings[ index ]; 350 351 const paths = this._paths, 352 parsedPaths = this._parsedPaths, 353 objects = this._objects, 354 nObjects = objects.length, 355 nCachedObjects = this.nCachedObjects_, 356 bindingsForPath = new Array( nObjects ); 357 358 index = bindings.length; 359 360 indicesByPath[ path ] = index; 361 362 paths.push( path ); 363 parsedPaths.push( parsedPath ); 364 bindings.push( bindingsForPath ); 365 366 for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { 367 368 const object = objects[ i ]; 369 bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); 370 371 } 372 373 return bindingsForPath; 374 375 } 376 377 unsubscribe_( path ) { 378 379 // tells the group to forget about a property path and no longer 380 // update the array previously obtained with 'subscribe_' 381 382 const indicesByPath = this._bindingsIndicesByPath, 383 index = indicesByPath[ path ]; 384 385 if ( index !== undefined ) { 386 387 const paths = this._paths, 388 parsedPaths = this._parsedPaths, 389 bindings = this._bindings, 390 lastBindingsIndex = bindings.length - 1, 391 lastBindings = bindings[ lastBindingsIndex ], 392 lastBindingsPath = path[ lastBindingsIndex ]; 393 394 indicesByPath[ lastBindingsPath ] = index; 395 396 bindings[ index ] = lastBindings; 397 bindings.pop(); 398 399 parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; 400 parsedPaths.pop(); 401 402 paths[ index ] = paths[ lastBindingsIndex ]; 403 paths.pop(); 404 405 } 406 407 } 408 409} 410 411export { AnimationObjectGroup };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.