PageSourceSearch

https://movimentoperpetuo.ca/wp-content/plugins/3d-scene-viewer/as…/3js/src/animation/AnimationObjectGroup.js

js movimentoperpetuo.ca collected 2026-10-07 00:49:37 UTC 10,397 bytes, 411 lines download raw bytes

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.