FBXLoader2.js 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956
  1. /**
  2. * @author Kyle-Larson https://github.com/Kyle-Larson
  3. *
  4. * Loader loads FBX file and generates Group representing FBX scene.
  5. * Requires FBX file to be >= 7.0 and in ASCII format.
  6. */
  7. ( function () {
  8. THREE.FBXLoader = function ( manager ) {
  9. THREE.Loader.call( this );
  10. this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
  11. this.fileLoader = new THREE.FileLoader( this.manager );
  12. this.textureLoader = new THREE.TextureLoader( this.manager );
  13. };
  14. Object.assign( THREE.FBXLoader.prototype, THREE.Loader.prototype );
  15. THREE.FBXLoader.prototype.constructor = THREE.FBXLoader;
  16. Object.assign( THREE.FBXLoader.prototype, {
  17. load: function ( url, onLoad, onProgress, onError ) {
  18. var self = this;
  19. var resourceDirectory = url.split( /[\\\/]/ );
  20. resourceDirectory.pop();
  21. resourceDirectory = resourceDirectory.join( '/' );
  22. this.fileLoader.load( url, function ( text ) {
  23. if ( ! isFbxFormatASCII( text ) ) {
  24. console.error( 'FBXLoader: FBX Binary format not supported.' );
  25. return;
  26. }
  27. if ( getFbxVersion( text ) < 7000 ) {
  28. console.error( 'FBXLoader: FBX version not supported for file at ' + url + ', FileVersion: ' + getFbxVersion( text ) );
  29. return;
  30. }
  31. var scene = self.parse( text, resourceDirectory );
  32. onLoad( scene );
  33. }, onProgress, onError );
  34. },
  35. parse: function ( FBXText, resourceDirectory ) {
  36. var loader = this;
  37. var FBXTree = new TextParser().parse( FBXText );
  38. console.log( FBXTree );
  39. var connections = parseConnections( FBXTree );
  40. /**
  41. * @returns {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>}
  42. */
  43. function parseConnections( FBXTree ) {
  44. var connectionMap = new Map();
  45. if ( 'Connections' in FBXTree ) {
  46. /**
  47. * @type {number[]}
  48. */
  49. var connectionArray = FBXTree.Connections.properties.connections;
  50. connectionArray.forEach( function ( connection ) {
  51. if ( ! connectionMap.has( connection[ 0 ] ) ) {
  52. connectionMap.set( connection[ 0 ], {
  53. parents: [],
  54. children: []
  55. } );
  56. }
  57. var parentRelationship = { ID: connection[ 1 ], relationship: connection[ 2 ] };
  58. connectionMap.get( connection[ 0 ] ).parents.push( parentRelationship );
  59. if ( ! connectionMap.has( connection[ 1 ] ) ) {
  60. connectionMap.set( connection[ 1 ], {
  61. parents: [],
  62. children: []
  63. } );
  64. }
  65. var childRelationship = { ID: connection[ 0 ], relationship: connection[ 2 ] };
  66. connectionMap.get( connection[ 1 ] ).children.push( childRelationship );
  67. } );
  68. }
  69. return connectionMap;
  70. }
  71. var textures = parseTextures( FBXTree );
  72. function parseTextures( FBXTree ) {
  73. var textureMap = new Map();
  74. if ( 'Texture' in FBXTree.Objects.subNodes ) {
  75. var textureNodes = FBXTree.Objects.subNodes.Texture;
  76. for ( var nodeID in textureNodes ) {
  77. var texture = parseTexture( textureNodes[ nodeID ] );
  78. textureMap.set( parseInt( nodeID ), texture );
  79. }
  80. }
  81. return textureMap;
  82. function parseTexture( textureNode ) {
  83. var FBX_ID = textureNode.id;
  84. var name = textureNode.name;
  85. var filePath = textureNode.properties.FileName;
  86. var split = filePath.split( /[\\\/]/ );
  87. if ( split.length > 0 ) {
  88. var fileName = split[ split.length - 1 ];
  89. } else {
  90. var fileName = filePath;
  91. }
  92. debugger;
  93. var texture = loader.textureLoader.load( resourceDirectory + '/' + fileName );
  94. texture.name = name;
  95. texture.FBX_ID = FBX_ID;
  96. return texture;
  97. }
  98. }
  99. var materials = parseMaterials( FBXTree, textures, connections );
  100. /**
  101. * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
  102. */
  103. function parseMaterials( FBXTree, textureMap, connections ) {
  104. var materialMap = new Map();
  105. if ( 'Material' in FBXTree.Objects.subNodes ) {
  106. var materialNodes = FBXTree.Objects.subNodes.Material;
  107. for ( var nodeID in materialNodes ) {
  108. var material = parseMaterial( materialNodes[ nodeID ], textureMap, connections );
  109. materialMap.set( parseInt( nodeID ), material );
  110. }
  111. }
  112. return materialMap;
  113. /**
  114. * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
  115. */
  116. function parseMaterial( materialNode, textureMap, connections ) {
  117. var FBX_ID = materialNode.id;
  118. var name = materialNode.attrName;
  119. var type = materialNode.properties.ShadingModel;
  120. var children = connections.get( FBX_ID ).children;
  121. var parameters = parseParameters( materialNode.properties, textureMap, children );
  122. var material;
  123. switch ( type ) {
  124. case 'phong':
  125. material = new THREE.MeshPhongMaterial();
  126. break;
  127. case 'lambert':
  128. material = new THREE.MeshLambertMaterial();
  129. break;
  130. default:
  131. console.warn( 'No implementation given for material type ' + type + ' in FBXLoader.js. Defaulting to basic material' );
  132. material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
  133. break;
  134. }
  135. material.setValues( parameters );
  136. material.name = name;
  137. return material;
  138. /**
  139. * @param {{ID: number, relationship: string}[]} childrenRelationships
  140. */
  141. function parseParameters( properties, textureMap, childrenRelationships ) {
  142. var parameters = {};
  143. if ( properties.Diffuse ) {
  144. parameters.color = parseColor( properties.Diffuse );
  145. }
  146. if ( properties.Specular ) {
  147. parameters.specular = parseColor( properties.Specular );
  148. }
  149. if ( properties.Shininess ) {
  150. parameters.shininess = properties.Shininess.value;
  151. }
  152. if ( properties.Emissive ) {
  153. parameters.emissive = parseColor( properties.Emissive );
  154. }
  155. if ( properties.EmissiveFactor ) {
  156. parameters.emissiveIntensity = properties.EmissiveFactor.value;
  157. }
  158. if ( properties.Opacity ) {
  159. parameters.opacity = properties.Opacity.value;
  160. }
  161. if ( parameters.opacity < 1.0 ) {
  162. parameters.transparent = true;
  163. }
  164. childrenRelationships.forEach( function ( relationship ) {
  165. var type = relationship.relationship;
  166. switch ( type ) {
  167. case " \"AmbientColor":
  168. //TODO: Support AmbientColor textures
  169. break;
  170. case " \"DiffuseColor":
  171. parameters.map = textureMap.get( relationship.ID );
  172. break;
  173. default:
  174. console.warn( 'Unknown texture application of type ' + type + ', skipping texture' );
  175. break;
  176. }
  177. } );
  178. return parameters;
  179. }
  180. }
  181. }
  182. console.log( materials );
  183. var deformerMap = parseDeformers( FBXTree, connections );
  184. console.log( deformerMap );
  185. /**
  186. * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
  187. * @returns {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>}
  188. */
  189. function parseDeformers( FBXTree, connections ) {
  190. var skeletonMap = new Map();
  191. if ( 'Deformer' in FBXTree.Objects.subNodes ) {
  192. var DeformerNodes = FBXTree.Objects.subNodes.Deformer;
  193. for ( var nodeID in DeformerNodes ) {
  194. var deformerNode = DeformerNodes[ nodeID ];
  195. if ( deformerNode.attrType === 'Skin' ) {
  196. var conns = connections.get( parseInt( nodeID ) );
  197. var skeleton = parseSkeleton( deformerNode, conns, DeformerNodes );
  198. skeleton.FBX_ID = parseInt( nodeID );
  199. skeletonMap.set( parseInt( nodeID ), skeleton );
  200. }
  201. }
  202. }
  203. return skeletonMap;
  204. /**
  205. * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} connections
  206. * @returns {{map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}}
  207. */
  208. function parseSkeleton( deformerNode, connections, DeformerNodes ) {
  209. var subDeformers = new Map();
  210. var subDeformerArray = [];
  211. connections.children.forEach( function ( child ) {
  212. var subDeformerNode = DeformerNodes[ child.ID ];
  213. var subDeformer = {
  214. FBX_ID: child.ID,
  215. indices: parseIntArray( subDeformerNode.subNodes.Indexes.properties.a ),
  216. weights: parseFloatArray( subDeformerNode.subNodes.Weights.properties.a ),
  217. transform: parseMatrixArray( subDeformerNode.subNodes.Transform.properties.a ),
  218. transformLink: parseMatrixArray( subDeformerNode.subNodes.TransformLink.properties.a ),
  219. linkMode: subDeformerNode.properties.Mode
  220. };
  221. subDeformers.set( child.ID, subDeformer );
  222. subDeformerArray.push( subDeformer );
  223. } );
  224. return {
  225. map: subDeformers,
  226. array: subDeformerArray,
  227. bones: []
  228. };
  229. }
  230. }
  231. var geometryMap = parseGeometries( FBXTree, connections, deformerMap );
  232. console.log( geometryMap );
  233. /**
  234. * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
  235. * @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} deformerMap
  236. * @returns {Map<number, THREE.BufferGeometry>}
  237. */
  238. function parseGeometries( FBXTree, connections, deformerMap ) {
  239. var geometryMap = new Map();
  240. if ( 'Geometry' in FBXTree.Objects.subNodes ) {
  241. var geometryNodes = FBXTree.Objects.subNodes.Geometry;
  242. for ( var nodeID in geometryNodes ) {
  243. var relationships = connections.get( parseInt( nodeID ) );
  244. var geo = parseGeometry( geometryNodes[ nodeID ], relationships, deformerMap );
  245. geometryMap.set( parseInt( nodeID ), geo );
  246. }
  247. }
  248. return geometryMap;
  249. /**
  250. * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships
  251. * @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}>} deformerMap
  252. */
  253. function parseGeometry( geometryNode, relationships, deformerMap ) {
  254. switch ( geometryNode.attrType ) {
  255. case 'Mesh':
  256. return parseMeshGeometry( geometryNode, relationships, deformerMap );
  257. break;
  258. case 'NurbsCurve':
  259. return parseNurbsGeometry( geometryNode, relationships, deformerMap );
  260. break;
  261. }
  262. /**
  263. * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships
  264. * @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}>} deformerMap
  265. */
  266. function parseMeshGeometry( geometryNode, relationships, deformerMap ) {
  267. var FBX_ID = geometryNode.id;
  268. var name = geometryNode.attrName;
  269. for ( var i = 0; i < relationships.children.length; ++ i ) {
  270. if ( deformerMap.has( relationships.children[ i ].ID ) ) {
  271. var deformer = deformerMap.get( relationships.children[ i ].ID );
  272. break;
  273. }
  274. }
  275. var geometry = genGeometry( geometryNode, deformer );
  276. return geometry;
  277. /**
  278. * @param {{map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}} deformer
  279. * @returns {THREE.BufferGeometry}
  280. */
  281. function genGeometry( geometryNode, deformer ) {
  282. var geometry = new Geometry();
  283. //First, each index is going to be its own vertex.
  284. var vertexBuffer = parseFloatArray( geometryNode.subNodes.Vertices.properties.a );
  285. var indexBuffer = parseIntArray( geometryNode.subNodes.PolygonVertexIndex.properties.a );
  286. if ( 'LayerElementNormal' in geometryNode.subNodes ) {
  287. var normalInfo = getNormals( geometryNode );
  288. }
  289. if ( 'LayerElementUV' in geometryNode.subNodes ) {
  290. var uvInfo = getUVs( geometryNode );
  291. }
  292. if ( 'LayerElementMaterial' in geometryNode.subNodes ) {
  293. var materialInfo = getMaterials( geometryNode );
  294. }
  295. var faceVertexBuffer = [];
  296. var polygonIndex = 0;
  297. for ( var polygonVertexIndex = 0; polygonVertexIndex < indexBuffer.length; ++ polygonVertexIndex ) {
  298. var endOfFace;
  299. var vertexIndex = indexBuffer[ polygonVertexIndex ];
  300. if ( indexBuffer[ polygonVertexIndex ] < 0 ) {
  301. vertexIndex = vertexIndex ^ - 1;
  302. endOfFace = true;
  303. }
  304. var vertex = new Vertex();
  305. var weightIndices = [];
  306. var weights = [];
  307. vertex.position.fromArray( vertexBuffer, vertexIndex * 3 );
  308. if ( deformer ) {
  309. for ( var j = 0; j < deformer.array.length; ++ j ) {
  310. var index = deformer.array[ j ].indices.findIndex( function ( index ) {
  311. return index === indexBuffer[ polygonVertexIndex ];
  312. } );
  313. if ( index !== - 1 ) {
  314. weights.push( deformer.array[ j ].weights[ index ] );
  315. weightIndices.push( j );
  316. }
  317. }
  318. if ( weights.length > 4 ) {
  319. console.warn( 'FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' );
  320. var WIndex = [ 0, 0, 0, 0 ];
  321. var Weight = [ 0, 0, 0, 0 ];
  322. for ( var polygonVertexIndex = 0; polygonVertexIndex < weights.length; ++ polygonVertexIndex ) {
  323. var currentWeight = weights[ polygonVertexIndex ];
  324. var currentIndex = weightIndices[ polygonVertexIndex ];
  325. for ( var j = 0; j < Weight.length; ++ j ) {
  326. if ( currentWeight > Weight[ j ] ) {
  327. var tmp = Weight[ j ];
  328. Weight[ j ] = currentWeight;
  329. currentWeight = tmp;
  330. tmp = WIndex[ j ];
  331. WIndex[ j ] = currentIndex;
  332. currentIndex = tmp;
  333. }
  334. }
  335. }
  336. weightIndices = WIndex;
  337. weights = Weight;
  338. }
  339. vertex.skinWeights.fromArray( weights );
  340. vertex.skinIndices.fromArray( weightIndices );
  341. vertex.skinWeights.normalize();
  342. }
  343. if ( normalInfo ) {
  344. vertex.normal.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, normalInfo ) );
  345. }
  346. if ( uvInfo ) {
  347. vertex.uv.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, uvInfo ) );
  348. }
  349. //Add vertex to face buffer.
  350. faceVertexBuffer.push( vertex );
  351. // If index was negative to start with, we have finished this individual face
  352. // and can generate the face data to the geometry.
  353. if ( endOfFace ) {
  354. var face = new Face();
  355. var materials = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo );
  356. face.genTrianglesFromVertices( faceVertexBuffer );
  357. face.materialIndex = materials[ 0 ];
  358. geometry.faces.push( face );
  359. faceVertexBuffer = [];
  360. polygonIndex ++;
  361. endOfFace = false;
  362. }
  363. }
  364. debugger;
  365. /**
  366. * @type {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}}
  367. */
  368. var bufferInfo = geometry.flattenToBuffers();
  369. var geo = new THREE.BufferGeometry();
  370. geo.name = geometryNode.name;
  371. geo.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( bufferInfo.vertexBuffer ), 3 ) );
  372. if ( bufferInfo.normalBuffer.length > 0 ) {
  373. geo.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( bufferInfo.normalBuffer ), 3 ) );
  374. }
  375. if ( bufferInfo.uvBuffer.length > 0 ) {
  376. geo.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( bufferInfo.uvBuffer ), 2 ) );
  377. }
  378. if ( deformer ) {
  379. geo.addAttribute( 'skinIndex', new THREE.BufferAttribute( new Float32Array( bufferInfo.skinIndexBuffer ), 4 ) );
  380. geo.addAttribute( 'skinWeight', new THREE.BufferAttribute( new Float32Array( bufferInfo.skinWeightBuffer ), 4 ) );
  381. geo.FBX_Deformer = deformer;
  382. }
  383. var prevMaterialIndex = bufferInfo.materialIndexBuffer[ 0 ];
  384. var startIndex = 0;
  385. for ( var materialBufferIndex = 0; materialBufferIndex < bufferInfo.materialIndexBuffer.length; ++ materialBufferIndex ) {
  386. if ( bufferInfo.materialIndexBuffer[ materialBufferIndex ] !== prevMaterialIndex ) {
  387. geo.addGroup( startIndex, materialBufferIndex - startIndex, prevMaterialIndex );
  388. startIndex = materialBufferIndex;
  389. prevMaterialIndex = bufferInfo.materialIndexBuffer[ materialBufferIndex ];
  390. }
  391. }
  392. return geo;
  393. /**
  394. * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
  395. */
  396. function getNormals( geometryNode ) {
  397. var NormalNode = geometryNode.subNodes.LayerElementNormal[ 0 ];
  398. var mappingType = NormalNode.properties.MappingInformationType;
  399. var referenceType = NormalNode.properties.ReferenceInformationType;
  400. var buffer = parseFloatArray( NormalNode.subNodes.Normals.properties.a );
  401. var indexBuffer = [];
  402. if ( referenceType === 'IndexToDirect' ) {
  403. indexBuffer = parseIntArray( NormalNode.subNodes.NormalIndex.properties.a );
  404. }
  405. return {
  406. dataSize: 3,
  407. buffer: buffer,
  408. indices: indexBuffer,
  409. mappingType: mappingType,
  410. referenceType: referenceType
  411. };
  412. }
  413. /**
  414. * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
  415. */
  416. function getUVs( geometryNode ) {
  417. var UVNode = geometryNode.subNodes.LayerElementUV[ 0 ];
  418. var mappingType = UVNode.properties.MappingInformationType;
  419. var referenceType = UVNode.properties.ReferenceInformationType;
  420. var buffer = parseFloatArray( UVNode.subNodes.UV.properties.a );
  421. var indexBuffer = [];
  422. if ( referenceType === 'IndexToDirect' ) {
  423. indexBuffer = parseIntArray( UVNode.subNodes.UVIndex.properties.a );
  424. }
  425. return {
  426. dataSize: 2,
  427. buffer: buffer,
  428. indices: indexBuffer,
  429. mappingType: mappingType,
  430. referenceType: referenceType
  431. };
  432. }
  433. /**
  434. * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
  435. */
  436. function getMaterials( geometryNode ) {
  437. var MaterialNode = geometryNode.subNodes.LayerElementMaterial[ 0 ];
  438. var mappingType = MaterialNode.properties.MappingInformationType;
  439. var referenceType = MaterialNode.properties.ReferenceInformationType;
  440. var materialIndexBuffer = parseIntArray( MaterialNode.subNodes.Materials.properties.a );
  441. // Since materials are stored as indices, there's a bit of a mismatch between FBX and what
  442. // we expect. So we create an intermediate buffer that points to the index in the buffer,
  443. // for conforming with the other functions we've written for other data.
  444. var materialIndices = [];
  445. materialIndexBuffer.forEach( function ( materialIndex, index ) {
  446. materialIndices.push( index );
  447. } );
  448. return {
  449. dataSize: 1,
  450. buffer: materialIndexBuffer,
  451. indices: materialIndices,
  452. mappingType: mappingType,
  453. referenceType: referenceType
  454. };
  455. }
  456. /**
  457. * Function uses the infoObject and given indices to return value array of object.
  458. * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
  459. * @param {number} polygonIndex - Index of polygon in geometry.
  460. * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
  461. * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
  462. * @returns {number[]}
  463. */
  464. function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
  465. var GetData = {
  466. ByPolygonVertex: {
  467. /**
  468. * Function uses the infoObject and given indices to return value array of object.
  469. * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
  470. * @param {number} polygonIndex - Index of polygon in geometry.
  471. * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
  472. * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
  473. * @returns {number[]}
  474. */
  475. Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
  476. return infoObject.buffer.slice( ( polygonVertexIndex * infoObject.dataSize ), ( polygonVertexIndex * infoObject.dataSize ) + infoObject.dataSize );
  477. },
  478. /**
  479. * Function uses the infoObject and given indices to return value array of object.
  480. * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
  481. * @param {number} polygonIndex - Index of polygon in geometry.
  482. * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
  483. * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
  484. * @returns {number[]}
  485. */
  486. IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
  487. var index = infoObject.indices[ polygonVertexIndex ];
  488. return infoObject.buffer.slice( ( index * infoObject.dataSize ), ( index * infoObject.dataSize ) + infoObject.dataSize );
  489. }
  490. },
  491. ByPolygon: {
  492. /**
  493. * Function uses the infoObject and given indices to return value array of object.
  494. * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
  495. * @param {number} polygonIndex - Index of polygon in geometry.
  496. * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
  497. * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
  498. * @returns {number[]}
  499. */
  500. Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
  501. return infoObject.buffer.slice( polygonIndex * infoObject.dataSize, polygonIndex * infoObject.dataSize + infoObject.dataSize );
  502. },
  503. /**
  504. * Function uses the infoObject and given indices to return value array of object.
  505. * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
  506. * @param {number} polygonIndex - Index of polygon in geometry.
  507. * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
  508. * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
  509. * @returns {number[]}
  510. */
  511. IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
  512. var index = infoObject.indices[ polygonIndex ];
  513. return infoObject.buffer.slice( index * infoObject.dataSize, index * infoObject.dataSize + infoObject.dataSize );
  514. }
  515. },
  516. AllSame: {
  517. /**
  518. * Function uses the infoObject and given indices to return value array of object.
  519. * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
  520. * @param {number} polygonIndex - Index of polygon in geometry.
  521. * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
  522. * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
  523. * @returns {number[]}
  524. */
  525. IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
  526. return infoObject.buffer.slice( infoObject.indices[ 0 ] * infoObject.dataSize, infoObject.indices[ 0 ] * infoObject.dataSize + infoObject.dataSize );
  527. }
  528. }
  529. };
  530. return GetData[ infoObject.mappingType ][ infoObject.referenceType ]( polygonVertexIndex, polygonIndex, vertexIndex, infoObject );
  531. }
  532. }
  533. }
  534. }
  535. }
  536. var sceneGraph = parseScene( FBXTree, connections, deformerMap, geometryMap, materials );
  537. console.log( sceneGraph );
  538. return sceneGraph;
  539. /**
  540. * Finally generates Scene graph and Scene graph Objects.
  541. * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
  542. * @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} deformerMap
  543. * @param {Map<number, THREE.BufferGeometry>} geometryMap
  544. * @param {Map<number, THREE.Material>} materialMap
  545. * @returns {THREE.Group}
  546. */
  547. function parseScene( FBXTree, connections, deformerMap, geometryMap, materialMap ) {
  548. var sceneGraph = new THREE.Group();
  549. var ModelNode = FBXTree.Objects.subNodes.Model;
  550. /**
  551. * @type {Array.<THREE.Object3D>}
  552. */
  553. var modelArray = [];
  554. /**
  555. * @type {Map.<number, THREE.Object3D>}
  556. */
  557. var modelMap = new Map();
  558. for ( var nodeID in ModelNode ) {
  559. var id = parseInt( nodeID );
  560. var node = ModelNode[ nodeID ];
  561. var conns = connections.get( id );
  562. var model = null;
  563. for ( var i = 0; i < conns.parents.length; ++ i ) {
  564. deformerMap.forEach( function ( deformer ) {
  565. if ( deformer.map.has( conns.parents[ i ].ID ) ) {
  566. model = new THREE.Bone();
  567. var index = deformer.array.findIndex( function ( subDeformer ) {
  568. return subDeformer.FBX_ID === conns.parents[ i ].ID;
  569. } );
  570. deformer.bones[ index ] = model;
  571. }
  572. } );
  573. }
  574. if ( ! model ) {
  575. switch ( node.attrType ) {
  576. case "Mesh":
  577. /**
  578. * @type {?THREE.BufferGeometry}
  579. */
  580. var geometry = null;
  581. /**
  582. * @type {THREE.MultiMaterial|THREE.Material}
  583. */
  584. var material = null;
  585. /**
  586. * @type {Array.<THREE.Material>}
  587. */
  588. var materials = [];
  589. conns.children.forEach( function ( child ) {
  590. if ( geometryMap.has( child.ID ) ) {
  591. geometry = geometryMap.get( child.ID );
  592. }
  593. if ( materialMap.has( child.ID ) ) {
  594. materials.push( materialMap.get( child.ID ) );
  595. }
  596. } );
  597. if ( materials.length > 1 ) {
  598. material = new THREE.MultiMaterial( materials );
  599. } else if ( materials.length > 0 ) {
  600. material = materials[ 0 ];
  601. } else {
  602. material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
  603. }
  604. if ( geometry.FBX_Deformer ) {
  605. model = new THREE.SkinnedMesh( geometry, material );
  606. } else {
  607. model = new THREE.Mesh( geometry, material );
  608. }
  609. break;
  610. default:
  611. model = new THREE.Object3D();
  612. break;
  613. }
  614. }
  615. model.name = node.attrName.replace( /:/, '' ).replace( /_/, '' ).replace( /-/, '' );
  616. model.FBX_ID = id;
  617. if ( 'Lcl_Translation' in node.properties ) {
  618. model.position.fromArray( parseFloatArray( node.properties.Lcl_Translation.value ) );
  619. }
  620. if ( 'Lcl_Rotation' in node.properties ) {
  621. var rotation = parseFloatArray( node.properties.Lcl_Rotation.value ).map( function ( value ) {
  622. return value * Math.PI / 180;
  623. } );
  624. rotation.push( 'ZYX' );
  625. model.rotation.fromArray( rotation );
  626. }
  627. if ( 'Lcl_Scaling' in node.properties ) {
  628. model.scale.fromArray( parseFloatArray( node.properties.Lcl_Scaling.value ) );
  629. }
  630. modelArray.push( model );
  631. modelMap.set( id, model );
  632. }
  633. modelArray.forEach( function ( model ) {
  634. var conns = connections.get( model.FBX_ID );
  635. conns.parents.forEach( function ( parent ) {
  636. for ( var i = 0; i < modelArray.length; ++ i ) {
  637. if ( modelArray[ i ].FBX_ID === parent.ID ) {
  638. modelArray[ i ].add( model );
  639. return;
  640. }
  641. }
  642. //Parent not found, root node?
  643. if ( parent.ID === 0 ) {
  644. sceneGraph.add( model );
  645. }
  646. } );
  647. } );
  648. // Now with the bones created, we can update the skeletons and bind them to the skinned meshes.
  649. sceneGraph.updateMatrixWorld( true );
  650. deformerMap.forEach( function ( deformer, FBX_ID ) {
  651. deformer.skeleton = new THREE.Skeleton( deformer.bones );
  652. var conns = connections.get( FBX_ID );
  653. conns.parents.forEach( function ( parent ) {
  654. if ( geometryMap.has( parent.ID ) ) {
  655. var geoID = parent.ID;
  656. var geoConns = connections.get( geoID );
  657. for ( var i = 0; i < geoConns.parents.length; ++ i ) {
  658. if ( modelMap.has( geoConns.parents[ i ].ID ) ) {
  659. var model = modelMap.get( geoConns.parents[ i ].ID );
  660. //ASSERT model typeof SkinnedMesh
  661. model.bind( deformer.skeleton, model.matrixWorld );
  662. break;
  663. }
  664. }
  665. }
  666. } );
  667. } );
  668. return sceneGraph;
  669. }
  670. // UTILS
  671. /**
  672. * Parses Vector3 property from FBXTree. Property is given as .value.x, .value.y, etc.
  673. * @param {{value: { x: string, y: string, z: string }}} property - Property to parse as Vector3.
  674. * @returns {THREE.Vector3}
  675. */
  676. function parseVector3( property ) {
  677. return new THREE.Vector3( parseFloat( property.value.x ), parseFloat( property.value.y ), parseFloat( property.value.z ) );
  678. }
  679. /**
  680. * Parses Color property from FBXTree. Property is given as .value.x, .value.y, etc.
  681. * @param {{value: { x: string, y: string, z: string }}} property - Property to parse as Color.
  682. * @returns {THREE.Color}
  683. */
  684. function parseColor( property ) {
  685. return new THREE.Color().fromArray( parseVector3( property ).toArray() );
  686. }
  687. }
  688. } );
  689. /**
  690. * An instance of a Vertex with data for drawing vertices to the screen.
  691. * @constructor
  692. */
  693. function Vertex() {
  694. /**
  695. * Position of the vertex.
  696. * @type {THREE.Vector3}
  697. */
  698. this.position = new THREE.Vector3( );
  699. /**
  700. * Normal of the vertex
  701. * @type {THREE.Vector3}
  702. */
  703. this.normal = new THREE.Vector3( );
  704. /**
  705. * UV coordinates of the vertex.
  706. * @type {THREE.Vector2}
  707. */
  708. this.uv = new THREE.Vector2( );
  709. /**
  710. * Indices of the bones vertex is influenced by.
  711. * @type {THREE.Vector4}
  712. */
  713. this.skinIndices = new THREE.Vector4( );
  714. /**
  715. * Weights that each bone influences the vertex.
  716. * @type {THREE.Vector4}
  717. */
  718. this.skinWeights = new THREE.Vector4( );
  719. }
  720. Object.assign( Vertex.prototype, {
  721. copy: function ( target ) {
  722. var returnVar = target || new Vertex();
  723. returnVar.position.copy( this.position );
  724. returnVar.normal.copy( this.normal );
  725. returnVar.uv.copy( this.uv );
  726. returnVar.skinIndices.copy( this.skinIndices );
  727. returnVar.skinWeights.copy( this.skinWeights );
  728. return returnVar;
  729. },
  730. flattenToBuffers: function () {
  731. var vertexBuffer = this.position.toArray();
  732. var normalBuffer = this.normal.toArray();
  733. var uvBuffer = this.uv.toArray();
  734. var skinIndexBuffer = this.skinIndices.toArray();
  735. var skinWeightBuffer = this.skinWeights.toArray();
  736. return {
  737. vertexBuffer: vertexBuffer,
  738. normalBuffer: normalBuffer,
  739. uvBuffer: uvBuffer,
  740. skinIndexBuffer: skinIndexBuffer,
  741. skinWeightBuffer: skinWeightBuffer,
  742. };
  743. }
  744. } );
  745. /**
  746. * @constructor
  747. */
  748. function Triangle() {
  749. /**
  750. * @type {{position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}
  751. */
  752. this.vertices = [ ];
  753. }
  754. Object.assign( Triangle.prototype, {
  755. copy: function ( target ) {
  756. var returnVar = target || new Triangle();
  757. for ( var i = 0; i < this.vertices.length; ++ i ) {
  758. this.vertices[ i ].copy( returnVar.vertices[ i ] );
  759. }
  760. return returnVar;
  761. },
  762. flattenToBuffers: function () {
  763. var vertexBuffer = [];
  764. var normalBuffer = [];
  765. var uvBuffer = [];
  766. var skinIndexBuffer = [];
  767. var skinWeightBuffer = [];
  768. this.vertices.forEach( function ( vertex ) {
  769. var flatVertex = vertex.flattenToBuffers();
  770. vertexBuffer = vertexBuffer.concat( flatVertex.vertexBuffer );
  771. normalBuffer = normalBuffer.concat( flatVertex.normalBuffer );
  772. uvBuffer = uvBuffer.concat( flatVertex.uvBuffer );
  773. skinIndexBuffer = skinIndexBuffer.concat( flatVertex.skinIndexBuffer );
  774. skinWeightBuffer = skinWeightBuffer.concat( flatVertex.skinWeightBuffer );
  775. } );
  776. return {
  777. vertexBuffer: vertexBuffer,
  778. normalBuffer: normalBuffer,
  779. uvBuffer: uvBuffer,
  780. skinIndexBuffer: skinIndexBuffer,
  781. skinWeightBuffer: skinWeightBuffer,
  782. };
  783. }
  784. } );
  785. /**
  786. * @constructor
  787. */
  788. function Face() {
  789. /**
  790. * @type {{vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[]}
  791. */
  792. this.triangles = [ ];
  793. this.materialIndex = 0;
  794. }
  795. Object.assign( Face.prototype, {
  796. copy: function ( target ) {
  797. var returnVar = target || new Face();
  798. for ( var i = 0; i < this.triangles.length; ++ i ) {
  799. this.triangles[ i ].copy( returnVar.triangles[ i ] );
  800. }
  801. returnVar.materialIndex = this.materialIndex;
  802. return returnVar;
  803. },
  804. genTrianglesFromVertices: function ( vertexArray ) {
  805. for ( var i = 2; i < vertexArray.length; ++ i ) {
  806. var triangle = new Triangle();
  807. triangle.vertices[ 0 ] = vertexArray[ 0 ];
  808. triangle.vertices[ 1 ] = vertexArray[ i - 1 ];
  809. triangle.vertices[ 2 ] = vertexArray[ i ];
  810. this.triangles.push( triangle );
  811. }
  812. },
  813. flattenToBuffers: function () {
  814. var vertexBuffer = [];
  815. var normalBuffer = [];
  816. var uvBuffer = [];
  817. var skinIndexBuffer = [];
  818. var skinWeightBuffer = [];
  819. var materialIndexBuffer = [];
  820. var materialIndex = this.materialIndex;
  821. this.triangles.forEach( function ( triangle ) {
  822. var flatTriangle = triangle.flattenToBuffers();
  823. vertexBuffer = vertexBuffer.concat( flatTriangle.vertexBuffer );
  824. normalBuffer = normalBuffer.concat( flatTriangle.normalBuffer );
  825. uvBuffer = uvBuffer.concat( flatTriangle.uvBuffer );
  826. skinIndexBuffer = skinIndexBuffer.concat( flatTriangle.skinIndexBuffer );
  827. skinWeightBuffer = skinWeightBuffer.concat( flatTriangle.skinWeightBuffer );
  828. materialIndexBuffer = materialIndexBuffer.concat( [ materialIndex, materialIndex, materialIndex ] );
  829. } );
  830. return {
  831. vertexBuffer: vertexBuffer,
  832. normalBuffer: normalBuffer,
  833. uvBuffer: uvBuffer,
  834. skinIndexBuffer: skinIndexBuffer,
  835. skinWeightBuffer: skinWeightBuffer,
  836. materialIndexBuffer: materialIndexBuffer
  837. };
  838. }
  839. } );
  840. /**
  841. * @constructor
  842. */
  843. function Geometry() {
  844. /**
  845. * @type {{triangles: {vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[], materialIndex: number}[]}
  846. */
  847. this.faces = [ ];
  848. /**
  849. * @type {{}|THREE.Skeleton}
  850. */
  851. this.skeleton = null;
  852. }
  853. Object.assign( Geometry.prototype, {
  854. /**
  855. * @returns {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}}
  856. */
  857. flattenToBuffers: function () {
  858. var vertexBuffer = [];
  859. var normalBuffer = [];
  860. var uvBuffer = [];
  861. var skinIndexBuffer = [];
  862. var skinWeightBuffer = [];
  863. var materialIndexBuffer = [];
  864. this.faces.forEach( function ( face ) {
  865. var flatFace = face.flattenToBuffers();
  866. vertexBuffer = vertexBuffer.concat( flatFace.vertexBuffer );
  867. normalBuffer = normalBuffer.concat( flatFace.normalBuffer );
  868. uvBuffer = uvBuffer.concat( flatFace.uvBuffer );
  869. skinIndexBuffer = skinIndexBuffer.concat( flatFace.skinIndexBuffer );
  870. skinWeightBuffer = skinWeightBuffer.concat( flatFace.skinWeightBuffer );
  871. materialIndexBuffer = materialIndexBuffer.concat( flatFace.materialIndexBuffer );
  872. } );
  873. return {
  874. vertexBuffer: vertexBuffer,
  875. normalBuffer: normalBuffer,
  876. uvBuffer: uvBuffer,
  877. skinIndexBuffer: skinIndexBuffer,
  878. skinWeightBuffer: skinWeightBuffer,
  879. materialIndexBuffer: materialIndexBuffer
  880. };
  881. }
  882. } );
  883. function TextParser() {}
  884. Object.assign( TextParser.prototype, {
  885. getPrevNode: function () {
  886. return this.nodeStack[ this.currentIndent - 2 ];
  887. },
  888. getCurrentNode: function () {
  889. return this.nodeStack[ this.currentIndent - 1 ];
  890. },
  891. getCurrentProp: function () {
  892. return this.currentProp;
  893. },
  894. pushStack: function ( node ) {
  895. this.nodeStack.push( node );
  896. this.currentIndent += 1;
  897. },
  898. popStack: function () {
  899. this.nodeStack.pop();
  900. this.currentIndent -= 1;
  901. },
  902. setCurrentProp: function ( val, name ) {
  903. this.currentProp = val;
  904. this.currentPropName = name;
  905. },
  906. // ----------parse ---------------------------------------------------
  907. parse: function ( text ) {
  908. this.currentIndent = 0;
  909. this.allNodes = new FBXTree();
  910. this.nodeStack = [];
  911. this.currentProp = [];
  912. this.currentPropName = '';
  913. var split = text.split( "\n" );
  914. for ( var line in split ) {
  915. var l = split[ line ];
  916. // short cut
  917. if ( l.match( /^[\s\t]*;/ ) ) {
  918. continue;
  919. } // skip comment line
  920. if ( l.match( /^[\s\t]*$/ ) ) {
  921. continue;
  922. } // skip empty line
  923. // beginning of node
  924. var beginningOfNodeExp = new RegExp( "^\\t{" + this.currentIndent + "}(\\w+):(.*){", '' );
  925. var match = l.match( beginningOfNodeExp );
  926. if ( match ) {
  927. var nodeName = match[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, "" );
  928. var nodeAttrs = match[ 2 ].split( ',' ).map( function ( element ) {
  929. return element.trim().replace( /^"/, '' ).replace( /"$/, '' );
  930. } );
  931. this.parseNodeBegin( l, nodeName, nodeAttrs || null );
  932. continue;
  933. }
  934. // node's property
  935. var propExp = new RegExp( "^\\t{" + ( this.currentIndent ) + "}(\\w+):[\\s\\t\\r\\n](.*)" );
  936. var match = l.match( propExp );
  937. if ( match ) {
  938. var propName = match[ 1 ].replace( /^"/, '' ).replace( /"$/, "" ).trim();
  939. var propValue = match[ 2 ].replace( /^"/, '' ).replace( /"$/, "" ).trim();
  940. this.parseNodeProperty( l, propName, propValue );
  941. continue;
  942. }
  943. // end of node
  944. var endOfNodeExp = new RegExp( "^\\t{" + ( this.currentIndent - 1 ) + "}}" );
  945. if ( l.match( endOfNodeExp ) ) {
  946. this.nodeEnd();
  947. continue;
  948. }
  949. // for special case,
  950. //
  951. // Vertices: *8670 {
  952. // a: 0.0356229953467846,13.9599733352661,-0.399196773.....(snip)
  953. // -0.0612030513584614,13.960485458374,-0.409748703241348,-0.10.....
  954. // 0.12490539252758,13.7450733184814,-0.454119384288788,0.09272.....
  955. // 0.0836158767342567,13.5432004928589,-0.435397416353226,0.028.....
  956. //
  957. // these case the lines must contiue with previous line
  958. if ( l.match( /^[^\s\t}]/ ) ) {
  959. this.parseNodePropertyContinued( l );
  960. }
  961. }
  962. return this.allNodes;
  963. },
  964. parseNodeBegin: function ( line, nodeName, nodeAttrs ) {
  965. // var nodeName = match[1];
  966. var node = { 'name': nodeName, properties: {}, 'subNodes': {} };
  967. var attrs = this.parseNodeAttr( nodeAttrs );
  968. var currentNode = this.getCurrentNode();
  969. // a top node
  970. if ( this.currentIndent === 0 ) {
  971. this.allNodes.add( nodeName, node );
  972. } else {
  973. // a subnode
  974. // already exists subnode, then append it
  975. if ( nodeName in currentNode.subNodes ) {
  976. var tmp = currentNode.subNodes[ nodeName ];
  977. // console.log( "duped entry found\nkey: " + nodeName + "\nvalue: " + propValue );
  978. if ( this.isFlattenNode( currentNode.subNodes[ nodeName ] ) ) {
  979. if ( attrs.id === '' ) {
  980. currentNode.subNodes[ nodeName ] = [];
  981. currentNode.subNodes[ nodeName ].push( tmp );
  982. } else {
  983. currentNode.subNodes[ nodeName ] = {};
  984. currentNode.subNodes[ nodeName ][ tmp.id ] = tmp;
  985. }
  986. }
  987. if ( attrs.id === '' ) {
  988. currentNode.subNodes[ nodeName ].push( node );
  989. } else {
  990. currentNode.subNodes[ nodeName ][ attrs.id ] = node;
  991. }
  992. } else if ( typeof attrs.id === 'number' || attrs.id.match( /^\d+$/ ) ) {
  993. currentNode.subNodes[ nodeName ] = {};
  994. currentNode.subNodes[ nodeName ][ attrs.id ] = node;
  995. } else {
  996. currentNode.subNodes[ nodeName ] = node;
  997. }
  998. }
  999. // for this ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  1000. // NodeAttribute: 1001463072, "NodeAttribute::", "LimbNode" {
  1001. if ( nodeAttrs ) {
  1002. node.id = attrs.id;
  1003. node.attrName = attrs.name;
  1004. node.attrType = attrs.type;
  1005. }
  1006. this.pushStack( node );
  1007. },
  1008. parseNodeAttr: function ( attrs ) {
  1009. var id = attrs[ 0 ];
  1010. if ( attrs[ 0 ] !== "" ) {
  1011. id = parseInt( attrs[ 0 ] );
  1012. if ( isNaN( id ) ) {
  1013. // PolygonVertexIndex: *16380 {
  1014. id = attrs[ 0 ];
  1015. }
  1016. }
  1017. var name;
  1018. var type;
  1019. if ( attrs.length > 1 ) {
  1020. name = attrs[ 1 ].replace( /^(\w+)::/, '' );
  1021. type = attrs[ 2 ];
  1022. }
  1023. return { id: id, name: name || '', type: type || '' };
  1024. },
  1025. parseNodeProperty: function ( line, propName, propValue ) {
  1026. var currentNode = this.getCurrentNode();
  1027. var parentName = currentNode.name;
  1028. // special case parent node's is like "Properties70"
  1029. // these chilren nodes must treat with careful
  1030. if ( parentName !== undefined ) {
  1031. var propMatch = parentName.match( /Properties(\d)+/ );
  1032. if ( propMatch ) {
  1033. this.parseNodeSpecialProperty( line, propName, propValue );
  1034. return;
  1035. }
  1036. }
  1037. // special case Connections
  1038. if ( propName == 'C' ) {
  1039. var connProps = propValue.split( ',' ).slice( 1 );
  1040. var from = parseInt( connProps[ 0 ] );
  1041. var to = parseInt( connProps[ 1 ] );
  1042. var rest = propValue.split( ',' ).slice( 3 );
  1043. propName = 'connections';
  1044. propValue = [ from, to ];
  1045. propValue = propValue.concat( rest );
  1046. if ( currentNode.properties[ propName ] === undefined ) {
  1047. currentNode.properties[ propName ] = [];
  1048. }
  1049. }
  1050. // special case Connections
  1051. if ( propName == 'Node' ) {
  1052. var id = parseInt( propValue );
  1053. currentNode.properties.id = id;
  1054. currentNode.id = id;
  1055. }
  1056. // already exists in properties, then append this
  1057. if ( propName in currentNode.properties ) {
  1058. // console.log( "duped entry found\nkey: " + propName + "\nvalue: " + propValue );
  1059. if ( Array.isArray( currentNode.properties[ propName ] ) ) {
  1060. currentNode.properties[ propName ].push( propValue );
  1061. } else {
  1062. currentNode.properties[ propName ] += propValue;
  1063. }
  1064. } else {
  1065. // console.log( propName + ": " + propValue );
  1066. if ( Array.isArray( currentNode.properties[ propName ] ) ) {
  1067. currentNode.properties[ propName ].push( propValue );
  1068. } else {
  1069. currentNode.properties[ propName ] = propValue;
  1070. }
  1071. }
  1072. this.setCurrentProp( currentNode.properties, propName );
  1073. },
  1074. // TODO:
  1075. parseNodePropertyContinued: function ( line ) {
  1076. this.currentProp[ this.currentPropName ] += line;
  1077. },
  1078. parseNodeSpecialProperty: function ( line, propName, propValue ) {
  1079. // split this
  1080. // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
  1081. // into array like below
  1082. // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ]
  1083. var props = propValue.split( '",' ).map( function ( element ) {
  1084. return element.trim().replace( /^\"/, '' ).replace( /\s/, '_' );
  1085. } );
  1086. var innerPropName = props[ 0 ];
  1087. var innerPropType1 = props[ 1 ];
  1088. var innerPropType2 = props[ 2 ];
  1089. var innerPropFlag = props[ 3 ];
  1090. var innerPropValue = props[ 4 ];
  1091. /*
  1092. if ( innerPropValue === undefined ) {
  1093. innerPropValue = props[3];
  1094. }
  1095. */
  1096. // cast value in its type
  1097. switch ( innerPropType1 ) {
  1098. case "int":
  1099. innerPropValue = parseInt( innerPropValue );
  1100. break;
  1101. case "double":
  1102. innerPropValue = parseFloat( innerPropValue );
  1103. break;
  1104. case "ColorRGB":
  1105. case "Vector3D":
  1106. var tmp = innerPropValue.split( ',' );
  1107. innerPropValue = new THREE.Vector3( tmp[ 0 ], tmp[ 1 ], tmp[ 2 ] );
  1108. break;
  1109. }
  1110. // CAUTION: these props must append to parent's parent
  1111. this.getPrevNode().properties[ innerPropName ] = {
  1112. 'type': innerPropType1,
  1113. 'type2': innerPropType2,
  1114. 'flag': innerPropFlag,
  1115. 'value': innerPropValue
  1116. };
  1117. this.setCurrentProp( this.getPrevNode().properties, innerPropName );
  1118. },
  1119. nodeEnd: function () {
  1120. this.popStack();
  1121. },
  1122. /* ---------------------------------------------------------------- */
  1123. /* util */
  1124. isFlattenNode: function ( node ) {
  1125. return ( 'subNodes' in node && 'properties' in node ) ? true : false;
  1126. }
  1127. } );
  1128. function FBXTree() {}
  1129. Object.assign( FBXTree.prototype, {
  1130. add: function ( key, val ) {
  1131. this[ key ] = val;
  1132. },
  1133. searchConnectionParent: function ( id ) {
  1134. if ( this.__cache_search_connection_parent === undefined ) {
  1135. this.__cache_search_connection_parent = [];
  1136. }
  1137. if ( this.__cache_search_connection_parent[ id ] !== undefined ) {
  1138. return this.__cache_search_connection_parent[ id ];
  1139. } else {
  1140. this.__cache_search_connection_parent[ id ] = [];
  1141. }
  1142. var conns = this.Connections.properties.connections;
  1143. var results = [];
  1144. for ( var i = 0; i < conns.length; ++ i ) {
  1145. if ( conns[ i ][ 0 ] == id ) {
  1146. // 0 means scene root
  1147. var res = conns[ i ][ 1 ] === 0 ? - 1 : conns[ i ][ 1 ];
  1148. results.push( res );
  1149. }
  1150. }
  1151. if ( results.length > 0 ) {
  1152. this.__cache_search_connection_parent[ id ] = this.__cache_search_connection_parent[ id ].concat( results );
  1153. return results;
  1154. } else {
  1155. this.__cache_search_connection_parent[ id ] = [ - 1 ];
  1156. return [ - 1 ];
  1157. }
  1158. },
  1159. searchConnectionChildren: function ( id ) {
  1160. if ( this.__cache_search_connection_children === undefined ) {
  1161. this.__cache_search_connection_children = [];
  1162. }
  1163. if ( this.__cache_search_connection_children[ id ] !== undefined ) {
  1164. return this.__cache_search_connection_children[ id ];
  1165. } else {
  1166. this.__cache_search_connection_children[ id ] = [];
  1167. }
  1168. var conns = this.Connections.properties.connections;
  1169. var res = [];
  1170. for ( var i = 0; i < conns.length; ++ i ) {
  1171. if ( conns[ i ][ 1 ] == id ) {
  1172. // 0 means scene root
  1173. res.push( conns[ i ][ 0 ] === 0 ? - 1 : conns[ i ][ 0 ] );
  1174. // there may more than one kid, then search to the end
  1175. }
  1176. }
  1177. if ( res.length > 0 ) {
  1178. this.__cache_search_connection_children[ id ] = this.__cache_search_connection_children[ id ].concat( res );
  1179. return res;
  1180. } else {
  1181. this.__cache_search_connection_children[ id ] = [ ];
  1182. return [ ];
  1183. }
  1184. },
  1185. searchConnectionType: function ( id, to ) {
  1186. var key = id + ',' + to; // TODO: to hash
  1187. if ( this.__cache_search_connection_type === undefined ) {
  1188. this.__cache_search_connection_type = {};
  1189. }
  1190. if ( this.__cache_search_connection_type[ key ] !== undefined ) {
  1191. return this.__cache_search_connection_type[ key ];
  1192. } else {
  1193. this.__cache_search_connection_type[ key ] = '';
  1194. }
  1195. var conns = this.Connections.properties.connections;
  1196. for ( var i = 0; i < conns.length; ++ i ) {
  1197. if ( conns[ i ][ 0 ] == id && conns[ i ][ 1 ] == to ) {
  1198. // 0 means scene root
  1199. this.__cache_search_connection_type[ key ] = conns[ i ][ 2 ];
  1200. return conns[ i ][ 2 ];
  1201. }
  1202. }
  1203. this.__cache_search_connection_type[ id ] = null;
  1204. return null;
  1205. }
  1206. } );
  1207. /**
  1208. * @returns {boolean}
  1209. */
  1210. function isFbxFormatASCII( text ) {
  1211. var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ];
  1212. var cursor = 0;
  1213. var read = function ( offset ) {
  1214. var result = text[ offset - 1 ];
  1215. text = text.slice( cursor + offset );
  1216. cursor ++;
  1217. return result;
  1218. };
  1219. for ( var i = 0; i < CORRECT.length; ++ i ) {
  1220. var num = read( 1 );
  1221. if ( num == CORRECT[ i ] ) {
  1222. return false;
  1223. }
  1224. }
  1225. return true;
  1226. }
  1227. /**
  1228. * @returns {number}
  1229. */
  1230. function getFbxVersion( text ) {
  1231. var versionRegExp = /FBXVersion: (\d+)/;
  1232. var match = text.match( versionRegExp );
  1233. if ( match ) {
  1234. var version = parseInt( match[ 1 ] );
  1235. return version;
  1236. }
  1237. throw new Error( 'FBXLoader: Cannot find the version number for the file given.' );
  1238. }
  1239. /**
  1240. * Parses comma separated list of float numbers and returns them in an array.
  1241. * @example
  1242. * // Returns [ 5.6, 9.4, 2.5, 1.4 ]
  1243. * parseFloatArray( "5.6,9.4,2.5,1.4" )
  1244. * @returns {number[]}
  1245. */
  1246. function parseFloatArray( floatString ) {
  1247. return floatString.split( ',' ).map( function ( stringValue ) {
  1248. return parseFloat( stringValue );
  1249. } );
  1250. }
  1251. /**
  1252. * Parses comma separated list of int numbers and returns them in an array.
  1253. * @example
  1254. * // Returns [ 5, 8, 2, 3 ]
  1255. * parseFloatArray( "5,8,2,3" )
  1256. * @returns {number[]}
  1257. */
  1258. function parseIntArray( intString ) {
  1259. return intString.split( ',' ).map( function ( stringValue ) {
  1260. return parseInt( stringValue );
  1261. } );
  1262. }
  1263. function parseMatrixArray( floatString ) {
  1264. return new THREE.Matrix4().fromArray( parseFloatArray( floatString ) );
  1265. }
  1266. } )();