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 };
							 |