FBXLoader2.js 48 KB

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