| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 | import {	Box3,	MathUtils,	MeshLambertMaterial,	Object3D,	TextureLoader,	UVMapping,	sRGBEncoding} from '../../../build/three.module.js';import { MD2Loader } from '../loaders/MD2Loader.js';import { MorphBlendMesh } from '../misc/MorphBlendMesh.js';class MD2CharacterComplex {	constructor() {		this.scale = 1;		// animation parameters		this.animationFPS = 6;		this.transitionFrames = 15;		// movement model parameters		this.maxSpeed = 275;		this.maxReverseSpeed = - 275;		this.frontAcceleration = 600;		this.backAcceleration = 600;		this.frontDecceleration = 600;		this.angularSpeed = 2.5;		// rig		this.root = new Object3D();		this.meshBody = null;		this.meshWeapon = null;		this.controls = null;		// skins		this.skinsBody = [];		this.skinsWeapon = [];		this.weapons = [];		this.currentSkin = undefined;		//		this.onLoadComplete = function () {};		// internals		this.meshes = [];		this.animations = {};		this.loadCounter = 0;		// internal movement control variables		this.speed = 0;		this.bodyOrientation = 0;		this.walkSpeed = this.maxSpeed;		this.crouchSpeed = this.maxSpeed * 0.5;		// internal animation parameters		this.activeAnimation = null;		this.oldAnimation = null;		// API	}	enableShadows( enable ) {		for ( let i = 0; i < this.meshes.length; i ++ ) {			this.meshes[ i ].castShadow = enable;			this.meshes[ i ].receiveShadow = enable;		}	}	setVisible( enable ) {		for ( let i = 0; i < this.meshes.length; i ++ ) {			this.meshes[ i ].visible = enable;			this.meshes[ i ].visible = enable;		}	}	shareParts( original ) {		this.animations = original.animations;		this.walkSpeed = original.walkSpeed;		this.crouchSpeed = original.crouchSpeed;		this.skinsBody = original.skinsBody;		this.skinsWeapon = original.skinsWeapon;		// BODY		const mesh = this._createPart( original.meshBody.geometry, this.skinsBody[ 0 ] );		mesh.scale.set( this.scale, this.scale, this.scale );		this.root.position.y = original.root.position.y;		this.root.add( mesh );		this.meshBody = mesh;		this.meshes.push( mesh );		// WEAPONS		for ( let i = 0; i < original.weapons.length; i ++ ) {			const meshWeapon = this._createPart( original.weapons[ i ].geometry, this.skinsWeapon[ i ] );			meshWeapon.scale.set( this.scale, this.scale, this.scale );			meshWeapon.visible = false;			meshWeapon.name = original.weapons[ i ].name;			this.root.add( meshWeapon );			this.weapons[ i ] = meshWeapon;			this.meshWeapon = meshWeapon;			this.meshes.push( meshWeapon );		}	}	loadParts( config ) {		const scope = this;		function loadTextures( baseUrl, textureUrls ) {			const textureLoader = new TextureLoader();			const textures = [];			for ( let i = 0; i < textureUrls.length; i ++ ) {				textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );				textures[ i ].mapping = UVMapping;				textures[ i ].name = textureUrls[ i ];				textures[ i ].encoding = sRGBEncoding;			}			return textures;		}		function checkLoadingComplete() {			scope.loadCounter -= 1;			if ( scope.loadCounter === 0 ) 	scope.onLoadComplete();		}		this.animations = config.animations;		this.walkSpeed = config.walkSpeed;		this.crouchSpeed = config.crouchSpeed;		this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;		const weaponsTextures = [];		for ( let i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ];		// SKINS		this.skinsBody = loadTextures( config.baseUrl + 'skins/', config.skins );		this.skinsWeapon = loadTextures( config.baseUrl + 'skins/', weaponsTextures );		// BODY		const loader = new MD2Loader();		loader.load( config.baseUrl + config.body, function ( geo ) {			const boundingBox = new Box3();			boundingBox.setFromBufferAttribute( geo.attributes.position );			scope.root.position.y = - scope.scale * boundingBox.min.y;			const mesh = scope._createPart( geo, scope.skinsBody[ 0 ] );			mesh.scale.set( scope.scale, scope.scale, scope.scale );			scope.root.add( mesh );			scope.meshBody = mesh;			scope.meshes.push( mesh );			checkLoadingComplete();		} );		// WEAPONS		const generateCallback = function ( index, name ) {			return function ( geo ) {				const mesh = scope._createPart( geo, scope.skinsWeapon[ index ] );				mesh.scale.set( scope.scale, scope.scale, scope.scale );				mesh.visible = false;				mesh.name = name;				scope.root.add( mesh );				scope.weapons[ index ] = mesh;				scope.meshWeapon = mesh;				scope.meshes.push( mesh );				checkLoadingComplete();			};		};		for ( let i = 0; i < config.weapons.length; i ++ ) {			loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );		}	}	setPlaybackRate( rate ) {		if ( this.meshBody ) this.meshBody.duration = this.meshBody.baseDuration / rate;		if ( this.meshWeapon ) this.meshWeapon.duration = this.meshWeapon.baseDuration / rate;	}	setWireframe( wireframeEnabled ) {		if ( wireframeEnabled ) {			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe;			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe;		} else {			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture;			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture;		}	}	setSkin( index ) {		if ( this.meshBody && this.meshBody.material.wireframe === false ) {			this.meshBody.material.map = this.skinsBody[ index ];			this.currentSkin = index;		}	}	setWeapon( index ) {		for ( let i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;		const activeWeapon = this.weapons[ index ];		if ( activeWeapon ) {			activeWeapon.visible = true;			this.meshWeapon = activeWeapon;			if ( this.activeAnimation ) {				activeWeapon.playAnimation( this.activeAnimation );				this.meshWeapon.setAnimationTime( this.activeAnimation, this.meshBody.getAnimationTime( this.activeAnimation ) );			}		}	}	setAnimation( animationName ) {		if ( animationName === this.activeAnimation || ! animationName ) return;		if ( this.meshBody ) {			this.meshBody.setAnimationWeight( animationName, 0 );			this.meshBody.playAnimation( animationName );			this.oldAnimation = this.activeAnimation;			this.activeAnimation = animationName;			this.blendCounter = this.transitionFrames;		}		if ( this.meshWeapon ) {			this.meshWeapon.setAnimationWeight( animationName, 0 );			this.meshWeapon.playAnimation( animationName );		}	}	update( delta ) {		if ( this.controls ) this.updateMovementModel( delta );		if ( this.animations ) {			this.updateBehaviors();			this.updateAnimations( delta );		}	}	updateAnimations( delta ) {		let mix = 1;		if ( this.blendCounter > 0 ) {			mix = ( this.transitionFrames - this.blendCounter ) / this.transitionFrames;			this.blendCounter -= 1;		}		if ( this.meshBody ) {			this.meshBody.update( delta );			this.meshBody.setAnimationWeight( this.activeAnimation, mix );			this.meshBody.setAnimationWeight( this.oldAnimation, 1 - mix );		}		if ( this.meshWeapon ) {			this.meshWeapon.update( delta );			this.meshWeapon.setAnimationWeight( this.activeAnimation, mix );			this.meshWeapon.setAnimationWeight( this.oldAnimation, 1 - mix );		}	}	updateBehaviors() {		const controls = this.controls;		const animations = this.animations;		let moveAnimation, idleAnimation;		// crouch vs stand		if ( controls.crouch ) {			moveAnimation = animations[ 'crouchMove' ];			idleAnimation = animations[ 'crouchIdle' ];		} else {			moveAnimation = animations[ 'move' ];			idleAnimation = animations[ 'idle' ];		}		// actions		if ( controls.jump ) {			moveAnimation = animations[ 'jump' ];			idleAnimation = animations[ 'jump' ];		}		if ( controls.attack ) {			if ( controls.crouch ) {				moveAnimation = animations[ 'crouchAttack' ];				idleAnimation = animations[ 'crouchAttack' ];			} else {				moveAnimation = animations[ 'attack' ];				idleAnimation = animations[ 'attack' ];			}		}		// set animations		if ( controls.moveForward || controls.moveBackward || controls.moveLeft || controls.moveRight ) {			if ( this.activeAnimation !== moveAnimation ) {				this.setAnimation( moveAnimation );			}		}		if ( Math.abs( this.speed ) < 0.2 * this.maxSpeed && ! ( controls.moveLeft || controls.moveRight || controls.moveForward || controls.moveBackward ) ) {			if ( this.activeAnimation !== idleAnimation ) {				this.setAnimation( idleAnimation );			}		}		// set animation direction		if ( controls.moveForward ) {			if ( this.meshBody ) {				this.meshBody.setAnimationDirectionForward( this.activeAnimation );				this.meshBody.setAnimationDirectionForward( this.oldAnimation );			}			if ( this.meshWeapon ) {				this.meshWeapon.setAnimationDirectionForward( this.activeAnimation );				this.meshWeapon.setAnimationDirectionForward( this.oldAnimation );			}		}		if ( controls.moveBackward ) {			if ( this.meshBody ) {				this.meshBody.setAnimationDirectionBackward( this.activeAnimation );				this.meshBody.setAnimationDirectionBackward( this.oldAnimation );			}			if ( this.meshWeapon ) {				this.meshWeapon.setAnimationDirectionBackward( this.activeAnimation );				this.meshWeapon.setAnimationDirectionBackward( this.oldAnimation );			}		}	}	updateMovementModel( delta ) {		function exponentialEaseOut( k ) {			return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;		}		const controls = this.controls;		// speed based on controls		if ( controls.crouch ) 	this.maxSpeed = this.crouchSpeed;		else this.maxSpeed = this.walkSpeed;		this.maxReverseSpeed = - this.maxSpeed;		if ( controls.moveForward ) this.speed = MathUtils.clamp( this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );		if ( controls.moveBackward ) this.speed = MathUtils.clamp( this.speed - delta * this.backAcceleration, this.maxReverseSpeed, this.maxSpeed );		// orientation based on controls		// (don't just stand while turning)		const dir = 1;		if ( controls.moveLeft ) {			this.bodyOrientation += delta * this.angularSpeed;			this.speed = MathUtils.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );		}		if ( controls.moveRight ) {			this.bodyOrientation -= delta * this.angularSpeed;			this.speed = MathUtils.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );		}		// speed decay		if ( ! ( controls.moveForward || controls.moveBackward ) ) {			if ( this.speed > 0 ) {				const k = exponentialEaseOut( this.speed / this.maxSpeed );				this.speed = MathUtils.clamp( this.speed - k * delta * this.frontDecceleration, 0, this.maxSpeed );			} else {				const k = exponentialEaseOut( this.speed / this.maxReverseSpeed );				this.speed = MathUtils.clamp( this.speed + k * delta * this.backAcceleration, this.maxReverseSpeed, 0 );			}		}		// displacement		const forwardDelta = this.speed * delta;		this.root.position.x += Math.sin( this.bodyOrientation ) * forwardDelta;		this.root.position.z += Math.cos( this.bodyOrientation ) * forwardDelta;		// steering		this.root.rotation.y = this.bodyOrientation;	}	// internal	_createPart( geometry, skinMap ) {		const materialWireframe = new MeshLambertMaterial( { color: 0xffaa00, wireframe: true } );		const materialTexture = new MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap } );		//		const mesh = new MorphBlendMesh( geometry, materialTexture );		mesh.rotation.y = - Math.PI / 2;		//		mesh.materialTexture = materialTexture;		mesh.materialWireframe = materialWireframe;		//		mesh.autoCreateAnimations( this.animationFPS );		return mesh;	}}export { MD2CharacterComplex };
 |