123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046 |
- /**
- * @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.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 keys = morphAnimation.hierarchy[ i ].keys;
- var times = [];
- var values = [];
- for ( var j = 0; j < keys.length; j++ ) {
- var key = keys[ j ];
- times.push( key.time );
- values.push( key.value );
- }
- tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + i + ']', times, values ) );
- }
- 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 );
- }
- };
|