Browse Source

THREE.MMDLoader and its example implementation.

takahiro 9 years ago
parent
commit
7907a41034

+ 1 - 0
examples/index.html

@@ -267,6 +267,7 @@
 				"webgl_loader_json_objconverter",
 				"webgl_loader_md2",
 				"webgl_loader_md2_control",
+				"webgl_loader_mmd",
 				"webgl_loader_msgpack",
 				"webgl_loader_obj",
 				"webgl_loader_obj_mtl",

+ 137 - 0
examples/js/animation/CCDIKSolver.js

@@ -0,0 +1,137 @@
+/**
+ * @author takahiro / https://github.com/takahirox
+ *
+ * CCD Algorithm
+ *  https://sites.google.com/site/auraliusproject/ccd-algorithm
+ *
+ * mesh.geometry needs to have iks array.
+ *
+ * ik parameter example
+ *
+ * ik = {
+ *	target: 1,
+ *	effector: 2,
+ *	links: [ { index: 5 }, { index: 4, limitation: new THREE.Vector3( 1, 0, 0 ) }, { index : 3 } ],
+ *	iteration: 10,
+ *	minAngle: 0.0,
+ *	maxAngle: 1.0,
+ * };
+ */
+
+THREE.CCDIKSolver = function ( mesh ) {
+
+	this.mesh = mesh;
+
+};
+
+THREE.CCDIKSolver.prototype = {
+
+	constructor: THREE.CCDIKSolver,
+
+	update: function () {
+
+		var effectorVec = new THREE.Vector3();
+		var targetVec = new THREE.Vector3();
+		var axis = new THREE.Vector3();
+		var q = new THREE.Quaternion();
+
+		var bones = this.mesh.skeleton.bones;
+		var iks = this.mesh.geometry.iks;
+
+		// for reference overhead reduction in loop
+		var math = Math;
+
+		for ( var i = 0, il = iks.length; i < il; i++ ) {
+
+			var ik = iks[ i ];
+			var effector = bones[ ik.effector ];
+			var target = bones[ ik.target ];
+			var targetPos = target.getWorldPosition();
+			var links = ik.links;
+			var iteration = ik.iteration !== undefined ? ik.iteration : 1;
+
+			for ( var j = 0; j < iteration; j++ ) {
+
+				for ( var k = 0, kl = links.length; k < kl; k++ ) {
+
+					var link = bones[ links[ k ].index ];
+					var limitation = links[ k ].limitation;
+					var linkPos = link.getWorldPosition();
+					var invLinkQ = link.getWorldQuaternion().inverse();
+					var effectorPos = effector.getWorldPosition();
+
+					// work in link world
+					effectorVec.subVectors( effectorPos, linkPos );
+					effectorVec.applyQuaternion( invLinkQ );
+					effectorVec.normalize();
+
+					targetVec.subVectors( targetPos, linkPos );
+					targetVec.applyQuaternion( invLinkQ );
+					targetVec.normalize();
+
+					var angle = targetVec.dot( effectorVec );
+
+					// TODO: continue (or break) the loop for the performance
+					//       if no longer needs to rotate (angle > 1.0-1e-5 ?)
+
+					if ( angle > 1.0 ) {
+
+						angle = 1.0;
+
+					} else if ( angle < -1.0 ) {
+
+						angle = -1.0;
+
+					}
+
+					angle = math.acos( angle );
+
+					if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
+
+						angle = ik.minAngle;
+
+					}
+
+					if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
+
+						angle = ik.maxAngle;
+
+					}
+
+					axis.crossVectors( effectorVec, targetVec );
+					axis.normalize();
+
+					q.setFromAxisAngle( axis, angle );
+					link.quaternion.multiply( q );
+
+					// TODO: re-consider the limitation specification
+					if ( limitation !== undefined ) {
+
+						var c = link.quaternion.w;
+
+						if ( c > 1.0 ) {
+
+							c = 1.0;
+
+						}
+
+						var c2 = math.sqrt( 1 - c * c );
+						link.quaternion.set( limitation.x * c2,
+						                     limitation.y * c2,
+						                     limitation.z * c2,
+						                     c );
+
+					}
+
+					link.updateMatrixWorld( true );
+
+				}
+
+			}
+
+		}
+
+	},
+
+};
+

File diff suppressed because it is too large
+ 0 - 0
examples/js/libs/charsetencoder.min.js


+ 2036 - 0
examples/js/loaders/MMDLoader.js

@@ -0,0 +1,2036 @@
+/**
+ * @author takahiro / https://github.com/takahirox
+ *
+ * Dependencies
+ *  charset-encoder-js https://github.com/takahirox/charset-encoder-js
+ *
+ *
+ * This loader loads and parses PMD/PMX and VMD binary files
+ * then creates mesh for Three.js.
+ *
+ * PMD/PMX is a model data format and VMD is a motion data format
+ * used in MMD(Miku Miku Dance).
+ *
+ * MMD is a 3D CG animation tool which is popular in Japan.
+ *
+ *
+ * MMD official site
+ *  http://www.geocities.jp/higuchuu4/index_e.htm
+ *
+ * PMD, VMD format
+ *  http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
+ *
+ * PMX format
+ *  http://gulshan-i-raz.geo.jp/labs/2012/10/17/pmx-format1/
+ *
+ * Model data requirements
+ *  convert .tga files to .png files if exist. (Should I use THREE.TGALoader?)
+ *  resize the texture image files to power_of_2*power_of_2
+ *
+ *
+ * TODO
+ *  separate model/vmd loaders.
+ *  multi vmd files support.
+ *  edge(outline) support.
+ *  culling support.
+ *  toon(cel) shadering support.
+ *  add-sphere-mapping support.
+ *  physics support.
+ *  camera motion in vmd support.
+ *  light motion in vmd support.
+ *  music support.
+ *  make own shader for the performance and functionarity.
+ *  SDEF support.
+ *  uv/material/bone morphing support.
+ *  tga file loading support.
+ *  supply skinning support.
+ *  shadow support.
+ */
+
+THREE.MMDLoader = function ( showStatus, manager ) {
+
+	THREE.Loader.call( this, showStatus );
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.MMDLoader.prototype = Object.create( THREE.Loader.prototype );
+THREE.MMDLoader.prototype.constructor = THREE.MMDLoader;
+
+THREE.MMDLoader.prototype.extractExtension = function ( url ) {
+
+	var index = url.lastIndexOf( '.' );
+
+	if ( index < 0 ) {
+
+		return null;
+
+	}
+
+	return url.slice( index + 1 );
+
+};
+
+THREE.MMDLoader.prototype.load = function ( modelUrl, vmdUrl, callback, onProgress, onError ) {
+
+	var texturePath = this.extractUrlBase( modelUrl );
+	var modelExtension = this.extractExtension( modelUrl );
+	this.loadModelFile( modelUrl, vmdUrl, texturePath, modelExtension, callback, onProgress, onError );
+
+};
+
+THREE.MMDLoader.prototype.loadFileAsBuffer = function ( url, onLoad, onProgress, onError ) {
+
+	var loader = new THREE.XHRLoader( this.manager );
+	loader.setCrossOrigin( this.crossOrigin );
+	loader.setResponseType( 'arraybuffer' );
+	loader.load( url, function ( buffer ) {
+
+		onLoad( buffer );
+
+	}, onProgress, onError );
+
+};
+
+THREE.MMDLoader.prototype.loadModelFile = function ( modelUrl, vmdUrl, texturePath, modelExtension, callback, onProgress, onError ) {
+
+	var scope = this;
+
+	this.loadFileAsBuffer( modelUrl, function ( buffer ) {
+
+		scope.loadVmdFile( buffer, vmdUrl, texturePath, modelExtension, callback, onProgress, onError );
+
+	}, onProgress, onError );
+
+};
+
+THREE.MMDLoader.prototype.loadVmdFile = function ( modelBuffer, vmdUrl, texturePath, modelExtension, callback, onProgress, onError ) {
+
+	var scope = this;
+
+	if ( ! vmdUrl ) {
+
+		scope.parse( modelBuffer, null, texturePath, modelExtension, callback );
+		return;
+
+	}
+
+	this.loadFileAsBuffer( vmdUrl, function ( buffer ) {
+
+		scope.parse( modelBuffer, buffer, texturePath, modelExtension, callback );
+
+	}, onProgress, onError );
+
+};
+
+THREE.MMDLoader.prototype.parse = function ( modelBuffer, vmdBuffer, texturePath, modelExtension, callback ) {
+
+	var model = this.parseModel( modelBuffer, modelExtension );
+	var vmd = vmdBuffer !== null ? this.parseVmd( vmdBuffer ) : null;
+	var mesh = this.createMesh( model, vmd, texturePath );
+	callback( mesh );
+
+};
+
+THREE.MMDLoader.prototype.parseModel = function ( buffer, modelExtension ) {
+
+	// Should I judge from model data header?
+	switch( modelExtension.toLowerCase() ) {
+
+		case 'pmd':
+			return this.parsePmd( buffer );
+
+		case 'pmx':
+			return this.parsePmx( buffer );
+
+		default:
+			throw 'extension ' + modelExtension + ' is not supported.';
+
+	}
+
+
+};
+
+THREE.MMDLoader.prototype.parsePmd = function ( buffer ) {
+
+	var scope = this;
+	var pmd = {};
+	var dv = new THREE.MMDLoader.DataView( buffer );
+
+	pmd.metadata = {};
+	pmd.metadata.format = 'pmd';
+
+	var parseHeader = function () {
+
+		var metadata = pmd.metadata;
+		metadata.magic = dv.getChars( 3 );
+
+		if ( metadata.magic !== 'Pmd' ) {
+
+			throw 'PMD file magic is not Pmd, but ' + metadata.magic;
+
+		}
+
+		metadata.version = dv.getFloat32();
+		metadata.modelName = dv.getSjisStringsAsUnicode( 20 );
+		metadata.comment = dv.getSjisStringsAsUnicode( 256 );
+
+	};
+
+	var parseVertices = function () {
+
+		var parseVertex = function () {
+
+			var p = {};
+			p.position = dv.getFloat32Array( 3 );
+			p.normal = dv.getFloat32Array( 3 );
+			p.uv = dv.getFloat32Array( 2 );
+			p.skinIndices = dv.getUint16Array( 2 );
+			p.skinWeights = [ dv.getUint8() / 100 ];
+			p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] );
+			p.edgeFlag = dv.getUint8();
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.vertexCount = dv.getUint32();
+
+		pmd.vertices = [];
+
+		for ( var i = 0; i < metadata.vertexCount; i++ ) {
+
+			pmd.vertices.push( parseVertex() );
+
+		}
+
+	};
+
+	var parseFaces = function () {
+
+		var parseFace = function () {
+
+			var p = {};
+			p.indices = dv.getUint16Array( 3 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.faceCount = dv.getUint32() / 3;
+
+		pmd.faces = [];
+
+		for ( var i = 0; i < metadata.faceCount; i++ ) {
+
+			pmd.faces.push( parseFace() );
+
+		}
+
+	};
+
+	var parseMaterials = function () {
+
+		var parseMaterial = function () {
+
+			var p = {};
+			p.diffuse = dv.getFloat32Array( 4 );
+			p.shiness = dv.getFloat32();
+			p.specular = dv.getFloat32Array( 3 );
+			p.emissive = dv.getFloat32Array( 3 );
+			p.toonIndex = dv.getUint8();
+			p.edgeFlag = dv.getUint8();
+			p.faceCount = dv.getUint32() / 3;
+			p.fileName = dv.getChars( 20 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.materialCount = dv.getUint32();
+
+		pmd.materials = [];
+
+		for ( var i = 0; i < metadata.materialCount; i++ ) {
+
+			pmd.materials.push( parseMaterial() );
+
+		}
+
+	};
+
+	var parseBones = function () {
+
+		var parseBone = function () {
+
+			var p = {};
+			// Skinning animation doesn't work when bone name is Japanese Unicode in r73.
+			// So using charcode strings as workaround and keep original strings in .originalName.
+			p.originalName = dv.getSjisStringsAsUnicode( 20 );
+			p.name = dv.toCharcodeStrings( p.originalName );
+			p.parentIndex = dv.getUint16();
+			p.tailIndex = dv.getUint16();
+			p.type = dv.getUint8();
+			p.ikIndex = dv.getUint16();
+			p.position = dv.getFloat32Array( 3 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.boneCount = dv.getUint16();
+
+		pmd.bones = [];
+
+		for ( var i = 0; i < metadata.boneCount; i++ ) {
+
+			pmd.bones.push( parseBone() );
+
+		}
+
+	};
+
+	var parseIks = function () {
+
+		var parseIk = function () {
+
+			var p = {};
+			p.target = dv.getUint16();
+			p.effector = dv.getUint16();
+			p.linkCount = dv.getUint8();
+			p.iteration = dv.getUint16();
+			p.maxAngle = dv.getFloat32();
+
+			p.links = [];
+			for ( var i = 0; i < p.linkCount; i++ ) {
+
+				var link = {}
+				link.index = dv.getUint16();
+				p.links.push( link );
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.ikCount = dv.getUint16();
+
+		pmd.iks = [];
+
+		for ( var i = 0; i < metadata.ikCount; i++ ) {
+
+			pmd.iks.push( parseIk() );
+
+		}
+
+	};
+
+	var parseMorphs = function () {
+
+		var parseMorph = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 20 );
+			p.elementCount = dv.getUint32();
+			p.type = dv.getUint8();
+
+			p.elements = [];
+			for ( var i = 0; i < p.elementCount; i++ ) {
+
+				p.elements.push( {
+					index: dv.getUint32(),
+					position: dv.getFloat32Array( 3 )
+				} ) ;
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.morphCount = dv.getUint16();
+
+		pmd.morphs = [];
+
+		for ( var i = 0; i < metadata.morphCount; i++ ) {
+
+			pmd.morphs.push( parseMorph() );
+
+		}
+
+
+	};
+
+	parseHeader();
+	parseVertices();
+	parseFaces();
+	parseMaterials();
+	parseBones();
+	parseIks();
+	parseMorphs();
+
+	return pmd;
+
+};
+
+THREE.MMDLoader.prototype.parsePmx = function ( buffer ) {
+
+	var scope = this;
+	var pmx = {};
+	var dv = new THREE.MMDLoader.DataView( buffer );
+
+	pmx.metadata = {};
+	pmx.metadata.format = 'pmx';
+
+	var parseHeader = function () {
+
+		var metadata = pmx.metadata;
+		metadata.magic = dv.getChars( 4 );
+
+		// Note: don't remove the last blank space.
+		if ( metadata.magic !== 'PMX ' ) {
+
+			throw 'PMX file magic is not PMX , but ' + metadata.magic;
+
+		}
+
+		metadata.version = dv.getFloat32();
+
+		if ( metadata.version !== 2.0 && metadata.version !== 2.1 ) {
+
+			throw 'PMX version ' + metadata.version + ' is not supported.';
+
+		}
+
+		metadata.headerSize = dv.getUint8();
+		metadata.encoding = dv.getUint8();
+		metadata.additionalUvNum = dv.getUint8();
+		metadata.vertexIndexSize = dv.getUint8();
+		metadata.textureIndexSize = dv.getUint8();
+		metadata.materialIndexSize = dv.getUint8();
+		metadata.boneIndexSize = dv.getUint8();
+		metadata.morphIndexSize = dv.getUint8();
+		metadata.rigidbodyIndexSize = dv.getUint8();
+		metadata.modelName = dv.getTextBuffer();
+		metadata.englishModelName = dv.getTextBuffer();
+		metadata.comment = dv.getTextBuffer();
+		metadata.englishComment = dv.getTextBuffer();
+
+	};
+
+	var parseVertices = function () {
+
+		var parseVertex = function () {
+
+			var p = {};
+			p.position = dv.getFloat32Array( 3 );
+			p.normal = dv.getFloat32Array( 3 );
+			p.uv = dv.getFloat32Array( 2 );
+
+			p.auvs = [];
+
+			for ( var i = 0; i < pmx.metadata.additionalUvNum; i++ ) {
+
+				p.auvs.push( dv.getFloat32Array( 4 ) );
+
+			}
+
+			p.type = dv.getUint8();
+
+			var indexSize = metadata.vertexIndexSize;
+
+			if ( p.type === 0 ) {  // BDEF1
+
+				p.skinIndices = dv.getNumberArray( indexSize, 1 );
+				p.skinWeights = [ 1.0 ];
+
+			} else if ( p.type === 1 ) {  // BDEF2
+
+				p.skinIndices = dv.getNumberArray( indexSize, 2 );
+				p.skinWeights = dv.getFloat32Array( 1 );
+				p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] );
+
+			} else if ( p.type === 2 ) {  // BDEF4
+
+				p.skinIndices = dv.getNumberArray( indexSize, 4 );
+				p.skinWeights = dv.getFloat32Array( 4 );
+
+			} else if ( p.type === 3 ) {  // SDEF
+
+				p.skinIndices = dv.getNumberArray( indexSize, 2 );
+				p.skinWeights = dv.getFloat32Array( 1 );
+				p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] );
+
+				p.skinC = dv.getFloat32Array( 3 );
+				p.skinR0 = dv.getFloat32Array( 3 );
+				p.skinR1 = dv.getFloat32Array( 3 );
+
+				// SDEF is not supported yet and is handled as BDEF2 so far.
+				// TODO: SDEF support
+				p.type = 1;
+
+			} else {
+
+				throw 'unsupport bone type ' + p.type + ' exception.';
+
+			}
+
+			p.edgeRatio = dv.getFloat32();
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.vertexCount = dv.getUint32();
+
+		pmx.vertices = [];
+
+		for ( var i = 0; i < metadata.vertexCount; i++ ) {
+
+			pmx.vertices.push( parseVertex() );
+
+		}
+
+	};
+
+	var parseFaces = function () {
+
+		var parseFace = function () {
+
+			var p = {};
+			p.indices = dv.getNumberArray( metadata.vertexIndexSize, 3 );
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.faceCount = dv.getUint32() / 3;
+
+		pmx.faces = [];
+
+		for ( var i = 0; i < metadata.faceCount; i++ ) {
+
+			pmx.faces.push( parseFace() );
+
+		}
+
+	};
+
+	var parseTextures = function () {
+
+		var parseTexture = function () {
+
+			return dv.getTextBuffer();
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.textureCount = dv.getUint32();
+
+		pmx.textures = [];
+
+		for ( var i = 0; i < metadata.textureCount; i++ ) {
+
+			pmx.textures.push( parseTexture() );
+
+		}
+
+	};
+
+	var parseMaterials = function () {
+
+		var parseMaterial = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.diffuse = dv.getFloat32Array( 4 );
+			p.specular = dv.getFloat32Array( 3 );
+			p.shiness = dv.getFloat32();
+			p.emissive = dv.getFloat32Array( 3 );
+			p.flag = dv.getUint8();
+			p.edgeColor = dv.getFloat32Array( 4 );
+			p.edgeSize = dv.getFloat32();
+			p.textureIndex = dv.getNumber( pmx.metadata.textureIndexSize );
+			p.envTextureIndex = dv.getNumber( pmx.metadata.textureIndexSize );
+			p.envFlag = dv.getUint8();
+			p.toonFlag = dv.getUint8();
+
+			if ( p.toonFlag === 0 ) {
+
+				p.toonTextureIndex = dv.getNumber( pmx.metadata.textureIndexSize );
+
+			} else if ( p.toonFlag === 1 ) {
+
+				p.toonTextureIndex = dv.getUint8();
+
+			} else {
+
+				throw 'unknown toon flag ' + p.toonFlag + ' exception.';
+
+			}
+
+			p.comment = dv.getTextBuffer();
+			p.faceCount = dv.getUint32() / 3;
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.materialCount = dv.getUint32();
+
+		pmx.materials = [];
+
+		for ( var i = 0; i < metadata.materialCount; i++ ) {
+
+			pmx.materials.push( parseMaterial() );
+
+		}
+
+	};
+
+	var parseBones = function () {
+
+		var parseBone = function () {
+
+			var p = {};
+			// Skinning animation doesn't work when bone name is Japanese Unicode in r73.
+			// So using charcode strings as workaround and keep original strings in .originalName.
+			p.originalName = dv.getTextBuffer();
+			p.name = dv.toCharcodeStrings( p.originalName );
+			p.englishName = dv.getTextBuffer();
+			p.position = dv.getFloat32Array( 3 );
+			p.parentIndex = dv.getNumber( pmx.metadata.boneIndexSize );
+			p.transformationClass = dv.getUint32();
+			p.flag = dv.getUint16();
+
+			if ( p.flag & 0x1 ) {
+
+				p.connectIndex = dv.getNumber( pmx.metadata.boneIndexSize );
+
+			} else {
+
+				p.offsetPosition = dv.getFloat32Array( 3 );
+
+			}
+
+			if ( p.flag & 0x100 || p.flag & 0x200 ) {
+
+				p.supplyParentIndex = dv.getNumber( pmx.metadata.boneIndexSize );
+				p.supplyRatio = dv.getFloat32();
+
+			}
+
+			if ( p.flag & 0x400 ) {
+
+				p.fixAxis = dv.getFloat32Array( 3 );
+
+			}
+
+			if ( p.flag & 0x800 ) {
+
+				p.localXVector = dv.getFloat32Array( 3 );
+				p.localZVector = dv.getFloat32Array( 3 );
+
+			}
+
+			if ( p.flag & 0x2000 ) {
+
+				p.key = dv.getUint32();
+
+			}
+
+			if ( p.flag & 0x20 ) {
+
+				var ik = {};
+
+				ik.effector = dv.getNumber( pmx.metadata.boneIndexSize );
+				ik.target = null;
+				ik.iteration = dv.getUint32();
+				ik.maxAngle = dv.getFloat32();
+				ik.linkCount = dv.getUint32();
+				ik.links = [];
+
+				for ( var i = 0; i < ik.linkCount; i++ ) {
+
+					var link = {};
+					link.index = dv.getNumber( pmx.metadata.boneIndexSize );
+					link.angleLimitation = dv.getUint8();
+
+					if ( link.angleLimitation === 1 ) {
+
+						link.lowerLimitationAngle = dv.getFloat32Array( 3 );
+						link.upperLimitationAngle = dv.getFloat32Array( 3 );
+
+					}
+
+					ik.links.push( link );
+
+				}
+
+				p.ik = ik;
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.boneCount = dv.getUint32();
+
+		pmx.bones = [];
+
+		for ( var i = 0; i < metadata.boneCount; i++ ) {
+
+			pmx.bones.push( parseBone() );
+
+		}
+
+	};
+
+	var parseMorphs = function () {
+
+		var parseMorph = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.panel = dv.getUint8();
+			p.type = dv.getUint8();
+			p.elementCount = dv.getUint32();
+			p.elements = [];
+
+			for ( var i = 0; i < p.elementCount; i++ ) {
+
+				if ( p.type === 0 ) {  // group morph
+
+					var m = {};
+					m.index = dv.getNumber( pmx.metadata.morphIndexSize );
+					m.ratio = dv.getFloat32();
+					p.elements.push( m );
+
+				} else if ( p.type === 1 ) {  // vertex morph
+
+					var m = {};
+					m.index = dv.getNumber( pmx.metadata.vertexIndexSize );
+					m.position = dv.getFloat32Array( 3 );
+					p.elements.push( m );
+
+				} else if ( p.type === 2 ) {  // bone morph
+
+					var m = {};
+					m.index = dv.getNumber( pmx.metadata.boneIndexSize );
+					m.position = dv.getFloat32Array( 3 );
+					m.rotation = dv.getFloat32Array( 4 );
+					p.elements.push( m );
+
+				} else if ( p.type === 3 ) {  // uv morph
+
+					var m = {};
+					m.index = dv.getNumber( pmx.metadata.vertexIndexSize );
+					m.uv = dv.getFloat32Array( 4 );
+					p.elements.push( m );
+
+				} else if ( p.type === 8 ) {  // material morph
+
+					var m = {};
+					m.index = dv.getNumber( pmx.metadata.materialIndexSize );
+					m.type = dv.getUint8();
+					m.diffuse = dv.getFloat32Array( 4 );
+					m.specular = dv.getFloat32Array( 3 );
+					m.shiness = dv.getFloat32();
+					m.emissive = dv.getFloat32Array( 3 );
+					m.edgeColor = dv.getFloat32Array( 4 );
+					m.edgeSize = dv.getFloat32();
+					m.textureColor = dv.getFloat32Array( 4 );
+					m.sphereTextureColor = dv.getFloat32Array( 4 );
+					m.toonColor = dv.getFloat32Array( 4 );
+					p.elements.push( m );
+
+				}
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.morphCount = dv.getUint32();
+
+		pmx.morphs = [];
+
+		for ( var i = 0; i < metadata.morphCount; i++ ) {
+
+			pmx.morphs.push( parseMorph() );
+
+		}
+
+
+	};
+
+	parseHeader();
+	parseVertices();
+	parseFaces();
+	parseTextures();
+	parseMaterials();
+	parseBones();
+	parseMorphs();
+
+	// console.log( pmx ); // for console debug
+
+	return pmx;
+
+};
+
+THREE.MMDLoader.prototype.parseVmd = function ( buffer ) {
+
+	var scope = this;
+	var vmd = {};
+	var dv = new THREE.MMDLoader.DataView( buffer );
+
+	vmd.metadata = {};
+
+	var parseHeader = function () {
+
+		var metadata = vmd.metadata;
+		metadata.magic = dv.getChars( 30 );
+
+		if ( metadata.magic !== 'Vocaloid Motion Data 0002' ) {
+
+			throw 'VMD file magic is not Vocaloid Motion Data 0002, but ' + metadata.magic;
+
+		}
+
+		metadata.name = dv.getSjisStringsAsUnicode( 20 );
+
+	};
+
+	var parseMotions = function () {
+
+		var parseMotion = function () {
+
+			var p = {};
+			// Skinning animation doesn't work when bone name is Japanese Unicode in r73.
+			// So using charcode strings as workaround and keep original strings in .originalName.
+			p.originalBoneName = dv.getSjisStringsAsUnicode( 15 );
+			p.boneName = dv.toCharcodeStrings( p.originalBoneName );
+			p.frameNum = dv.getUint32();
+			p.position = dv.getFloat32Array( 3 );
+			p.rotation = dv.getFloat32Array( 4 );
+			p.interpolation = dv.getUint8Array( 64 );
+
+			return p;
+
+		};
+
+		var metadata = vmd.metadata;
+		metadata.motionCount = dv.getUint32();
+
+		vmd.motions = [];
+		for ( var i = 0; i < metadata.motionCount; i++ ) {
+
+			vmd.motions.push( parseMotion() );
+
+		}
+
+	};
+
+	var parseMorphs = function () {
+
+		var parseMorph = function () {
+
+			var p = {};
+			p.morphName = dv.getSjisStringsAsUnicode( 15 );
+			p.frameNum = dv.getUint32();
+			p.weight = dv.getFloat32();
+			return p;
+
+		};
+
+		var metadata = vmd.metadata;
+		metadata.morphCount = dv.getUint32();
+
+		vmd.morphs = [];
+		for ( var i = 0; i < metadata.morphCount; i++ ) {
+
+			vmd.morphs.push( parseMorph() );
+
+		}
+
+	};
+
+	parseHeader();
+	parseMotions();
+	parseMorphs();
+
+	return vmd;
+
+};
+
+// maybe better to create json and then use JSONLoader...
+THREE.MMDLoader.prototype.createMesh = function ( model, vmd, texturePath, onProgress, onError ) {
+
+	var scope = this;
+	var geometry = new THREE.Geometry();
+        var material = new THREE.MeshFaceMaterial();
+
+	var leftToRight = function() {
+
+		var convertVector = function ( v ) {
+
+			v[ 2 ] = -v[ 2 ];
+
+		};
+
+		var convertQuaternion = function ( q ) {
+
+			q[ 0 ] = -q[ 0 ];
+			q[ 1 ] = -q[ 1 ];
+
+		};
+
+		var convertIndexOrder = function ( p ) {
+
+			var tmp = p[ 2 ];
+			p[ 2 ] = p[ 0 ];
+			p[ 0 ] = tmp;
+
+		};
+
+		for ( var i = 0; i < model.metadata.vertexCount; i++ ) {
+
+			convertVector( model.vertices[ i ].position );
+			convertVector( model.vertices[ i ].normal );
+
+		}
+
+		for ( var i = 0; i < model.metadata.faceCount; i++ ) {
+
+			convertIndexOrder( model.faces[ i ].indices );
+
+		}
+
+		for ( var i = 0; i < model.metadata.boneCount; i++ ) {
+
+			convertVector( model.bones[ i ].position );
+
+		}
+
+		// TODO: support other morph for PMX
+		for ( var i = 0; i < model.metadata.morphCount; i++ ) {
+
+			var m = model.morphs[ i ];
+
+			if ( model.metadata.format === 'pmx' ) {
+
+				if ( m.type === 1 ) {
+
+					m = m.elements;
+
+				} else {
+
+					continue;
+
+				}
+
+			}
+
+			for ( var j = 0; j < m.elementCount; j++ ) {
+
+				convertVector( m.elements[ j ].position );
+
+			}
+
+		}
+
+		if ( vmd === null ) {
+
+			return;
+
+		}
+
+		for ( var i = 0; i < vmd.metadata.motionCount; i++ ) {
+
+			convertVector( vmd.motions[ i ].position );
+			convertQuaternion( vmd.motions[ i ].rotation );
+
+		}
+
+	};
+
+	var initVartices = function () {
+
+		for ( var i = 0; i < model.metadata.vertexCount; i++ ) {
+
+			var v = model.vertices[ i ];
+
+			geometry.vertices.push(
+				new THREE.Vector3(
+					v.position[ 0 ],
+					v.position[ 1 ],
+					v.position[ 2 ]
+				)
+			);
+
+			geometry.skinIndices.push(
+				new THREE.Vector4(
+					v.skinIndices.length >= 1 ? v.skinIndices[ 0 ] : 0.0,
+					v.skinIndices.length >= 2 ? v.skinIndices[ 1 ] : 0.0,
+					v.skinIndices.length >= 3 ? v.skinIndices[ 2 ] : 0.0,
+					v.skinIndices.length >= 4 ? v.skinIndices[ 3 ] : 0.0
+				)
+			);
+
+			geometry.skinWeights.push(
+				new THREE.Vector4(
+					v.skinWeights.length >= 1 ? v.skinWeights[ 0 ] : 0.0,
+					v.skinWeights.length >= 2 ? v.skinWeights[ 1 ] : 0.0,
+					v.skinWeights.length >= 3 ? v.skinWeights[ 2 ] : 0.0,
+					v.skinWeights.length >= 4 ? v.skinWeights[ 3 ] : 0.0
+				)
+			);
+
+		}
+
+	};
+
+	var initFaces = function () {
+
+		for ( var i = 0; i < model.metadata.faceCount; i++ ) {
+
+			geometry.faces.push(
+				new THREE.Face3(
+					model.faces[ i ].indices[ 0 ],
+					model.faces[ i ].indices[ 1 ],
+					model.faces[ i ].indices[ 2 ]
+				)
+			);
+
+			for ( var j = 0; j < 3; j++ ) {
+
+				geometry.faces[ i ].vertexNormals[ j ] =
+					new THREE.Vector3(
+						model.vertices[ model.faces[ i ].indices[ j ] ].normal[ 0 ],
+						model.vertices[ model.faces[ i ].indices[ j ] ].normal[ 1 ],
+						model.vertices[ model.faces[ i ].indices[ j ] ].normal[ 2 ]
+					);
+
+			}
+
+		}
+
+	};
+
+	var initBones = function () {
+
+		var bones = [];
+
+		for ( var i = 0; i < model.metadata.boneCount; i++ ) {
+
+			var bone = {};
+			var b = model.bones[ i ];
+
+			bone.parent = b.parentIndex;
+
+			if ( model.metadata.format === 'pmd' ) {
+
+				bone.parent = ( b.parentIndex === 0xFFFF ) ? -1 : b.parentIndex;
+
+			}
+
+			bone.name = b.name;
+			bone.pos = [ b.position[ 0 ], b.position[ 1 ], b.position[ 2 ] ];
+			bone.rotq = [ 0, 0, 0, 1 ];
+			bone.scl = [ 1, 1, 1 ];
+
+			if ( bone.parent !== -1 ) {
+
+				bone.pos[ 0 ] -= model.bones[ bone.parent ].position[ 0 ];
+				bone.pos[ 1 ] -= model.bones[ bone.parent ].position[ 1 ];
+				bone.pos[ 2 ] -= model.bones[ bone.parent ].position[ 2 ];
+
+			}
+
+			bones.push( bone );
+
+		}
+
+		geometry.bones = bones;
+
+	};
+
+	var initIKs = function () {
+
+		var iks = [];
+
+		// TODO: remove duplicated codes between PMD and PMX
+		if ( model.metadata.format === 'pmd' ) {
+
+			for ( var i = 0; i < model.metadata.ikCount; i++ ) {
+
+				var ik = model.iks[i];
+				var param = {};
+
+				param.target = ik.target;
+				param.effector = ik.effector;
+				param.iteration = ik.iteration;
+				param.maxAngle = ik.maxAngle * 4;
+				param.links = [];
+
+				for ( var j = 0; j < ik.links.length; j++ ) {
+
+					var link = {};
+					link.index = ik.links[ j ].index;
+
+					// Checking with .originalName, not .name.
+					// See parseBone() for the detail.
+					if ( model.bones[ link.index ].originalName.indexOf( 'ひざ' ) >= 0 ) {
+
+						link.limitation = new THREE.Vector3( 1.0, 0.0, 0.0 );
+
+					}
+
+					param.links.push( link );
+
+				}
+
+				iks.push( param );
+
+			}
+
+		} else {
+
+			for ( var i = 0; i < model.metadata.boneCount; i++ ) {
+
+				var b = model.bones[ i ];
+				var ik = b.ik;
+
+				if ( ik === undefined ) {
+
+					continue;
+
+				}
+
+				var param = {};
+
+				param.target = i;
+				param.effector = ik.effector;
+				param.iteration = ik.iteration;
+				param.maxAngle = ik.maxAngle;
+				param.links = [];
+
+				for ( var j = 0; j < ik.links.length; j++ ) {
+
+					var link = {};
+					link.index = ik.links[ j ].index;
+
+					if ( ik.links[ j ].angleLimitation === 1 ) {
+
+						link.limitation = new THREE.Vector3( 1.0, 0.0, 0.0 );
+						// TODO: use limitation angles
+						// link.lowerLimitationAngle;
+						// link.upperLimitationAngle;
+
+					}
+
+					param.links.push( link );
+
+				}
+
+				iks.push( param );
+
+			}
+
+		}
+
+		geometry.iks = iks;
+
+	};
+
+	var initMorphs = function () {
+
+		function updateVertex ( params, index, v, ratio ) {
+
+			params.vertices[ index ].x += v.position[ 0 ] * ratio;
+			params.vertices[ index ].y += v.position[ 1 ] * ratio;
+			params.vertices[ index ].z += v.position[ 2 ] * ratio;
+
+		};
+
+		function updateVertices ( params, m, ratio ) {
+
+			for ( var i = 0; i < m.elementCount; i++ ) {
+
+				var v = m.elements[ i ];
+
+				var index;
+
+				if ( model.metadata.format === 'pmd' ) {
+
+					index = model.morphs[ 0 ].elements[ v.index ].index;
+
+				} else {
+
+					index = v.index;
+
+				}
+
+				updateVertex( params, index, v, ratio );
+
+			}
+
+		};
+
+		for ( var i = 0; i < model.metadata.morphCount; i++ ) {
+
+			var m = model.morphs[ i ];
+			var params = {};
+
+			params.name = m.name;
+			params.vertices = [];
+
+			for ( var j = 0; j < model.metadata.vertexCount; j++ ) {
+
+				params.vertices[ j ] = new THREE.Vector3( 0, 0, 0 );
+				params.vertices[ j ].x = geometry.vertices[ j ].x;
+				params.vertices[ j ].y = geometry.vertices[ j ].y;
+				params.vertices[ j ].z = geometry.vertices[ j ].z;
+
+			}
+
+			if ( model.metadata.format === 'pmd' ) {
+
+				if ( i !== 0 ) {
+
+					updateVertices( params, m, 1.0 );
+
+				}
+
+			} else {
+
+				if ( m.type === 0 ) {
+
+					for ( var j = 0; j < m.elementCount; j++ ) {
+
+						var m2 = model.morphs[ m.elements[ j ].index ];
+						var ratio = m.elements[ j ].ratio;
+
+						if ( m2.type === 1 ) {
+
+							updateVertices( params, m2, ratio );
+
+						}
+
+					}
+
+				} else if ( m.type === 1 ) {
+
+					updateVertices( params, m, 1.0 );
+
+				}
+
+			}
+
+			// TODO: skip if this's non-vertex morphing of PMX to reduce CPU/Memory use
+			geometry.morphTargets.push( params );
+
+		}
+
+	};
+
+	var initMaterials = function () {
+
+		var offset = 0;
+		var materialParams = [];
+
+		for ( var i = 1; i < model.metadata.materialCount; i++ ) {
+
+			geometry.faceVertexUvs.push( [] );
+
+		}
+
+		for ( var i = 0; i < model.metadata.materialCount; i++ ) {
+
+			var m = model.materials[ i ];
+			var params = {};
+
+			for ( var j = 0; j < m.faceCount; j++ ) {
+
+				geometry.faces[ offset ].materialIndex = i;
+
+				var uvs = [];
+
+				for ( var k = 0; k < 3; k++ ) {
+
+					var v = model.vertices[ model.faces[ offset ].indices[ k ] ];
+					uvs.push( new THREE.Vector2( v.uv[ 0 ], v.uv[ 1 ] ) );
+
+				}
+
+				geometry.faceVertexUvs[ 0 ].push( uvs );
+
+				offset++;
+
+			}
+
+			params.shading = 'phong';
+			params.colorDiffuse = [ m.diffuse[ 0 ], m.diffuse[ 1 ], m.diffuse[ 2 ] ];
+			params.opacity = m.diffuse[ 3 ];
+			params.colorSpecular = [ m.specular[ 0 ], m.specular[ 1 ], m.specular[ 2 ] ];
+			params.specularCoef = m.shiness;
+
+			// temporal workaround
+			// TODO: implement correctly
+			params.doubleSided = true;
+
+			if ( model.metadata.format === 'pmd' ) {
+
+				if ( m.fileName ) {
+
+					var fileName = m.fileName;
+					var fileNames = [];
+
+					// temporal workaround, disable sphere mapping so far
+					// TODO: sphere mapping support
+					var index = fileName.lastIndexOf( '*' );
+
+					if ( index >= 0 ) {
+
+						fileNames.push( fileName.slice( 0, index ) );
+						fileNames.push( fileName.slice( index + 1 ) );
+
+					} else {
+
+						fileNames.push( fileName );
+
+					}
+
+					for ( var j = 0; j < fileNames.length; j++ ) {
+
+						var n = fileNames[ j ];
+
+						// TODO: support spa
+						if ( /* n.indexOf( '.spa' ) >= 0 || */ n.indexOf( '.sph' ) >= 0 ) {
+
+							params.mapEnv = n;
+
+						} else {
+
+							// temporal workaround, use .png instead of .tga
+							// TODO: tga file support
+							params.mapDiffuse = n.replace( '.tga', '.png' );
+
+						}
+
+					}
+
+				}
+
+			} else {
+
+				if ( m.textureIndex !== -1 ) {
+
+					var n = model.textures[ m.textureIndex ];
+					// temporal workaround, use .png instead of .tga
+					// TODO: tga file support
+					params.mapDiffuse = n.replace( '.tga', '.png' );
+
+				}
+
+				// TODO: support m.envFlag === 0, 2, 3
+				if ( m.envTextureIndex !== -1 && m.envFlag === 1 ) {
+
+					var n = model.textures[ m.envTextureIndex ];
+					params.mapEnv = n;
+
+				}
+
+			}
+
+			if ( params.mapDiffuse === undefined ) {
+
+				params.colorEmissive = [ m.emissive[ 0 ], m.emissive[ 1 ], m.emissive[ 2 ] ];
+
+			}
+
+			materialParams.push( params );
+
+		}
+
+		var materials = scope.initMaterials( materialParams, texturePath );
+
+		for ( var i = 0; i < materials.length; i++ ) {
+
+			var m = materials[ i ];
+			var p = materialParams[ i ];
+
+			if ( m.map ) {
+
+				m.map.flipY = false;
+
+			}
+
+			// this should be in THREE.Loader.createMaterial.
+			// remove this if it supports.
+			// TODO: make patch of THREE.Loader.createMaterial?
+			if ( p.mapEnv !== undefined ) {
+
+				var fullPath = texturePath + p.mapEnv;
+				var loader = THREE.Loader.Handlers.get( fullPath );
+
+				if ( loader === null ) {
+
+					loader = new THREE.TextureLoader( this.manager );
+
+				}
+
+				var texture = loader.load( fullPath );
+				// currently only support multiply-sphere-mapping
+				// TODO: support add-sphere-mapping
+				texture.mapping = THREE.SphericalReflectionMapping;
+				m.envMap = texture;
+
+			}
+
+			m.skinning = true;
+			m.morphTargets = true;
+			material.materials.push( m );
+
+		}
+
+	};
+
+	var initMotionAnimations = function () {
+
+		var orderedMotions = [];
+		var boneTable = {};
+
+		for ( var i = 0; i < model.metadata.boneCount; i++ ) {
+
+			var b = model.bones[ i ];
+			boneTable[ b.name ] = i;
+			orderedMotions[ i ] = [];
+
+		}
+
+		for ( var i = 0; i < vmd.motions.length; i++ ) {
+
+			var m = vmd.motions[ i ];
+			var num = boneTable[ m.boneName ];
+
+			if ( num === undefined )
+				continue;
+
+			orderedMotions[ num ].push( m );
+
+		}
+
+		for ( var i = 0; i < orderedMotions.length; i++ ) {
+
+			orderedMotions[ i ].sort( function ( a, b ) {
+
+				return a.frameNum - b.frameNum;
+
+			} ) ;
+
+		}
+
+		var animation = {
+			name: 'Action',
+			fps: 30,
+			length: 0.0,
+			hierarchy: []
+		};
+
+		for ( var i = 0; i < geometry.bones.length; i++ ) {
+
+			animation.hierarchy.push(
+				{
+					parent: geometry.bones[ i ].parent,
+					keys: []
+				}
+			);
+
+		}
+
+		var maxTime = 0.0;
+
+		for ( var i = 0; i < orderedMotions.length; i++ ) {
+
+			var array = orderedMotions[ i ];
+
+			for ( var j = 0; j < array.length; j++ ) {
+
+				var t = array[ j ].frameNum / 30;
+				var p = array[ j ].position;
+				var r = array[ j ].rotation;
+
+				animation.hierarchy[ i ].keys.push(
+					{
+						time: t,
+						pos: [ geometry.bones[ i ].pos[ 0 ] + p[ 0 ],
+						       geometry.bones[ i ].pos[ 1 ] + p[ 1 ],
+						       geometry.bones[ i ].pos[ 2 ] + p[ 2 ] ],
+						rot: [ r[ 0 ], r[ 1 ], r[ 2 ], r[ 3 ] ],
+						scl: [ 1, 1, 1 ]
+					}
+				);
+
+				if ( t > maxTime )
+					maxTime = t;
+
+			}
+
+		}
+
+		// add 2 secs as afterglow
+		maxTime += 2.0;
+		animation.length = maxTime;
+
+		for ( var i = 0; i < orderedMotions.length; i++ ) {
+
+			var keys = animation.hierarchy[ i ].keys;
+
+			if ( keys.length === 0 ) {
+
+				keys.push( { time: 0.0,
+				             pos: [ geometry.bones[ i ].pos[ 0 ],
+				                    geometry.bones[ i ].pos[ 1 ],
+				                    geometry.bones[ i ].pos[ 2 ] ],
+				             rot: [ 0, 0, 0, 1 ],
+				             scl: [ 1, 1, 1 ]
+				           } );
+
+			}
+
+			var k = keys[ 0 ];
+
+			if ( k.time !== 0.0 ) {
+
+				keys.unshift( { time: 0.0,
+				                 pos: [ k.pos[ 0 ], k.pos[ 1 ], k.pos[ 2 ] ],
+				                 rot: [ k.rot[ 0 ], k.rot[ 1 ], k.rot[ 2 ], k.rot[ 3 ] ],
+				                 scl: [ 1, 1, 1 ]
+				              } );
+
+			}
+
+			k = keys[ keys.length - 1 ];
+
+			if ( k.time < maxTime ) {
+
+				keys.push( { time: maxTime,
+				             pos: [ k.pos[ 0 ], k.pos[ 1 ], k.pos[ 2 ] ],
+				             rot: [ k.rot[ 0 ], k.rot[ 1 ], k.rot[ 2 ], k.rot[ 3 ] ],
+				             scl: [ 1, 1, 1 ]
+			        	   } );
+
+			}
+
+		}
+
+//		geometry.animation = animation;
+		geometry.animations = [];
+		geometry.animations.push( THREE.AnimationClip.parseAnimation( animation, geometry.bones ) );
+
+
+	};
+
+	var initMorphAnimations = function () {
+
+		var orderedMorphs = [];
+		var morphTable = {}
+
+		for ( var i = 0; i < model.metadata.morphCount; i++ ) {
+
+			var m = model.morphs[ i ];
+			morphTable[ m.name ] = i;
+			orderedMorphs[ i ] = [];
+
+		}
+
+		for ( var i = 0; i < vmd.morphs.length; i++ ) {
+
+			var m = vmd.morphs[ i ];
+			var num = morphTable[ m.morphName ];
+
+			if ( num === undefined )
+				continue;
+
+			orderedMorphs[ num ].push( m );
+
+		}
+
+		for ( var i = 0; i < orderedMorphs.length; i++ ) {
+
+			orderedMorphs[ i ].sort( function ( a, b ) {
+
+				return a.frameNum - b.frameNum;
+
+			} ) ;
+
+		}
+
+		var morphAnimation = {
+			fps: 30,
+			length: 0.0,
+			hierarchy: []
+		};
+
+		for ( var i = 0; i < model.metadata.morphCount; i++ ) {
+
+			morphAnimation.hierarchy.push( { keys: [] } );
+
+		}
+
+		var maxTime = 0.0;
+
+		for ( var i = 0; i < orderedMorphs.length; i++ ) {
+
+			var array = orderedMorphs[ i ];
+
+			for ( var j = 0; j < array.length; j++ ) {
+
+				var t = array[ j ].frameNum / 30;
+				var w = array[ j ].weight;
+
+				morphAnimation.hierarchy[ i ].keys.push( { time: t, value: w } );
+
+				if ( t > maxTime ) {
+
+					maxTime = t;
+
+				}
+
+			}
+
+		}
+
+		// add 2 secs as afterglow
+		maxTime += 2.0;
+
+		// use animation's length if exists. animation is master.
+		maxTime = ( geometry.animation !== undefined &&
+		            geometry.animation.length > 0.0 )
+		            	? geometry.animation.length : maxTime;
+		morphAnimation.length = maxTime;
+
+		for ( var i = 0; i < orderedMorphs.length; i++ ) {
+
+			var keys = morphAnimation.hierarchy[ i ].keys;
+
+			if ( keys.length === 0 ) {
+
+				keys.push( { time: 0.0, value: 0.0 } );
+
+			}
+
+			var k = keys[ 0 ];
+
+			if ( k.time !== 0.0 ) {
+
+				keys.unshift( { time: 0.0, value: k.value } );
+
+			}
+
+			k = keys[ keys.length - 1 ];
+
+			if ( k.time < maxTime ) {
+
+				keys.push( { time: maxTime, value: k.value } );
+
+			}
+
+		}
+
+//		geometry.morphAnimation = morphAnimation;
+
+		var tracks = [];
+
+		for ( var i = 1; i < orderedMorphs.length; i++ ) {
+
+			var h = morphAnimation.hierarchy[ i ];
+			tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + i + ']', h.keys ) );
+
+		}
+
+		geometry.morphAnimations = [];
+		geometry.morphAnimations.push( new THREE.AnimationClip( 'morphAnimation', -1, tracks ) );
+
+	};
+
+	leftToRight();
+	initVartices();
+	initFaces();
+	initBones();
+	initIKs();
+	initMorphs();
+	initMaterials();
+
+	if ( vmd !== null ) {
+
+		initMotionAnimations();
+		initMorphAnimations();
+
+	}
+
+	geometry.computeFaceNormals();
+	geometry.verticesNeedUpdate = true;
+	geometry.normalsNeedUpdate = true;
+	geometry.uvsNeedUpdate = true;
+	geometry.mmdFormat = model.metadata.format;
+
+	var mesh = new THREE.SkinnedMesh( geometry, material );
+
+	// console.log( mesh ); // for console debug
+
+	return mesh;
+
+};
+
+THREE.MMDLoader.DataView = function ( buffer, littleEndian ) {
+
+	this.dv = new DataView( buffer );
+	this.offset = 0;
+	this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true;
+	this.encoder = new CharsetEncoder();
+
+};
+
+THREE.MMDLoader.DataView.prototype = {
+
+	constructor: THREE.MMDLoader.DataView,
+
+	getInt8: function () {
+
+		var value = this.dv.getInt8( this.offset );
+		this.offset += 1;
+		return value;
+
+	},
+
+	getInt8Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getInt8() );
+
+		}
+
+		return a;
+
+	},
+
+	getUint8: function () {
+
+		var value = this.dv.getUint8( this.offset );
+		this.offset += 1;
+		return value;
+
+	},
+
+	getUint8Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getUint8() );
+
+		}
+
+		return a;
+
+	},
+
+
+	getInt16: function () {
+
+		var value = this.dv.getInt16( this.offset, this.littleEndian );
+		this.offset += 2;
+		return value;
+
+	},
+
+	getInt16Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getInt16() );
+
+		}
+
+		return a;
+
+	},
+
+	getUint16: function () {
+
+		var value = this.dv.getUint16( this.offset, this.littleEndian );
+		this.offset += 2;
+		return value;
+
+	},
+
+	getUint16Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getUint16() );
+
+		}
+
+		return a;
+
+	},
+
+	getInt32: function () {
+
+		var value = this.dv.getInt32( this.offset, this.littleEndian );
+		this.offset += 4;
+		return value;
+
+	},
+
+	getInt32Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getInt32() );
+
+		}
+
+		return a;
+
+	},
+
+	getUint32: function () {
+
+		var value = this.dv.getUint32( this.offset, this.littleEndian );
+		this.offset += 4;
+		return value;
+
+	},
+
+	getUint32Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getUint32() );
+
+		}
+
+		return a;
+
+	},
+
+	getFloat32: function () {
+
+		var value = this.dv.getFloat32( this.offset, this.littleEndian );
+		this.offset += 4;
+		return value;
+
+	},
+
+	getFloat32Array: function( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getFloat32() );
+
+		}
+
+		return a;
+
+	},
+
+	getFloat64: function () {
+
+		var value = this.dv.getFloat64( this.offset, this.littleEndian );
+		this.offset += 8;
+		return value;
+
+	},
+
+	getFloat64Array: function( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getFloat64() );
+
+		}
+
+		return a;
+
+	},
+
+	getNumber: function ( type ) {
+
+		switch ( type ) {
+
+			case 1:
+				return this.getInt8();
+
+			case 2:
+				return this.getInt16();
+
+			case 4:
+				return this.getInt32();
+
+			default:
+				throw 'unknown number type ' + type + ' exception.';
+
+		}
+
+	},
+
+	getNumberArray: function ( type, size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i++ ) {
+
+			a.push( this.getNumber( type ) );
+
+		}
+
+		return a;
+
+	},
+
+	getChars: function ( size ) {
+
+		var str = '';
+
+		while ( size > 0 ) {
+
+			var value = this.getUint8();
+			size--;
+
+			if ( value === 0 ) {
+
+				break;
+
+			}
+
+			str += String.fromCharCode( value );
+
+		}
+
+		while ( size > 0 ) {
+
+			this.getUint8();
+			size--;
+
+		}
+
+		return str;
+
+	},
+
+	getSjisStringsAsUnicode: function ( size ) {
+
+		var a = [];
+
+		while ( size > 0 ) {
+
+			var value = this.getUint8();
+			size--;
+
+			if ( value === 0 ) {
+
+				break;
+
+			}
+
+			a.push( value );
+
+		}
+
+		while ( size > 0 ) {
+
+			this.getUint8();
+			size--;
+
+		}
+
+		return this.encoder.s2u( new Uint8Array( a ) );
+
+	},
+
+	/*
+         * Note: Sometimes to use Japanese Unicode characters runs into problems in Three.js.
+	 *       In such a case, use this method to convert it to Unicode hex charcode strings,
+         *       like 'あいう' -> '0x30420x30440x3046'
+         */
+	toCharcodeStrings: function ( s ) {
+
+		var str = '';
+
+		for ( var i = 0; i < s.length; i++ ) {
+
+			str += '0x' + ( '0000' + s[ i ].charCodeAt().toString( 16 ) ).substr( -4 );
+
+		}
+
+		return str;
+
+	},
+
+	getUnicodeStrings: function ( size ) {
+
+		var str = '';
+
+		while ( size > 0 ) {
+
+			var value = this.getUint16();
+			size -= 2;
+
+			if ( value === 0 ) {
+
+				break;
+
+			}
+
+			str += String.fromCharCode( value );
+
+		}
+
+		while ( size > 0 ) {
+
+			this.getUint8();
+			size--;
+
+		}
+
+		return str;
+
+	},
+
+	getTextBuffer: function () {
+
+		var size = this.getUint32();
+		return this.getUnicodeStrings( size );
+
+	}
+
+};
+

+ 24 - 0
examples/models/mmd/Readme.txt

@@ -0,0 +1,24 @@
+You must read piapro character guidline/license
+when you use Vocaloid(Miku, Meiko, etc.) related stuffs (including MMD models)
+and must know what you're allowed/unallowed to do with them.
+
+http://piapro.jp/license/character_guideline
+
+
+And also you must know that MMD model/motion use is very restricted.
+You're required to read each MMD model/motion data readme/guideline/license
+when you use it.
+
+Generally the followings are NOT allowed
+- file redistribution
+- porn use
+- commercial use
+- etc.
+
+If you want to use the model/motion data for them,
+you must look for the data which the author EXPLICITLY allows for them.
+
+DON'T judge as it's allowed if it isn't mentioned.
+
+
+Takahiro

BIN
examples/models/mmd/miku/eyeM2.bmp


BIN
examples/models/mmd/miku/miku_v2.pmd


+ 396 - 0
examples/models/mmd/miku/readme_miku_v2.txt

@@ -0,0 +1,396 @@
+The models in this directory are bandle models of MMD software.
+So I copied MMD readme.txt here.
+
+by takahiro
+
+----------------------------------------------------------------------
+
+MikuMikuDance.exe Ver.5.24
+
+3Dポリゴンモデルの振り付け用ツール
+
+■操作方法■
+ニコニコ動画にて公開中
+http://www.nicovideo.jp/watch/sm2420025
+
+■WAVE出力時の注意点■
+WAVEはフレーム0から録画する時にしか出力できません。
+
+■Windows2000での注意点■
+Windows2000以前のOS(XPよりも前)で使用する場合には、
+MMDxShow.dllをレジストリに登録しなければ使用できません。
+Dataフォルダ内にあるWin2000.batを一度実行して下さい。
+
+■お断り■
+クリプトン・フューチャー・メディア株式会社様、その他
+Vocaloidシリーズに関して権利を有する方からクレームが
+あった場合には、本ツールの公開を中止する場合があります。
+
+■JPEGライブラリ■
+Independent JPEG Group(http://www.ijg.org/)のJPeg-6b
+ライブラリを使用しています。
+
+■免責事項■
+本ツールを実行したことによって損害・不利益・事故等が
+発生した場合でも、一切の責任を負いません。
+
+ツールを使って製作した画像・動画を公開する場合には、
+こちらには連絡や許可申請は必要ありません。
+pmm、vpd、vmdファイルの公開もご自由に。
+ただし、問題が発生しても一切責任を負いません。
+**音楽等の著作権には特に気をつけて下さい!!**
+
+■物理演算モード■
+物理エンジンにはBullet Physics Engineを使用しています。
+(http://www.bulletphysics.com/wordpress/ )
+
+「ヘルプ」→「モデル拡張」→「物理演算」で、モデルの物理演算化用エディタが開きます
+
+エディタの豆知識
+・剛体の形状(位置・回転・大きさ)は画面右下のXYZアイコンで操作できます。
+
+特に『大きさ』は、SHIFTキーを押しながら位置アイコンのドラッグで変更できますので、気付きにくいと思います。
+
+・グループ横の欄は、その剛体と衝突判定をしないグループを羅列します
+
+・「ボーン追従」と「物理演算」において、「物理演算」の剛体は何らかの形で「ボーン追従」の剛体とジョイントで接続されていなければ地面に落ちます
+
+・「物理演算」の「ボーン位置合せ」は、「物理演算」の剛体に繋がっているジョイントの位置は、ボーンの位置からズレることがあるので、それを強制的にボーンの位置に合せるための仕様です。
+ボーンの位置からジョイントがズレるとモデルの破綻が起きる場合にのみ使用して下さい(使うと計算が遅くなるのと、動きが不自然になります)
+
+
+■連絡先■
[email protected]   樋口優
+
+http://www.geocities.jp/higuchuu4/index.htm
+
+■バージョン履歴■
+Ver.5.24(2010/11/02)
+・回転連動ボーンの挙動変更
+
+Ver.5.23(2010/08/27)
+・MEIKOモデル追加
+・物理演算のジョイント軸を回転させた場合の挙動に関するバグ修正
+
+Ver.5.22a(2010/01/21)
+・鏡音リンact2モデル追加
+ (MMD本体はVer.5.22のまま)
+
+Ver.5.22(2009/10/25)
+・カメラ・照明・アクセサリモードでvsq,vpdを読み込むとMMDが落ちるバグ修正
+
+Ver.5.21(2009/10/25)
+・フレーム窓内でボーン名をクリックしてボーンを選択する際、トグルする仕様に変更
+・フレーム窓内のボーン括りをクリックすると、括り内の全ボーンを選択する仕様追加
+・範囲選択に、現在選択中のボーンで設定する『選択ボーン』項目追加
+・縦選択を、現在選択中のフレーム全てに適応する仕様に変更
+・移動のみでなく、回転時も Shift→10倍、Ctrl→0.1倍 のキー操作追加
+・背景BMPの読み込みにJpegも対応
+・画面保存にBMPのみでなく、Jpegも対応
+・MMDの画面に、各種ファイルをドラッグ&ドロップできる仕様追加
+・その他色々バグ修正
+
+
+Ver.5.20(2009/10/23)
+・AVI出力時に、モデル表示フレームが1フレーム遅れていた点を修正
+・VistaのUAC作動時に落ちるバグ修正
+ (Windows7については未確認。直ってるといいなぁ)
+・pmmデータ保存時にバッファオーバーフローを起こす危険のあるバグ修正
+ (Ver.5.19のバグ。Ver.5.19は落ちる可能性がある為、使用しないで下さい)
+・その他色々バグ修正
+
+Ver.5.19(2009/10/19)
+・色々細かいバグ修正
+
+Ver.5.18(2009/10/17)
+・モデルのjpeg対応バグ修正
+
+Ver.5.17(2009/10/17)
+・テクスチャにjpegファイルを使用できるよう変更
+・その他色々バグ修正
+
+Ver.5.16(2009/09/27)
+・Ver.5.15修正によるバグ修正
+
+Ver.5.15(2009/09/27)
+・ビットマップ+スフィアマップモデルを拡張モデル保存した際にスフィアが消えるバグ修正
+
+Ver.5.14(2009/09/24)
+・スフィアマップアクセサリとモデルを同時表示した際のバグ修正
+
+Ver.5.13(2009/09/15)
+・物理エンジンBullet Ver.2.75 正式版公開に伴いBulletバージョン更新
+
+Ver.5.12(2009/09/10)
+・テクスチャBMP上にスフィアマップ表示する際のファイル名を
+   "テクスチャ名.bmp*スフィア名.bmp" に変更。
+ Ver.5.11で"/"を使っていた方は"*"に変更して下さい。
+・スフィアマップのファイル名拡張子"sph"を、"spa"にすることにより
+ スフィアマップの展開が乗算でなく、加算で行われる仕様を追加
+
+Ver.5.11(2009/09/7)
+・スフィアマップの計算法をVer.5.09に戻す
+・テクスチャBMP上にスフィアマップ表示できる仕様に変更
+ テクスチャ名を "テクスチャ名.bmp/スフィア名.bmp"にすることにより
+ テクスチャ上にスフィアマップが展開されます(例:fuk1.bmp/metal.sph)
+ *ただし、PMDフォーマットはテクスチャ名の長さが19文字分しか無いため、
+ モデルで使用する場合はファイル名全体(テクスチャ+スフィア)で19文字以下に
+ 収める必要があります(アクセサリ(xファイル)は256文字までOK)
+・その他バグ色々修正
+
+Ver.5.10(2009/08/29)
+・スフィアマップ計算法を入射角を考慮する方法に変更
+ (髪のキューティクル表現等が可能になりました(多分))
+
+Ver.5.09(2009/08/25)
+・スフィアマップ機能追加
+ スフィアマップ用BMPを拡張子sphとしてモデル・アクセサリのテクスチャに指定
+ することにより、スフィアマップが展開されて表示されます
+
+Ver.5.08(2009/08/09)
+・pmm読込時ファイルが見つからなかった場合、ファイルの場所をあらためて聞く仕様を追加
+
+Ver.5.07(2009/08/08)
+・アクセサリ拡大・縮小時にアクセサリの明るさが変化するバグ修正
+・アイコンによる移動時にCtrlキーを押すことにより移動距離を1/10に減らす機能を追加
+
+Ver.5.06(2009/08/05)
+・Ver.5.05のCPU負荷が高すぎたため修正
+・背景AVI表示時にも地面影を表示するように変更
+
+Ver.5.05(2009/08/05)
+・色々とバグ修正 MMDxShow.dllも修正しています。
+ (※※旧VerのMMDフォルダを利用する場合にはMMDxShow.dllも書き換えること※※)
+・Windows2000以前のOS(XPよりも前)で使用する場合には、MMDxShow.dllをレジストリに登録しなければ
+ 使用できません。Dataフォルダ内にあるWin2000.batを一度実行して下さい(一度実行すればおk)
+
+Ver.5.04(2009/08/02)
+・Ver.5.03のMMDxShow.dll実行に伴い、MSVCR71.DLL関連のエラーが出る場合があったため、
+ MMDxShow.dll修正(※※旧VerのMMDフォルダを利用する場合にはMMDxShow.dllも書き換えること※※)。
+・地面影描写が、背景BMP読込時に表示されないバグ修正
+
+Ver.5.03(2009/08/02)
+・AVI出力
+ ・DirectShowにて出力する仕様に変更(2GBの壁がなくなりました)
+  (Dataフォルダ内に新たに'MMDxShow.dll'が追加されています。以前のVerのフォルダに
+  MikuMikuDance.exeをコピーして使う場合には、上記ファイルもコピーして下さい)
+ ・アルファチャンネルをグラボ・出力画質によらず出力できるよう変更
+ ・WAVEを含む場合にインターリーブ出力するよう変更
+  (ただしEscキーによる録画中断時にはWaveが映像よりも長くなってしまうので注意してください)
+・地面影描画法変更
+  半透明でも綺麗に描写されます。地面影はモデル描写の時に描かれますので、
+  アクセサリ編集より、モデルとアクセサリの描写順をうまく設定してください。
+・アクセサリ数が128個を超えると、アクセサリ編集が落ちるバグ修正
+・その他色々バグ修正
+
+Ver.5.02(2009/07/14)
+・PCによって再生時に音ズレが生じるバグを修正
+
+Ver.5.01(2009/07/06)
+・AVI出力にモード追加
+ ・画質優先   Ver.5β版の出力法(βよりもさらに画質は良いが遅いです)
+ ・速度優先   Ver.5.00版の出力法
+ ・連番分割出力 2GBを超える場合は分割して出力して下さい。下にフレーム数を入力すると
+         そのフレーム数出力する毎に連番を付けた別ファイルに記録して行きます。
+         ただし、WAVEは記録できなくなります。
+
+Ver.5.00(2009/07/02)
+・物理演算モード追加(カイトモデルのみ未対応)
+・描写深度を10倍まで拡大
+・カメラ・モデルのアイコンによる移動時にShiftキーを押すと10倍の距離移動
+・AVI出力法を、スピード優先方式に戻しました
+・その他バグ色々修正
+
+Ver.4.05(2009/05/27)
+・Ver.4.04に伴うバグ修正
+
+Ver.4.04(2009/05/27)
+・再生時の動画時間計算を厳密化(音ズレ防止)
+
+Ver.4.03(2009/05/25)
+・英語対応(「ヘルプ」→「English Mode」。外国語版OSの場合は自動的に英語モードで立ち上がる)
+・モデルの英語化法
+  1.「ヘルプ」→「モデル拡張」→「英語名編集」にて、モデル名やボーン・表情の英語名を入力
+  2.「ヘルプ」→「モデル拡張」→「拡張モデル保存」で、英語対応版pmdを保存
+・pmdモデル毎にトゥーン用テクスチャを設定できるように変更
+  1.「ヘルプ」→「モデル拡張」→「トゥーンテクスチャ変更」でテクスチャをデフォルトのものから変更
+   ・テクスチャはモデルファイル(*.pmd)と同じフォルダ内に格納しておく事
+   ・テクスチャは同名のファイルがあった場合には最初に読み込んだもののみが使用されるので、
+    テクスチャ名はなるべく他と被らない、ユニークな名前を使用して下さい
+  2.「ヘルプ」→「モデル拡張」→「拡張モデル保存」で、トゥーンテクスチャを変更したモデルを保存
+       ・モデル公開時には、使用したテクスチャをモデル(*.pmd)と一緒に公開して下さい
+・ボーンフレームに空のフレーム挿入、削除機能追加
+  「フレーム編集」→「空フレーム挿入」or「列フレーム削除」
+  (複数モデルで同じモーションを行う時に、モデル毎に動作の遅れや進みを入れる時等に使用)
+・その他バグ色々修正
+
+Ver.4.02(2009/05/08)
+・ボーン、カメラの回転・移動に数値入力モード追加(「ボーン編集」→「数値入力」)
+・バグ色々修正
+
+Ver.4.0(2009/03/05)
+・MMD杯記念に、まささんより初音ミクVer2モデル追加
+・アイコンによる回転・移動に、localモードとglobalモード追加
+・モデル仕様に捩じり用ボーン追加
+・AVI出力時、スピードよりも画質優先の仕様に変更
+・screen.bmpの解像度変更
+
+Ver.3.45(2009/01/25)
+・エッジの太さを各モデルごとに設定できる仕様に変更
+
+Ver.3.44(2009/01/10)
+・アクセサリの地面影表示バグ修正
+
+Ver.3.43(2009/01/08)
+・pmmデータを読み込んだ際に地面影色がおかしくなるバグ修正
+・エッジのZ深度調整(視野角を極端に小さくしても、前より少しだけ綺麗に表示されるようになりますた)
+
+Ver.3.42(2009/01/07)
+・KAITOモデル半目時修正
+・加算合成時の表示バグ修正
+・地面影に半透明モード追加
+ 地面影は、1.モデル描写前のアクセサリ描写 2.モデル描写 の1と2の間に描写されます。
+ 地面影を透けるようにするには、ステージ等のアクセサリをモデル描写の前に描く必要があります。
+ あまり綺麗に描けるわけではないので、期待しないでねw
+・セーブデータ(pmm)にエッジの太さ、および地面影透けるモードのデータ追加
+
+Ver.3.41(2009/01/05)
+・KAITOモデル表情バグ修正
+・表情窓バグ修正
+
+Ver.3.40(2009/01/05)
+・KAITOモデル追加
+・地面影をモデル、アクセサリよりも前に描画する描画順に変更
+・モデルのエッジの太さ設定機能追加
+
+Ver.3.30(2008/12/22)
+・24bitテクスチャ対応
+・別フレームへペースト機能追加
+ コピー&ペーストの際、操作対象ボーン(画面左上に表示されるボーン)へコピーされた全ボーンの
+ フレームデータをペーストします。これにより、"前髪1"フレームの内容を"左髪"フレームに
+ コピーする等、別フレーム間でのコピー&ペーストが可能になります
+・アクセサリ編集機能追加
+ アクセサリ名を自由に変更可能(デフォルトはxファイル名)
+ アクセサリの描画順序設定可能(半透明のモノを描写する時に使用)
+ モデル描写の前にアクセサリを描写することも可能です
+・アクセサリのSizeに補間計算適用
+・アクセサリフレームのコピー&ペースト機能追加
+ アクセサリのコピー&ペーストは、モデルの”別フレームへペースト機能追加”と同じく現在選択中の
+ アクセサリにペーストします。これにより別アクセサリ間でのフレームコピーが可能です
+・アクセサリ、モデルに加算合成表示機能追加
+ 光の表現強化用。加算合成表示は、フレーム登録はできません。
+ また、加算合成なのでバックが白だと何も表示されていないように見えるので注意
+・アクセサリフレームの範囲選択機能追加
+・フレーム位置角度補正機能追加
+ 選択フレームポイントの位置・角度の値をx倍します。 ネンドロイドタイプの手足が
+ 短いモデルにモーションをコピーした際や、動きを大きくしたり小さくする際に利用
+・表情大きさ補正機能追加
+ 選択フレームポイントの表情の大きさをx倍します。
+ リップシンク後大きさを小さくしてささやくような口の動きにしたりする事が可能です
+・その他様々バグ修正
+
+Ver.3.23(2008/12/19)
+・カメラフレームのバグ修正
+
+Ver.3.22a(2008/11/29)
+・咲音メイコモデル修正(手足の長さを短くしました)
+
+Ver.3.22(2008/11/24)
+・pmmファイル読込時のアクセサリ関連バグによる強制終了の回避修正
+・アクセサリに咲音マイク追加
+
+Ver.3.21b(2008/11/24)
+・咲音メイコモデルスキニング修正
+
+Ver.3.21a(2008/11/23)
+・咲音メイコモデル追加
+
+Ver.3.21(2008/11/8)
+・フレーム窓コピーボタンのモデル間での不具合修正
+・地面影の明るさ調整機能追加(「表示」→「地面影色設定」)
+
+Ver.3.20b(2008/10/31)
+・亞北ネルモデル不具合修正(右スカート前、後ボーンの修正)
+
+Ver.3.20a(2008/10/30)
+・亞北ネルモデル不具合修正
+
+Ver.3.20(2008/10/30)
+・亞北ネルモデル追加
+・「モデル編集時カメラ・照明(アクセサリ)追従」機能のバグ修正
+・モデル編集時、視点パネルの「下面」を「カメラ」に変更する仕様に修正
+
+Ver.3.16(2008/10/09)
+・vacファイル読込時ラジアン度数表示バグ修正
+・各種窓内数字修正時Deleteキー反応修正
+・一部読み込めないvsqファイルに対応
+
+Ver.3.15(2008/09/27)
+・アクセサリを含むpmmファイル読込時バグ修正(ver.3.14によるバグ)
+
+Ver.3.14(2008/09/26)
+・表示・IKがらみのバグ修正
+・pmmロード時、アクセサリxファイルが発見出来なかった場合に
+ xファイルの場所を指定するように変更
+
+Ver.3.13(2008/09/21)
+・メニュー項目ショートカットキー重複修正
+・ショートカットキー追加
+ A:全て選択 S:未登録選択 D:センター位置バイアス付加
+・フレーム操作窓Enterキー入力時の登録処理バグ修正
+
+Ver.3.12(2008/09/18)
+・ダミーボーン範囲選択時バグ修正
+・アクセサリ数128個以上時バグ修正
+・フレーム選択窓縦スクロールバーバグ修正
+
+Ver.3.11(2008/09/17)
+・wave波形表示ずれ修正
+・ダイアログ表示時Enterキーでフレーム登録されるバグ修正
+・pmmファイル関連付け起動対応
+・レンモデル瞬き・セーラー服修正
+
+Ver.3.10a(2008/09/12)
+・鏡音レンモデルフレーム窓バグ修正
+
+Ver.3.10(2008/09/11)
+・鏡音レンモデル追加
+・ディスプレイ解像度が高すぎる際に生じるエラー対策追加
+(樋口の環境では出ないエラーの対策のため、うまくいったかどうかは不明)
+
+Ver.3.05(2008/09/07)
+・初音ミクモデル著作権表示修正
+・弱音ハクモデル右肩スキニング修正
+・アクセサリモデル255個まで追加
+
+Ver.3.04(2008/08/31)
+・線形補間ボタン不具合修正
+・AVI出力時キャンセルボタン不具合修正
+
+Ver.3.03(2008/08/30)
+・ハクモデル不具合修正
+・vacファイル読込追加
+・その他
+
+Ver.3.02(2008/08/30)
+・表情フレーム登録ができないバグ修正
+・モデルを消去したあと保存したデータのロード時エラーバグ修正
+
+Ver.3.01(2008/08/30)
+・マルチモデル化
+・その他多数変更
+
+Ver.2.02(2008/07/16)
+・vsqファイル(tempo変更)バグ修正
+・AVI出力時のフレームずれ修正
+
+Ver.2.01(2008/04/08)
+・トゥーンレンダリングのエッジの太さを距離によって変更させた
+・Vista版対応(AVI出力時、高速モードを選択すると画質が落ちます)
+・フレーム窓内も、SHIFTキー選択時に選択状態をトグルさせるように変更
+
+Ver.2.00(2008/04/03)
+・演出モードの追加
+・アクセサリに地面モード追加(詳しくは"ステージ.vac"の中身参照)
+・背景AVIを再生時にも同期させるようにした
+・その他色々

BIN
examples/models/mmd/miku/toon00.bmp


BIN
examples/models/mmd/miku/toon01.bmp


BIN
examples/models/mmd/miku/toon02.bmp


BIN
examples/models/mmd/miku/toon03.bmp


BIN
examples/models/mmd/miku/toon04.bmp


BIN
examples/models/mmd/miku/toon05.bmp


BIN
examples/models/mmd/miku/toon06.bmp


BIN
examples/models/mmd/miku/toon07.bmp


BIN
examples/models/mmd/miku/toon08.bmp


BIN
examples/models/mmd/miku/toon09.bmp


BIN
examples/models/mmd/miku/toon10.bmp


+ 43 - 0
examples/models/mmd/vmd/readme_wavefile.txt

@@ -0,0 +1,43 @@
+MikuMikuDance用モーションデータ
+WAVEFILE
+
+○配布元動画
+【MMD-DMC2】 WAVEFILE 【モーション配布】
+http://www.nicovideo.jp/watch/sm13147122
+
+○トレース元動画:かんな@むーみんさん
+【ピピピ♪】 WAVEFILE 【踊ってみた】
+http://www.nicovideo.jp/watch/sm12016176
+
+○オリジナル曲:ラマーズP
+WAVEFILE/初音ミク
+http://www.nicovideo.jp/watch/sm11938255
+
+■使用条件
+
+改変、再配布はご自由にどうぞ。
+ご利用は自己責任で権利者の方々の迷惑にならないよう常識の範囲内でお願いします。
+営利目的の利用はご一報ください。無断での商用利用は禁止します。
+
+■説明・注意点
+
+Lat式ミクVer2.3 Whiteを利用して作成しております。
+
+手を合わせるモーションがあるため、モデルの誤差による修正はかなり困難になると思います。
+そのため、Lat式、レア様、標準ミクVer2に調整した3種類のモーションを同梱しました。
+ターンが多いため、ツインテールが荒ぶるのでLat式以外のミクモデルは辛いものがありますがなんとか使ってやってくだちい
+
+標準ミクVer2変更点
+・241フレーム
+ 右腕をめいいっぱいあげると顔に腕がめり込むため低めに調整しています
+・2675-2680フレーム
+ 右腕でツインテールを抱え込んでしまうため、右腕のモーションを下からくぐり抜けるように大幅に変更しています。
+ 違和感がある場合はLat式のモーションからコピーして調整してください。
+・表情
+ Lat式の「あ」x0.4 「い」「う」「お」x0.6 で調整してます。
+ 小さすぎると感じた場合はLat式の表情からコピーして調整してください。
+
+
+mail:[email protected]
+
+hino

BIN
examples/models/mmd/vmd/wavefile_v2.vmd


+ 181 - 0
examples/webgl_loader_mmd.html

@@ -0,0 +1,181 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - loaders - MMD loader</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #fff;
+				color: #000;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				color: #000;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+			}
+			#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
+		</style>
+	</head>
+
+	<body>
+		<div id="info">
+		<a href="http://threejs.org" target="_blank">three.js</a> - MMDLoader test<br />
+		Copyright
+		<a href="http://www.geocities.jp/higuchuu4/index_e.htm">Model Data</a>
+		<a href="http://www.nicovideo.jp/watch/sm13147122">Dance Data</a>
+		</div>
+
+		<script src="../build/three.min.js"></script>
+
+		<script src="js/libs/charsetencoder.min.js"></script>
+		<script src="js/loaders/MMDLoader.js"></script>
+		<script src="js/animation/CCDIKSolver.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			var container, stats;
+
+			var mesh, camera, mixer, scene, renderer;
+
+			var mouseX = 0, mouseY = 0;
+
+			var ikSolver;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			var clock = new THREE.Clock();
+
+			init();
+			animate();
+
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.z = 25;
+
+				// scene
+
+				scene = new THREE.Scene();
+
+				var ambient = new THREE.AmbientLight( 0x444444 );
+				scene.add( ambient );
+
+				var directionalLight = new THREE.DirectionalLight( 0xffeedd );
+				directionalLight.position.set( -1, 1, 1 ).normalize();
+				scene.add( directionalLight );
+
+				// model
+
+				var onProgress = function ( xhr ) {
+					if ( xhr.lengthComputable ) {
+						var percentComplete = xhr.loaded / xhr.total * 100;
+						console.log( Math.round(percentComplete, 2) + '% downloaded' );
+					}
+				};
+
+				var onError = function ( xhr ) {
+				};
+
+				var loader = new THREE.MMDLoader();
+				loader.load( 'models/mmd/miku/miku_v2.pmd', 'models/mmd/vmd/wavefile_v2.vmd', function ( object ) {
+
+					mesh = object;
+					mesh.position.y = -10;
+					scene.add( mesh );
+
+					mixer = new THREE.AnimationMixer( mesh );
+					mixer.addAction( new THREE.AnimationAction( mesh.geometry.animations[ 0 ] ) );
+					mixer.addAction( new THREE.AnimationAction( mesh.geometry.morphAnimations[ 0 ] ) ) ;
+
+					ikSolver = new THREE.CCDIKSolver( mesh );
+
+				}, onProgress, onError );
+
+				//
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setClearColor( new THREE.Color( 0xffffff ) );
+				container.appendChild( renderer.domElement );
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = ( event.clientX - windowHalfX ) / 2;
+				mouseY = ( event.clientY - windowHalfY ) / 2;
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+				render();
+
+			}
+
+			function render() {
+
+				camera.position.x += ( - mouseX - camera.position.x ) * .05;
+				camera.position.y += ( - mouseY - camera.position.y ) * .05;
+
+				camera.lookAt( scene.position );
+
+				if( mesh ) {
+
+					mixer.update( clock.getDelta() );
+
+					// this may should be in THREE.AnimationMixer or somewhere
+					if( ikSolver ) {
+
+						ikSolver.update();
+
+					}
+
+				}
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

Some files were not shown because too many files changed in this diff