218 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			218 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import { | ||
|  | 	Matrix4, | ||
|  | 	Quaternion, | ||
|  | 	Vector3 | ||
|  | } from 'three'; | ||
|  | import { MMDParser } from '../libs/mmdparser.module.js'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Dependencies | ||
|  |  *  - mmd-parser https://github.com/takahirox/mmd-parser
 | ||
|  |  */ | ||
|  | 
 | ||
|  | class MMDExporter { | ||
|  | 
 | ||
|  | 	/* TODO: implement | ||
|  | 	// mesh -> pmd
 | ||
|  | 	this.parsePmd = function ( object ) { | ||
|  | 
 | ||
|  | 	}; | ||
|  | 	*/ | ||
|  | 
 | ||
|  | 	/* TODO: implement | ||
|  | 	// mesh -> pmx
 | ||
|  | 	this.parsePmx = function ( object ) { | ||
|  | 
 | ||
|  | 	}; | ||
|  | 	*/ | ||
|  | 
 | ||
|  | 	/* TODO: implement | ||
|  | 	// animation + skeleton -> vmd
 | ||
|  | 	this.parseVmd = function ( object ) { | ||
|  | 
 | ||
|  | 	}; | ||
|  | 	*/ | ||
|  | 
 | ||
|  | 	/* | ||
|  | 	 * skeleton -> vpd | ||
|  | 	 * Returns Shift_JIS encoded Uint8Array. Otherwise return strings. | ||
|  | 	 */ | ||
|  | 	parseVpd( skin, outputShiftJis, useOriginalBones ) { | ||
|  | 
 | ||
|  | 		if ( skin.isSkinnedMesh !== true ) { | ||
|  | 
 | ||
|  | 			console.warn( 'THREE.MMDExporter: parseVpd() requires SkinnedMesh instance.' ); | ||
|  | 			return null; | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		function toStringsFromNumber( num ) { | ||
|  | 
 | ||
|  | 			if ( Math.abs( num ) < 1e-6 ) num = 0; | ||
|  | 
 | ||
|  | 			let a = num.toString(); | ||
|  | 
 | ||
|  | 			if ( a.indexOf( '.' ) === - 1 ) { | ||
|  | 
 | ||
|  | 				a += '.'; | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			a += '000000'; | ||
|  | 
 | ||
|  | 			const index = a.indexOf( '.' ); | ||
|  | 
 | ||
|  | 			const d = a.slice( 0, index ); | ||
|  | 			const p = a.slice( index + 1, index + 7 ); | ||
|  | 
 | ||
|  | 			return d + '.' + p; | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		function toStringsFromArray( array ) { | ||
|  | 
 | ||
|  | 			const a = []; | ||
|  | 
 | ||
|  | 			for ( let i = 0, il = array.length; i < il; i ++ ) { | ||
|  | 
 | ||
|  | 				a.push( toStringsFromNumber( array[ i ] ) ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return a.join( ',' ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		skin.updateMatrixWorld( true ); | ||
|  | 
 | ||
|  | 		const bones = skin.skeleton.bones; | ||
|  | 		const bones2 = getBindBones( skin ); | ||
|  | 
 | ||
|  | 		const position = new Vector3(); | ||
|  | 		const quaternion = new Quaternion(); | ||
|  | 		const quaternion2 = new Quaternion(); | ||
|  | 		const matrix = new Matrix4(); | ||
|  | 
 | ||
|  | 		const array = []; | ||
|  | 		array.push( 'Vocaloid Pose Data file' ); | ||
|  | 		array.push( '' ); | ||
|  | 		array.push( ( skin.name !== '' ? skin.name.replace( /\s/g, '_' ) : 'skin' ) + '.osm;' ); | ||
|  | 		array.push( bones.length + ';' ); | ||
|  | 		array.push( '' ); | ||
|  | 
 | ||
|  | 		for ( let i = 0, il = bones.length; i < il; i ++ ) { | ||
|  | 
 | ||
|  | 			const bone = bones[ i ]; | ||
|  | 			const bone2 = bones2[ i ]; | ||
|  | 
 | ||
|  | 			/* | ||
|  | 			 * use the bone matrix saved before solving IK. | ||
|  | 			 * see CCDIKSolver for the detail. | ||
|  | 			 */ | ||
|  | 			if ( useOriginalBones === true && | ||
|  | 				bone.userData.ik !== undefined && | ||
|  | 				bone.userData.ik.originalMatrix !== undefined ) { | ||
|  | 
 | ||
|  | 				matrix.fromArray( bone.userData.ik.originalMatrix ); | ||
|  | 
 | ||
|  | 			} else { | ||
|  | 
 | ||
|  | 				matrix.copy( bone.matrix ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			position.setFromMatrixPosition( matrix ); | ||
|  | 			quaternion.setFromRotationMatrix( matrix ); | ||
|  | 
 | ||
|  | 			const pArray = position.sub( bone2.position ).toArray(); | ||
|  | 			const qArray = quaternion2.copy( bone2.quaternion ).conjugate().multiply( quaternion ).toArray(); | ||
|  | 
 | ||
|  | 			// right to left
 | ||
|  | 			pArray[ 2 ] = - pArray[ 2 ]; | ||
|  | 			qArray[ 0 ] = - qArray[ 0 ]; | ||
|  | 			qArray[ 1 ] = - qArray[ 1 ]; | ||
|  | 
 | ||
|  | 			array.push( 'Bone' + i + '{' + bone.name ); | ||
|  | 			array.push( '  ' + toStringsFromArray( pArray ) + ';' ); | ||
|  | 			array.push( '  ' + toStringsFromArray( qArray ) + ';' ); | ||
|  | 			array.push( '}' ); | ||
|  | 			array.push( '' ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		array.push( '' ); | ||
|  | 
 | ||
|  | 		const lines = array.join( '\n' ); | ||
|  | 
 | ||
|  | 		return ( outputShiftJis === true ) ? unicodeToShiftjis( lines ) : lines; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | // Unicode to Shift_JIS table
 | ||
|  | let u2sTable; | ||
|  | 
 | ||
|  | function unicodeToShiftjis( str ) { | ||
|  | 
 | ||
|  | 	if ( u2sTable === undefined ) { | ||
|  | 
 | ||
|  | 		const encoder = new MMDParser.CharsetEncoder(); | ||
|  | 		const table = encoder.s2uTable; | ||
|  | 		u2sTable = {}; | ||
|  | 
 | ||
|  | 		const keys = Object.keys( table ); | ||
|  | 
 | ||
|  | 		for ( let i = 0, il = keys.length; i < il; i ++ ) { | ||
|  | 
 | ||
|  | 			let key = keys[ i ]; | ||
|  | 
 | ||
|  | 			const value = table[ key ]; | ||
|  | 			key = parseInt( key ); | ||
|  | 
 | ||
|  | 			u2sTable[ value ] = key; | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	const array = []; | ||
|  | 
 | ||
|  | 	for ( let i = 0, il = str.length; i < il; i ++ ) { | ||
|  | 
 | ||
|  | 		const code = str.charCodeAt( i ); | ||
|  | 
 | ||
|  | 		const value = u2sTable[ code ]; | ||
|  | 
 | ||
|  | 		if ( value === undefined ) { | ||
|  | 
 | ||
|  | 			throw new Error( 'cannot convert charcode 0x' + code.toString( 16 ) ); | ||
|  | 
 | ||
|  | 		} else if ( value > 0xff ) { | ||
|  | 
 | ||
|  | 			array.push( ( value >> 8 ) & 0xff ); | ||
|  | 			array.push( value & 0xff ); | ||
|  | 
 | ||
|  | 		} else { | ||
|  | 
 | ||
|  | 			array.push( value & 0xff ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return new Uint8Array( array ); | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | function getBindBones( skin ) { | ||
|  | 
 | ||
|  | 	// any more efficient ways?
 | ||
|  | 	const poseSkin = skin.clone(); | ||
|  | 	poseSkin.pose(); | ||
|  | 	return poseSkin.skeleton.bones; | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | export { MMDExporter }; |