Buckets:
| import * as AnimationUtils from './AnimationUtils.js'; | |
| import { KeyframeTrack } from './KeyframeTrack.js'; | |
| import { BooleanKeyframeTrack } from './tracks/BooleanKeyframeTrack.js'; | |
| import { ColorKeyframeTrack } from './tracks/ColorKeyframeTrack.js'; | |
| import { NumberKeyframeTrack } from './tracks/NumberKeyframeTrack.js'; | |
| import { QuaternionKeyframeTrack } from './tracks/QuaternionKeyframeTrack.js'; | |
| import { StringKeyframeTrack } from './tracks/StringKeyframeTrack.js'; | |
| import { VectorKeyframeTrack } from './tracks/VectorKeyframeTrack.js'; | |
| import * as MathUtils from '../math/MathUtils.js'; | |
| import { NormalAnimationBlendMode } from '../constants.js'; | |
| class AnimationClip { | |
| constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { | |
| this.name = name; | |
| this.tracks = tracks; | |
| this.duration = duration; | |
| this.blendMode = blendMode; | |
| this.uuid = MathUtils.generateUUID(); | |
| // this means it should figure out its duration by scanning the tracks | |
| if ( this.duration < 0 ) { | |
| this.resetDuration(); | |
| } | |
| } | |
| static parse( json ) { | |
| const tracks = [], | |
| jsonTracks = json.tracks, | |
| frameTime = 1.0 / ( json.fps || 1.0 ); | |
| for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { | |
| tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); | |
| } | |
| const clip = new this( json.name, json.duration, tracks, json.blendMode ); | |
| clip.uuid = json.uuid; | |
| return clip; | |
| } | |
| static toJSON( clip ) { | |
| const tracks = [], | |
| clipTracks = clip.tracks; | |
| const json = { | |
| 'name': clip.name, | |
| 'duration': clip.duration, | |
| 'tracks': tracks, | |
| 'uuid': clip.uuid, | |
| 'blendMode': clip.blendMode | |
| }; | |
| for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { | |
| tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); | |
| } | |
| return json; | |
| } | |
| static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { | |
| const numMorphTargets = morphTargetSequence.length; | |
| const tracks = []; | |
| for ( let i = 0; i < numMorphTargets; i ++ ) { | |
| let times = []; | |
| let values = []; | |
| times.push( | |
| ( i + numMorphTargets - 1 ) % numMorphTargets, | |
| i, | |
| ( i + 1 ) % numMorphTargets ); | |
| values.push( 0, 1, 0 ); | |
| const order = AnimationUtils.getKeyframeOrder( times ); | |
| times = AnimationUtils.sortedArray( times, 1, order ); | |
| values = AnimationUtils.sortedArray( values, 1, order ); | |
| // if there is a key at the first frame, duplicate it as the | |
| // last frame as well for perfect loop. | |
| if ( ! noLoop && times[ 0 ] === 0 ) { | |
| times.push( numMorphTargets ); | |
| values.push( values[ 0 ] ); | |
| } | |
| tracks.push( | |
| new NumberKeyframeTrack( | |
| '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', | |
| times, values | |
| ).scale( 1.0 / fps ) ); | |
| } | |
| return new this( name, - 1, tracks ); | |
| } | |
| static findByName( objectOrClipArray, name ) { | |
| let clipArray = objectOrClipArray; | |
| if ( ! Array.isArray( objectOrClipArray ) ) { | |
| const o = objectOrClipArray; | |
| clipArray = o.geometry && o.geometry.animations || o.animations; | |
| } | |
| for ( let i = 0; i < clipArray.length; i ++ ) { | |
| if ( clipArray[ i ].name === name ) { | |
| return clipArray[ i ]; | |
| } | |
| } | |
| return null; | |
| } | |
| static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { | |
| const animationToMorphTargets = {}; | |
| // tested with https://regex101.com/ on trick sequences | |
| // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 | |
| const pattern = /^([\w-]*?)([\d]+)$/; | |
| // sort morph target names into animation groups based | |
| // patterns like Walk_001, Walk_002, Run_001, Run_002 | |
| for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { | |
| const morphTarget = morphTargets[ i ]; | |
| const parts = morphTarget.name.match( pattern ); | |
| if ( parts && parts.length > 1 ) { | |
| const name = parts[ 1 ]; | |
| let animationMorphTargets = animationToMorphTargets[ name ]; | |
| if ( ! animationMorphTargets ) { | |
| animationToMorphTargets[ name ] = animationMorphTargets = []; | |
| } | |
| animationMorphTargets.push( morphTarget ); | |
| } | |
| } | |
| const clips = []; | |
| for ( const name in animationToMorphTargets ) { | |
| clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); | |
| } | |
| return clips; | |
| } | |
| // parse the animation.hierarchy format | |
| static parseAnimation( animation, bones ) { | |
| if ( ! animation ) { | |
| console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); | |
| return null; | |
| } | |
| const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { | |
| // only return track if there are actually keys. | |
| if ( animationKeys.length !== 0 ) { | |
| const times = []; | |
| const values = []; | |
| AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); | |
| // empty keys are filtered out, so check again | |
| if ( times.length !== 0 ) { | |
| destTracks.push( new trackType( trackName, times, values ) ); | |
| } | |
| } | |
| }; | |
| const tracks = []; | |
| const clipName = animation.name || 'default'; | |
| const fps = animation.fps || 30; | |
| const blendMode = animation.blendMode; | |
| // automatic length determination in AnimationClip. | |
| let duration = animation.length || - 1; | |
| const hierarchyTracks = animation.hierarchy || []; | |
| for ( let h = 0; h < hierarchyTracks.length; h ++ ) { | |
| const animationKeys = hierarchyTracks[ h ].keys; | |
| // skip empty tracks | |
| if ( ! animationKeys || animationKeys.length === 0 ) continue; | |
| // process morph targets | |
| if ( animationKeys[ 0 ].morphTargets ) { | |
| // figure out all morph targets used in this track | |
| const morphTargetNames = {}; | |
| let k; | |
| for ( k = 0; k < animationKeys.length; k ++ ) { | |
| if ( animationKeys[ k ].morphTargets ) { | |
| for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { | |
| morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; | |
| } | |
| } | |
| } | |
| // create a track for each morph target with all zero | |
| // morphTargetInfluences except for the keys in which | |
| // the morphTarget is named. | |
| for ( const morphTargetName in morphTargetNames ) { | |
| const times = []; | |
| const values = []; | |
| for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { | |
| const animationKey = animationKeys[ k ]; | |
| times.push( animationKey.time ); | |
| values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); | |
| } | |
| tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); | |
| } | |
| duration = morphTargetNames.length * fps; | |
| } else { | |
| // ...assume skeletal animation | |
| const boneName = '.bones[' + bones[ h ].name + ']'; | |
| addNonemptyTrack( | |
| VectorKeyframeTrack, boneName + '.position', | |
| animationKeys, 'pos', tracks ); | |
| addNonemptyTrack( | |
| QuaternionKeyframeTrack, boneName + '.quaternion', | |
| animationKeys, 'rot', tracks ); | |
| addNonemptyTrack( | |
| VectorKeyframeTrack, boneName + '.scale', | |
| animationKeys, 'scl', tracks ); | |
| } | |
| } | |
| if ( tracks.length === 0 ) { | |
| return null; | |
| } | |
| const clip = new this( clipName, duration, tracks, blendMode ); | |
| return clip; | |
| } | |
| resetDuration() { | |
| const tracks = this.tracks; | |
| let duration = 0; | |
| for ( let i = 0, n = tracks.length; i !== n; ++ i ) { | |
| const track = this.tracks[ i ]; | |
| duration = Math.max( duration, track.times[ track.times.length - 1 ] ); | |
| } | |
| this.duration = duration; | |
| return this; | |
| } | |
| trim() { | |
| for ( let i = 0; i < this.tracks.length; i ++ ) { | |
| this.tracks[ i ].trim( 0, this.duration ); | |
| } | |
| return this; | |
| } | |
| validate() { | |
| let valid = true; | |
| for ( let i = 0; i < this.tracks.length; i ++ ) { | |
| valid = valid && this.tracks[ i ].validate(); | |
| } | |
| return valid; | |
| } | |
| optimize() { | |
| for ( let i = 0; i < this.tracks.length; i ++ ) { | |
| this.tracks[ i ].optimize(); | |
| } | |
| return this; | |
| } | |
| clone() { | |
| const tracks = []; | |
| for ( let i = 0; i < this.tracks.length; i ++ ) { | |
| tracks.push( this.tracks[ i ].clone() ); | |
| } | |
| return new this.constructor( this.name, this.duration, tracks, this.blendMode ); | |
| } | |
| toJSON() { | |
| return this.constructor.toJSON( this ); | |
| } | |
| } | |
| function getTrackTypeForValueTypeName( typeName ) { | |
| switch ( typeName.toLowerCase() ) { | |
| case 'scalar': | |
| case 'double': | |
| case 'float': | |
| case 'number': | |
| case 'integer': | |
| return NumberKeyframeTrack; | |
| case 'vector': | |
| case 'vector2': | |
| case 'vector3': | |
| case 'vector4': | |
| return VectorKeyframeTrack; | |
| case 'color': | |
| return ColorKeyframeTrack; | |
| case 'quaternion': | |
| return QuaternionKeyframeTrack; | |
| case 'bool': | |
| case 'boolean': | |
| return BooleanKeyframeTrack; | |
| case 'string': | |
| return StringKeyframeTrack; | |
| } | |
| throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); | |
| } | |
| function parseKeyframeTrack( json ) { | |
| if ( json.type === undefined ) { | |
| throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); | |
| } | |
| const trackType = getTrackTypeForValueTypeName( json.type ); | |
| if ( json.times === undefined ) { | |
| const times = [], values = []; | |
| AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); | |
| json.times = times; | |
| json.values = values; | |
| } | |
| // derived classes can define a static parse method | |
| if ( trackType.parse !== undefined ) { | |
| return trackType.parse( json ); | |
| } else { | |
| // by default, we assume a constructor compatible with the base | |
| return new trackType( json.name, json.times, json.values, json.interpolation ); | |
| } | |
| } | |
| export { AnimationClip }; | |
Xet Storage Details
- Size:
- 9.54 kB
- Xet hash:
- 8acd3cce30cfe62fc4a954d91d3f979dca7a8c3ccc53b7edbb3b49b9c75786fd
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.