323 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			323 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 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 }; |