Browse Source

Merge pull request #16722 from Mugen87/dev30

JSM: Added module and TS files for MD2Loader and MD2Character.
Michael Herzog 6 years ago
parent
commit
022cf74911

+ 3 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -178,6 +178,7 @@
 						<li>KMZLoader</li>
 						<li>KTXLoader</li>
 						<li>LWOLoader</li>
+						<li>MD2Loader</li>
 						<li>MTLLoader</li>
 						<li>OBJLoader</li>
 						<li>PCDLoader</li>
@@ -207,6 +208,8 @@
 				</li>
 				<li>misc
 					<ul>
+						<li>Car</li>
+						<li>MD2Character</li>
 						<li>Ocean</li>
 					</ul>
 				</li>

+ 1 - 1
examples/js/MD2Character.js → examples/js/misc/MD2Character.js

@@ -46,7 +46,7 @@ THREE.MD2Character = function () {
 
 			var boundingBox = new THREE.Box3();
 			boundingBox.setFromBufferAttribute( geo.attributes.position );
-			
+
 			scope.root.position.y = - scope.scale * boundingBox.min.y;
 
 			var mesh = createPart( geo, scope.skinsBody[ 0 ] );

+ 14 - 0
examples/jsm/loaders/MD2Loader.d.ts

@@ -0,0 +1,14 @@
+import {
+  BufferGeometry,
+  LoadingManager
+} from '../../../src/Three';
+
+export class MD2Loader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+
+  load(url: string, onLoad: (geometry: BufferGeometry) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setPath(path: string): this;
+  parse(data: ArrayBuffer): BufferGeometry;
+}

+ 398 - 0
examples/jsm/loaders/MD2Loader.js

@@ -0,0 +1,398 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+import {
+	AnimationClip,
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	Vector3
+} from "../../../build/three.module.js";
+
+var MD2Loader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+};
+
+MD2Loader.prototype = {
+
+	constructor: MD2Loader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( buffer ) {
+
+			onLoad( scope.parse( buffer ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: ( function () {
+
+		var normalData = [
+			[ - 0.525731, 0.000000, 0.850651 ], [ - 0.442863, 0.238856, 0.864188 ],
+			[ - 0.295242, 0.000000, 0.955423 ], [ - 0.309017, 0.500000, 0.809017 ],
+			[ - 0.162460, 0.262866, 0.951056 ], [ 0.000000, 0.000000, 1.000000 ],
+			[ 0.000000, 0.850651, 0.525731 ], [ - 0.147621, 0.716567, 0.681718 ],
+			[ 0.147621, 0.716567, 0.681718 ], [ 0.000000, 0.525731, 0.850651 ],
+			[ 0.309017, 0.500000, 0.809017 ], [ 0.525731, 0.000000, 0.850651 ],
+			[ 0.295242, 0.000000, 0.955423 ], [ 0.442863, 0.238856, 0.864188 ],
+			[ 0.162460, 0.262866, 0.951056 ], [ - 0.681718, 0.147621, 0.716567 ],
+			[ - 0.809017, 0.309017, 0.500000 ], [ - 0.587785, 0.425325, 0.688191 ],
+			[ - 0.850651, 0.525731, 0.000000 ], [ - 0.864188, 0.442863, 0.238856 ],
+			[ - 0.716567, 0.681718, 0.147621 ], [ - 0.688191, 0.587785, 0.425325 ],
+			[ - 0.500000, 0.809017, 0.309017 ], [ - 0.238856, 0.864188, 0.442863 ],
+			[ - 0.425325, 0.688191, 0.587785 ], [ - 0.716567, 0.681718, - 0.147621 ],
+			[ - 0.500000, 0.809017, - 0.309017 ], [ - 0.525731, 0.850651, 0.000000 ],
+			[ 0.000000, 0.850651, - 0.525731 ], [ - 0.238856, 0.864188, - 0.442863 ],
+			[ 0.000000, 0.955423, - 0.295242 ], [ - 0.262866, 0.951056, - 0.162460 ],
+			[ 0.000000, 1.000000, 0.000000 ], [ 0.000000, 0.955423, 0.295242 ],
+			[ - 0.262866, 0.951056, 0.162460 ], [ 0.238856, 0.864188, 0.442863 ],
+			[ 0.262866, 0.951056, 0.162460 ], [ 0.500000, 0.809017, 0.309017 ],
+			[ 0.238856, 0.864188, - 0.442863 ], [ 0.262866, 0.951056, - 0.162460 ],
+			[ 0.500000, 0.809017, - 0.309017 ], [ 0.850651, 0.525731, 0.000000 ],
+			[ 0.716567, 0.681718, 0.147621 ], [ 0.716567, 0.681718, - 0.147621 ],
+			[ 0.525731, 0.850651, 0.000000 ], [ 0.425325, 0.688191, 0.587785 ],
+			[ 0.864188, 0.442863, 0.238856 ], [ 0.688191, 0.587785, 0.425325 ],
+			[ 0.809017, 0.309017, 0.500000 ], [ 0.681718, 0.147621, 0.716567 ],
+			[ 0.587785, 0.425325, 0.688191 ], [ 0.955423, 0.295242, 0.000000 ],
+			[ 1.000000, 0.000000, 0.000000 ], [ 0.951056, 0.162460, 0.262866 ],
+			[ 0.850651, - 0.525731, 0.000000 ], [ 0.955423, - 0.295242, 0.000000 ],
+			[ 0.864188, - 0.442863, 0.238856 ], [ 0.951056, - 0.162460, 0.262866 ],
+			[ 0.809017, - 0.309017, 0.500000 ], [ 0.681718, - 0.147621, 0.716567 ],
+			[ 0.850651, 0.000000, 0.525731 ], [ 0.864188, 0.442863, - 0.238856 ],
+			[ 0.809017, 0.309017, - 0.500000 ], [ 0.951056, 0.162460, - 0.262866 ],
+			[ 0.525731, 0.000000, - 0.850651 ], [ 0.681718, 0.147621, - 0.716567 ],
+			[ 0.681718, - 0.147621, - 0.716567 ], [ 0.850651, 0.000000, - 0.525731 ],
+			[ 0.809017, - 0.309017, - 0.500000 ], [ 0.864188, - 0.442863, - 0.238856 ],
+			[ 0.951056, - 0.162460, - 0.262866 ], [ 0.147621, 0.716567, - 0.681718 ],
+			[ 0.309017, 0.500000, - 0.809017 ], [ 0.425325, 0.688191, - 0.587785 ],
+			[ 0.442863, 0.238856, - 0.864188 ], [ 0.587785, 0.425325, - 0.688191 ],
+			[ 0.688191, 0.587785, - 0.425325 ], [ - 0.147621, 0.716567, - 0.681718 ],
+			[ - 0.309017, 0.500000, - 0.809017 ], [ 0.000000, 0.525731, - 0.850651 ],
+			[ - 0.525731, 0.000000, - 0.850651 ], [ - 0.442863, 0.238856, - 0.864188 ],
+			[ - 0.295242, 0.000000, - 0.955423 ], [ - 0.162460, 0.262866, - 0.951056 ],
+			[ 0.000000, 0.000000, - 1.000000 ], [ 0.295242, 0.000000, - 0.955423 ],
+			[ 0.162460, 0.262866, - 0.951056 ], [ - 0.442863, - 0.238856, - 0.864188 ],
+			[ - 0.309017, - 0.500000, - 0.809017 ], [ - 0.162460, - 0.262866, - 0.951056 ],
+			[ 0.000000, - 0.850651, - 0.525731 ], [ - 0.147621, - 0.716567, - 0.681718 ],
+			[ 0.147621, - 0.716567, - 0.681718 ], [ 0.000000, - 0.525731, - 0.850651 ],
+			[ 0.309017, - 0.500000, - 0.809017 ], [ 0.442863, - 0.238856, - 0.864188 ],
+			[ 0.162460, - 0.262866, - 0.951056 ], [ 0.238856, - 0.864188, - 0.442863 ],
+			[ 0.500000, - 0.809017, - 0.309017 ], [ 0.425325, - 0.688191, - 0.587785 ],
+			[ 0.716567, - 0.681718, - 0.147621 ], [ 0.688191, - 0.587785, - 0.425325 ],
+			[ 0.587785, - 0.425325, - 0.688191 ], [ 0.000000, - 0.955423, - 0.295242 ],
+			[ 0.000000, - 1.000000, 0.000000 ], [ 0.262866, - 0.951056, - 0.162460 ],
+			[ 0.000000, - 0.850651, 0.525731 ], [ 0.000000, - 0.955423, 0.295242 ],
+			[ 0.238856, - 0.864188, 0.442863 ], [ 0.262866, - 0.951056, 0.162460 ],
+			[ 0.500000, - 0.809017, 0.309017 ], [ 0.716567, - 0.681718, 0.147621 ],
+			[ 0.525731, - 0.850651, 0.000000 ], [ - 0.238856, - 0.864188, - 0.442863 ],
+			[ - 0.500000, - 0.809017, - 0.309017 ], [ - 0.262866, - 0.951056, - 0.162460 ],
+			[ - 0.850651, - 0.525731, 0.000000 ], [ - 0.716567, - 0.681718, - 0.147621 ],
+			[ - 0.716567, - 0.681718, 0.147621 ], [ - 0.525731, - 0.850651, 0.000000 ],
+			[ - 0.500000, - 0.809017, 0.309017 ], [ - 0.238856, - 0.864188, 0.442863 ],
+			[ - 0.262866, - 0.951056, 0.162460 ], [ - 0.864188, - 0.442863, 0.238856 ],
+			[ - 0.809017, - 0.309017, 0.500000 ], [ - 0.688191, - 0.587785, 0.425325 ],
+			[ - 0.681718, - 0.147621, 0.716567 ], [ - 0.442863, - 0.238856, 0.864188 ],
+			[ - 0.587785, - 0.425325, 0.688191 ], [ - 0.309017, - 0.500000, 0.809017 ],
+			[ - 0.147621, - 0.716567, 0.681718 ], [ - 0.425325, - 0.688191, 0.587785 ],
+			[ - 0.162460, - 0.262866, 0.951056 ], [ 0.442863, - 0.238856, 0.864188 ],
+			[ 0.162460, - 0.262866, 0.951056 ], [ 0.309017, - 0.500000, 0.809017 ],
+			[ 0.147621, - 0.716567, 0.681718 ], [ 0.000000, - 0.525731, 0.850651 ],
+			[ 0.425325, - 0.688191, 0.587785 ], [ 0.587785, - 0.425325, 0.688191 ],
+			[ 0.688191, - 0.587785, 0.425325 ], [ - 0.955423, 0.295242, 0.000000 ],
+			[ - 0.951056, 0.162460, 0.262866 ], [ - 1.000000, 0.000000, 0.000000 ],
+			[ - 0.850651, 0.000000, 0.525731 ], [ - 0.955423, - 0.295242, 0.000000 ],
+			[ - 0.951056, - 0.162460, 0.262866 ], [ - 0.864188, 0.442863, - 0.238856 ],
+			[ - 0.951056, 0.162460, - 0.262866 ], [ - 0.809017, 0.309017, - 0.500000 ],
+			[ - 0.864188, - 0.442863, - 0.238856 ], [ - 0.951056, - 0.162460, - 0.262866 ],
+			[ - 0.809017, - 0.309017, - 0.500000 ], [ - 0.681718, 0.147621, - 0.716567 ],
+			[ - 0.681718, - 0.147621, - 0.716567 ], [ - 0.850651, 0.000000, - 0.525731 ],
+			[ - 0.688191, 0.587785, - 0.425325 ], [ - 0.587785, 0.425325, - 0.688191 ],
+			[ - 0.425325, 0.688191, - 0.587785 ], [ - 0.425325, - 0.688191, - 0.587785 ],
+			[ - 0.587785, - 0.425325, - 0.688191 ], [ - 0.688191, - 0.587785, - 0.425325 ]
+		];
+
+		return function ( buffer ) {
+
+			console.time( 'MD2Loader' );
+
+			var data = new DataView( buffer );
+
+			// http://tfc.duke.free.fr/coding/md2-specs-en.html
+
+			var header = {};
+			var headerNames = [
+				'ident', 'version',
+				'skinwidth', 'skinheight',
+				'framesize',
+				'num_skins', 'num_vertices', 'num_st', 'num_tris', 'num_glcmds', 'num_frames',
+				'offset_skins', 'offset_st', 'offset_tris', 'offset_frames', 'offset_glcmds', 'offset_end'
+			];
+
+			for ( var i = 0; i < headerNames.length; i ++ ) {
+
+				header[ headerNames[ i ] ] = data.getInt32( i * 4, true );
+
+			}
+
+			if ( header.ident !== 844121161 || header.version !== 8 ) {
+
+				console.error( 'Not a valid MD2 file' );
+				return;
+
+			}
+
+			if ( header.offset_end !== data.byteLength ) {
+
+				console.error( 'Corrupted MD2 file' );
+				return;
+
+			}
+
+			//
+
+			var geometry = new BufferGeometry();
+
+			// uvs
+
+			var uvsTemp = [];
+			var offset = header.offset_st;
+
+			for ( var i = 0, l = header.num_st; i < l; i ++ ) {
+
+				var u = data.getInt16( offset + 0, true );
+				var v = data.getInt16( offset + 2, true );
+
+				uvsTemp.push( u / header.skinwidth, 1 - ( v / header.skinheight ) );
+
+				offset += 4;
+
+			}
+
+			// triangles
+
+			offset = header.offset_tris;
+
+			var vertexIndices = [];
+			var uvIndices = [];
+
+			for ( var i = 0, l = header.num_tris; i < l; i ++ ) {
+
+				vertexIndices.push(
+					data.getUint16( offset + 0, true ),
+					data.getUint16( offset + 2, true ),
+					data.getUint16( offset + 4, true )
+				);
+
+				uvIndices.push(
+					data.getUint16( offset + 6, true ),
+					data.getUint16( offset + 8, true ),
+					data.getUint16( offset + 10, true )
+				);
+
+				offset += 12;
+
+			}
+
+			// frames
+
+			var translation = new Vector3();
+			var scale = new Vector3();
+			var string = [];
+
+			var frames = [];
+
+			offset = header.offset_frames;
+
+			for ( var i = 0, l = header.num_frames; i < l; i ++ ) {
+
+				scale.set(
+					data.getFloat32( offset + 0, true ),
+					data.getFloat32( offset + 4, true ),
+					data.getFloat32( offset + 8, true )
+				);
+
+				translation.set(
+					data.getFloat32( offset + 12, true ),
+					data.getFloat32( offset + 16, true ),
+					data.getFloat32( offset + 20, true )
+				);
+
+				offset += 24;
+
+				for ( var j = 0; j < 16; j ++ ) {
+
+					var character = data.getUint8( offset + j, true );
+					if ( character === 0 ) break;
+
+					string[ j ] = character;
+
+				}
+
+				var frame = {
+					name: String.fromCharCode.apply( null, string ),
+					vertices: [],
+					normals: []
+				};
+
+				offset += 16;
+
+				for ( var j = 0; j < header.num_vertices; j ++ ) {
+
+					var x = data.getUint8( offset ++, true );
+					var y = data.getUint8( offset ++, true );
+					var z = data.getUint8( offset ++, true );
+					var n = normalData[ data.getUint8( offset ++, true ) ];
+
+					x = x * scale.x + translation.x;
+					y = y * scale.y + translation.y;
+					z = z * scale.z + translation.z;
+
+					frame.vertices.push( x, z, y ); // convert to Y-up
+					frame.normals.push( n[ 0 ], n[ 2 ], n[ 1 ] ); // convert to Y-up
+
+				}
+
+				frames.push( frame );
+
+			}
+
+			// static
+
+			var positions = [];
+			var normals = [];
+			var uvs = [];
+
+			var verticesTemp = frames[ 0 ].vertices;
+			var normalsTemp = frames[ 0 ].normals;
+
+			for ( var i = 0, l = vertexIndices.length; i < l; i ++ ) {
+
+				var vertexIndex = vertexIndices[ i ];
+				var stride = vertexIndex * 3;
+
+				//
+
+				var x = verticesTemp[ stride ];
+				var y = verticesTemp[ stride + 1 ];
+				var z = verticesTemp[ stride + 2 ];
+
+				positions.push( x, y, z );
+
+				//
+
+				var nx = normalsTemp[ stride ];
+				var ny = normalsTemp[ stride + 1 ];
+				var nz = normalsTemp[ stride + 2 ];
+
+				normals.push( nx, ny, nz );
+
+				//
+
+				var uvIndex = uvIndices[ i ];
+				stride = uvIndex * 2;
+
+				var u = uvsTemp[ stride ];
+				var v = uvsTemp[ stride + 1 ];
+
+				uvs.push( u, v );
+
+			}
+
+			geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
+			geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+			geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+
+			// animation
+
+			var morphPositions = [];
+			var morphNormals = [];
+
+			for ( var i = 0, l = frames.length; i < l; i ++ ) {
+
+				var frame = frames[ i ];
+				var attributeName = frame.name;
+
+				if ( frame.vertices.length > 0 ) {
+
+					var positions = [];
+
+					for ( var j = 0, jl = vertexIndices.length; j < jl; j ++ ) {
+
+						var vertexIndex = vertexIndices[ j ];
+						var stride = vertexIndex * 3;
+
+						var x = frame.vertices[ stride ];
+						var y = frame.vertices[ stride + 1 ];
+						var z = frame.vertices[ stride + 2 ];
+
+						positions.push( x, y, z );
+
+					}
+
+					var positionAttribute = new Float32BufferAttribute( positions, 3 );
+					positionAttribute.name = attributeName;
+
+					morphPositions.push( positionAttribute );
+
+				}
+
+				if ( frame.normals.length > 0 ) {
+
+					var normals = [];
+
+					for ( var j = 0, jl = vertexIndices.length; j < jl; j ++ ) {
+
+						var vertexIndex = vertexIndices[ j ];
+						var stride = vertexIndex * 3;
+
+						var nx = frame.normals[ stride ];
+						var ny = frame.normals[ stride + 1 ];
+						var nz = frame.normals[ stride + 2 ];
+
+						normals.push( nx, ny, nz );
+
+					}
+
+					var normalAttribute = new Float32BufferAttribute( normals, 3 );
+					normalAttribute.name = attributeName;
+
+					morphNormals.push( normalAttribute );
+
+				}
+
+			}
+
+			geometry.morphAttributes.position = morphPositions;
+			geometry.morphAttributes.normal = morphNormals;
+
+			geometry.animations = AnimationClip.CreateClipsFromMorphTargetSequences( frames, 10 );
+
+			console.timeEnd( 'MD2Loader' );
+
+			return geometry;
+
+		};
+
+	} )()
+
+};
+
+export { MD2Loader };

+ 1 - 1
examples/jsm/misc/ConvexObjectBreaker.d.ts

@@ -1,5 +1,5 @@
 import {
-  Object3D
+  Object3D,
   Plane,
   Vector3
 } from '../../../src/Three';

+ 24 - 0
examples/jsm/misc/MD2Character.d.ts

@@ -0,0 +1,24 @@
+import {
+  Object3D,
+  Mesh,
+  Texture
+} from '../../../src/Three';
+
+export class MD2Character {
+  constructor();
+  scale: number;
+  animationFPS: number;
+  root: Object3D;
+  meshBody: Mesh | null;
+  meshWeapon: Mesh | null;
+  skinsBody: Texture[];
+  skinsWeapon: Texture[];
+
+  setPlaybackRate(rate: number): void;
+  setWireframe(wireframeEnabled: boolean): void;
+  setSkin(index: number): void;
+  setWeapon(index: number): void;
+  setAnimation(clipName: string): void;
+  syncWeaponAnimation(): void;
+  update(delta: number): void;
+}

+ 274 - 0
examples/jsm/misc/MD2Character.js

@@ -0,0 +1,274 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+import {
+	AnimationMixer,
+	Box3,
+	Mesh,
+	MeshLambertMaterial,
+	Object3D,
+	TextureLoader,
+	UVMapping
+} from "../../../build/three.module.js";
+import { MD2Loader } from "../loaders/MD2Loader.js";
+
+var MD2Character = function () {
+
+	var scope = this;
+
+	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;
+
+	this.loadParts = function ( config ) {
+
+		this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
+
+		var weaponsTextures = [];
+		for ( var 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
+
+		var loader = new MD2Loader();
+
+		loader.load( config.baseUrl + config.body, function ( geo ) {
+
+			var boundingBox = new Box3();
+			boundingBox.setFromBufferAttribute( geo.attributes.position );
+
+			scope.root.position.y = - scope.scale * boundingBox.min.y;
+
+			var 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
+
+		var generateCallback = function ( index, name ) {
+
+			return function ( geo ) {
+
+				var 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 ( var i = 0; i < config.weapons.length; i ++ ) {
+
+			loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );
+
+		}
+
+	};
+
+	this.setPlaybackRate = function ( rate ) {
+
+		if ( rate !== 0 ) {
+
+			this.mixer.timeScale = 1 / rate;
+
+		} else {
+
+			this.mixer.timeScale = 0;
+
+		}
+
+	};
+
+	this.setWireframe = function ( 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;
+
+		}
+
+	};
+
+	this.setSkin = function ( index ) {
+
+		if ( this.meshBody && this.meshBody.material.wireframe === false ) {
+
+			this.meshBody.material.map = this.skinsBody[ index ];
+
+		}
+
+	};
+
+	this.setWeapon = function ( index ) {
+
+		for ( var i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;
+
+		var activeWeapon = this.weapons[ index ];
+
+		if ( activeWeapon ) {
+
+			activeWeapon.visible = true;
+			this.meshWeapon = activeWeapon;
+
+			scope.syncWeaponAnimation();
+
+		}
+
+	};
+
+	this.setAnimation = function ( clipName ) {
+
+		if ( this.meshBody ) {
+
+			if ( this.meshBody.activeAction ) {
+
+				this.meshBody.activeAction.stop();
+				this.meshBody.activeAction = null;
+
+			}
+
+			var action = this.mixer.clipAction( clipName, this.meshBody );
+
+			if ( action ) {
+
+				this.meshBody.activeAction = action.play();
+
+			}
+
+		}
+
+		scope.activeClipName = clipName;
+
+		scope.syncWeaponAnimation();
+
+	};
+
+	this.syncWeaponAnimation = function () {
+
+		var clipName = scope.activeClipName;
+
+		if ( scope.meshWeapon ) {
+
+			if ( this.meshWeapon.activeAction ) {
+
+				this.meshWeapon.activeAction.stop();
+				this.meshWeapon.activeAction = null;
+
+			}
+
+			var action = this.mixer.clipAction( clipName, this.meshWeapon );
+
+			if ( action ) {
+
+				this.meshWeapon.activeAction = action.syncWith( this.meshBody.activeAction ).play();
+
+			}
+
+		}
+
+	};
+
+	this.update = function ( delta ) {
+
+		if ( this.mixer ) this.mixer.update( delta );
+
+	};
+
+	function loadTextures( baseUrl, textureUrls ) {
+
+		var textureLoader = new TextureLoader();
+		var textures = [];
+
+		for ( var i = 0; i < textureUrls.length; i ++ ) {
+
+			textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );
+			textures[ i ].mapping = UVMapping;
+			textures[ i ].name = textureUrls[ i ];
+
+		}
+
+		return textures;
+
+	}
+
+	function createPart( geometry, skinMap ) {
+
+		var materialWireframe = new MeshLambertMaterial( { color: 0xffaa00, wireframe: true, morphTargets: true, morphNormals: true } );
+		var materialTexture = new MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true } );
+
+		//
+
+		var 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 checkLoadingComplete() {
+
+		scope.loadCounter -= 1;
+
+		if ( scope.loadCounter === 0 ) scope.onLoadComplete();
+
+	}
+
+};
+
+export { MD2Character };

+ 1 - 1
examples/webgl_loader_md2.html

@@ -18,7 +18,7 @@
 		<script src="js/controls/OrbitControls.js"></script>
 
 		<script src="js/loaders/MD2Loader.js"></script>
-		<script src="js/MD2Character.js"></script>
+		<script src="js/misc/MD2Character.js"></script>
 
 		<script src="js/libs/stats.min.js"></script>
 		<script src="js/libs/dat.gui.min.js"></script>

+ 2 - 0
utils/modularize.js

@@ -82,6 +82,7 @@ var files = [
 	{ path: 'loaders/KMZLoader.js', dependencies: [ { name: 'ColladaLoader', path: 'loaders/ColladaLoader.js' } ], ignoreList: [] },
 	{ path: 'loaders/LWOLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/KTXLoader.js', dependencies: [], ignoreList: [] },
+	{ path: 'loaders/MD2Loader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/MTLLoader.js', dependencies: [], ignoreList: [ 'BackSide', 'DoubleSide', 'ClampToEdgeWrapping', 'MirroredRepeatWrapping' ] },
 	{ path: 'loaders/OBJLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/PCDLoader.js', dependencies: [], ignoreList: [] },
@@ -107,6 +108,7 @@ var files = [
 
 	{ path: 'misc/CarControls.js', dependencies: [], ignoreList: [] },
 	{ path: 'misc/ConvexObjectBreaker.js', dependencies: [ { name: 'ConvexBufferGeometry', path: 'geometries/ConvexGeometry.js' } ], ignoreList: [ 'Matrix4' ] },
+	{ path: 'misc/MD2Character.js', dependencies: [ { name: 'MD2Loader', path: 'loaders/MD2Loader.js' } ], ignoreList: [] },
 	{ path: 'misc/Ocean.js', dependencies: [ { name: 'OceanShaders', path: 'shaders/OceanShaders.js' } ], ignoreList: [] },
 
 	{ path: 'modifiers/ExplodeModifier.js', dependencies: [], ignoreList: [] },