Buckets:
| import { Quaternion } from '../math/Quaternion.js'; | |
| import { AdditiveAnimationBlendMode } from '../constants.js'; | |
| /** | |
| * Converts an array to a specific type. | |
| * | |
| * @param {TypedArray|Array} array - The array to convert. | |
| * @param {TypedArray.constructor} type - The constructor of a typed array that defines the new type. | |
| * @return {TypedArray} The converted array. | |
| */ | |
| function convertArray( array, type ) { | |
| if ( ! array || array.constructor === type ) return array; | |
| if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { | |
| return new type( array ); // create typed array | |
| } | |
| return Array.prototype.slice.call( array ); // create Array | |
| } | |
| /** | |
| * Returns `true` if the given object is a typed array. | |
| * | |
| * @param {any} object - The object to check. | |
| * @return {boolean} Whether the given object is a typed array. | |
| */ | |
| function isTypedArray( object ) { | |
| return ArrayBuffer.isView( object ) && ! ( object instanceof DataView ); | |
| } | |
| /** | |
| * Returns an array by which times and values can be sorted. | |
| * | |
| * @param {Array<number>} times - The keyframe time values. | |
| * @return {Array<number>} The array. | |
| */ | |
| function getKeyframeOrder( times ) { | |
| function compareTime( i, j ) { | |
| return times[ i ] - times[ j ]; | |
| } | |
| const n = times.length; | |
| const result = new Array( n ); | |
| for ( let i = 0; i !== n; ++ i ) result[ i ] = i; | |
| result.sort( compareTime ); | |
| return result; | |
| } | |
| /** | |
| * Sorts the given array by the previously computed order via `getKeyframeOrder()`. | |
| * | |
| * @param {Array<number>} values - The values to sort. | |
| * @param {number} stride - The stride. | |
| * @param {Array<number>} order - The sort order. | |
| * @return {Array<number>} The sorted values. | |
| */ | |
| function sortedArray( values, stride, order ) { | |
| const nValues = values.length; | |
| const result = new values.constructor( nValues ); | |
| for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { | |
| const srcOffset = order[ i ] * stride; | |
| for ( let j = 0; j !== stride; ++ j ) { | |
| result[ dstOffset ++ ] = values[ srcOffset + j ]; | |
| } | |
| } | |
| return result; | |
| } | |
| /** | |
| * Used for parsing AOS keyframe formats. | |
| * | |
| * @param {Array<number>} jsonKeys - A list of JSON keyframes. | |
| * @param {Array<number>} times - This array will be filled with keyframe times by this function. | |
| * @param {Array<number>} values - This array will be filled with keyframe values by this function. | |
| * @param {string} valuePropertyName - The name of the property to use. | |
| */ | |
| function flattenJSON( jsonKeys, times, values, valuePropertyName ) { | |
| let i = 1, key = jsonKeys[ 0 ]; | |
| while ( key !== undefined && key[ valuePropertyName ] === undefined ) { | |
| key = jsonKeys[ i ++ ]; | |
| } | |
| if ( key === undefined ) return; // no data | |
| let value = key[ valuePropertyName ]; | |
| if ( value === undefined ) return; // no data | |
| if ( Array.isArray( value ) ) { | |
| do { | |
| value = key[ valuePropertyName ]; | |
| if ( value !== undefined ) { | |
| times.push( key.time ); | |
| values.push( ...value ); // push all elements | |
| } | |
| key = jsonKeys[ i ++ ]; | |
| } while ( key !== undefined ); | |
| } else if ( value.toArray !== undefined ) { | |
| // ...assume THREE.Math-ish | |
| do { | |
| value = key[ valuePropertyName ]; | |
| if ( value !== undefined ) { | |
| times.push( key.time ); | |
| value.toArray( values, values.length ); | |
| } | |
| key = jsonKeys[ i ++ ]; | |
| } while ( key !== undefined ); | |
| } else { | |
| // otherwise push as-is | |
| do { | |
| value = key[ valuePropertyName ]; | |
| if ( value !== undefined ) { | |
| times.push( key.time ); | |
| values.push( value ); | |
| } | |
| key = jsonKeys[ i ++ ]; | |
| } while ( key !== undefined ); | |
| } | |
| } | |
| /** | |
| * Creates a new clip, containing only the segment of the original clip between the given frames. | |
| * | |
| * @param {AnimationClip} sourceClip - The values to sort. | |
| * @param {string} name - The name of the clip. | |
| * @param {number} startFrame - The start frame. | |
| * @param {number} endFrame - The end frame. | |
| * @param {number} [fps=30] - The FPS. | |
| * @return {AnimationClip} The new sub clip. | |
| */ | |
| function subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) { | |
| const clip = sourceClip.clone(); | |
| clip.name = name; | |
| const tracks = []; | |
| for ( let i = 0; i < clip.tracks.length; ++ i ) { | |
| const track = clip.tracks[ i ]; | |
| const valueSize = track.getValueSize(); | |
| const times = []; | |
| const values = []; | |
| for ( let j = 0; j < track.times.length; ++ j ) { | |
| const frame = track.times[ j ] * fps; | |
| if ( frame < startFrame || frame >= endFrame ) continue; | |
| times.push( track.times[ j ] ); | |
| for ( let k = 0; k < valueSize; ++ k ) { | |
| values.push( track.values[ j * valueSize + k ] ); | |
| } | |
| } | |
| if ( times.length === 0 ) continue; | |
| track.times = convertArray( times, track.times.constructor ); | |
| track.values = convertArray( values, track.values.constructor ); | |
| tracks.push( track ); | |
| } | |
| clip.tracks = tracks; | |
| // find minimum .times value across all tracks in the trimmed clip | |
| let minStartTime = Infinity; | |
| for ( let i = 0; i < clip.tracks.length; ++ i ) { | |
| if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { | |
| minStartTime = clip.tracks[ i ].times[ 0 ]; | |
| } | |
| } | |
| // shift all tracks such that clip begins at t=0 | |
| for ( let i = 0; i < clip.tracks.length; ++ i ) { | |
| clip.tracks[ i ].shift( - 1 * minStartTime ); | |
| } | |
| clip.resetDuration(); | |
| return clip; | |
| } | |
| /** | |
| * Converts the keyframes of the given animation clip to an additive format. | |
| * | |
| * @param {AnimationClip} targetClip - The clip to make additive. | |
| * @param {number} [referenceFrame=0] - The reference frame. | |
| * @param {AnimationClip} [referenceClip=targetClip] - The reference clip. | |
| * @param {number} [fps=30] - The FPS. | |
| * @return {AnimationClip} The updated clip which is now additive. | |
| */ | |
| function makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { | |
| if ( fps <= 0 ) fps = 30; | |
| const numTracks = referenceClip.tracks.length; | |
| const referenceTime = referenceFrame / fps; | |
| // Make each track's values relative to the values at the reference frame | |
| for ( let i = 0; i < numTracks; ++ i ) { | |
| const referenceTrack = referenceClip.tracks[ i ]; | |
| const referenceTrackType = referenceTrack.ValueTypeName; | |
| // Skip this track if it's non-numeric | |
| if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; | |
| // Find the track in the target clip whose name and type matches the reference track | |
| const targetTrack = targetClip.tracks.find( function ( track ) { | |
| return track.name === referenceTrack.name | |
| && track.ValueTypeName === referenceTrackType; | |
| } ); | |
| if ( targetTrack === undefined ) continue; | |
| let referenceOffset = 0; | |
| const referenceValueSize = referenceTrack.getValueSize(); | |
| if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { | |
| referenceOffset = referenceValueSize / 3; | |
| } | |
| let targetOffset = 0; | |
| const targetValueSize = targetTrack.getValueSize(); | |
| if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { | |
| targetOffset = targetValueSize / 3; | |
| } | |
| const lastIndex = referenceTrack.times.length - 1; | |
| let referenceValue; | |
| // Find the value to subtract out of the track | |
| if ( referenceTime <= referenceTrack.times[ 0 ] ) { | |
| // Reference frame is earlier than the first keyframe, so just use the first keyframe | |
| const startIndex = referenceOffset; | |
| const endIndex = referenceValueSize - referenceOffset; | |
| referenceValue = referenceTrack.values.slice( startIndex, endIndex ); | |
| } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { | |
| // Reference frame is after the last keyframe, so just use the last keyframe | |
| const startIndex = lastIndex * referenceValueSize + referenceOffset; | |
| const endIndex = startIndex + referenceValueSize - referenceOffset; | |
| referenceValue = referenceTrack.values.slice( startIndex, endIndex ); | |
| } else { | |
| // Interpolate to the reference value | |
| const interpolant = referenceTrack.createInterpolant(); | |
| const startIndex = referenceOffset; | |
| const endIndex = referenceValueSize - referenceOffset; | |
| interpolant.evaluate( referenceTime ); | |
| referenceValue = interpolant.resultBuffer.slice( startIndex, endIndex ); | |
| } | |
| // Conjugate the quaternion | |
| if ( referenceTrackType === 'quaternion' ) { | |
| const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); | |
| referenceQuat.toArray( referenceValue ); | |
| } | |
| // Subtract the reference value from all of the track values | |
| const numTimes = targetTrack.times.length; | |
| for ( let j = 0; j < numTimes; ++ j ) { | |
| const valueStart = j * targetValueSize + targetOffset; | |
| if ( referenceTrackType === 'quaternion' ) { | |
| // Multiply the conjugate for quaternion track types | |
| Quaternion.multiplyQuaternionsFlat( | |
| targetTrack.values, | |
| valueStart, | |
| referenceValue, | |
| 0, | |
| targetTrack.values, | |
| valueStart | |
| ); | |
| } else { | |
| const valueEnd = targetValueSize - targetOffset * 2; | |
| // Subtract each value for all other numeric track types | |
| for ( let k = 0; k < valueEnd; ++ k ) { | |
| targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; | |
| } | |
| } | |
| } | |
| } | |
| targetClip.blendMode = AdditiveAnimationBlendMode; | |
| return targetClip; | |
| } | |
| /** | |
| * A class with various methods to assist with animations. | |
| * | |
| * @hideconstructor | |
| */ | |
| class AnimationUtils { | |
| /** | |
| * Converts an array to a specific type | |
| * | |
| * @static | |
| * @param {TypedArray|Array} array - The array to convert. | |
| * @param {TypedArray.constructor} type - The constructor of a type array. | |
| * @return {TypedArray} The converted array | |
| */ | |
| static convertArray( array, type ) { | |
| return convertArray( array, type ); | |
| } | |
| /** | |
| * Returns `true` if the given object is a typed array. | |
| * | |
| * @static | |
| * @param {any} object - The object to check. | |
| * @return {boolean} Whether the given object is a typed array. | |
| */ | |
| static isTypedArray( object ) { | |
| return isTypedArray( object ); | |
| } | |
| /** | |
| * Returns an array by which times and values can be sorted. | |
| * | |
| * @static | |
| * @param {Array<number>} times - The keyframe time values. | |
| * @return {Array<number>} The array. | |
| */ | |
| static getKeyframeOrder( times ) { | |
| return getKeyframeOrder( times ); | |
| } | |
| /** | |
| * Sorts the given array by the previously computed order via `getKeyframeOrder()`. | |
| * | |
| * @static | |
| * @param {Array<number>} values - The values to sort. | |
| * @param {number} stride - The stride. | |
| * @param {Array<number>} order - The sort order. | |
| * @return {Array<number>} The sorted values. | |
| */ | |
| static sortedArray( values, stride, order ) { | |
| return sortedArray( values, stride, order ); | |
| } | |
| /** | |
| * Used for parsing AOS keyframe formats. | |
| * | |
| * @static | |
| * @param {Array<number>} jsonKeys - A list of JSON keyframes. | |
| * @param {Array<number>} times - This array will be filled with keyframe times by this method. | |
| * @param {Array<number>} values - This array will be filled with keyframe values by this method. | |
| * @param {string} valuePropertyName - The name of the property to use. | |
| */ | |
| static flattenJSON( jsonKeys, times, values, valuePropertyName ) { | |
| flattenJSON( jsonKeys, times, values, valuePropertyName ); | |
| } | |
| /** | |
| * Creates a new clip, containing only the segment of the original clip between the given frames. | |
| * | |
| * @static | |
| * @param {AnimationClip} sourceClip - The values to sort. | |
| * @param {string} name - The name of the clip. | |
| * @param {number} startFrame - The start frame. | |
| * @param {number} endFrame - The end frame. | |
| * @param {number} [fps=30] - The FPS. | |
| * @return {AnimationClip} The new sub clip. | |
| */ | |
| static subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) { | |
| return subclip( sourceClip, name, startFrame, endFrame, fps ); | |
| } | |
| /** | |
| * Converts the keyframes of the given animation clip to an additive format. | |
| * | |
| * @static | |
| * @param {AnimationClip} targetClip - The clip to make additive. | |
| * @param {number} [referenceFrame=0] - The reference frame. | |
| * @param {AnimationClip} [referenceClip=targetClip] - The reference clip. | |
| * @param {number} [fps=30] - The FPS. | |
| * @return {AnimationClip} The updated clip which is now additive. | |
| */ | |
| static makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { | |
| return makeClipAdditive( targetClip, referenceFrame, referenceClip, fps ); | |
| } | |
| } | |
| export { | |
| convertArray, | |
| isTypedArray, | |
| getKeyframeOrder, | |
| sortedArray, | |
| flattenJSON, | |
| subclip, | |
| makeClipAdditive, | |
| AnimationUtils | |
| }; | |
Xet Storage Details
- Size:
- 12.4 kB
- Xet hash:
- 7a4144b0dcfc2fae1a9b8aa69acf2adee4c679e9dcebe316faabf82b0e258f3e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.