277 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import {
 | |
| 	AnimationMixer,
 | |
| 	Box3,
 | |
| 	Mesh,
 | |
| 	MeshLambertMaterial,
 | |
| 	Object3D,
 | |
| 	TextureLoader,
 | |
| 	UVMapping,
 | |
| 	SRGBColorSpace
 | |
| } from 'three';
 | |
| import { MD2Loader } from '../loaders/MD2Loader.js';
 | |
| 
 | |
| class MD2Character {
 | |
| 
 | |
| 	constructor() {
 | |
| 
 | |
| 		this.scale = 1;
 | |
| 		this.animationFPS = 6;
 | |
| 
 | |
| 		this.root = new Object3D();
 | |
| 
 | |
| 		this.meshBody = null;
 | |
| 		this.meshWeapon = null;
 | |
| 
 | |
| 		this.skinsBody = [];
 | |
| 		this.skinsWeapon = [];
 | |
| 
 | |
| 		this.weapons = [];
 | |
| 
 | |
| 		this.activeAnimation = null;
 | |
| 
 | |
| 		this.mixer = null;
 | |
| 
 | |
| 		this.onLoadComplete = function () {};
 | |
| 
 | |
| 		this.loadCounter = 0;
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	loadParts( config ) {
 | |
| 
 | |
| 		const scope = this;
 | |
| 
 | |
| 		function createPart( geometry, skinMap ) {
 | |
| 
 | |
| 			const materialWireframe = new MeshLambertMaterial( { color: 0xffaa00, wireframe: true } );
 | |
| 			const materialTexture = new MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap } );
 | |
| 
 | |
| 			//
 | |
| 
 | |
| 			const mesh = new Mesh( geometry, materialTexture );
 | |
| 			mesh.rotation.y = - Math.PI / 2;
 | |
| 
 | |
| 			mesh.castShadow = true;
 | |
| 			mesh.receiveShadow = true;
 | |
| 
 | |
| 			//
 | |
| 
 | |
| 			mesh.materialTexture = materialTexture;
 | |
| 			mesh.materialWireframe = materialWireframe;
 | |
| 
 | |
| 			return mesh;
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		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 ].colorSpace = SRGBColorSpace;
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			return textures;
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		function checkLoadingComplete() {
 | |
| 
 | |
| 			scope.loadCounter -= 1;
 | |
| 
 | |
| 			if ( scope.loadCounter === 0 ) scope.onLoadComplete();
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		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 = createPart( geo, scope.skinsBody[ 0 ] );
 | |
| 			mesh.scale.set( scope.scale, scope.scale, scope.scale );
 | |
| 
 | |
| 			scope.root.add( mesh );
 | |
| 
 | |
| 			scope.meshBody = mesh;
 | |
| 
 | |
| 			scope.meshBody.clipOffset = 0;
 | |
| 			scope.activeAnimationClipName = mesh.geometry.animations[ 0 ].name;
 | |
| 
 | |
| 			scope.mixer = new AnimationMixer( mesh );
 | |
| 
 | |
| 			checkLoadingComplete();
 | |
| 
 | |
| 		} );
 | |
| 
 | |
| 		// WEAPONS
 | |
| 
 | |
| 		const generateCallback = function ( index, name ) {
 | |
| 
 | |
| 			return function ( geo ) {
 | |
| 
 | |
| 				const mesh = 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;
 | |
| 
 | |
| 				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 ( rate !== 0 ) {
 | |
| 
 | |
| 			this.mixer.timeScale = 1 / rate;
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 			this.mixer.timeScale = 0;
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	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 ];
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 
 | |
| 			this.syncWeaponAnimation();
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	setAnimation( clipName ) {
 | |
| 
 | |
| 		if ( this.meshBody ) {
 | |
| 
 | |
| 			if ( this.meshBody.activeAction ) {
 | |
| 
 | |
| 				this.meshBody.activeAction.stop();
 | |
| 				this.meshBody.activeAction = null;
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			const action = this.mixer.clipAction( clipName, this.meshBody );
 | |
| 
 | |
| 			if ( action ) {
 | |
| 
 | |
| 				this.meshBody.activeAction = action.play();
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		this.activeClipName = clipName;
 | |
| 
 | |
| 		this.syncWeaponAnimation();
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	syncWeaponAnimation() {
 | |
| 
 | |
| 		const clipName = this.activeClipName;
 | |
| 
 | |
| 		if ( this.meshWeapon ) {
 | |
| 
 | |
| 			if ( this.meshWeapon.activeAction ) {
 | |
| 
 | |
| 				this.meshWeapon.activeAction.stop();
 | |
| 				this.meshWeapon.activeAction = null;
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			const action = this.mixer.clipAction( clipName, this.meshWeapon );
 | |
| 
 | |
| 			if ( action ) {
 | |
| 
 | |
| 				this.meshWeapon.activeAction = action.syncWith( this.meshBody.activeAction ).play();
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	update( delta ) {
 | |
| 
 | |
| 		if ( this.mixer ) this.mixer.update( delta );
 | |
| 
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| export { MD2Character };
 |