import { MathUtils, Mesh } from 'three'; class MorphBlendMesh extends Mesh { constructor( geometry, material ) { super( geometry, material ); this.animationsMap = {}; this.animationsList = []; // prepare default animation // (all frames played together in 1 second) const numFrames = Object.keys( this.morphTargetDictionary ).length; const name = '__default'; const startFrame = 0; const endFrame = numFrames - 1; const fps = numFrames / 1; this.createAnimation( name, startFrame, endFrame, fps ); this.setAnimationWeight( name, 1 ); } createAnimation( name, start, end, fps ) { const animation = { start: start, end: end, length: end - start + 1, fps: fps, duration: ( end - start ) / fps, lastFrame: 0, currentFrame: 0, active: false, time: 0, direction: 1, weight: 1, directionBackwards: false, mirroredLoop: false }; this.animationsMap[ name ] = animation; this.animationsList.push( animation ); } autoCreateAnimations( fps ) { const pattern = /([a-z]+)_?(\d+)/i; let firstAnimation; const frameRanges = {}; let i = 0; for ( const key in this.morphTargetDictionary ) { const chunks = key.match( pattern ); if ( chunks && chunks.length > 1 ) { const name = chunks[ 1 ]; if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; const range = frameRanges[ name ]; if ( i < range.start ) range.start = i; if ( i > range.end ) range.end = i; if ( ! firstAnimation ) firstAnimation = name; } i ++; } for ( const name in frameRanges ) { const range = frameRanges[ name ]; this.createAnimation( name, range.start, range.end, fps ); } this.firstAnimation = firstAnimation; } setAnimationDirectionForward( name ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.direction = 1; animation.directionBackwards = false; } } setAnimationDirectionBackward( name ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.direction = - 1; animation.directionBackwards = true; } } setAnimationFPS( name, fps ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.fps = fps; animation.duration = ( animation.end - animation.start ) / animation.fps; } } setAnimationDuration( name, duration ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.duration = duration; animation.fps = ( animation.end - animation.start ) / animation.duration; } } setAnimationWeight( name, weight ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.weight = weight; } } setAnimationTime( name, time ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.time = time; } } getAnimationTime( name ) { let time = 0; const animation = this.animationsMap[ name ]; if ( animation ) { time = animation.time; } return time; } getAnimationDuration( name ) { let duration = - 1; const animation = this.animationsMap[ name ]; if ( animation ) { duration = animation.duration; } return duration; } playAnimation( name ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.time = 0; animation.active = true; } else { console.warn( 'THREE.MorphBlendMesh: animation[' + name + '] undefined in .playAnimation()' ); } } stopAnimation( name ) { const animation = this.animationsMap[ name ]; if ( animation ) { animation.active = false; } } update( delta ) { for ( let i = 0, il = this.animationsList.length; i < il; i ++ ) { const animation = this.animationsList[ i ]; if ( ! animation.active ) continue; const frameTime = animation.duration / animation.length; animation.time += animation.direction * delta; if ( animation.mirroredLoop ) { if ( animation.time > animation.duration || animation.time < 0 ) { animation.direction *= - 1; if ( animation.time > animation.duration ) { animation.time = animation.duration; animation.directionBackwards = true; } if ( animation.time < 0 ) { animation.time = 0; animation.directionBackwards = false; } } } else { animation.time = animation.time % animation.duration; if ( animation.time < 0 ) animation.time += animation.duration; } const keyframe = animation.start + MathUtils.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); const weight = animation.weight; if ( keyframe !== animation.currentFrame ) { this.morphTargetInfluences[ animation.lastFrame ] = 0; this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; this.morphTargetInfluences[ keyframe ] = 0; animation.lastFrame = animation.currentFrame; animation.currentFrame = keyframe; } let mix = ( animation.time % frameTime ) / frameTime; if ( animation.directionBackwards ) mix = 1 - mix; if ( animation.currentFrame !== animation.lastFrame ) { this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; } else { this.morphTargetInfluences[ animation.currentFrame ] = weight; } } } } export { MorphBlendMesh };