aframe-extras.loaders.js 110 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119
  1. (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. 'use strict';
  3. require('./src/loaders');
  4. },{"./src/loaders":8}],2:[function(require,module,exports){
  5. 'use strict';
  6. var _typeof2 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  7. var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
  8. return typeof obj === "undefined" ? "undefined" : _typeof2(obj);
  9. } : function (obj) {
  10. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj === "undefined" ? "undefined" : _typeof2(obj);
  11. };
  12. /**
  13. * @author Kyle-Larson https://github.com/Kyle-Larson
  14. * @author Takahiro https://github.com/takahirox
  15. * @author Lewy Blue https://github.com/looeee
  16. *
  17. * Loader loads FBX file and generates Group representing FBX scene.
  18. * Requires FBX file to be >= 7.0 and in ASCII or to be any version in Binary format.
  19. *
  20. * Supports:
  21. * Mesh Generation (Positional Data)
  22. * Normal Data (Per Vertex Drawing Instance)
  23. * UV Data (Per Vertex Drawing Instance)
  24. * Skinning
  25. * Animation
  26. * - Separated Animations based on stacks.
  27. * - Skeletal & Non-Skeletal Animations
  28. * NURBS (Open, Closed and Periodic forms)
  29. *
  30. * Needs Support:
  31. * Euler rotation order
  32. *
  33. *
  34. * FBX format references:
  35. * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure
  36. *
  37. * Binary format specification:
  38. * https://code.blender.org/2013/08/fbx-binary-file-format-specification/
  39. * https://wiki.rogiken.org/specifications/file-format/fbx/ (more detail but Japanese)
  40. */
  41. // Monkeypatch for <r89
  42. THREE.LoaderUtils = {
  43. extractUrlBase: THREE.Loader.prototype.extractUrlBase,
  44. decodeText: function decodeText(array) {
  45. return new TextDecoder().decode(array);
  46. }
  47. };
  48. (function () {
  49. module.exports = THREE.FBXLoader = function (manager) {
  50. this.manager = manager !== undefined ? manager : THREE.DefaultLoadingManager;
  51. };
  52. Object.assign(THREE.FBXLoader.prototype, {
  53. load: function load(url, onLoad, onProgress, onError) {
  54. var self = this;
  55. var resourceDirectory = THREE.LoaderUtils.extractUrlBase(url);
  56. var loader = new THREE.FileLoader(this.manager);
  57. loader.setResponseType('arraybuffer');
  58. loader.load(url, function (buffer) {
  59. try {
  60. var scene = self.parse(buffer, resourceDirectory);
  61. onLoad(scene);
  62. } catch (error) {
  63. window.setTimeout(function () {
  64. if (onError) onError(error);
  65. self.manager.itemError(url);
  66. }, 0);
  67. }
  68. }, onProgress, onError);
  69. },
  70. parse: function parse(FBXBuffer, resourceDirectory) {
  71. var FBXTree;
  72. if (isFbxFormatBinary(FBXBuffer)) {
  73. FBXTree = new BinaryParser().parse(FBXBuffer);
  74. } else {
  75. var FBXText = convertArrayBufferToString(FBXBuffer);
  76. if (!isFbxFormatASCII(FBXText)) {
  77. throw new Error('THREE.FBXLoader: Unknown format.');
  78. }
  79. if (getFbxVersion(FBXText) < 7000) {
  80. throw new Error('THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion(FBXText));
  81. }
  82. FBXTree = new TextParser().parse(FBXText);
  83. }
  84. // console.log( FBXTree );
  85. var connections = parseConnections(FBXTree);
  86. var images = parseImages(FBXTree);
  87. var textures = parseTextures(FBXTree, new THREE.TextureLoader(this.manager).setPath(resourceDirectory), images, connections);
  88. var materials = parseMaterials(FBXTree, textures, connections);
  89. var skeletons = parseDeformers(FBXTree, connections);
  90. var geometryMap = parseGeometries(FBXTree, connections, skeletons);
  91. var sceneGraph = parseScene(FBXTree, connections, skeletons, geometryMap, materials);
  92. return sceneGraph;
  93. }
  94. });
  95. // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry )
  96. // and details the connection type
  97. function parseConnections(FBXTree) {
  98. var connectionMap = new Map();
  99. if ('Connections' in FBXTree) {
  100. var rawConnections = FBXTree.Connections.connections;
  101. rawConnections.forEach(function (rawConnection) {
  102. var fromID = rawConnection[0];
  103. var toID = rawConnection[1];
  104. var relationship = rawConnection[2];
  105. if (!connectionMap.has(fromID)) {
  106. connectionMap.set(fromID, {
  107. parents: [],
  108. children: []
  109. });
  110. }
  111. var parentRelationship = { ID: toID, relationship: relationship };
  112. connectionMap.get(fromID).parents.push(parentRelationship);
  113. if (!connectionMap.has(toID)) {
  114. connectionMap.set(toID, {
  115. parents: [],
  116. children: []
  117. });
  118. }
  119. var childRelationship = { ID: fromID, relationship: relationship };
  120. connectionMap.get(toID).children.push(childRelationship);
  121. });
  122. }
  123. return connectionMap;
  124. }
  125. // Parse FBXTree.Objects.Video for embedded image data
  126. // These images are connected to textures in FBXTree.Objects.Textures
  127. // via FBXTree.Connections.
  128. function parseImages(FBXTree) {
  129. var images = {};
  130. var blobs = {};
  131. if ('Video' in FBXTree.Objects) {
  132. var videoNodes = FBXTree.Objects.Video;
  133. for (var nodeID in videoNodes) {
  134. var videoNode = videoNodes[nodeID];
  135. var id = parseInt(nodeID);
  136. images[id] = videoNode.Filename;
  137. // raw image data is in videoNode.Content
  138. if ('Content' in videoNode) {
  139. var arrayBufferContent = videoNode.Content instanceof ArrayBuffer && videoNode.Content.byteLength > 0;
  140. var base64Content = typeof videoNode.Content === 'string' && videoNode.Content !== '';
  141. if (arrayBufferContent || base64Content) {
  142. var image = parseImage(videoNodes[nodeID]);
  143. blobs[videoNode.Filename] = image;
  144. }
  145. }
  146. }
  147. }
  148. for (var id in images) {
  149. var filename = images[id];
  150. if (blobs[filename] !== undefined) images[id] = blobs[filename];else images[id] = images[id].split('\\').pop();
  151. }
  152. return images;
  153. }
  154. // Parse embedded image data in FBXTree.Video.Content
  155. function parseImage(videoNode) {
  156. var content = videoNode.Content;
  157. var fileName = videoNode.RelativeFilename || videoNode.Filename;
  158. var extension = fileName.slice(fileName.lastIndexOf('.') + 1).toLowerCase();
  159. var type;
  160. switch (extension) {
  161. case 'bmp':
  162. type = 'image/bmp';
  163. break;
  164. case 'jpg':
  165. case 'jpeg':
  166. type = 'image/jpeg';
  167. break;
  168. case 'png':
  169. type = 'image/png';
  170. break;
  171. case 'tif':
  172. type = 'image/tiff';
  173. break;
  174. default:
  175. console.warn('FBXLoader: Image type "' + extension + '" is not supported.');
  176. return;
  177. }
  178. if (typeof content === 'string') {
  179. // ASCII format
  180. return 'data:' + type + ';base64,' + content;
  181. } else {
  182. // Binary Format
  183. var array = new Uint8Array(content);
  184. return window.URL.createObjectURL(new Blob([array], { type: type }));
  185. }
  186. }
  187. // Parse nodes in FBXTree.Objects.Texture
  188. // These contain details such as UV scaling, cropping, rotation etc and are connected
  189. // to images in FBXTree.Objects.Video
  190. function parseTextures(FBXTree, loader, images, connections) {
  191. var textureMap = new Map();
  192. if ('Texture' in FBXTree.Objects) {
  193. var textureNodes = FBXTree.Objects.Texture;
  194. for (var nodeID in textureNodes) {
  195. var texture = parseTexture(textureNodes[nodeID], loader, images, connections);
  196. textureMap.set(parseInt(nodeID), texture);
  197. }
  198. }
  199. return textureMap;
  200. }
  201. // Parse individual node in FBXTree.Objects.Texture
  202. function parseTexture(textureNode, loader, images, connections) {
  203. var texture = loadTexture(textureNode, loader, images, connections);
  204. texture.ID = textureNode.id;
  205. texture.name = textureNode.attrName;
  206. var wrapModeU = textureNode.WrapModeU;
  207. var wrapModeV = textureNode.WrapModeV;
  208. var valueU = wrapModeU !== undefined ? wrapModeU.value : 0;
  209. var valueV = wrapModeV !== undefined ? wrapModeV.value : 0;
  210. // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a
  211. // 0: repeat(default), 1: clamp
  212. texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
  213. texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
  214. if ('Scaling' in textureNode) {
  215. var values = textureNode.Scaling.value;
  216. texture.repeat.x = values[0];
  217. texture.repeat.y = values[1];
  218. }
  219. return texture;
  220. }
  221. // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader
  222. function loadTexture(textureNode, loader, images, connections) {
  223. var fileName;
  224. var currentPath = loader.path;
  225. var children = connections.get(textureNode.id).children;
  226. if (children !== undefined && children.length > 0 && images[children[0].ID] !== undefined) {
  227. fileName = images[children[0].ID];
  228. if (fileName.indexOf('blob:') === 0 || fileName.indexOf('data:') === 0) {
  229. loader.setPath(undefined);
  230. }
  231. }
  232. var texture = loader.load(fileName);
  233. loader.setPath(currentPath);
  234. return texture;
  235. }
  236. // Parse nodes in FBXTree.Objects.Material
  237. function parseMaterials(FBXTree, textureMap, connections) {
  238. var materialMap = new Map();
  239. if ('Material' in FBXTree.Objects) {
  240. var materialNodes = FBXTree.Objects.Material;
  241. for (var nodeID in materialNodes) {
  242. var material = parseMaterial(FBXTree, materialNodes[nodeID], textureMap, connections);
  243. if (material !== null) materialMap.set(parseInt(nodeID), material);
  244. }
  245. }
  246. return materialMap;
  247. }
  248. // Parse single node in FBXTree.Objects.Material
  249. // Materials are connected to texture maps in FBXTree.Objects.Textures
  250. // FBX format currently only supports Lambert and Phong shading models
  251. function parseMaterial(FBXTree, materialNode, textureMap, connections) {
  252. var ID = materialNode.id;
  253. var name = materialNode.attrName;
  254. var type = materialNode.ShadingModel;
  255. //Case where FBX wraps shading model in property object.
  256. if ((typeof type === 'undefined' ? 'undefined' : _typeof(type)) === 'object') {
  257. type = type.value;
  258. }
  259. // Ignore unused materials which don't have any connections.
  260. if (!connections.has(ID)) return null;
  261. var parameters = parseParameters(FBXTree, materialNode, textureMap, ID, connections);
  262. var material;
  263. switch (type.toLowerCase()) {
  264. case 'phong':
  265. material = new THREE.MeshPhongMaterial();
  266. break;
  267. case 'lambert':
  268. material = new THREE.MeshLambertMaterial();
  269. break;
  270. default:
  271. console.warn('THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type);
  272. material = new THREE.MeshPhongMaterial({ color: 0x3300ff });
  273. break;
  274. }
  275. material.setValues(parameters);
  276. material.name = name;
  277. return material;
  278. }
  279. // Parse FBX material and return parameters suitable for a three.js material
  280. // Also parse the texture map and return any textures associated with the material
  281. function parseParameters(FBXTree, properties, textureMap, ID, connections) {
  282. var parameters = {};
  283. if (properties.BumpFactor) {
  284. parameters.bumpScale = properties.BumpFactor.value;
  285. }
  286. if (properties.Diffuse) {
  287. parameters.color = new THREE.Color().fromArray(properties.Diffuse.value);
  288. }
  289. if (properties.DisplacementFactor) {
  290. parameters.displacementScale = properties.DisplacementFactor.value;
  291. }
  292. if (properties.ReflectionFactor) {
  293. parameters.reflectivity = properties.ReflectionFactor.value;
  294. }
  295. if (properties.Specular) {
  296. parameters.specular = new THREE.Color().fromArray(properties.Specular.value);
  297. }
  298. if (properties.Shininess) {
  299. parameters.shininess = properties.Shininess.value;
  300. }
  301. if (properties.Emissive) {
  302. parameters.emissive = new THREE.Color().fromArray(properties.Emissive.value);
  303. }
  304. if (properties.EmissiveFactor) {
  305. parameters.emissiveIntensity = parseFloat(properties.EmissiveFactor.value);
  306. }
  307. if (properties.Opacity) {
  308. parameters.opacity = parseFloat(properties.Opacity.value);
  309. }
  310. if (parameters.opacity < 1.0) {
  311. parameters.transparent = true;
  312. }
  313. connections.get(ID).children.forEach(function (child) {
  314. var type = child.relationship;
  315. switch (type) {
  316. case 'Bump':
  317. parameters.bumpMap = textureMap.get(child.ID);
  318. break;
  319. case 'DiffuseColor':
  320. parameters.map = getTexture(FBXTree, textureMap, child.ID, connections);
  321. break;
  322. case 'DisplacementColor':
  323. parameters.displacementMap = getTexture(FBXTree, textureMap, child.ID, connections);
  324. break;
  325. case 'EmissiveColor':
  326. parameters.emissiveMap = getTexture(FBXTree, textureMap, child.ID, connections);
  327. break;
  328. case 'NormalMap':
  329. parameters.normalMap = getTexture(FBXTree, textureMap, child.ID, connections);
  330. break;
  331. case 'ReflectionColor':
  332. parameters.envMap = getTexture(FBXTree, textureMap, child.ID, connections);
  333. parameters.envMap.mapping = THREE.EquirectangularReflectionMapping;
  334. break;
  335. case 'SpecularColor':
  336. parameters.specularMap = getTexture(FBXTree, textureMap, child.ID, connections);
  337. break;
  338. case 'TransparentColor':
  339. parameters.alphaMap = getTexture(FBXTree, textureMap, child.ID, connections);
  340. parameters.transparent = true;
  341. break;
  342. case 'AmbientColor':
  343. case 'ShininessExponent': // AKA glossiness map
  344. case 'SpecularFactor': // AKA specularLevel
  345. case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor
  346. default:
  347. console.warn('THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type);
  348. break;
  349. }
  350. });
  351. return parameters;
  352. }
  353. // get a texture from the textureMap for use by a material.
  354. function getTexture(FBXTree, textureMap, id, connections) {
  355. // if the texture is a layered texture, just use the first layer and issue a warning
  356. if ('LayeredTexture' in FBXTree.Objects && id in FBXTree.Objects.LayeredTexture) {
  357. console.warn('THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.');
  358. id = connections.get(id).children[0].ID;
  359. }
  360. return textureMap.get(id);
  361. }
  362. // Parse nodes in FBXTree.Objects.Deformer
  363. // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here
  364. // Generates map of Skeleton-like objects for use later when generating and binding skeletons.
  365. function parseDeformers(FBXTree, connections) {
  366. var skeletons = {};
  367. if ('Deformer' in FBXTree.Objects) {
  368. var DeformerNodes = FBXTree.Objects.Deformer;
  369. for (var nodeID in DeformerNodes) {
  370. var deformerNode = DeformerNodes[nodeID];
  371. if (deformerNode.attrType === 'Skin') {
  372. var relationships = connections.get(parseInt(nodeID));
  373. var skeleton = parseSkeleton(relationships, DeformerNodes);
  374. skeleton.ID = nodeID;
  375. if (relationships.parents.length > 1) console.warn('THREE.FBXLoader: skeleton attached to more than one geometry is not supported.');
  376. skeleton.geometryID = relationships.parents[0].ID;
  377. skeletons[nodeID] = skeleton;
  378. }
  379. }
  380. }
  381. return skeletons;
  382. }
  383. // Parse single nodes in FBXTree.Objects.Deformer
  384. // The top level deformer nodes have type 'Skin' and subDeformer nodes have type 'Cluster'
  385. // Each skin node represents a skeleton and each cluster node represents a bone
  386. function parseSkeleton(connections, deformerNodes) {
  387. var rawBones = [];
  388. connections.children.forEach(function (child) {
  389. var subDeformerNode = deformerNodes[child.ID];
  390. if (subDeformerNode.attrType !== 'Cluster') return;
  391. var rawBone = {
  392. ID: child.ID,
  393. indices: [],
  394. weights: [],
  395. transform: new THREE.Matrix4().fromArray(subDeformerNode.Transform.a),
  396. transformLink: new THREE.Matrix4().fromArray(subDeformerNode.TransformLink.a),
  397. linkMode: subDeformerNode.Mode
  398. };
  399. if ('Indexes' in subDeformerNode) {
  400. rawBone.indices = subDeformerNode.Indexes.a;
  401. rawBone.weights = subDeformerNode.Weights.a;
  402. }
  403. rawBones.push(rawBone);
  404. });
  405. return {
  406. rawBones: rawBones,
  407. bones: []
  408. };
  409. }
  410. // Parse nodes in FBXTree.Objects.Geometry
  411. function parseGeometries(FBXTree, connections, skeletons) {
  412. var geometryMap = new Map();
  413. if ('Geometry' in FBXTree.Objects) {
  414. var geometryNodes = FBXTree.Objects.Geometry;
  415. for (var nodeID in geometryNodes) {
  416. var relationships = connections.get(parseInt(nodeID));
  417. var geo = parseGeometry(FBXTree, relationships, geometryNodes[nodeID], skeletons);
  418. geometryMap.set(parseInt(nodeID), geo);
  419. }
  420. }
  421. return geometryMap;
  422. }
  423. // Parse single node in FBXTree.Objects.Geometry
  424. function parseGeometry(FBXTree, relationships, geometryNode, skeletons) {
  425. switch (geometryNode.attrType) {
  426. case 'Mesh':
  427. return parseMeshGeometry(FBXTree, relationships, geometryNode, skeletons);
  428. break;
  429. case 'NurbsCurve':
  430. return parseNurbsGeometry(geometryNode);
  431. break;
  432. }
  433. }
  434. // Parse single node mesh geometry in FBXTree.Objects.Geometry
  435. function parseMeshGeometry(FBXTree, relationships, geometryNode, skeletons) {
  436. var modelNodes = relationships.parents.map(function (parent) {
  437. return FBXTree.Objects.Model[parent.ID];
  438. });
  439. // don't create geometry if it is not associated with any models
  440. if (modelNodes.length === 0) return;
  441. var skeleton = relationships.children.reduce(function (skeleton, child) {
  442. if (skeletons[child.ID] !== undefined) skeleton = skeletons[child.ID];
  443. return skeleton;
  444. }, null);
  445. var preTransform = new THREE.Matrix4();
  446. // TODO: if there is more than one model associated with the geometry, AND the models have
  447. // different geometric transforms, then this will cause problems
  448. // if ( modelNodes.length > 1 ) { }
  449. // For now just assume one model and get the preRotations from that
  450. var modelNode = modelNodes[0];
  451. if ('GeometricRotation' in modelNode) {
  452. var array = modelNode.GeometricRotation.value.map(THREE.Math.degToRad);
  453. array[3] = 'ZYX';
  454. preTransform.makeRotationFromEuler(new THREE.Euler().fromArray(array));
  455. }
  456. if ('GeometricTranslation' in modelNode) {
  457. preTransform.setPosition(new THREE.Vector3().fromArray(modelNode.GeometricTranslation.value));
  458. }
  459. return genGeometry(FBXTree, relationships, geometryNode, skeleton, preTransform);
  460. }
  461. // Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry
  462. function genGeometry(FBXTree, relationships, geometryNode, skeleton, preTransform) {
  463. var vertexPositions = geometryNode.Vertices.a;
  464. var vertexIndices = geometryNode.PolygonVertexIndex.a;
  465. // create arrays to hold the final data used to build the buffergeometry
  466. var vertexBuffer = [];
  467. var normalBuffer = [];
  468. var colorsBuffer = [];
  469. var uvsBuffer = [];
  470. var materialIndexBuffer = [];
  471. var vertexWeightsBuffer = [];
  472. var weightsIndicesBuffer = [];
  473. if (geometryNode.LayerElementColor) {
  474. var colorInfo = getColors(geometryNode.LayerElementColor[0]);
  475. }
  476. if (geometryNode.LayerElementMaterial) {
  477. var materialInfo = getMaterials(geometryNode.LayerElementMaterial[0]);
  478. }
  479. if (geometryNode.LayerElementNormal) {
  480. var normalInfo = getNormals(geometryNode.LayerElementNormal[0]);
  481. }
  482. if (geometryNode.LayerElementUV) {
  483. var uvInfo = [];
  484. var i = 0;
  485. while (geometryNode.LayerElementUV[i]) {
  486. uvInfo.push(getUVs(geometryNode.LayerElementUV[i]));
  487. i++;
  488. }
  489. }
  490. var weightTable = {};
  491. if (skeleton !== null) {
  492. skeleton.rawBones.forEach(function (rawBone, i) {
  493. // loop over the bone's vertex indices and weights
  494. rawBone.indices.forEach(function (index, j) {
  495. if (weightTable[index] === undefined) weightTable[index] = [];
  496. weightTable[index].push({
  497. id: i,
  498. weight: rawBone.weights[j]
  499. });
  500. });
  501. });
  502. }
  503. var polygonIndex = 0;
  504. var faceLength = 0;
  505. var displayedWeightsWarning = false;
  506. // these will hold data for a single face
  507. var vertexPositionIndexes = [];
  508. var faceNormals = [];
  509. var faceColors = [];
  510. var faceUVs = [];
  511. var faceWeights = [];
  512. var faceWeightIndices = [];
  513. vertexIndices.forEach(function (vertexIndex, polygonVertexIndex) {
  514. var endOfFace = false;
  515. // Face index and vertex index arrays are combined in a single array
  516. // A cube with quad faces looks like this:
  517. // PolygonVertexIndex: *24 {
  518. // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5
  519. // }
  520. // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3
  521. // to find index of last vertex multiply by -1 and subtract 1: -3 * - 1 - 1 = 2
  522. if (vertexIndex < 0) {
  523. vertexIndex = vertexIndex ^ -1; // equivalent to ( x * -1 ) - 1
  524. vertexIndices[polygonVertexIndex] = vertexIndex;
  525. endOfFace = true;
  526. }
  527. var weightIndices = [];
  528. var weights = [];
  529. vertexPositionIndexes.push(vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2);
  530. if (colorInfo) {
  531. var data = getData(polygonVertexIndex, polygonIndex, vertexIndex, colorInfo);
  532. faceColors.push(data[0], data[1], data[2]);
  533. }
  534. if (skeleton) {
  535. if (weightTable[vertexIndex] !== undefined) {
  536. weightTable[vertexIndex].forEach(function (wt) {
  537. weights.push(wt.weight);
  538. weightIndices.push(wt.id);
  539. });
  540. }
  541. if (weights.length > 4) {
  542. if (!displayedWeightsWarning) {
  543. console.warn('THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.');
  544. displayedWeightsWarning = true;
  545. }
  546. var wIndex = [0, 0, 0, 0];
  547. var Weight = [0, 0, 0, 0];
  548. weights.forEach(function (weight, weightIndex) {
  549. var currentWeight = weight;
  550. var currentIndex = weightIndices[weightIndex];
  551. Weight.forEach(function (comparedWeight, comparedWeightIndex, comparedWeightArray) {
  552. if (currentWeight > comparedWeight) {
  553. comparedWeightArray[comparedWeightIndex] = currentWeight;
  554. currentWeight = comparedWeight;
  555. var tmp = wIndex[comparedWeightIndex];
  556. wIndex[comparedWeightIndex] = currentIndex;
  557. currentIndex = tmp;
  558. }
  559. });
  560. });
  561. weightIndices = wIndex;
  562. weights = Weight;
  563. }
  564. // if the weight array is shorter than 4 pad with 0s
  565. while (weights.length < 4) {
  566. weights.push(0);
  567. weightIndices.push(0);
  568. }
  569. for (var i = 0; i < 4; ++i) {
  570. faceWeights.push(weights[i]);
  571. faceWeightIndices.push(weightIndices[i]);
  572. }
  573. }
  574. if (normalInfo) {
  575. var data = getData(polygonVertexIndex, polygonIndex, vertexIndex, normalInfo);
  576. faceNormals.push(data[0], data[1], data[2]);
  577. }
  578. if (materialInfo && materialInfo.mappingType !== 'AllSame') {
  579. var materialIndex = getData(polygonVertexIndex, polygonIndex, vertexIndex, materialInfo)[0];
  580. }
  581. if (uvInfo) {
  582. uvInfo.forEach(function (uv, i) {
  583. var data = getData(polygonVertexIndex, polygonIndex, vertexIndex, uv);
  584. if (faceUVs[i] === undefined) {
  585. faceUVs[i] = [];
  586. }
  587. faceUVs[i].push(data[0]);
  588. faceUVs[i].push(data[1]);
  589. });
  590. }
  591. faceLength++;
  592. // we have reached the end of a face - it may have 4 sides though
  593. // in which case the data is split to represent two 3 sided faces
  594. if (endOfFace) {
  595. for (var i = 2; i < faceLength; i++) {
  596. vertexBuffer.push(vertexPositions[vertexPositionIndexes[0]]);
  597. vertexBuffer.push(vertexPositions[vertexPositionIndexes[1]]);
  598. vertexBuffer.push(vertexPositions[vertexPositionIndexes[2]]);
  599. vertexBuffer.push(vertexPositions[vertexPositionIndexes[(i - 1) * 3]]);
  600. vertexBuffer.push(vertexPositions[vertexPositionIndexes[(i - 1) * 3 + 1]]);
  601. vertexBuffer.push(vertexPositions[vertexPositionIndexes[(i - 1) * 3 + 2]]);
  602. vertexBuffer.push(vertexPositions[vertexPositionIndexes[i * 3]]);
  603. vertexBuffer.push(vertexPositions[vertexPositionIndexes[i * 3 + 1]]);
  604. vertexBuffer.push(vertexPositions[vertexPositionIndexes[i * 3 + 2]]);
  605. if (skeleton) {
  606. vertexWeightsBuffer.push(faceWeights[0]);
  607. vertexWeightsBuffer.push(faceWeights[1]);
  608. vertexWeightsBuffer.push(faceWeights[2]);
  609. vertexWeightsBuffer.push(faceWeights[3]);
  610. vertexWeightsBuffer.push(faceWeights[(i - 1) * 4]);
  611. vertexWeightsBuffer.push(faceWeights[(i - 1) * 4 + 1]);
  612. vertexWeightsBuffer.push(faceWeights[(i - 1) * 4 + 2]);
  613. vertexWeightsBuffer.push(faceWeights[(i - 1) * 4 + 3]);
  614. vertexWeightsBuffer.push(faceWeights[i * 4]);
  615. vertexWeightsBuffer.push(faceWeights[i * 4 + 1]);
  616. vertexWeightsBuffer.push(faceWeights[i * 4 + 2]);
  617. vertexWeightsBuffer.push(faceWeights[i * 4 + 3]);
  618. weightsIndicesBuffer.push(faceWeightIndices[0]);
  619. weightsIndicesBuffer.push(faceWeightIndices[1]);
  620. weightsIndicesBuffer.push(faceWeightIndices[2]);
  621. weightsIndicesBuffer.push(faceWeightIndices[3]);
  622. weightsIndicesBuffer.push(faceWeightIndices[(i - 1) * 4]);
  623. weightsIndicesBuffer.push(faceWeightIndices[(i - 1) * 4 + 1]);
  624. weightsIndicesBuffer.push(faceWeightIndices[(i - 1) * 4 + 2]);
  625. weightsIndicesBuffer.push(faceWeightIndices[(i - 1) * 4 + 3]);
  626. weightsIndicesBuffer.push(faceWeightIndices[i * 4]);
  627. weightsIndicesBuffer.push(faceWeightIndices[i * 4 + 1]);
  628. weightsIndicesBuffer.push(faceWeightIndices[i * 4 + 2]);
  629. weightsIndicesBuffer.push(faceWeightIndices[i * 4 + 3]);
  630. }
  631. if (colorInfo) {
  632. colorsBuffer.push(faceColors[0]);
  633. colorsBuffer.push(faceColors[1]);
  634. colorsBuffer.push(faceColors[2]);
  635. colorsBuffer.push(faceColors[(i - 1) * 3]);
  636. colorsBuffer.push(faceColors[(i - 1) * 3 + 1]);
  637. colorsBuffer.push(faceColors[(i - 1) * 3 + 2]);
  638. colorsBuffer.push(faceColors[i * 3]);
  639. colorsBuffer.push(faceColors[i * 3 + 1]);
  640. colorsBuffer.push(faceColors[i * 3 + 2]);
  641. }
  642. if (materialInfo && materialInfo.mappingType !== 'AllSame') {
  643. materialIndexBuffer.push(materialIndex);
  644. materialIndexBuffer.push(materialIndex);
  645. materialIndexBuffer.push(materialIndex);
  646. }
  647. if (normalInfo) {
  648. normalBuffer.push(faceNormals[0]);
  649. normalBuffer.push(faceNormals[1]);
  650. normalBuffer.push(faceNormals[2]);
  651. normalBuffer.push(faceNormals[(i - 1) * 3]);
  652. normalBuffer.push(faceNormals[(i - 1) * 3 + 1]);
  653. normalBuffer.push(faceNormals[(i - 1) * 3 + 2]);
  654. normalBuffer.push(faceNormals[i * 3]);
  655. normalBuffer.push(faceNormals[i * 3 + 1]);
  656. normalBuffer.push(faceNormals[i * 3 + 2]);
  657. }
  658. if (uvInfo) {
  659. uvInfo.forEach(function (uv, j) {
  660. if (uvsBuffer[j] === undefined) uvsBuffer[j] = [];
  661. uvsBuffer[j].push(faceUVs[j][0]);
  662. uvsBuffer[j].push(faceUVs[j][1]);
  663. uvsBuffer[j].push(faceUVs[j][(i - 1) * 2]);
  664. uvsBuffer[j].push(faceUVs[j][(i - 1) * 2 + 1]);
  665. uvsBuffer[j].push(faceUVs[j][i * 2]);
  666. uvsBuffer[j].push(faceUVs[j][i * 2 + 1]);
  667. });
  668. }
  669. }
  670. polygonIndex++;
  671. faceLength = 0;
  672. // reset arrays for the next face
  673. vertexPositionIndexes = [];
  674. faceNormals = [];
  675. faceColors = [];
  676. faceUVs = [];
  677. faceWeights = [];
  678. faceWeightIndices = [];
  679. }
  680. });
  681. var geo = new THREE.BufferGeometry();
  682. geo.name = geometryNode.name;
  683. var positionAttribute = new THREE.Float32BufferAttribute(vertexBuffer, 3);
  684. preTransform.applyToBufferAttribute(positionAttribute);
  685. geo.addAttribute('position', positionAttribute);
  686. if (colorsBuffer.length > 0) {
  687. geo.addAttribute('color', new THREE.Float32BufferAttribute(colorsBuffer, 3));
  688. }
  689. if (skeleton) {
  690. geo.addAttribute('skinIndex', new THREE.Float32BufferAttribute(weightsIndicesBuffer, 4));
  691. geo.addAttribute('skinWeight', new THREE.Float32BufferAttribute(vertexWeightsBuffer, 4));
  692. // used later to bind the skeleton to the model
  693. geo.FBX_Deformer = skeleton;
  694. }
  695. if (normalBuffer.length > 0) {
  696. var normalAttribute = new THREE.Float32BufferAttribute(normalBuffer, 3);
  697. var normalMatrix = new THREE.Matrix3().getNormalMatrix(preTransform);
  698. normalMatrix.applyToBufferAttribute(normalAttribute);
  699. geo.addAttribute('normal', normalAttribute);
  700. }
  701. uvsBuffer.forEach(function (uvBuffer, i) {
  702. // subsequent uv buffers are called 'uv1', 'uv2', ...
  703. var name = 'uv' + (i + 1).toString();
  704. // the first uv buffer is just called 'uv'
  705. if (i === 0) {
  706. name = 'uv';
  707. }
  708. geo.addAttribute(name, new THREE.Float32BufferAttribute(uvsBuffer[i], 2));
  709. });
  710. if (materialInfo && materialInfo.mappingType !== 'AllSame') {
  711. // Convert the material indices of each vertex into rendering groups on the geometry.
  712. var prevMaterialIndex = materialIndexBuffer[0];
  713. var startIndex = 0;
  714. materialIndexBuffer.forEach(function (currentIndex, i) {
  715. if (currentIndex !== prevMaterialIndex) {
  716. geo.addGroup(startIndex, i - startIndex, prevMaterialIndex);
  717. prevMaterialIndex = currentIndex;
  718. startIndex = i;
  719. }
  720. });
  721. // the loop above doesn't add the last group, do that here.
  722. if (geo.groups.length > 0) {
  723. var lastGroup = geo.groups[geo.groups.length - 1];
  724. var lastIndex = lastGroup.start + lastGroup.count;
  725. if (lastIndex !== materialIndexBuffer.length) {
  726. geo.addGroup(lastIndex, materialIndexBuffer.length - lastIndex, prevMaterialIndex);
  727. }
  728. }
  729. // case where there are multiple materials but the whole geometry is only
  730. // using one of them
  731. if (geo.groups.length === 0) {
  732. geo.addGroup(0, materialIndexBuffer.length, materialIndexBuffer[0]);
  733. }
  734. }
  735. return geo;
  736. }
  737. // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists
  738. function getNormals(NormalNode) {
  739. var mappingType = NormalNode.MappingInformationType;
  740. var referenceType = NormalNode.ReferenceInformationType;
  741. var buffer = NormalNode.Normals.a;
  742. var indexBuffer = [];
  743. if (referenceType === 'IndexToDirect') {
  744. if ('NormalIndex' in NormalNode) {
  745. indexBuffer = NormalNode.NormalIndex.a;
  746. } else if ('NormalsIndex' in NormalNode) {
  747. indexBuffer = NormalNode.NormalsIndex.a;
  748. }
  749. }
  750. return {
  751. dataSize: 3,
  752. buffer: buffer,
  753. indices: indexBuffer,
  754. mappingType: mappingType,
  755. referenceType: referenceType
  756. };
  757. }
  758. // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists
  759. function getUVs(UVNode) {
  760. var mappingType = UVNode.MappingInformationType;
  761. var referenceType = UVNode.ReferenceInformationType;
  762. var buffer = UVNode.UV.a;
  763. var indexBuffer = [];
  764. if (referenceType === 'IndexToDirect') {
  765. indexBuffer = UVNode.UVIndex.a;
  766. }
  767. return {
  768. dataSize: 2,
  769. buffer: buffer,
  770. indices: indexBuffer,
  771. mappingType: mappingType,
  772. referenceType: referenceType
  773. };
  774. }
  775. // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists
  776. function getColors(ColorNode) {
  777. var mappingType = ColorNode.MappingInformationType;
  778. var referenceType = ColorNode.ReferenceInformationType;
  779. var buffer = ColorNode.Colors.a;
  780. var indexBuffer = [];
  781. if (referenceType === 'IndexToDirect') {
  782. indexBuffer = ColorNode.ColorIndex.a;
  783. }
  784. return {
  785. dataSize: 4,
  786. buffer: buffer,
  787. indices: indexBuffer,
  788. mappingType: mappingType,
  789. referenceType: referenceType
  790. };
  791. }
  792. // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists
  793. function getMaterials(MaterialNode) {
  794. var mappingType = MaterialNode.MappingInformationType;
  795. var referenceType = MaterialNode.ReferenceInformationType;
  796. if (mappingType === 'NoMappingInformation') {
  797. return {
  798. dataSize: 1,
  799. buffer: [0],
  800. indices: [0],
  801. mappingType: 'AllSame',
  802. referenceType: referenceType
  803. };
  804. }
  805. var materialIndexBuffer = MaterialNode.Materials.a;
  806. // Since materials are stored as indices, there's a bit of a mismatch between FBX and what
  807. // we expect.So we create an intermediate buffer that points to the index in the buffer,
  808. // for conforming with the other functions we've written for other data.
  809. var materialIndices = [];
  810. for (var i = 0; i < materialIndexBuffer.length; ++i) {
  811. materialIndices.push(i);
  812. }
  813. return {
  814. dataSize: 1,
  815. buffer: materialIndexBuffer,
  816. indices: materialIndices,
  817. mappingType: mappingType,
  818. referenceType: referenceType
  819. };
  820. }
  821. // Functions use the infoObject and given indices to return value array of geometry.
  822. // Parameters:
  823. // - polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
  824. // - polygonIndex - Index of polygon in geometry.
  825. // - vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
  826. // - infoObject: can be materialInfo, normalInfo, UVInfo or colorInfo
  827. // Index type:
  828. // - Direct: index is same as polygonVertexIndex
  829. // - IndexToDirect: infoObject has it's own set of indices
  830. var dataArray = [];
  831. var GetData = {
  832. ByPolygonVertex: {
  833. Direct: function Direct(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
  834. var from = polygonVertexIndex * infoObject.dataSize;
  835. var to = polygonVertexIndex * infoObject.dataSize + infoObject.dataSize;
  836. return slice(dataArray, infoObject.buffer, from, to);
  837. },
  838. IndexToDirect: function IndexToDirect(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
  839. var index = infoObject.indices[polygonVertexIndex];
  840. var from = index * infoObject.dataSize;
  841. var to = index * infoObject.dataSize + infoObject.dataSize;
  842. return slice(dataArray, infoObject.buffer, from, to);
  843. }
  844. },
  845. ByPolygon: {
  846. Direct: function Direct(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
  847. var from = polygonIndex * infoObject.dataSize;
  848. var to = polygonIndex * infoObject.dataSize + infoObject.dataSize;
  849. return slice(dataArray, infoObject.buffer, from, to);
  850. },
  851. IndexToDirect: function IndexToDirect(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
  852. var index = infoObject.indices[polygonIndex];
  853. var from = index * infoObject.dataSize;
  854. var to = index * infoObject.dataSize + infoObject.dataSize;
  855. return slice(dataArray, infoObject.buffer, from, to);
  856. }
  857. },
  858. ByVertice: {
  859. Direct: function Direct(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
  860. var from = vertexIndex * infoObject.dataSize;
  861. var to = vertexIndex * infoObject.dataSize + infoObject.dataSize;
  862. return slice(dataArray, infoObject.buffer, from, to);
  863. }
  864. },
  865. AllSame: {
  866. IndexToDirect: function IndexToDirect(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
  867. var from = infoObject.indices[0] * infoObject.dataSize;
  868. var to = infoObject.indices[0] * infoObject.dataSize + infoObject.dataSize;
  869. return slice(dataArray, infoObject.buffer, from, to);
  870. }
  871. }
  872. };
  873. function getData(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
  874. return GetData[infoObject.mappingType][infoObject.referenceType](polygonVertexIndex, polygonIndex, vertexIndex, infoObject);
  875. }
  876. // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry
  877. function parseNurbsGeometry(geometryNode) {
  878. if (THREE.NURBSCurve === undefined) {
  879. console.error('THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.');
  880. return new THREE.BufferGeometry();
  881. }
  882. var order = parseInt(geometryNode.Order);
  883. if (isNaN(order)) {
  884. console.error('THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geometryNode.Order, geometryNode.id);
  885. return new THREE.BufferGeometry();
  886. }
  887. var degree = order - 1;
  888. var knots = geometryNode.KnotVector.a;
  889. var controlPoints = [];
  890. var pointsValues = geometryNode.Points.a;
  891. for (var i = 0, l = pointsValues.length; i < l; i += 4) {
  892. controlPoints.push(new THREE.Vector4().fromArray(pointsValues, i));
  893. }
  894. var startKnot, endKnot;
  895. if (geometryNode.Form === 'Closed') {
  896. controlPoints.push(controlPoints[0]);
  897. } else if (geometryNode.Form === 'Periodic') {
  898. startKnot = degree;
  899. endKnot = knots.length - 1 - startKnot;
  900. for (var i = 0; i < degree; ++i) {
  901. controlPoints.push(controlPoints[i]);
  902. }
  903. }
  904. var curve = new THREE.NURBSCurve(degree, knots, controlPoints, startKnot, endKnot);
  905. var vertices = curve.getPoints(controlPoints.length * 7);
  906. var positions = new Float32Array(vertices.length * 3);
  907. vertices.forEach(function (vertex, i) {
  908. vertex.toArray(positions, i * 3);
  909. });
  910. var geometry = new THREE.BufferGeometry();
  911. geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
  912. return geometry;
  913. }
  914. // create the main THREE.Group() to be returned by the loader
  915. function parseScene(FBXTree, connections, skeletons, geometryMap, materialMap) {
  916. var sceneGraph = new THREE.Group();
  917. var modelMap = parseModels(FBXTree, skeletons, geometryMap, materialMap, connections);
  918. var modelNodes = FBXTree.Objects.Model;
  919. modelMap.forEach(function (model) {
  920. var modelNode = modelNodes[model.ID];
  921. setLookAtProperties(FBXTree, model, modelNode, connections, sceneGraph);
  922. var parentConnections = connections.get(model.ID).parents;
  923. parentConnections.forEach(function (connection) {
  924. var parent = modelMap.get(connection.ID);
  925. if (parent !== undefined) parent.add(model);
  926. });
  927. if (model.parent === null) {
  928. sceneGraph.add(model);
  929. }
  930. });
  931. bindSkeleton(FBXTree, skeletons, geometryMap, modelMap, connections);
  932. addAnimations(FBXTree, connections, sceneGraph);
  933. createAmbientLight(FBXTree, sceneGraph);
  934. return sceneGraph;
  935. }
  936. // parse nodes in FBXTree.Objects.Model
  937. function parseModels(FBXTree, skeletons, geometryMap, materialMap, connections) {
  938. var modelMap = new Map();
  939. var modelNodes = FBXTree.Objects.Model;
  940. for (var nodeID in modelNodes) {
  941. var id = parseInt(nodeID);
  942. var node = modelNodes[nodeID];
  943. var relationships = connections.get(id);
  944. var model = buildSkeleton(relationships, skeletons, id, node.attrName);
  945. if (!model) {
  946. switch (node.attrType) {
  947. case 'Camera':
  948. model = createCamera(FBXTree, relationships);
  949. break;
  950. case 'Light':
  951. model = createLight(FBXTree, relationships);
  952. break;
  953. case 'Mesh':
  954. model = createMesh(FBXTree, relationships, geometryMap, materialMap);
  955. break;
  956. case 'NurbsCurve':
  957. model = createCurve(relationships, geometryMap);
  958. break;
  959. case 'LimbNode': // usually associated with a Bone, however if a Bone was not created we'll make a Group instead
  960. case 'Null':
  961. default:
  962. model = new THREE.Group();
  963. break;
  964. }
  965. model.name = THREE.PropertyBinding.sanitizeNodeName(node.attrName);
  966. model.ID = id;
  967. }
  968. setModelTransforms(FBXTree, model, node);
  969. modelMap.set(id, model);
  970. }
  971. return modelMap;
  972. }
  973. function buildSkeleton(relationships, skeletons, id, name) {
  974. var bone = null;
  975. relationships.parents.forEach(function (parent) {
  976. for (var ID in skeletons) {
  977. var skeleton = skeletons[ID];
  978. skeleton.rawBones.forEach(function (rawBone, i) {
  979. if (rawBone.ID === parent.ID) {
  980. var subBone = bone;
  981. bone = new THREE.Bone();
  982. bone.matrixWorld.copy(rawBone.transformLink);
  983. // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id
  984. bone.name = THREE.PropertyBinding.sanitizeNodeName(name);
  985. bone.ID = id;
  986. skeleton.bones[i] = bone;
  987. // In cases where a bone is shared between multiple meshes
  988. // duplicate the bone here and and it as a child of the first bone
  989. if (subBone !== null) {
  990. bone.add(subBone);
  991. }
  992. }
  993. });
  994. }
  995. });
  996. return bone;
  997. }
  998. // create a THREE.PerspectiveCamera or THREE.OrthographicCamera
  999. function createCamera(FBXTree, relationships) {
  1000. var model;
  1001. var cameraAttribute;
  1002. relationships.children.forEach(function (child) {
  1003. var attr = FBXTree.Objects.NodeAttribute[child.ID];
  1004. if (attr !== undefined) {
  1005. cameraAttribute = attr;
  1006. }
  1007. });
  1008. if (cameraAttribute === undefined) {
  1009. model = new THREE.Object3D();
  1010. } else {
  1011. var type = 0;
  1012. if (cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1) {
  1013. type = 1;
  1014. }
  1015. var nearClippingPlane = 1;
  1016. if (cameraAttribute.NearPlane !== undefined) {
  1017. nearClippingPlane = cameraAttribute.NearPlane.value / 1000;
  1018. }
  1019. var farClippingPlane = 1000;
  1020. if (cameraAttribute.FarPlane !== undefined) {
  1021. farClippingPlane = cameraAttribute.FarPlane.value / 1000;
  1022. }
  1023. var width = window.innerWidth;
  1024. var height = window.innerHeight;
  1025. if (cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined) {
  1026. width = cameraAttribute.AspectWidth.value;
  1027. height = cameraAttribute.AspectHeight.value;
  1028. }
  1029. var aspect = width / height;
  1030. var fov = 45;
  1031. if (cameraAttribute.FieldOfView !== undefined) {
  1032. fov = cameraAttribute.FieldOfView.value;
  1033. }
  1034. var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null;
  1035. switch (type) {
  1036. case 0:
  1037. // Perspective
  1038. model = new THREE.PerspectiveCamera(fov, aspect, nearClippingPlane, farClippingPlane);
  1039. if (focalLength !== null) model.setFocalLength(focalLength);
  1040. break;
  1041. case 1:
  1042. // Orthographic
  1043. model = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, nearClippingPlane, farClippingPlane);
  1044. break;
  1045. default:
  1046. console.warn('THREE.FBXLoader: Unknown camera type ' + type + '.');
  1047. model = new THREE.Object3D();
  1048. break;
  1049. }
  1050. }
  1051. return model;
  1052. }
  1053. // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight
  1054. function createLight(FBXTree, relationships) {
  1055. var model;
  1056. var lightAttribute;
  1057. relationships.children.forEach(function (child) {
  1058. var attr = FBXTree.Objects.NodeAttribute[child.ID];
  1059. if (attr !== undefined) {
  1060. lightAttribute = attr;
  1061. }
  1062. });
  1063. if (lightAttribute === undefined) {
  1064. model = new THREE.Object3D();
  1065. } else {
  1066. var type;
  1067. // LightType can be undefined for Point lights
  1068. if (lightAttribute.LightType === undefined) {
  1069. type = 0;
  1070. } else {
  1071. type = lightAttribute.LightType.value;
  1072. }
  1073. var color = 0xffffff;
  1074. if (lightAttribute.Color !== undefined) {
  1075. color = new THREE.Color().fromArray(lightAttribute.Color.value);
  1076. }
  1077. var intensity = lightAttribute.Intensity === undefined ? 1 : lightAttribute.Intensity.value / 100;
  1078. // light disabled
  1079. if (lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0) {
  1080. intensity = 0;
  1081. }
  1082. var distance = 0;
  1083. if (lightAttribute.FarAttenuationEnd !== undefined) {
  1084. if (lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0) {
  1085. distance = 0;
  1086. } else {
  1087. distance = lightAttribute.FarAttenuationEnd.value / 1000;
  1088. }
  1089. }
  1090. // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd?
  1091. var decay = 1;
  1092. switch (type) {
  1093. case 0:
  1094. // Point
  1095. model = new THREE.PointLight(color, intensity, distance, decay);
  1096. break;
  1097. case 1:
  1098. // Directional
  1099. model = new THREE.DirectionalLight(color, intensity);
  1100. break;
  1101. case 2:
  1102. // Spot
  1103. var angle = Math.PI / 3;
  1104. if (lightAttribute.InnerAngle !== undefined) {
  1105. angle = THREE.Math.degToRad(lightAttribute.InnerAngle.value);
  1106. }
  1107. var penumbra = 0;
  1108. if (lightAttribute.OuterAngle !== undefined) {
  1109. // TODO: this is not correct - FBX calculates outer and inner angle in degrees
  1110. // with OuterAngle > InnerAngle && OuterAngle <= Math.PI
  1111. // while three.js uses a penumbra between (0, 1) to attenuate the inner angle
  1112. penumbra = THREE.Math.degToRad(lightAttribute.OuterAngle.value);
  1113. penumbra = Math.max(penumbra, 1);
  1114. }
  1115. model = new THREE.SpotLight(color, intensity, distance, angle, penumbra, decay);
  1116. break;
  1117. default:
  1118. console.warn('THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.');
  1119. model = new THREE.PointLight(color, intensity);
  1120. break;
  1121. }
  1122. if (lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1) {
  1123. model.castShadow = true;
  1124. }
  1125. }
  1126. return model;
  1127. }
  1128. function createMesh(FBXTree, relationships, geometryMap, materialMap) {
  1129. var model;
  1130. var geometry = null;
  1131. var material = null;
  1132. var materials = [];
  1133. // get geometry and materials(s) from connections
  1134. relationships.children.forEach(function (child) {
  1135. if (geometryMap.has(child.ID)) {
  1136. geometry = geometryMap.get(child.ID);
  1137. }
  1138. if (materialMap.has(child.ID)) {
  1139. materials.push(materialMap.get(child.ID));
  1140. }
  1141. });
  1142. if (materials.length > 1) {
  1143. material = materials;
  1144. } else if (materials.length > 0) {
  1145. material = materials[0];
  1146. } else {
  1147. material = new THREE.MeshPhongMaterial({ color: 0xcccccc });
  1148. materials.push(material);
  1149. }
  1150. if ('color' in geometry.attributes) {
  1151. materials.forEach(function (material) {
  1152. material.vertexColors = THREE.VertexColors;
  1153. });
  1154. }
  1155. if (geometry.FBX_Deformer) {
  1156. materials.forEach(function (material) {
  1157. material.skinning = true;
  1158. });
  1159. model = new THREE.SkinnedMesh(geometry, material);
  1160. } else {
  1161. model = new THREE.Mesh(geometry, material);
  1162. }
  1163. return model;
  1164. }
  1165. function createCurve(relationships, geometryMap) {
  1166. var geometry = relationships.children.reduce(function (geo, child) {
  1167. if (geometryMap.has(child.ID)) geo = geometryMap.get(child.ID);
  1168. return geo;
  1169. }, null);
  1170. // FBX does not list materials for Nurbs lines, so we'll just put our own in here.
  1171. var material = new THREE.LineBasicMaterial({ color: 0x3300ff, linewidth: 1 });
  1172. return new THREE.Line(geometry, material);
  1173. }
  1174. // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light
  1175. function createAmbientLight(FBXTree, sceneGraph) {
  1176. if ('GlobalSettings' in FBXTree && 'AmbientColor' in FBXTree.GlobalSettings) {
  1177. var ambientColor = FBXTree.GlobalSettings.AmbientColor.value;
  1178. var r = ambientColor[0];
  1179. var g = ambientColor[1];
  1180. var b = ambientColor[2];
  1181. if (r !== 0 || g !== 0 || b !== 0) {
  1182. var color = new THREE.Color(r, g, b);
  1183. sceneGraph.add(new THREE.AmbientLight(color, 1));
  1184. }
  1185. }
  1186. }
  1187. function setLookAtProperties(FBXTree, model, modelNode, connections, sceneGraph) {
  1188. if ('LookAtProperty' in modelNode) {
  1189. var children = connections.get(model.ID).children;
  1190. children.forEach(function (child) {
  1191. if (child.relationship === 'LookAtProperty') {
  1192. var lookAtTarget = FBXTree.Objects.Model[child.ID];
  1193. if ('Lcl_Translation' in lookAtTarget) {
  1194. var pos = lookAtTarget.Lcl_Translation.value;
  1195. // DirectionalLight, SpotLight
  1196. if (model.target !== undefined) {
  1197. model.target.position.fromArray(pos);
  1198. sceneGraph.add(model.target);
  1199. } else {
  1200. // Cameras and other Object3Ds
  1201. model.lookAt(new THREE.Vector3().fromArray(pos));
  1202. }
  1203. }
  1204. }
  1205. });
  1206. }
  1207. }
  1208. // parse the model node for transform details and apply them to the model
  1209. function setModelTransforms(FBXTree, model, modelNode) {
  1210. // http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html
  1211. if ('RotationOrder' in modelNode) {
  1212. var enums = ['XYZ', // default
  1213. 'XZY', 'YZX', 'ZXY', 'YXZ', 'ZYX', 'SphericXYZ'];
  1214. var value = parseInt(modelNode.RotationOrder.value, 10);
  1215. if (value > 0 && value < 6) {
  1216. // model.rotation.order = enums[ value ];
  1217. // Note: Euler order other than XYZ is currently not supported, so just display a warning for now
  1218. console.warn('THREE.FBXLoader: unsupported Euler Order: %s. Currently only XYZ order is supported. Animations and rotations may be incorrect.', enums[value]);
  1219. } else if (value === 6) {
  1220. console.warn('THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.');
  1221. }
  1222. }
  1223. if ('Lcl_Translation' in modelNode) {
  1224. model.position.fromArray(modelNode.Lcl_Translation.value);
  1225. }
  1226. if ('Lcl_Rotation' in modelNode) {
  1227. var rotation = modelNode.Lcl_Rotation.value.map(THREE.Math.degToRad);
  1228. rotation.push('ZYX');
  1229. model.rotation.fromArray(rotation);
  1230. }
  1231. if ('Lcl_Scaling' in modelNode) {
  1232. model.scale.fromArray(modelNode.Lcl_Scaling.value);
  1233. }
  1234. if ('PreRotation' in modelNode) {
  1235. var array = modelNode.PreRotation.value.map(THREE.Math.degToRad);
  1236. array[3] = 'ZYX';
  1237. var preRotations = new THREE.Euler().fromArray(array);
  1238. preRotations = new THREE.Quaternion().setFromEuler(preRotations);
  1239. var currentRotation = new THREE.Quaternion().setFromEuler(model.rotation);
  1240. preRotations.multiply(currentRotation);
  1241. model.rotation.setFromQuaternion(preRotations, 'ZYX');
  1242. }
  1243. }
  1244. function bindSkeleton(FBXTree, skeletons, geometryMap, modelMap, connections) {
  1245. var bindMatrices = parsePoseNodes(FBXTree);
  1246. for (var ID in skeletons) {
  1247. var skeleton = skeletons[ID];
  1248. var parents = connections.get(parseInt(skeleton.ID)).parents;
  1249. parents.forEach(function (parent) {
  1250. if (geometryMap.has(parent.ID)) {
  1251. var geoID = parent.ID;
  1252. var geoRelationships = connections.get(geoID);
  1253. geoRelationships.parents.forEach(function (geoConnParent) {
  1254. if (modelMap.has(geoConnParent.ID)) {
  1255. var model = modelMap.get(geoConnParent.ID);
  1256. model.bind(new THREE.Skeleton(skeleton.bones), bindMatrices[geoConnParent.ID]);
  1257. }
  1258. });
  1259. }
  1260. });
  1261. }
  1262. }
  1263. function parsePoseNodes(FBXTree) {
  1264. var bindMatrices = {};
  1265. if ('Pose' in FBXTree.Objects) {
  1266. var BindPoseNode = FBXTree.Objects.Pose;
  1267. for (var nodeID in BindPoseNode) {
  1268. if (BindPoseNode[nodeID].attrType === 'BindPose') {
  1269. var poseNodes = BindPoseNode[nodeID].PoseNode;
  1270. if (Array.isArray(poseNodes)) {
  1271. poseNodes.forEach(function (poseNode) {
  1272. bindMatrices[poseNode.Node] = new THREE.Matrix4().fromArray(poseNode.Matrix.a);
  1273. });
  1274. } else {
  1275. bindMatrices[poseNodes.Node] = new THREE.Matrix4().fromArray(poseNodes.Matrix.a);
  1276. }
  1277. }
  1278. }
  1279. }
  1280. return bindMatrices;
  1281. }
  1282. function parseAnimations(FBXTree, connections) {
  1283. // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve,
  1284. // if this is undefined we can safely assume there are no animations
  1285. if (FBXTree.Objects.AnimationCurve === undefined) return undefined;
  1286. var curveNodesMap = parseAnimationCurveNodes(FBXTree);
  1287. parseAnimationCurves(FBXTree, connections, curveNodesMap);
  1288. var layersMap = parseAnimationLayers(FBXTree, connections, curveNodesMap);
  1289. var rawClips = parseAnimStacks(FBXTree, connections, layersMap);
  1290. return rawClips;
  1291. }
  1292. // parse nodes in FBXTree.Objects.AnimationCurveNode
  1293. // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation )
  1294. // and is referenced by an AnimationLayer
  1295. function parseAnimationCurveNodes(FBXTree) {
  1296. var rawCurveNodes = FBXTree.Objects.AnimationCurveNode;
  1297. var curveNodesMap = new Map();
  1298. for (var nodeID in rawCurveNodes) {
  1299. var rawCurveNode = rawCurveNodes[nodeID];
  1300. if (rawCurveNode.attrName.match(/S|R|T/) !== null) {
  1301. var curveNode = {
  1302. id: rawCurveNode.id,
  1303. attr: rawCurveNode.attrName,
  1304. curves: {}
  1305. };
  1306. curveNodesMap.set(curveNode.id, curveNode);
  1307. }
  1308. }
  1309. return curveNodesMap;
  1310. }
  1311. // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to
  1312. // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated
  1313. // axis ( e.g. times and values of x rotation)
  1314. function parseAnimationCurves(FBXTree, connections, curveNodesMap) {
  1315. var rawCurves = FBXTree.Objects.AnimationCurve;
  1316. for (var nodeID in rawCurves) {
  1317. var animationCurve = {
  1318. id: rawCurves[nodeID].id,
  1319. times: rawCurves[nodeID].KeyTime.a.map(convertFBXTimeToSeconds),
  1320. values: rawCurves[nodeID].KeyValueFloat.a
  1321. };
  1322. var relationships = connections.get(animationCurve.id);
  1323. if (relationships !== undefined) {
  1324. var animationCurveID = relationships.parents[0].ID;
  1325. var animationCurveRelationship = relationships.parents[0].relationship;
  1326. var axis = '';
  1327. if (animationCurveRelationship.match(/X/)) {
  1328. axis = 'x';
  1329. } else if (animationCurveRelationship.match(/Y/)) {
  1330. axis = 'y';
  1331. } else if (animationCurveRelationship.match(/Z/)) {
  1332. axis = 'z';
  1333. } else {
  1334. continue;
  1335. }
  1336. curveNodesMap.get(animationCurveID).curves[axis] = animationCurve;
  1337. }
  1338. }
  1339. }
  1340. // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references
  1341. // to various AnimationCurveNodes and is referenced by an AnimationStack node
  1342. // note: theoretically a stack can multiple layers, however in practice there always seems to be one per stack
  1343. function parseAnimationLayers(FBXTree, connections, curveNodesMap) {
  1344. var rawLayers = FBXTree.Objects.AnimationLayer;
  1345. var layersMap = new Map();
  1346. for (var nodeID in rawLayers) {
  1347. var layerCurveNodes = [];
  1348. var connection = connections.get(parseInt(nodeID));
  1349. if (connection !== undefined) {
  1350. // all the animationCurveNodes used in the layer
  1351. var children = connection.children;
  1352. children.forEach(function (child, i) {
  1353. if (curveNodesMap.has(child.ID)) {
  1354. var curveNode = curveNodesMap.get(child.ID);
  1355. // check that the curves are defined for at least one axis, otherwise ignore the curveNode
  1356. if (curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined) {
  1357. if (layerCurveNodes[i] === undefined) {
  1358. var modelID;
  1359. connections.get(child.ID).parents.forEach(function (parent) {
  1360. if (parent.relationship !== undefined) modelID = parent.ID;
  1361. });
  1362. var rawModel = FBXTree.Objects.Model[modelID.toString()];
  1363. var node = {
  1364. modelName: THREE.PropertyBinding.sanitizeNodeName(rawModel.attrName),
  1365. initialPosition: [0, 0, 0],
  1366. initialRotation: [0, 0, 0],
  1367. initialScale: [1, 1, 1]
  1368. };
  1369. if ('Lcl_Translation' in rawModel) node.initialPosition = rawModel.Lcl_Translation.value;
  1370. if ('Lcl_Rotation' in rawModel) node.initialRotation = rawModel.Lcl_Rotation.value;
  1371. if ('Lcl_Scaling' in rawModel) node.initialScale = rawModel.Lcl_Scaling.value;
  1372. // if the animated model is pre rotated, we'll have to apply the pre rotations to every
  1373. // animation value as well
  1374. if ('PreRotation' in rawModel) node.preRotations = rawModel.PreRotation.value;
  1375. layerCurveNodes[i] = node;
  1376. }
  1377. layerCurveNodes[i][curveNode.attr] = curveNode;
  1378. }
  1379. }
  1380. });
  1381. layersMap.set(parseInt(nodeID), layerCurveNodes);
  1382. }
  1383. }
  1384. return layersMap;
  1385. }
  1386. // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation
  1387. // hierarchy. Each Stack node will be used to create a THREE.AnimationClip
  1388. function parseAnimStacks(FBXTree, connections, layersMap) {
  1389. var rawStacks = FBXTree.Objects.AnimationStack;
  1390. // connect the stacks (clips) up to the layers
  1391. var rawClips = {};
  1392. for (var nodeID in rawStacks) {
  1393. var children = connections.get(parseInt(nodeID)).children;
  1394. if (children.length > 1) {
  1395. // it seems like stacks will always be associated with a single layer. But just in case there are files
  1396. // where there are multiple layers per stack, we'll display a warning
  1397. console.warn('THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.');
  1398. }
  1399. var layer = layersMap.get(children[0].ID);
  1400. rawClips[nodeID] = {
  1401. name: rawStacks[nodeID].attrName,
  1402. layer: layer
  1403. };
  1404. }
  1405. return rawClips;
  1406. }
  1407. // take raw animation data from parseAnimations and connect it up to the loaded models
  1408. function addAnimations(FBXTree, connections, sceneGraph) {
  1409. sceneGraph.animations = [];
  1410. var rawClips = parseAnimations(FBXTree, connections);
  1411. if (rawClips === undefined) return;
  1412. for (var key in rawClips) {
  1413. var rawClip = rawClips[key];
  1414. var clip = addClip(rawClip);
  1415. sceneGraph.animations.push(clip);
  1416. }
  1417. }
  1418. function addClip(rawClip) {
  1419. var tracks = [];
  1420. rawClip.layer.forEach(function (rawTracks) {
  1421. tracks = tracks.concat(generateTracks(rawTracks));
  1422. });
  1423. return new THREE.AnimationClip(rawClip.name, -1, tracks);
  1424. }
  1425. function generateTracks(rawTracks) {
  1426. var tracks = [];
  1427. if (rawTracks.T !== undefined && Object.keys(rawTracks.T.curves).length > 0) {
  1428. var positionTrack = generateVectorTrack(rawTracks.modelName, rawTracks.T.curves, rawTracks.initialPosition, 'position');
  1429. if (positionTrack !== undefined) tracks.push(positionTrack);
  1430. }
  1431. if (rawTracks.R !== undefined && Object.keys(rawTracks.R.curves).length > 0) {
  1432. var rotationTrack = generateRotationTrack(rawTracks.modelName, rawTracks.R.curves, rawTracks.initialRotation, rawTracks.preRotations);
  1433. if (rotationTrack !== undefined) tracks.push(rotationTrack);
  1434. }
  1435. if (rawTracks.S !== undefined && Object.keys(rawTracks.S.curves).length > 0) {
  1436. var scaleTrack = generateVectorTrack(rawTracks.modelName, rawTracks.S.curves, rawTracks.initialScale, 'scale');
  1437. if (scaleTrack !== undefined) tracks.push(scaleTrack);
  1438. }
  1439. return tracks;
  1440. }
  1441. function generateVectorTrack(modelName, curves, initialValue, type) {
  1442. var times = getTimesForAllAxes(curves);
  1443. var values = getKeyframeTrackValues(times, curves, initialValue);
  1444. return new THREE.VectorKeyframeTrack(modelName + '.' + type, times, values);
  1445. }
  1446. function generateRotationTrack(modelName, curves, initialValue, preRotations) {
  1447. if (curves.x !== undefined) curves.x.values = curves.x.values.map(THREE.Math.degToRad);
  1448. if (curves.y !== undefined) curves.y.values = curves.y.values.map(THREE.Math.degToRad);
  1449. if (curves.z !== undefined) curves.z.values = curves.z.values.map(THREE.Math.degToRad);
  1450. var times = getTimesForAllAxes(curves);
  1451. var values = getKeyframeTrackValues(times, curves, initialValue);
  1452. if (preRotations !== undefined) {
  1453. preRotations = preRotations.map(THREE.Math.degToRad);
  1454. preRotations.push('ZYX');
  1455. preRotations = new THREE.Euler().fromArray(preRotations);
  1456. preRotations = new THREE.Quaternion().setFromEuler(preRotations);
  1457. }
  1458. var quaternion = new THREE.Quaternion();
  1459. var euler = new THREE.Euler();
  1460. var quaternionValues = [];
  1461. for (var i = 0; i < values.length; i += 3) {
  1462. euler.set(values[i], values[i + 1], values[i + 2], 'ZYX');
  1463. quaternion.setFromEuler(euler);
  1464. if (preRotations !== undefined) quaternion.premultiply(preRotations);
  1465. quaternion.toArray(quaternionValues, i / 3 * 4);
  1466. }
  1467. return new THREE.QuaternionKeyframeTrack(modelName + '.quaternion', times, quaternionValues);
  1468. }
  1469. function getKeyframeTrackValues(times, curves, initialValue) {
  1470. var prevValue = initialValue;
  1471. var values = [];
  1472. var xIndex = -1;
  1473. var yIndex = -1;
  1474. var zIndex = -1;
  1475. times.forEach(function (time) {
  1476. if (curves.x) xIndex = curves.x.times.indexOf(time);
  1477. if (curves.y) yIndex = curves.y.times.indexOf(time);
  1478. if (curves.z) zIndex = curves.z.times.indexOf(time);
  1479. // if there is an x value defined for this frame, use that
  1480. if (xIndex !== -1) {
  1481. var xValue = curves.x.values[xIndex];
  1482. values.push(xValue);
  1483. prevValue[0] = xValue;
  1484. } else {
  1485. // otherwise use the x value from the previous frame
  1486. values.push(prevValue[0]);
  1487. }
  1488. if (yIndex !== -1) {
  1489. var yValue = curves.y.values[yIndex];
  1490. values.push(yValue);
  1491. prevValue[1] = yValue;
  1492. } else {
  1493. values.push(prevValue[1]);
  1494. }
  1495. if (zIndex !== -1) {
  1496. var zValue = curves.z.values[zIndex];
  1497. values.push(zValue);
  1498. prevValue[2] = zValue;
  1499. } else {
  1500. values.push(prevValue[2]);
  1501. }
  1502. });
  1503. return values;
  1504. }
  1505. // For all animated objects, times are defined separately for each axis
  1506. // Here we'll combine the times into one sorted array without duplicates
  1507. function getTimesForAllAxes(curves) {
  1508. var times = [];
  1509. // first join together the times for each axis, if defined
  1510. if (curves.x !== undefined) times = times.concat(curves.x.times);
  1511. if (curves.y !== undefined) times = times.concat(curves.y.times);
  1512. if (curves.z !== undefined) times = times.concat(curves.z.times);
  1513. // then sort them and remove duplicates
  1514. times = times.sort(function (a, b) {
  1515. return a - b;
  1516. }).filter(function (elem, index, array) {
  1517. return array.indexOf(elem) == index;
  1518. });
  1519. return times;
  1520. }
  1521. // parse an FBX file in ASCII format
  1522. function TextParser() {}
  1523. Object.assign(TextParser.prototype, {
  1524. getPrevNode: function getPrevNode() {
  1525. return this.nodeStack[this.currentIndent - 2];
  1526. },
  1527. getCurrentNode: function getCurrentNode() {
  1528. return this.nodeStack[this.currentIndent - 1];
  1529. },
  1530. getCurrentProp: function getCurrentProp() {
  1531. return this.currentProp;
  1532. },
  1533. pushStack: function pushStack(node) {
  1534. this.nodeStack.push(node);
  1535. this.currentIndent += 1;
  1536. },
  1537. popStack: function popStack() {
  1538. this.nodeStack.pop();
  1539. this.currentIndent -= 1;
  1540. },
  1541. setCurrentProp: function setCurrentProp(val, name) {
  1542. this.currentProp = val;
  1543. this.currentPropName = name;
  1544. },
  1545. parse: function parse(text) {
  1546. this.currentIndent = 0;
  1547. this.allNodes = new FBXTree();
  1548. this.nodeStack = [];
  1549. this.currentProp = [];
  1550. this.currentPropName = '';
  1551. var self = this;
  1552. var split = text.split('\n');
  1553. split.forEach(function (line, i) {
  1554. var matchComment = line.match(/^[\s\t]*;/);
  1555. var matchEmpty = line.match(/^[\s\t]*$/);
  1556. if (matchComment || matchEmpty) return;
  1557. var matchBeginning = line.match('^\\t{' + self.currentIndent + '}(\\w+):(.*){', '');
  1558. var matchProperty = line.match('^\\t{' + self.currentIndent + '}(\\w+):[\\s\\t\\r\\n](.*)');
  1559. var matchEnd = line.match('^\\t{' + (self.currentIndent - 1) + '}}');
  1560. if (matchBeginning) {
  1561. self.parseNodeBegin(line, matchBeginning);
  1562. } else if (matchProperty) {
  1563. self.parseNodeProperty(line, matchProperty, split[++i]);
  1564. } else if (matchEnd) {
  1565. self.popStack();
  1566. } else if (line.match(/^[^\s\t}]/)) {
  1567. // large arrays are split over multiple lines terminated with a ',' character
  1568. // if this is encountered the line needs to be joined to the previous line
  1569. self.parseNodePropertyContinued(line);
  1570. }
  1571. });
  1572. return this.allNodes;
  1573. },
  1574. parseNodeBegin: function parseNodeBegin(line, property) {
  1575. var nodeName = property[1].trim().replace(/^"/, '').replace(/"$/, '');
  1576. var nodeAttrs = property[2].split(',').map(function (attr) {
  1577. return attr.trim().replace(/^"/, '').replace(/"$/, '');
  1578. });
  1579. var node = { name: nodeName };
  1580. var attrs = this.parseNodeAttr(nodeAttrs);
  1581. var currentNode = this.getCurrentNode();
  1582. // a top node
  1583. if (this.currentIndent === 0) {
  1584. this.allNodes.add(nodeName, node);
  1585. } else {
  1586. // a subnode
  1587. // if the subnode already exists, append it
  1588. if (nodeName in currentNode) {
  1589. // special case Pose needs PoseNodes as an array
  1590. if (nodeName === 'PoseNode') {
  1591. currentNode.PoseNode.push(node);
  1592. } else if (currentNode[nodeName].id !== undefined) {
  1593. currentNode[nodeName] = {};
  1594. currentNode[nodeName][currentNode[nodeName].id] = currentNode[nodeName];
  1595. }
  1596. if (attrs.id !== '') currentNode[nodeName][attrs.id] = node;
  1597. } else if (typeof attrs.id === 'number') {
  1598. currentNode[nodeName] = {};
  1599. currentNode[nodeName][attrs.id] = node;
  1600. } else if (nodeName !== 'Properties70') {
  1601. if (nodeName === 'PoseNode') currentNode[nodeName] = [node];else currentNode[nodeName] = node;
  1602. }
  1603. }
  1604. if (typeof attrs.id === 'number') node.id = attrs.id;
  1605. if (attrs.name !== '') node.attrName = attrs.name;
  1606. if (attrs.type !== '') node.attrType = attrs.type;
  1607. this.pushStack(node);
  1608. },
  1609. parseNodeAttr: function parseNodeAttr(attrs) {
  1610. var id = attrs[0];
  1611. if (attrs[0] !== '') {
  1612. id = parseInt(attrs[0]);
  1613. if (isNaN(id)) {
  1614. id = attrs[0];
  1615. }
  1616. }
  1617. var name = '',
  1618. type = '';
  1619. if (attrs.length > 1) {
  1620. name = attrs[1].replace(/^(\w+)::/, '');
  1621. type = attrs[2];
  1622. }
  1623. return { id: id, name: name, type: type };
  1624. },
  1625. parseNodeProperty: function parseNodeProperty(line, property, contentLine) {
  1626. var propName = property[1].replace(/^"/, '').replace(/"$/, '').trim();
  1627. var propValue = property[2].replace(/^"/, '').replace(/"$/, '').trim();
  1628. // for special case: base64 image data follows "Content: ," line
  1629. // Content: ,
  1630. // "/9j/4RDaRXhpZgAATU0A..."
  1631. if (propName === 'Content' && propValue === ',') {
  1632. propValue = contentLine.replace(/"/g, '').replace(/,$/, '').trim();
  1633. }
  1634. var currentNode = this.getCurrentNode();
  1635. var parentName = currentNode.name;
  1636. if (parentName === 'Properties70') {
  1637. this.parseNodeSpecialProperty(line, propName, propValue);
  1638. return;
  1639. }
  1640. // Connections
  1641. if (propName === 'C') {
  1642. var connProps = propValue.split(',').slice(1);
  1643. var from = parseInt(connProps[0]);
  1644. var to = parseInt(connProps[1]);
  1645. var rest = propValue.split(',').slice(3);
  1646. rest = rest.map(function (elem) {
  1647. return elem.trim().replace(/^"/, '');
  1648. });
  1649. propName = 'connections';
  1650. propValue = [from, to];
  1651. append(propValue, rest);
  1652. if (currentNode[propName] === undefined) {
  1653. currentNode[propName] = [];
  1654. }
  1655. }
  1656. // Node
  1657. if (propName === 'Node') currentNode.id = propValue;
  1658. // connections
  1659. if (propName in currentNode && Array.isArray(currentNode[propName])) {
  1660. currentNode[propName].push(propValue);
  1661. } else {
  1662. if (propName !== 'a') currentNode[propName] = propValue;else currentNode.a = propValue;
  1663. }
  1664. this.setCurrentProp(currentNode, propName);
  1665. // convert string to array, unless it ends in ',' in which case more will be added to it
  1666. if (propName === 'a' && propValue.slice(-1) !== ',') {
  1667. currentNode.a = parseNumberArray(propValue);
  1668. }
  1669. },
  1670. parseNodePropertyContinued: function parseNodePropertyContinued(line) {
  1671. var currentNode = this.getCurrentNode();
  1672. currentNode.a += line;
  1673. // if the line doesn't end in ',' we have reached the end of the property value
  1674. // so convert the string to an array
  1675. if (line.slice(-1) !== ',') {
  1676. currentNode.a = parseNumberArray(currentNode.a);
  1677. }
  1678. },
  1679. // parse "Property70"
  1680. parseNodeSpecialProperty: function parseNodeSpecialProperty(line, propName, propValue) {
  1681. // split this
  1682. // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
  1683. // into array like below
  1684. // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ]
  1685. var props = propValue.split('",').map(function (prop) {
  1686. return prop.trim().replace(/^\"/, '').replace(/\s/, '_');
  1687. });
  1688. var innerPropName = props[0];
  1689. var innerPropType1 = props[1];
  1690. var innerPropType2 = props[2];
  1691. var innerPropFlag = props[3];
  1692. var innerPropValue = props[4];
  1693. // cast values where needed, otherwise leave as strings
  1694. switch (innerPropType1) {
  1695. case 'int':
  1696. case 'enum':
  1697. case 'bool':
  1698. case 'ULongLong':
  1699. case 'double':
  1700. case 'Number':
  1701. case 'FieldOfView':
  1702. innerPropValue = parseFloat(innerPropValue);
  1703. break;
  1704. case 'Color':
  1705. case 'ColorRGB':
  1706. case 'Vector3D':
  1707. case 'Lcl_Translation':
  1708. case 'Lcl_Rotation':
  1709. case 'Lcl_Scaling':
  1710. innerPropValue = parseNumberArray(innerPropValue);
  1711. break;
  1712. }
  1713. // CAUTION: these props must append to parent's parent
  1714. this.getPrevNode()[innerPropName] = {
  1715. 'type': innerPropType1,
  1716. 'type2': innerPropType2,
  1717. 'flag': innerPropFlag,
  1718. 'value': innerPropValue
  1719. };
  1720. this.setCurrentProp(this.getPrevNode(), innerPropName);
  1721. }
  1722. });
  1723. // Parse an FBX file in Binary format
  1724. function BinaryParser() {}
  1725. Object.assign(BinaryParser.prototype, {
  1726. parse: function parse(buffer) {
  1727. var reader = new BinaryReader(buffer);
  1728. reader.skip(23); // skip magic 23 bytes
  1729. var version = reader.getUint32();
  1730. console.log('THREE.FBXLoader: FBX binary version: ' + version);
  1731. var allNodes = new FBXTree();
  1732. while (!this.endOfContent(reader)) {
  1733. var node = this.parseNode(reader, version);
  1734. if (node !== null) allNodes.add(node.name, node);
  1735. }
  1736. return allNodes;
  1737. },
  1738. // Check if reader has reached the end of content.
  1739. endOfContent: function endOfContent(reader) {
  1740. // footer size: 160bytes + 16-byte alignment padding
  1741. // - 16bytes: magic
  1742. // - padding til 16-byte alignment (at least 1byte?)
  1743. // (seems like some exporters embed fixed 15 or 16bytes?)
  1744. // - 4bytes: magic
  1745. // - 4bytes: version
  1746. // - 120bytes: zero
  1747. // - 16bytes: magic
  1748. if (reader.size() % 16 === 0) {
  1749. return (reader.getOffset() + 160 + 16 & ~0xf) >= reader.size();
  1750. } else {
  1751. return reader.getOffset() + 160 + 16 >= reader.size();
  1752. }
  1753. },
  1754. // recursively parse nodes until the end of the file is reached
  1755. parseNode: function parseNode(reader, version) {
  1756. var node = {};
  1757. // The first three data sizes depends on version.
  1758. var endOffset = version >= 7500 ? reader.getUint64() : reader.getUint32();
  1759. var numProperties = version >= 7500 ? reader.getUint64() : reader.getUint32();
  1760. // note: do not remove this even if you get a linter warning as it moves the buffer forward
  1761. var propertyListLen = version >= 7500 ? reader.getUint64() : reader.getUint32();
  1762. var nameLen = reader.getUint8();
  1763. var name = reader.getString(nameLen);
  1764. // Regards this node as NULL-record if endOffset is zero
  1765. if (endOffset === 0) return null;
  1766. var propertyList = [];
  1767. for (var i = 0; i < numProperties; i++) {
  1768. propertyList.push(this.parseProperty(reader));
  1769. }
  1770. // Regards the first three elements in propertyList as id, attrName, and attrType
  1771. var id = propertyList.length > 0 ? propertyList[0] : '';
  1772. var attrName = propertyList.length > 1 ? propertyList[1] : '';
  1773. var attrType = propertyList.length > 2 ? propertyList[2] : '';
  1774. // check if this node represents just a single property
  1775. // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]}
  1776. node.singleProperty = numProperties === 1 && reader.getOffset() === endOffset ? true : false;
  1777. while (endOffset > reader.getOffset()) {
  1778. var subNode = this.parseNode(reader, version);
  1779. if (subNode !== null) this.parseSubNode(name, node, subNode);
  1780. }
  1781. node.propertyList = propertyList; // raw property list used by parent
  1782. if (typeof id === 'number') node.id = id;
  1783. if (attrName !== '') node.attrName = attrName;
  1784. if (attrType !== '') node.attrType = attrType;
  1785. if (name !== '') node.name = name;
  1786. return node;
  1787. },
  1788. parseSubNode: function parseSubNode(name, node, subNode) {
  1789. // special case: child node is single property
  1790. if (subNode.singleProperty === true) {
  1791. var value = subNode.propertyList[0];
  1792. if (Array.isArray(value)) {
  1793. node[subNode.name] = subNode;
  1794. subNode.a = value;
  1795. } else {
  1796. node[subNode.name] = value;
  1797. }
  1798. } else if (name === 'Connections' && subNode.name === 'C') {
  1799. var array = [];
  1800. subNode.propertyList.forEach(function (property, i) {
  1801. // first Connection is FBX type (OO, OP, etc.). We'll discard these
  1802. if (i !== 0) array.push(property);
  1803. });
  1804. if (node.connections === undefined) {
  1805. node.connections = [];
  1806. }
  1807. node.connections.push(array);
  1808. } else if (subNode.name === 'Properties70') {
  1809. var keys = Object.keys(subNode);
  1810. keys.forEach(function (key) {
  1811. node[key] = subNode[key];
  1812. });
  1813. } else if (name === 'Properties70' && subNode.name === 'P') {
  1814. var innerPropName = subNode.propertyList[0];
  1815. var innerPropType1 = subNode.propertyList[1];
  1816. var innerPropType2 = subNode.propertyList[2];
  1817. var innerPropFlag = subNode.propertyList[3];
  1818. var innerPropValue;
  1819. if (innerPropName.indexOf('Lcl ') === 0) innerPropName = innerPropName.replace('Lcl ', 'Lcl_');
  1820. if (innerPropType1.indexOf('Lcl ') === 0) innerPropType1 = innerPropType1.replace('Lcl ', 'Lcl_');
  1821. if (innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf('Lcl_') === 0) {
  1822. innerPropValue = [subNode.propertyList[4], subNode.propertyList[5], subNode.propertyList[6]];
  1823. } else {
  1824. innerPropValue = subNode.propertyList[4];
  1825. }
  1826. // this will be copied to parent, see above
  1827. node[innerPropName] = {
  1828. 'type': innerPropType1,
  1829. 'type2': innerPropType2,
  1830. 'flag': innerPropFlag,
  1831. 'value': innerPropValue
  1832. };
  1833. } else if (node[subNode.name] === undefined) {
  1834. if (typeof subNode.id === 'number') {
  1835. node[subNode.name] = {};
  1836. node[subNode.name][subNode.id] = subNode;
  1837. } else {
  1838. node[subNode.name] = subNode;
  1839. }
  1840. } else {
  1841. if (subNode.name === 'PoseNode') {
  1842. if (!Array.isArray(node[subNode.name])) {
  1843. node[subNode.name] = [node[subNode.name]];
  1844. }
  1845. node[subNode.name].push(subNode);
  1846. } else if (node[subNode.name][subNode.id] === undefined) {
  1847. node[subNode.name][subNode.id] = subNode;
  1848. }
  1849. }
  1850. },
  1851. parseProperty: function parseProperty(reader) {
  1852. var type = reader.getString(1);
  1853. switch (type) {
  1854. case 'C':
  1855. return reader.getBoolean();
  1856. case 'D':
  1857. return reader.getFloat64();
  1858. case 'F':
  1859. return reader.getFloat32();
  1860. case 'I':
  1861. return reader.getInt32();
  1862. case 'L':
  1863. return reader.getInt64();
  1864. case 'R':
  1865. var length = reader.getUint32();
  1866. return reader.getArrayBuffer(length);
  1867. case 'S':
  1868. var length = reader.getUint32();
  1869. return reader.getString(length);
  1870. case 'Y':
  1871. return reader.getInt16();
  1872. case 'b':
  1873. case 'c':
  1874. case 'd':
  1875. case 'f':
  1876. case 'i':
  1877. case 'l':
  1878. var arrayLength = reader.getUint32();
  1879. var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed
  1880. var compressedLength = reader.getUint32();
  1881. if (encoding === 0) {
  1882. switch (type) {
  1883. case 'b':
  1884. case 'c':
  1885. return reader.getBooleanArray(arrayLength);
  1886. case 'd':
  1887. return reader.getFloat64Array(arrayLength);
  1888. case 'f':
  1889. return reader.getFloat32Array(arrayLength);
  1890. case 'i':
  1891. return reader.getInt32Array(arrayLength);
  1892. case 'l':
  1893. return reader.getInt64Array(arrayLength);
  1894. }
  1895. }
  1896. if (window.Zlib === undefined) {
  1897. console.error('THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js');
  1898. }
  1899. var inflate = new Zlib.Inflate(new Uint8Array(reader.getArrayBuffer(compressedLength))); // eslint-disable-line no-undef
  1900. var reader2 = new BinaryReader(inflate.decompress().buffer);
  1901. switch (type) {
  1902. case 'b':
  1903. case 'c':
  1904. return reader2.getBooleanArray(arrayLength);
  1905. case 'd':
  1906. return reader2.getFloat64Array(arrayLength);
  1907. case 'f':
  1908. return reader2.getFloat32Array(arrayLength);
  1909. case 'i':
  1910. return reader2.getInt32Array(arrayLength);
  1911. case 'l':
  1912. return reader2.getInt64Array(arrayLength);
  1913. }
  1914. default:
  1915. throw new Error('THREE.FBXLoader: Unknown property type ' + type);
  1916. }
  1917. }
  1918. });
  1919. function BinaryReader(buffer, littleEndian) {
  1920. this.dv = new DataView(buffer);
  1921. this.offset = 0;
  1922. this.littleEndian = littleEndian !== undefined ? littleEndian : true;
  1923. }
  1924. Object.assign(BinaryReader.prototype, {
  1925. getOffset: function getOffset() {
  1926. return this.offset;
  1927. },
  1928. size: function size() {
  1929. return this.dv.buffer.byteLength;
  1930. },
  1931. skip: function skip(length) {
  1932. this.offset += length;
  1933. },
  1934. // seems like true/false representation depends on exporter.
  1935. // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54)
  1936. // then sees LSB.
  1937. getBoolean: function getBoolean() {
  1938. return (this.getUint8() & 1) === 1;
  1939. },
  1940. getBooleanArray: function getBooleanArray(size) {
  1941. var a = [];
  1942. for (var i = 0; i < size; i++) {
  1943. a.push(this.getBoolean());
  1944. }
  1945. return a;
  1946. },
  1947. getUint8: function getUint8() {
  1948. var value = this.dv.getUint8(this.offset);
  1949. this.offset += 1;
  1950. return value;
  1951. },
  1952. getInt16: function getInt16() {
  1953. var value = this.dv.getInt16(this.offset, this.littleEndian);
  1954. this.offset += 2;
  1955. return value;
  1956. },
  1957. getInt32: function getInt32() {
  1958. var value = this.dv.getInt32(this.offset, this.littleEndian);
  1959. this.offset += 4;
  1960. return value;
  1961. },
  1962. getInt32Array: function getInt32Array(size) {
  1963. var a = [];
  1964. for (var i = 0; i < size; i++) {
  1965. a.push(this.getInt32());
  1966. }
  1967. return a;
  1968. },
  1969. getUint32: function getUint32() {
  1970. var value = this.dv.getUint32(this.offset, this.littleEndian);
  1971. this.offset += 4;
  1972. return value;
  1973. },
  1974. // JavaScript doesn't support 64-bit integer so calculate this here
  1975. // 1 << 32 will return 1 so using multiply operation instead here.
  1976. // There's a possibility that this method returns wrong value if the value
  1977. // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER.
  1978. // TODO: safely handle 64-bit integer
  1979. getInt64: function getInt64() {
  1980. var low, high;
  1981. if (this.littleEndian) {
  1982. low = this.getUint32();
  1983. high = this.getUint32();
  1984. } else {
  1985. high = this.getUint32();
  1986. low = this.getUint32();
  1987. }
  1988. // calculate negative value
  1989. if (high & 0x80000000) {
  1990. high = ~high & 0xFFFFFFFF;
  1991. low = ~low & 0xFFFFFFFF;
  1992. if (low === 0xFFFFFFFF) high = high + 1 & 0xFFFFFFFF;
  1993. low = low + 1 & 0xFFFFFFFF;
  1994. return -(high * 0x100000000 + low);
  1995. }
  1996. return high * 0x100000000 + low;
  1997. },
  1998. getInt64Array: function getInt64Array(size) {
  1999. var a = [];
  2000. for (var i = 0; i < size; i++) {
  2001. a.push(this.getInt64());
  2002. }
  2003. return a;
  2004. },
  2005. // Note: see getInt64() comment
  2006. getUint64: function getUint64() {
  2007. var low, high;
  2008. if (this.littleEndian) {
  2009. low = this.getUint32();
  2010. high = this.getUint32();
  2011. } else {
  2012. high = this.getUint32();
  2013. low = this.getUint32();
  2014. }
  2015. return high * 0x100000000 + low;
  2016. },
  2017. getFloat32: function getFloat32() {
  2018. var value = this.dv.getFloat32(this.offset, this.littleEndian);
  2019. this.offset += 4;
  2020. return value;
  2021. },
  2022. getFloat32Array: function getFloat32Array(size) {
  2023. var a = [];
  2024. for (var i = 0; i < size; i++) {
  2025. a.push(this.getFloat32());
  2026. }
  2027. return a;
  2028. },
  2029. getFloat64: function getFloat64() {
  2030. var value = this.dv.getFloat64(this.offset, this.littleEndian);
  2031. this.offset += 8;
  2032. return value;
  2033. },
  2034. getFloat64Array: function getFloat64Array(size) {
  2035. var a = [];
  2036. for (var i = 0; i < size; i++) {
  2037. a.push(this.getFloat64());
  2038. }
  2039. return a;
  2040. },
  2041. getArrayBuffer: function getArrayBuffer(size) {
  2042. var value = this.dv.buffer.slice(this.offset, this.offset + size);
  2043. this.offset += size;
  2044. return value;
  2045. },
  2046. getString: function getString(size) {
  2047. var a = new Uint8Array(size);
  2048. for (var i = 0; i < size; i++) {
  2049. a[i] = this.getUint8();
  2050. }
  2051. var nullByte = a.indexOf(0);
  2052. if (nullByte >= 0) a = a.slice(0, nullByte);
  2053. return THREE.LoaderUtils.decodeText(a);
  2054. }
  2055. });
  2056. // FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format)
  2057. // and BinaryParser( FBX Binary format)
  2058. function FBXTree() {}
  2059. Object.assign(FBXTree.prototype, {
  2060. add: function add(key, val) {
  2061. this[key] = val;
  2062. }
  2063. });
  2064. function isFbxFormatBinary(buffer) {
  2065. var CORRECT = 'Kaydara FBX Binary \0';
  2066. return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString(buffer, 0, CORRECT.length);
  2067. }
  2068. function isFbxFormatASCII(text) {
  2069. var CORRECT = ['K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\'];
  2070. var cursor = 0;
  2071. function read(offset) {
  2072. var result = text[offset - 1];
  2073. text = text.slice(cursor + offset);
  2074. cursor++;
  2075. return result;
  2076. }
  2077. for (var i = 0; i < CORRECT.length; ++i) {
  2078. var num = read(1);
  2079. if (num === CORRECT[i]) {
  2080. return false;
  2081. }
  2082. }
  2083. return true;
  2084. }
  2085. function getFbxVersion(text) {
  2086. var versionRegExp = /FBXVersion: (\d+)/;
  2087. var match = text.match(versionRegExp);
  2088. if (match) {
  2089. var version = parseInt(match[1]);
  2090. return version;
  2091. }
  2092. throw new Error('THREE.FBXLoader: Cannot find the version number for the file given.');
  2093. }
  2094. // Converts FBX ticks into real time seconds.
  2095. function convertFBXTimeToSeconds(time) {
  2096. return time / 46186158000;
  2097. }
  2098. // Parses comma separated list of numbers and returns them an array.
  2099. // Used internally by the TextParser
  2100. function parseNumberArray(value) {
  2101. var array = value.split(',').map(function (val) {
  2102. return parseFloat(val);
  2103. });
  2104. return array;
  2105. }
  2106. function convertArrayBufferToString(buffer, from, to) {
  2107. if (from === undefined) from = 0;
  2108. if (to === undefined) to = buffer.byteLength;
  2109. return THREE.LoaderUtils.decodeText(new Uint8Array(buffer, from, to));
  2110. }
  2111. function append(a, b) {
  2112. for (var i = 0, j = a.length, l = b.length; i < l; i++, j++) {
  2113. a[j] = b[i];
  2114. }
  2115. }
  2116. function slice(a, b, from, to) {
  2117. for (var i = from, j = 0; i < to; i++, j++) {
  2118. a[j] = b[i];
  2119. }
  2120. return a;
  2121. }
  2122. })();
  2123. },{}],3:[function(require,module,exports){
  2124. 'use strict';
  2125. /**
  2126. * @author Wei Meng / http://about.me/menway
  2127. *
  2128. * Description: A THREE loader for PLY ASCII files (known as the Polygon File Format or the Stanford Triangle Format).
  2129. *
  2130. *
  2131. * Limitations: ASCII decoding assumes file is UTF-8.
  2132. *
  2133. * Usage:
  2134. * var loader = new THREE.PLYLoader();
  2135. * loader.load('./models/ply/ascii/dolphins.ply', function (geometry) {
  2136. *
  2137. * scene.add( new THREE.Mesh( geometry ) );
  2138. *
  2139. * } );
  2140. *
  2141. * If the PLY file uses non standard property names, they can be mapped while
  2142. * loading. For example, the following maps the properties
  2143. * “diffuse_(red|green|blue)” in the file to standard color names.
  2144. *
  2145. * loader.setPropertyNameMapping( {
  2146. * diffuse_red: 'red',
  2147. * diffuse_green: 'green',
  2148. * diffuse_blue: 'blue'
  2149. * } );
  2150. *
  2151. */
  2152. module.exports = THREE.PLYLoader = function (manager) {
  2153. this.manager = manager !== undefined ? manager : THREE.DefaultLoadingManager;
  2154. this.propertyNameMapping = {};
  2155. };
  2156. THREE.PLYLoader.prototype = {
  2157. constructor: THREE.PLYLoader,
  2158. load: function load(url, onLoad, onProgress, onError) {
  2159. var scope = this;
  2160. var loader = new THREE.XHRLoader(this.manager);
  2161. loader.setResponseType('arraybuffer');
  2162. loader.load(url, function (text) {
  2163. onLoad(scope.parse(text));
  2164. }, onProgress, onError);
  2165. },
  2166. setPropertyNameMapping: function setPropertyNameMapping(mapping) {
  2167. this.propertyNameMapping = mapping;
  2168. },
  2169. bin2str: function bin2str(buf) {
  2170. var array_buffer = new Uint8Array(buf);
  2171. var str = '';
  2172. for (var i = 0; i < buf.byteLength; i++) {
  2173. str += String.fromCharCode(array_buffer[i]); // implicitly assumes little-endian
  2174. }
  2175. return str;
  2176. },
  2177. isASCII: function isASCII(data) {
  2178. var header = this.parseHeader(this.bin2str(data));
  2179. return header.format === "ascii";
  2180. },
  2181. parse: function parse(data) {
  2182. if (data instanceof ArrayBuffer) {
  2183. return this.isASCII(data) ? this.parseASCII(this.bin2str(data)) : this.parseBinary(data);
  2184. } else {
  2185. return this.parseASCII(data);
  2186. }
  2187. },
  2188. parseHeader: function parseHeader(data) {
  2189. var patternHeader = /ply([\s\S]*)end_header\s/;
  2190. var headerText = "";
  2191. var headerLength = 0;
  2192. var result = patternHeader.exec(data);
  2193. if (result !== null) {
  2194. headerText = result[1];
  2195. headerLength = result[0].length;
  2196. }
  2197. var header = {
  2198. comments: [],
  2199. elements: [],
  2200. headerLength: headerLength
  2201. };
  2202. var lines = headerText.split('\n');
  2203. var currentElement = undefined;
  2204. var lineType, lineValues;
  2205. function make_ply_element_property(propertValues, propertyNameMapping) {
  2206. var property = {
  2207. type: propertValues[0]
  2208. };
  2209. if (property.type === 'list') {
  2210. property.name = propertValues[3];
  2211. property.countType = propertValues[1];
  2212. property.itemType = propertValues[2];
  2213. } else {
  2214. property.name = propertValues[1];
  2215. }
  2216. if (property.name in propertyNameMapping) {
  2217. property.name = propertyNameMapping[property.name];
  2218. }
  2219. return property;
  2220. }
  2221. for (var i = 0; i < lines.length; i++) {
  2222. var line = lines[i];
  2223. line = line.trim();
  2224. if (line === "") {
  2225. continue;
  2226. }
  2227. lineValues = line.split(/\s+/);
  2228. lineType = lineValues.shift();
  2229. line = lineValues.join(" ");
  2230. switch (lineType) {
  2231. case "format":
  2232. header.format = lineValues[0];
  2233. header.version = lineValues[1];
  2234. break;
  2235. case "comment":
  2236. header.comments.push(line);
  2237. break;
  2238. case "element":
  2239. if (!(currentElement === undefined)) {
  2240. header.elements.push(currentElement);
  2241. }
  2242. currentElement = Object();
  2243. currentElement.name = lineValues[0];
  2244. currentElement.count = parseInt(lineValues[1]);
  2245. currentElement.properties = [];
  2246. break;
  2247. case "property":
  2248. currentElement.properties.push(make_ply_element_property(lineValues, this.propertyNameMapping));
  2249. break;
  2250. default:
  2251. console.log("unhandled", lineType, lineValues);
  2252. }
  2253. }
  2254. if (!(currentElement === undefined)) {
  2255. header.elements.push(currentElement);
  2256. }
  2257. return header;
  2258. },
  2259. parseASCIINumber: function parseASCIINumber(n, type) {
  2260. switch (type) {
  2261. case 'char':case 'uchar':case 'short':case 'ushort':case 'int':case 'uint':
  2262. case 'int8':case 'uint8':case 'int16':case 'uint16':case 'int32':case 'uint32':
  2263. return parseInt(n);
  2264. case 'float':case 'double':case 'float32':case 'float64':
  2265. return parseFloat(n);
  2266. }
  2267. },
  2268. parseASCIIElement: function parseASCIIElement(properties, line) {
  2269. var values = line.split(/\s+/);
  2270. var element = Object();
  2271. for (var i = 0; i < properties.length; i++) {
  2272. if (properties[i].type === "list") {
  2273. var list = [];
  2274. var n = this.parseASCIINumber(values.shift(), properties[i].countType);
  2275. for (var j = 0; j < n; j++) {
  2276. list.push(this.parseASCIINumber(values.shift(), properties[i].itemType));
  2277. }
  2278. element[properties[i].name] = list;
  2279. } else {
  2280. element[properties[i].name] = this.parseASCIINumber(values.shift(), properties[i].type);
  2281. }
  2282. }
  2283. return element;
  2284. },
  2285. parseASCII: function parseASCII(data) {
  2286. // PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format)
  2287. var geometry = new THREE.Geometry();
  2288. var result;
  2289. var header = this.parseHeader(data);
  2290. var patternBody = /end_header\s([\s\S]*)$/;
  2291. var body = "";
  2292. if ((result = patternBody.exec(data)) !== null) {
  2293. body = result[1];
  2294. }
  2295. var lines = body.split('\n');
  2296. var currentElement = 0;
  2297. var currentElementCount = 0;
  2298. geometry.useColor = false;
  2299. for (var i = 0; i < lines.length; i++) {
  2300. var line = lines[i];
  2301. line = line.trim();
  2302. if (line === "") {
  2303. continue;
  2304. }
  2305. if (currentElementCount >= header.elements[currentElement].count) {
  2306. currentElement++;
  2307. currentElementCount = 0;
  2308. }
  2309. var element = this.parseASCIIElement(header.elements[currentElement].properties, line);
  2310. this.handleElement(geometry, header.elements[currentElement].name, element);
  2311. currentElementCount++;
  2312. }
  2313. return this.postProcess(geometry);
  2314. },
  2315. postProcess: function postProcess(geometry) {
  2316. if (geometry.useColor) {
  2317. for (var i = 0; i < geometry.faces.length; i++) {
  2318. geometry.faces[i].vertexColors = [geometry.colors[geometry.faces[i].a], geometry.colors[geometry.faces[i].b], geometry.colors[geometry.faces[i].c]];
  2319. }
  2320. geometry.elementsNeedUpdate = true;
  2321. }
  2322. geometry.computeBoundingSphere();
  2323. return geometry;
  2324. },
  2325. handleElement: function handleElement(geometry, elementName, element) {
  2326. if (elementName === "vertex") {
  2327. geometry.vertices.push(new THREE.Vector3(element.x, element.y, element.z));
  2328. if ('red' in element && 'green' in element && 'blue' in element) {
  2329. geometry.useColor = true;
  2330. var color = new THREE.Color();
  2331. color.setRGB(element.red / 255.0, element.green / 255.0, element.blue / 255.0);
  2332. geometry.colors.push(color);
  2333. }
  2334. } else if (elementName === "face") {
  2335. // BEGIN: Edits by donmccurdy.
  2336. var vertex_indices = element.vertex_indices || element.vertex_index;
  2337. // END: Edits by donmccurdy.
  2338. if (vertex_indices.length === 3) {
  2339. geometry.faces.push(new THREE.Face3(vertex_indices[0], vertex_indices[1], vertex_indices[2]));
  2340. } else if (vertex_indices.length === 4) {
  2341. geometry.faces.push(new THREE.Face3(vertex_indices[0], vertex_indices[1], vertex_indices[3]), new THREE.Face3(vertex_indices[1], vertex_indices[2], vertex_indices[3]));
  2342. }
  2343. }
  2344. },
  2345. binaryRead: function binaryRead(dataview, at, type, little_endian) {
  2346. switch (type) {
  2347. // corespondences for non-specific length types here match rply:
  2348. case 'int8':case 'char':
  2349. return [dataview.getInt8(at), 1];
  2350. case 'uint8':case 'uchar':
  2351. return [dataview.getUint8(at), 1];
  2352. case 'int16':case 'short':
  2353. return [dataview.getInt16(at, little_endian), 2];
  2354. case 'uint16':case 'ushort':
  2355. return [dataview.getUint16(at, little_endian), 2];
  2356. case 'int32':case 'int':
  2357. return [dataview.getInt32(at, little_endian), 4];
  2358. case 'uint32':case 'uint':
  2359. return [dataview.getUint32(at, little_endian), 4];
  2360. case 'float32':case 'float':
  2361. return [dataview.getFloat32(at, little_endian), 4];
  2362. case 'float64':case 'double':
  2363. return [dataview.getFloat64(at, little_endian), 8];
  2364. }
  2365. },
  2366. binaryReadElement: function binaryReadElement(dataview, at, properties, little_endian) {
  2367. var element = Object();
  2368. var result,
  2369. read = 0;
  2370. for (var i = 0; i < properties.length; i++) {
  2371. if (properties[i].type === "list") {
  2372. var list = [];
  2373. result = this.binaryRead(dataview, at + read, properties[i].countType, little_endian);
  2374. var n = result[0];
  2375. read += result[1];
  2376. for (var j = 0; j < n; j++) {
  2377. result = this.binaryRead(dataview, at + read, properties[i].itemType, little_endian);
  2378. list.push(result[0]);
  2379. read += result[1];
  2380. }
  2381. element[properties[i].name] = list;
  2382. } else {
  2383. result = this.binaryRead(dataview, at + read, properties[i].type, little_endian);
  2384. element[properties[i].name] = result[0];
  2385. read += result[1];
  2386. }
  2387. }
  2388. return [element, read];
  2389. },
  2390. parseBinary: function parseBinary(data) {
  2391. var geometry = new THREE.Geometry();
  2392. var header = this.parseHeader(this.bin2str(data));
  2393. var little_endian = header.format === "binary_little_endian";
  2394. var body = new DataView(data, header.headerLength);
  2395. var result,
  2396. loc = 0;
  2397. for (var currentElement = 0; currentElement < header.elements.length; currentElement++) {
  2398. for (var currentElementCount = 0; currentElementCount < header.elements[currentElement].count; currentElementCount++) {
  2399. result = this.binaryReadElement(body, loc, header.elements[currentElement].properties, little_endian);
  2400. loc += result[1];
  2401. var element = result[0];
  2402. this.handleElement(geometry, header.elements[currentElement].name, element);
  2403. }
  2404. }
  2405. return this.postProcess(geometry);
  2406. }
  2407. };
  2408. },{}],4:[function(require,module,exports){
  2409. 'use strict';
  2410. /**
  2411. * Source: https://github.com/Adobe-Marketing-Cloud/fetch-script
  2412. */
  2413. function getScriptId() {
  2414. return 'script_' + Date.now() + '_' + Math.ceil(Math.random() * 100000);
  2415. }
  2416. function createScript(url, id) {
  2417. var script = document.createElement('script');
  2418. script.type = 'text/javascript';
  2419. script.async = true;
  2420. script.id = id;
  2421. script.src = url;
  2422. return script;
  2423. }
  2424. function removeScript(id) {
  2425. var script = document.getElementById(id);
  2426. var parent = script.parentNode;
  2427. try {
  2428. parent && parent.removeChild(script);
  2429. } catch (e) {
  2430. // ignore
  2431. }
  2432. }
  2433. function appendScript(script) {
  2434. var firstScript = document.getElementsByTagName('script')[0];
  2435. firstScript.parentNode.insertBefore(script, firstScript);
  2436. }
  2437. function fetchScriptInternal(url, options, Promise) {
  2438. return new Promise(function (resolve, reject) {
  2439. var timeout = options.timeout || 5000;
  2440. var scriptId = getScriptId();
  2441. var script = createScript(url, scriptId);
  2442. var timeoutId = setTimeout(function () {
  2443. reject(new Error('Script request to ' + url + ' timed out'));
  2444. removeScript(scriptId);
  2445. }, timeout);
  2446. var disableTimeout = function disableTimeout(timeoutId) {
  2447. clearTimeout(timeoutId);
  2448. };
  2449. script.addEventListener('load', function (e) {
  2450. resolve({ ok: true });
  2451. disableTimeout(timeoutId);
  2452. removeScript(scriptId);
  2453. });
  2454. script.addEventListener('error', function (e) {
  2455. reject(new Error('Script request to ' + url + ' failed ' + e));
  2456. disableTimeout(timeoutId);
  2457. removeScript(scriptId);
  2458. });
  2459. appendScript(script);
  2460. });
  2461. }
  2462. function fetchScript(settings) {
  2463. settings = settings || {};
  2464. return function (url, options) {
  2465. options = options || {};
  2466. return fetchScriptInternal(url, options, settings.Promise || Promise);
  2467. };
  2468. }
  2469. module.exports = fetchScript;
  2470. },{}],5:[function(require,module,exports){
  2471. 'use strict';
  2472. var LoopMode = {
  2473. once: THREE.LoopOnce,
  2474. repeat: THREE.LoopRepeat,
  2475. pingpong: THREE.LoopPingPong
  2476. };
  2477. /**
  2478. * animation-mixer
  2479. *
  2480. * Player for animation clips. Intended to be compatible with any model format that supports
  2481. * skeletal or morph animations through THREE.AnimationMixer.
  2482. * See: https://threejs.org/docs/?q=animation#Reference/Animation/AnimationMixer
  2483. */
  2484. module.exports = AFRAME.registerComponent('animation-mixer', {
  2485. schema: {
  2486. clip: { default: '*' },
  2487. duration: { default: 0 },
  2488. crossFadeDuration: { default: 0 },
  2489. loop: { default: 'repeat', oneOf: Object.keys(LoopMode) },
  2490. repetitions: { default: Infinity, min: 0 }
  2491. },
  2492. init: function init() {
  2493. var _this = this;
  2494. /** @type {THREE.Mesh} */
  2495. this.model = null;
  2496. /** @type {THREE.AnimationMixer} */
  2497. this.mixer = null;
  2498. /** @type {Array<THREE.AnimationAction>} */
  2499. this.activeActions = [];
  2500. var model = this.el.getObject3D('mesh');
  2501. if (model) {
  2502. this.load(model);
  2503. } else {
  2504. this.el.addEventListener('model-loaded', function (e) {
  2505. _this.load(e.detail.model);
  2506. });
  2507. }
  2508. },
  2509. load: function load(model) {
  2510. var el = this.el;
  2511. this.model = model;
  2512. this.mixer = new THREE.AnimationMixer(model);
  2513. this.mixer.addEventListener('loop', function (e) {
  2514. el.emit('animation-loop', { action: e.action, loopDelta: e.loopDelta });
  2515. });
  2516. this.mixer.addEventListener('finished', function (e) {
  2517. el.emit('animation-finished', { action: e.action, direction: e.direction });
  2518. });
  2519. if (this.data.clip) this.update({});
  2520. },
  2521. remove: function remove() {
  2522. if (this.mixer) this.mixer.stopAllAction();
  2523. },
  2524. update: function update(previousData) {
  2525. if (!previousData) return;
  2526. this.stopAction();
  2527. if (this.data.clip) {
  2528. this.playAction();
  2529. }
  2530. },
  2531. stopAction: function stopAction() {
  2532. var data = this.data;
  2533. for (var i = 0; i < this.activeActions.length; i++) {
  2534. data.crossFadeDuration ? this.activeActions[i].fadeOut(data.crossFadeDuration) : this.activeActions[i].stop();
  2535. }
  2536. this.activeActions.length = 0;
  2537. },
  2538. playAction: function playAction() {
  2539. if (!this.mixer) return;
  2540. var model = this.model,
  2541. data = this.data,
  2542. clips = model.animations || (model.geometry || {}).animations || [];
  2543. if (!clips.length) return;
  2544. var re = wildcardToRegExp(data.clip);
  2545. for (var clip, i = 0; clip = clips[i]; i++) {
  2546. if (clip.name.match(re)) {
  2547. var action = this.mixer.clipAction(clip, model);
  2548. action.enabled = true;
  2549. if (data.duration) action.setDuration(data.duration);
  2550. action.setLoop(LoopMode[data.loop], data.repetitions).fadeIn(data.crossFadeDuration).play();
  2551. this.activeActions.push(action);
  2552. }
  2553. }
  2554. },
  2555. tick: function tick(t, dt) {
  2556. if (this.mixer && !isNaN(dt)) this.mixer.update(dt / 1000);
  2557. }
  2558. });
  2559. /**
  2560. * Creates a RegExp from the given string, converting asterisks to .* expressions,
  2561. * and escaping all other characters.
  2562. */
  2563. function wildcardToRegExp(s) {
  2564. return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$');
  2565. }
  2566. /**
  2567. * RegExp-escapes all characters in the given string.
  2568. */
  2569. function regExpEscape(s) {
  2570. return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
  2571. }
  2572. },{}],6:[function(require,module,exports){
  2573. 'use strict';
  2574. THREE.FBXLoader = require('../../lib/FBXLoader');
  2575. /**
  2576. * fbx-model
  2577. *
  2578. * Loader for FBX format. Supports ASCII, but *not* binary, models.
  2579. */
  2580. module.exports = AFRAME.registerComponent('fbx-model', {
  2581. schema: {
  2582. src: { type: 'asset' },
  2583. crossorigin: { default: '' }
  2584. },
  2585. init: function init() {
  2586. this.model = null;
  2587. },
  2588. update: function update() {
  2589. var data = this.data;
  2590. if (!data.src) return;
  2591. this.remove();
  2592. var loader = new THREE.FBXLoader();
  2593. if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
  2594. loader.load(data.src, this.load.bind(this));
  2595. },
  2596. load: function load(model) {
  2597. this.model = model;
  2598. this.el.setObject3D('mesh', model);
  2599. this.el.emit('model-loaded', { format: 'fbx', model: model });
  2600. },
  2601. remove: function remove() {
  2602. if (this.model) this.el.removeObject3D('mesh');
  2603. }
  2604. });
  2605. },{"../../lib/FBXLoader":2}],7:[function(require,module,exports){
  2606. 'use strict';
  2607. var fetchScript = require('../../lib/fetch-script')();
  2608. var LOADER_SRC = 'https://rawgit.com/mrdoob/three.js/r86/examples/js/loaders/GLTFLoader.js';
  2609. var loadLoader = function () {
  2610. var promise = void 0;
  2611. return function () {
  2612. promise = promise || fetchScript(LOADER_SRC);
  2613. return promise;
  2614. };
  2615. }();
  2616. /**
  2617. * Legacy loader for glTF 1.0 models.
  2618. * Asynchronously loads THREE.GLTFLoader from rawgit.
  2619. */
  2620. module.exports = AFRAME.registerComponent('gltf-model-legacy', {
  2621. schema: { type: 'model' },
  2622. init: function init() {
  2623. var _this = this;
  2624. this.model = null;
  2625. this.loader = null;
  2626. this.loaderPromise = loadLoader().then(function () {
  2627. _this.loader = new THREE.GLTFLoader();
  2628. _this.loader.setCrossOrigin('Anonymous');
  2629. });
  2630. },
  2631. update: function update() {
  2632. var _this2 = this;
  2633. var self = this;
  2634. var el = this.el;
  2635. var src = this.data;
  2636. if (!src) {
  2637. return;
  2638. }
  2639. this.remove();
  2640. this.loaderPromise.then(function () {
  2641. _this2.loader.load(src, function gltfLoaded(gltfModel) {
  2642. self.model = gltfModel.scene;
  2643. self.model.animations = gltfModel.animations;
  2644. el.setObject3D('mesh', self.model);
  2645. el.emit('model-loaded', { format: 'gltf', model: self.model });
  2646. });
  2647. });
  2648. },
  2649. remove: function remove() {
  2650. if (!this.model) {
  2651. return;
  2652. }
  2653. this.el.removeObject3D('mesh');
  2654. }
  2655. });
  2656. },{"../../lib/fetch-script":4}],8:[function(require,module,exports){
  2657. 'use strict';
  2658. require('./animation-mixer');
  2659. require('./fbx-model');
  2660. require('./gltf-model-legacy');
  2661. require('./json-model');
  2662. require('./object-model');
  2663. require('./ply-model');
  2664. },{"./animation-mixer":5,"./fbx-model":6,"./gltf-model-legacy":7,"./json-model":9,"./object-model":10,"./ply-model":11}],9:[function(require,module,exports){
  2665. 'use strict';
  2666. /**
  2667. * json-model
  2668. *
  2669. * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
  2670. * both having the .json extension. This loader supports only THREE.JsonLoader, which typically
  2671. * includes only a single mesh.
  2672. *
  2673. * Check the console for errors, if in doubt. You may need to use `object-model` or
  2674. * `blend-character-model` for some .js and .json files.
  2675. *
  2676. * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
  2677. */
  2678. module.exports = AFRAME.registerComponent('json-model', {
  2679. schema: {
  2680. src: { type: 'asset' },
  2681. crossorigin: { default: '' }
  2682. },
  2683. init: function init() {
  2684. this.model = null;
  2685. },
  2686. update: function update() {
  2687. var _this = this;
  2688. var loader = void 0;
  2689. var data = this.data;
  2690. if (!data.src) return;
  2691. this.remove();
  2692. loader = new THREE.JSONLoader();
  2693. if (data.crossorigin) loader.crossOrigin = data.crossorigin;
  2694. loader.load(data.src, function (geometry, materials) {
  2695. // Attempt to automatically detect common material options.
  2696. materials.forEach(function (mat) {
  2697. mat.vertexColors = (geometry.faces[0] || {}).color ? THREE.FaceColors : THREE.NoColors;
  2698. mat.skinning = !!(geometry.bones || []).length;
  2699. mat.morphTargets = !!(geometry.morphTargets || []).length;
  2700. mat.morphNormals = !!(geometry.morphNormals || []).length;
  2701. });
  2702. var model = (geometry.bones || []).length ? new THREE.SkinnedMesh(geometry, new THREE.MultiMaterial(materials)) : new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
  2703. _this.load(model);
  2704. });
  2705. },
  2706. load: function load(model) {
  2707. this.model = model;
  2708. this.el.setObject3D('mesh', model);
  2709. this.el.emit('model-loaded', { format: 'json', model: model });
  2710. },
  2711. remove: function remove() {
  2712. if (this.model) this.el.removeObject3D('mesh');
  2713. }
  2714. });
  2715. },{}],10:[function(require,module,exports){
  2716. 'use strict';
  2717. /**
  2718. * object-model
  2719. *
  2720. * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
  2721. * both having the .json extension. This loader supports only THREE.ObjectLoader, which typically
  2722. * includes multiple meshes or an entire scene.
  2723. *
  2724. * Check the console for errors, if in doubt. You may need to use `json-model` or
  2725. * `blend-character-model` for some .js and .json files.
  2726. *
  2727. * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
  2728. */
  2729. module.exports = AFRAME.registerComponent('object-model', {
  2730. schema: {
  2731. src: { type: 'asset' },
  2732. crossorigin: { default: '' }
  2733. },
  2734. init: function init() {
  2735. this.model = null;
  2736. },
  2737. update: function update() {
  2738. var _this = this;
  2739. var loader = void 0;
  2740. var data = this.data;
  2741. if (!data.src) return;
  2742. this.remove();
  2743. loader = new THREE.ObjectLoader();
  2744. if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
  2745. loader.load(data.src, function (object) {
  2746. // Enable skinning, if applicable.
  2747. object.traverse(function (o) {
  2748. if (o instanceof THREE.SkinnedMesh && o.material) {
  2749. o.material.skinning = !!(o.geometry && o.geometry.bones || []).length;
  2750. }
  2751. });
  2752. _this.load(object);
  2753. });
  2754. },
  2755. load: function load(model) {
  2756. this.model = model;
  2757. this.el.setObject3D('mesh', model);
  2758. this.el.emit('model-loaded', { format: 'json', model: model });
  2759. },
  2760. remove: function remove() {
  2761. if (this.model) this.el.removeObject3D('mesh');
  2762. }
  2763. });
  2764. },{}],11:[function(require,module,exports){
  2765. 'use strict';
  2766. /**
  2767. * ply-model
  2768. *
  2769. * Wraps THREE.PLYLoader.
  2770. */
  2771. THREE.PLYLoader = require('../../lib/PLYLoader');
  2772. /**
  2773. * Loads, caches, resolves geometries.
  2774. *
  2775. * @member cache - Promises that resolve geometries keyed by `src`.
  2776. */
  2777. module.exports.System = AFRAME.registerSystem('ply-model', {
  2778. init: function init() {
  2779. this.cache = {};
  2780. },
  2781. /**
  2782. * @returns {Promise}
  2783. */
  2784. getOrLoadGeometry: function getOrLoadGeometry(src, skipCache) {
  2785. var cache = this.cache;
  2786. var cacheItem = cache[src];
  2787. if (!skipCache && cacheItem) {
  2788. return cacheItem;
  2789. }
  2790. cache[src] = new Promise(function (resolve) {
  2791. var loader = new THREE.PLYLoader();
  2792. loader.load(src, function (geometry) {
  2793. resolve(geometry);
  2794. });
  2795. });
  2796. return cache[src];
  2797. }
  2798. });
  2799. module.exports.Component = AFRAME.registerComponent('ply-model', {
  2800. schema: {
  2801. skipCache: { type: 'boolean', default: false },
  2802. src: { type: 'asset' }
  2803. },
  2804. init: function init() {
  2805. this.model = null;
  2806. },
  2807. update: function update() {
  2808. var data = this.data;
  2809. var el = this.el;
  2810. if (!data.src) {
  2811. console.warn('[%s] `src` property is required.', this.name);
  2812. return;
  2813. }
  2814. // Get geometry from system, create and set mesh.
  2815. this.system.getOrLoadGeometry(data.src, data.skipCache).then(function (geometry) {
  2816. var model = createModel(geometry);
  2817. el.setObject3D('mesh', model);
  2818. el.emit('model-loaded', { format: 'ply', model: model });
  2819. });
  2820. },
  2821. remove: function remove() {
  2822. if (this.model) {
  2823. this.el.removeObject3D('mesh');
  2824. }
  2825. }
  2826. });
  2827. function createModel(geometry) {
  2828. return new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
  2829. color: 0xFFFFFF,
  2830. shading: THREE.FlatShading,
  2831. vertexColors: THREE.VertexColors,
  2832. shininess: 0
  2833. }));
  2834. }
  2835. },{"../../lib/PLYLoader":3}]},{},[1]);