elation-engine 0.9.113 → 0.9.115

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/css/systems/render.css +5 -1
  2. package/package.json +1 -1
  3. package/scripts/assets.js +103 -20
  4. package/scripts/assetworker.js +18 -1
  5. package/scripts/external/holoplay.js +1494 -0
  6. package/scripts/external/octree.js +0 -0
  7. package/scripts/external/three/CSS3DRenderer.js +46 -43
  8. package/scripts/external/three/CubemapToEquirectangular.js +1 -1
  9. package/scripts/external/three/three-extras.js +1553 -392
  10. package/scripts/external/three/three-icosa.js +2575 -0
  11. package/scripts/external/three/three-loaders.js +925 -133
  12. package/scripts/external/three/three-postprocessing.js +3 -3
  13. package/scripts/external/three/three-r116dev.js +50930 -0
  14. package/scripts/external/three/three-spotlighttextures.js +50953 -0
  15. package/scripts/external/three/three-vrm.js +2 -2
  16. package/scripts/external/three/three-working.js +35968 -0
  17. package/scripts/external/three/three.js +38532 -24087
  18. package/scripts/external/three-mesh-bvh.js +5370 -0
  19. package/scripts/external/three-old/BVHLoader.js +406 -0
  20. package/scripts/external/three-old/ColladaLoader.js +5519 -0
  21. package/scripts/external/three-old/ColladaLoader2.js +1694 -0
  22. package/scripts/external/three-old/CubemapToEquirectangular.js +188 -0
  23. package/scripts/external/three-old/DDSLoader.js +269 -0
  24. package/scripts/external/three-old/FBXLoader-mine.js +5063 -0
  25. package/scripts/external/three-old/FBXLoader.js +5112 -0
  26. package/scripts/external/three-old/FlyControls.js +295 -0
  27. package/scripts/external/three-old/GLTF2Loader.js +2950 -0
  28. package/scripts/external/three-old/GLTFLoader.js +2213 -0
  29. package/scripts/external/three-old/JSONLoader.js +435 -0
  30. package/scripts/external/three-old/MTLLoader.js +533 -0
  31. package/scripts/external/three-old/OBJLoader-experimental.js +874 -0
  32. package/scripts/external/three-old/OBJLoader-working.js +727 -0
  33. package/scripts/external/three-old/OBJLoader.js +723 -0
  34. package/scripts/external/three-old/OBJMTLLoader.js +440 -0
  35. package/scripts/external/three-old/OrbitControls.js +592 -0
  36. package/scripts/external/three-old/PLYLoader.js +517 -0
  37. package/scripts/external/three-old/TransformControls.js +1100 -0
  38. package/scripts/external/three-old/VRMLLoader.js +1021 -0
  39. package/scripts/external/three-old/glTFLoader-combined.js +2513 -0
  40. package/scripts/external/three-old/nodethree.js +44018 -0
  41. package/scripts/external/three-old/render/BleachBypassShader.js +64 -0
  42. package/scripts/external/three-old/render/BloomPass.js +116 -0
  43. package/scripts/external/three-old/render/CSS3DRenderer.js +310 -0
  44. package/scripts/external/three-old/render/ClearPass.js +44 -0
  45. package/scripts/external/three-old/render/ConvolutionShader.js +101 -0
  46. package/scripts/external/three-old/render/CopyShader.js +46 -0
  47. package/scripts/external/three-old/render/EffectComposer.js +211 -0
  48. package/scripts/external/three-old/render/FXAAShader.js +88 -0
  49. package/scripts/external/three-old/render/FilmPass.js +60 -0
  50. package/scripts/external/three-old/render/FilmShader.js +104 -0
  51. package/scripts/external/three-old/render/ManualMSAARenderPass.js +168 -0
  52. package/scripts/external/three-old/render/MaskPass.js +97 -0
  53. package/scripts/external/three-old/render/OculusRenderPass.js +84 -0
  54. package/scripts/external/three-old/render/OculusRiftEffect.js +240 -0
  55. package/scripts/external/three-old/render/PortalRenderPass.js +166 -0
  56. package/scripts/external/three-old/render/RecordingPass.js +208 -0
  57. package/scripts/external/three-old/render/RenderPass.js +57 -0
  58. package/scripts/external/three-old/render/SSAOShader.js +259 -0
  59. package/scripts/external/three-old/render/SepiaShader.js +54 -0
  60. package/scripts/external/three-old/render/ShaderPass.js +66 -0
  61. package/scripts/external/three-old/render/VREffect.js +482 -0
  62. package/scripts/external/three-old/shimthree.js +23 -0
  63. package/scripts/external/three-old/stats.js +6 -0
  64. package/scripts/external/three-old/three-88dev.js +45004 -0
  65. package/scripts/external/three-old/three-backgroundoptimization.js +44432 -0
  66. package/scripts/external/three-old/three-updates.js +44735 -0
  67. package/scripts/external/three-old/three-working.js +44719 -0
  68. package/scripts/external/three-old/three.js +44431 -0
  69. package/scripts/external/three-old/threex.rendererstats.js +66 -0
  70. package/scripts/external/three-old/tween.js +13 -0
  71. package/scripts/external/webvr-polyfill-new.js +3497 -0
  72. package/scripts/external/webvr-polyfill-newest.js +3491 -0
  73. package/scripts/external/webvr-polyfill-old.js +6337 -0
  74. package/scripts/geometries.js +2 -2
  75. package/scripts/math.js +6 -6
  76. package/scripts/systems/admin.js +1 -1
  77. package/scripts/systems/controls.js +6 -4
  78. package/scripts/systems/physics.js +10 -10
  79. package/scripts/systems/render.js +58 -20
  80. package/scripts/systems/render2.js +38 -0
  81. package/scripts/things/camera.js +6 -1
  82. package/scripts/things/generic-trackedvectors.js +1875 -0
  83. package/scripts/things/generic.js +3 -4
  84. package/scripts/things/label2d.js +1 -1
  85. package/scripts/things/leapmotion.js +6 -6
  86. package/scripts/things/menu.js +1 -1
  87. package/scripts/things/player-bak.js +638 -0
  88. package/scripts/things/player.js +28 -10
  89. package/scripts/things/skysphere.js +1 -1
  90. package/scripts/things/terrain.js +1 -1
  91. package/scripts/things/text.js +1 -1
@@ -0,0 +1,5112 @@
1
+ /**
2
+ * @author Kyle-Larson https://github.com/Kyle-Larson
3
+ * @author Takahiro https://github.com/takahirox
4
+ *
5
+ * Loader loads FBX file and generates Group representing FBX scene.
6
+ * Requires FBX file to be >= 7.0 and in ASCII or to be any version in Binary format.
7
+ *
8
+ * Supports:
9
+ * Mesh Generation (Positional Data)
10
+ * Normal Data (Per Vertex Drawing Instance)
11
+ * UV Data (Per Vertex Drawing Instance)
12
+ * Skinning
13
+ * Animation
14
+ * - Separated Animations based on stacks.
15
+ * - Skeletal & Non-Skeletal Animations
16
+ * NURBS (Open, Closed and Periodic forms)
17
+ *
18
+ * Needs Support:
19
+ * Indexed Buffers
20
+ * PreRotation support.
21
+ */
22
+
23
+ ( function () {
24
+
25
+ /**
26
+ * Generates a loader for loading FBX files from URL and parsing into
27
+ * a THREE.Group.
28
+ * @param {THREE.LoadingManager} manager - Loading Manager for loader to use.
29
+ */
30
+ THREE.FBXLoader = function ( manager ) {
31
+
32
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
33
+
34
+ };
35
+
36
+ Object.assign( THREE.FBXLoader.prototype, {
37
+
38
+ /**
39
+ * Loads an ASCII/Binary FBX file from URL and parses into a THREE.Group.
40
+ * THREE.Group will have an animations property of AnimationClips
41
+ * of the different animations exported with the FBX.
42
+ * @param {string} url - URL of the FBX file.
43
+ * @param {function(THREE.Group):void} onLoad - Callback for when FBX file is loaded and parsed.
44
+ * @param {function(ProgressEvent):void} onProgress - Callback fired periodically when file is being retrieved from server.
45
+ * @param {function(Event):void} onError - Callback fired when error occurs (Currently only with retrieving file, not with parsing errors).
46
+ */
47
+ load: function ( url, onLoad, onProgress, onError ) {
48
+
49
+ var self = this;
50
+
51
+ var resourceDirectory = url.split( /[\\\/]/ );
52
+ resourceDirectory.pop();
53
+ resourceDirectory = resourceDirectory.join( '/' ) + '/';
54
+
55
+ var loader = new THREE.FileLoader( this.manager );
56
+ loader.setResponseType( 'arraybuffer' );
57
+ loader.load( url, function ( buffer ) {
58
+
59
+ try {
60
+
61
+ var scene = self.parse( buffer, resourceDirectory );
62
+
63
+ onLoad( scene );
64
+
65
+ } catch ( error ) {
66
+
67
+ setTimeout( function () {
68
+
69
+ if ( onError ) onError( error );
70
+
71
+ self.manager.itemError( url );
72
+
73
+ }, 0 );
74
+
75
+ }
76
+
77
+ }, onProgress, onError );
78
+
79
+ },
80
+
81
+ /**
82
+ * Parses an ASCII/Binary FBX file and returns a THREE.Group.
83
+ * THREE.Group will have an animations property of AnimationClips
84
+ * of the different animations within the FBX file.
85
+ * @param {ArrayBuffer} FBXBuffer - Contents of FBX file to parse.
86
+ * @param {string} resourceDirectory - Directory to load external assets (e.g. textures ) from.
87
+ * @returns {THREE.Group}
88
+ */
89
+ parse: function ( FBXBuffer, resourceDirectory ) {
90
+
91
+ var FBXTree;
92
+
93
+ if ( isFbxFormatBinary( FBXBuffer ) ) {
94
+
95
+ FBXTree = new BinaryParser().parse( FBXBuffer );
96
+
97
+ } else {
98
+
99
+ var FBXText = convertArrayBufferToString( FBXBuffer );
100
+
101
+ if ( ! isFbxFormatASCII( FBXText ) ) {
102
+
103
+ self.manager.itemError( url );
104
+ throw new Error( 'FBXLoader: Unknown format.' );
105
+
106
+ }
107
+
108
+ if ( getFbxVersion( FBXText ) < 7000 ) {
109
+
110
+ self.manager.itemError( url );
111
+ throw new Error( 'FBXLoader: FBX version not supported for file at ' + url + ', FileVersion: ' + getFbxVersion( FBXText ) );
112
+
113
+ }
114
+
115
+ FBXTree = new TextParser().parse( FBXText );
116
+
117
+ }
118
+
119
+ // console.log( FBXTree );
120
+
121
+ var connections = parseConnections( FBXTree );
122
+ var images = parseImages( FBXTree );
123
+ var textures = parseTextures( FBXTree, new THREE.TextureLoader( this.manager ).setPath( resourceDirectory ), images, connections );
124
+ var materials = parseMaterials( FBXTree, textures, connections );
125
+ var deformers = parseDeformers( FBXTree, connections );
126
+ var geometryMap = parseGeometries( FBXTree, connections, deformers );
127
+ var sceneGraph = parseScene( FBXTree, connections, deformers, geometryMap, materials );
128
+
129
+ return sceneGraph;
130
+
131
+ }
132
+
133
+ } );
134
+
135
+ /**
136
+ * Parses map of relationships between objects.
137
+ * @param {{Connections: { properties: { connections: [number, number, string][]}}}} FBXTree
138
+ * @returns {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>}
139
+ */
140
+ function parseConnections( FBXTree ) {
141
+
142
+ /**
143
+ * @type {Map<number, { parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>}
144
+ */
145
+ var connectionMap = new Map();
146
+
147
+ if ( 'Connections' in FBXTree ) {
148
+
149
+ /**
150
+ * @type {[number, number, string][]}
151
+ */
152
+ var connectionArray = FBXTree.Connections.properties.connections;
153
+ for ( var connectionArrayIndex = 0, connectionArrayLength = connectionArray.length; connectionArrayIndex < connectionArrayLength; ++ connectionArrayIndex ) {
154
+
155
+ var connection = connectionArray[ connectionArrayIndex ];
156
+
157
+ if ( ! connectionMap.has( connection[ 0 ] ) ) {
158
+
159
+ connectionMap.set( connection[ 0 ], {
160
+ parents: [],
161
+ children: []
162
+ } );
163
+
164
+ }
165
+
166
+ var parentRelationship = { ID: connection[ 1 ], relationship: connection[ 2 ] };
167
+ connectionMap.get( connection[ 0 ] ).parents.push( parentRelationship );
168
+
169
+ if ( ! connectionMap.has( connection[ 1 ] ) ) {
170
+
171
+ connectionMap.set( connection[ 1 ], {
172
+ parents: [],
173
+ children: []
174
+ } );
175
+
176
+ }
177
+
178
+ var childRelationship = { ID: connection[ 0 ], relationship: connection[ 2 ] };
179
+ connectionMap.get( connection[ 1 ] ).children.push( childRelationship );
180
+
181
+ }
182
+
183
+ }
184
+
185
+ return connectionMap;
186
+
187
+ }
188
+
189
+ /**
190
+ * Parses map of images referenced in FBXTree.
191
+ * @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
192
+ * @returns {Map<number, string(image blob URL)>}
193
+ */
194
+ function parseImages( FBXTree ) {
195
+
196
+ /**
197
+ * @type {Map<number, string(image blob URL)>}
198
+ */
199
+ var imageMap = new Map();
200
+
201
+ if ( 'Video' in FBXTree.Objects.subNodes ) {
202
+
203
+ var videoNodes = FBXTree.Objects.subNodes.Video;
204
+
205
+ for ( var nodeID in videoNodes ) {
206
+
207
+ var videoNode = videoNodes[ nodeID ];
208
+
209
+ // raw image data is in videoNode.properties.Content
210
+ if ( 'Content' in videoNode.properties ) {
211
+
212
+ var image = parseImage( videoNodes[ nodeID ] );
213
+ imageMap.set( parseInt( nodeID ), image );
214
+
215
+ }
216
+
217
+ }
218
+
219
+ }
220
+
221
+ return imageMap;
222
+
223
+ }
224
+
225
+ /**
226
+ * @param {videoNode} videoNode - Node to get texture image information from.
227
+ * @returns {string} - image blob URL
228
+ */
229
+ function parseImage( videoNode ) {
230
+
231
+ var buffer = videoNode.properties.Content;
232
+ var array = new Uint8Array( buffer );
233
+ var fileName = videoNode.properties.RelativeFilename || videoNode.properties.Filename;
234
+ var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase();
235
+
236
+ var type;
237
+
238
+ switch ( extension ) {
239
+
240
+ case 'bmp':
241
+
242
+ type = 'image/bmp';
243
+ break;
244
+
245
+ case 'jpg':
246
+
247
+ type = 'image/jpeg';
248
+ break;
249
+
250
+ case 'png':
251
+
252
+ type = 'image/png';
253
+ break;
254
+
255
+ case 'tif':
256
+
257
+ type = 'image/tiff';
258
+ break;
259
+
260
+ default:
261
+
262
+ console.warn( 'FBXLoader: No support image type ' + extension );
263
+ return;
264
+
265
+ }
266
+
267
+ return URL.createObjectURL( new Blob( [ array ], { type: type } ) );
268
+
269
+ }
270
+
271
+ /**
272
+ * Parses map of textures referenced in FBXTree.
273
+ * @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
274
+ * @param {THREE.TextureLoader} loader
275
+ * @param {Map<number, string(image blob URL)>} imageMap
276
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
277
+ * @returns {Map<number, THREE.Texture>}
278
+ */
279
+ function parseTextures( FBXTree, loader, imageMap, connections ) {
280
+
281
+ /**
282
+ * @type {Map<number, THREE.Texture>}
283
+ */
284
+ var textureMap = new Map();
285
+
286
+ if ( 'Texture' in FBXTree.Objects.subNodes ) {
287
+
288
+ var textureNodes = FBXTree.Objects.subNodes.Texture;
289
+ for ( var nodeID in textureNodes ) {
290
+
291
+ var texture = parseTexture( textureNodes[ nodeID ], loader, imageMap, connections );
292
+ textureMap.set( parseInt( nodeID ), texture );
293
+
294
+ }
295
+
296
+ }
297
+
298
+ return textureMap;
299
+
300
+ }
301
+
302
+ /**
303
+ * @param {textureNode} textureNode - Node to get texture information from.
304
+ * @param {THREE.TextureLoader} loader
305
+ * @param {Map<number, string(image blob URL)>} imageMap
306
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
307
+ * @returns {THREE.Texture}
308
+ */
309
+ function parseTexture( textureNode, loader, imageMap, connections ) {
310
+
311
+ var FBX_ID = textureNode.id;
312
+
313
+ var name = textureNode.name;
314
+
315
+ var fileName;
316
+
317
+ var filePath = textureNode.properties.FileName;
318
+ var relativeFilePath = textureNode.properties.RelativeFilename;
319
+
320
+ var children = connections.get( FBX_ID ).children;
321
+
322
+ if ( children !== undefined && children.length > 0 && imageMap.has( children[ 0 ].ID ) ) {
323
+
324
+ fileName = imageMap.get( children[ 0 ].ID );
325
+
326
+ } else if ( relativeFilePath !== undefined && relativeFilePath[ 0 ] !== '/' &&
327
+ relativeFilePath.match( /^[a-zA-Z]:/ ) === null ) {
328
+
329
+ // use textureNode.properties.RelativeFilename
330
+ // if it exists and it doesn't seem an absolute path
331
+
332
+ fileName = relativeFilePath;
333
+
334
+ } else {
335
+
336
+ var split = filePath.split( /[\\\/]/ );
337
+
338
+ if ( split.length > 0 ) {
339
+
340
+ fileName = split[ split.length - 1 ];
341
+
342
+ } else {
343
+
344
+ fileName = filePath;
345
+
346
+ }
347
+
348
+ }
349
+
350
+ var currentPath = loader.path;
351
+ if (!fileName) {
352
+ console.warn('FBXLoader: texture had no fileName', textureNode);
353
+ return;
354
+ }
355
+
356
+ if ( fileName.indexOf( 'blob:' ) === 0 ) {
357
+
358
+ loader.setPath( undefined );
359
+
360
+ }
361
+
362
+ /**
363
+ * @type {THREE.Texture}
364
+ */
365
+ var texture = loader.load( fileName );
366
+ texture.name = name;
367
+ texture.FBX_ID = FBX_ID;
368
+
369
+ var wrapModeU = textureNode.properties.WrapModeU;
370
+ var wrapModeV = textureNode.properties.WrapModeV;
371
+
372
+ var valueU = wrapModeU !== undefined ? wrapModeU.value : 0;
373
+ var valueV = wrapModeV !== undefined ? wrapModeV.value : 0;
374
+
375
+ // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a
376
+ // 0: repeat(default), 1: clamp
377
+
378
+ texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
379
+ texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
380
+
381
+ loader.setPath( currentPath );
382
+
383
+ return texture;
384
+
385
+ }
386
+
387
+ /**
388
+ * Parses map of Material information.
389
+ * @param {{Objects: {subNodes: {Material: Object.<number, FBXMaterialNode>}}}} FBXTree
390
+ * @param {Map<number, THREE.Texture>} textureMap
391
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
392
+ * @returns {Map<number, THREE.Material>}
393
+ */
394
+ function parseMaterials( FBXTree, textureMap, connections ) {
395
+
396
+ var materialMap = new Map();
397
+
398
+ if ( 'Material' in FBXTree.Objects.subNodes ) {
399
+
400
+ var materialNodes = FBXTree.Objects.subNodes.Material;
401
+ for ( var nodeID in materialNodes ) {
402
+
403
+ var material = parseMaterial( materialNodes[ nodeID ], textureMap, connections );
404
+ materialMap.set( parseInt( nodeID ), material );
405
+
406
+ }
407
+
408
+ }
409
+
410
+ return materialMap;
411
+
412
+ }
413
+
414
+ /**
415
+ * Takes information from Material node and returns a generated THREE.Material
416
+ * @param {FBXMaterialNode} materialNode
417
+ * @param {Map<number, THREE.Texture>} textureMap
418
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
419
+ * @returns {THREE.Material}
420
+ */
421
+ function parseMaterial( materialNode, textureMap, connections ) {
422
+
423
+ var FBX_ID = materialNode.id;
424
+ var name = materialNode.attrName;
425
+ var type = materialNode.properties.ShadingModel;
426
+
427
+ //Case where FBXs wrap shading model in property object.
428
+ if ( typeof type === 'object' ) {
429
+
430
+ type = type.value;
431
+
432
+ }
433
+
434
+ var connection = connections.get( FBX_ID );
435
+
436
+ if (!connection) {
437
+ return new THREE.MeshBasicMaterial({color: 0xff0000});
438
+ }
439
+
440
+ var children = connections.get( FBX_ID ).children;
441
+
442
+ var parameters = parseParameters( materialNode.properties, textureMap, children );
443
+
444
+ var material;
445
+
446
+ switch ( type.toLowerCase() ) {
447
+
448
+ case 'phong':
449
+ material = new THREE.MeshPhongMaterial();
450
+ break;
451
+ case 'lambert':
452
+ material = new THREE.MeshLambertMaterial();
453
+ break;
454
+ default:
455
+ console.warn( 'No implementation given for material type ' + type + ' in FBXLoader.js. Defaulting to basic material' );
456
+ material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
457
+ break;
458
+
459
+ }
460
+
461
+ material.setValues( parameters );
462
+ material.name = name;
463
+
464
+ return material;
465
+
466
+ }
467
+
468
+ /**
469
+ * @typedef {{Diffuse: FBXVector3, Specular: FBXVector3, Shininess: FBXValue, Emissive: FBXVector3, EmissiveFactor: FBXValue, Opacity: FBXValue}} FBXMaterialProperties
470
+ */
471
+ /**
472
+ * @typedef {{color: THREE.Color=, specular: THREE.Color=, shininess: number=, emissive: THREE.Color=, emissiveIntensity: number=, opacity: number=, transparent: boolean=, map: THREE.Texture=}} THREEMaterialParameterPack
473
+ */
474
+ /**
475
+ * @param {FBXMaterialProperties} properties
476
+ * @param {Map<number, THREE.Texture>} textureMap
477
+ * @param {{ID: number, relationship: string}[]} childrenRelationships
478
+ * @returns {THREEMaterialParameterPack}
479
+ */
480
+ function parseParameters( properties, textureMap, childrenRelationships ) {
481
+
482
+ var parameters = {};
483
+
484
+ if ( properties.Diffuse ) {
485
+
486
+ parameters.color = parseColor( properties.Diffuse );
487
+
488
+ }
489
+ if ( properties.Specular ) {
490
+
491
+ parameters.specular = parseColor( properties.Specular );
492
+
493
+ }
494
+ if ( properties.Shininess ) {
495
+
496
+ parameters.shininess = properties.Shininess.value;
497
+
498
+ }
499
+ if ( properties.Emissive ) {
500
+
501
+ parameters.emissive = parseColor( properties.Emissive );
502
+
503
+ }
504
+ if ( properties.EmissiveFactor ) {
505
+
506
+ parameters.emissiveIntensity = properties.EmissiveFactor.value;
507
+
508
+ }
509
+ if ( properties.Opacity ) {
510
+
511
+ parameters.opacity = properties.Opacity.value;
512
+
513
+ }
514
+ if ( parameters.opacity < 1.0 ) {
515
+
516
+ parameters.transparent = true;
517
+
518
+ }
519
+
520
+ for ( var childrenRelationshipsIndex = 0, childrenRelationshipsLength = childrenRelationships.length; childrenRelationshipsIndex < childrenRelationshipsLength; ++ childrenRelationshipsIndex ) {
521
+
522
+ var relationship = childrenRelationships[ childrenRelationshipsIndex ];
523
+
524
+ var type = relationship.relationship;
525
+
526
+ switch ( type ) {
527
+
528
+ case "DiffuseColor":
529
+ case " \"DiffuseColor":
530
+ parameters.map = textureMap.get( relationship.ID );
531
+ break;
532
+
533
+ case "Bump":
534
+ case " \"Bump":
535
+ parameters.bumpMap = textureMap.get( relationship.ID );
536
+ break;
537
+
538
+ case "NormalMap":
539
+ case " \"NormalMap":
540
+ parameters.normalMap = textureMap.get( relationship.ID );
541
+ break;
542
+
543
+ case " \"AmbientColor":
544
+ case " \"EmissiveColor":
545
+ case "AmbientColor":
546
+ case "EmissiveColor":
547
+ default:
548
+ console.warn( 'Unknown texture application of type ' + type + ', skipping texture' );
549
+ break;
550
+
551
+ }
552
+
553
+ }
554
+
555
+ return parameters;
556
+
557
+ }
558
+
559
+ /**
560
+ * Generates map of Skeleton-like objects for use later when generating and binding skeletons.
561
+ * @param {{Objects: {subNodes: {Deformer: Object.<number, FBXSubDeformerNode>}}}} FBXTree
562
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
563
+ * @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}>}
564
+ */
565
+ function parseDeformers( FBXTree, connections ) {
566
+
567
+ var deformers = {};
568
+
569
+ if ( 'Deformer' in FBXTree.Objects.subNodes ) {
570
+
571
+ var DeformerNodes = FBXTree.Objects.subNodes.Deformer;
572
+
573
+ for ( var nodeID in DeformerNodes ) {
574
+
575
+ var deformerNode = DeformerNodes[ nodeID ];
576
+
577
+ if ( deformerNode.attrType === 'Skin' ) {
578
+
579
+ var conns = connections.get( parseInt( nodeID ) );
580
+ var skeleton = parseSkeleton( conns, DeformerNodes );
581
+ skeleton.FBX_ID = parseInt( nodeID );
582
+
583
+ deformers[ nodeID ] = skeleton;
584
+
585
+ }
586
+
587
+ }
588
+
589
+ }
590
+
591
+ return deformers;
592
+
593
+ }
594
+
595
+ /**
596
+ * Generates a "Skeleton Representation" of FBX nodes based on an FBX Skin Deformer's connections and an object containing SubDeformer nodes.
597
+ * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} connections
598
+ * @param {Object.<number, FBXSubDeformerNode>} DeformerNodes
599
+ * @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}}
600
+ */
601
+ function parseSkeleton( connections, DeformerNodes ) {
602
+
603
+ var subDeformers = {};
604
+ var children = connections.children;
605
+
606
+ for ( var i = 0, l = children.length; i < l; ++ i ) {
607
+
608
+ var child = children[ i ];
609
+
610
+ var subDeformerNode = DeformerNodes[ child.ID ];
611
+
612
+ var subDeformer = {
613
+ FBX_ID: child.ID,
614
+ index: i,
615
+ indices: [],
616
+ weights: [],
617
+ transform: parseMatrixArray( subDeformerNode.subNodes.Transform.properties.a ),
618
+ transformLink: parseMatrixArray( subDeformerNode.subNodes.TransformLink.properties.a ),
619
+ linkMode: subDeformerNode.properties.Mode
620
+ };
621
+
622
+ if ( 'Indexes' in subDeformerNode.subNodes ) {
623
+
624
+ subDeformer.indices = parseIntArray( subDeformerNode.subNodes.Indexes.properties.a );
625
+ subDeformer.weights = parseFloatArray( subDeformerNode.subNodes.Weights.properties.a );
626
+
627
+ }
628
+
629
+ subDeformers[ child.ID ] = subDeformer;
630
+
631
+ }
632
+
633
+ return {
634
+ map: subDeformers,
635
+ bones: []
636
+ };
637
+
638
+ }
639
+
640
+ /**
641
+ * Generates Buffer geometries from geometry information in FBXTree, and generates map of THREE.BufferGeometries
642
+ * @param {{Objects: {subNodes: {Geometry: Object.<number, FBXGeometryNode}}}} FBXTree
643
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
644
+ * @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}>} deformers
645
+ * @returns {Map<number, THREE.BufferGeometry>}
646
+ */
647
+ function parseGeometries( FBXTree, connections, deformers ) {
648
+
649
+ var geometryMap = new Map();
650
+
651
+ if ( 'Geometry' in FBXTree.Objects.subNodes ) {
652
+
653
+ var geometryNodes = FBXTree.Objects.subNodes.Geometry;
654
+
655
+ for ( var nodeID in geometryNodes ) {
656
+
657
+ var relationships = connections.get( parseInt( nodeID ) );
658
+ var geo = parseGeometry( geometryNodes[ nodeID ], relationships, deformers );
659
+ geometryMap.set( parseInt( nodeID ), geo );
660
+
661
+ }
662
+
663
+ }
664
+
665
+ return geometryMap;
666
+
667
+ }
668
+
669
+ /**
670
+ * Generates BufferGeometry from FBXGeometryNode.
671
+ * @param {FBXGeometryNode} geometryNode
672
+ * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships
673
+ * @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}[]}>} deformers
674
+ * @returns {THREE.BufferGeometry}
675
+ */
676
+ function parseGeometry( geometryNode, relationships, deformers ) {
677
+
678
+ switch ( geometryNode.attrType ) {
679
+
680
+ case 'Mesh':
681
+ return parseMeshGeometry( geometryNode, relationships, deformers );
682
+ break;
683
+
684
+ case 'NurbsCurve':
685
+ return parseNurbsGeometry( geometryNode );
686
+ break;
687
+
688
+ }
689
+
690
+ }
691
+
692
+ /**
693
+ * Specialty function for parsing Mesh based Geometry Nodes.
694
+ * @param {FBXGeometryNode} geometryNode
695
+ * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships - Object representing relationships between specific geometry node and other nodes.
696
+ * @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}[]}>} deformers - Map object of deformers and subDeformers by ID.
697
+ * @returns {THREE.BufferGeometry}
698
+ */
699
+ function parseMeshGeometry( geometryNode, relationships, deformers ) {
700
+
701
+ for ( var i = 0; i < relationships.children.length; ++ i ) {
702
+
703
+ var deformer = deformers[ relationships.children[ i ].ID ];
704
+ if ( deformer !== undefined ) break;
705
+
706
+ }
707
+
708
+ return genGeometry( geometryNode, deformer );
709
+
710
+ }
711
+
712
+ /**
713
+ * @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 - Skeleton representation for geometry instance.
714
+ * @returns {THREE.BufferGeometry}
715
+ */
716
+ function genGeometry( geometryNode, deformer ) {
717
+
718
+ var geometry = new Geometry();
719
+
720
+ var subNodes = geometryNode.subNodes;
721
+
722
+ // First, each index is going to be its own vertex.
723
+
724
+ var vertexBuffer = parseFloatArray( subNodes.Vertices.properties.a );
725
+ var indexBuffer = parseIntArray( subNodes.PolygonVertexIndex.properties.a );
726
+
727
+ if ( subNodes.LayerElementNormal ) {
728
+
729
+ var normalInfo = getNormals( subNodes.LayerElementNormal[ 0 ] );
730
+
731
+ }
732
+
733
+ if ( subNodes.LayerElementUV ) {
734
+
735
+ var uvInfo = getUVs( subNodes.LayerElementUV[ 0 ] );
736
+
737
+ }
738
+
739
+ if ( subNodes.LayerElementColor ) {
740
+
741
+ var colorInfo = getColors( subNodes.LayerElementColor[ 0 ] );
742
+
743
+ }
744
+
745
+ if ( subNodes.LayerElementMaterial ) {
746
+
747
+ var materialInfo = getMaterials( subNodes.LayerElementMaterial[ 0 ] );
748
+
749
+ }
750
+
751
+ var weightTable = {};
752
+
753
+ if ( deformer ) {
754
+
755
+ var subDeformers = deformer.map;
756
+
757
+ for ( var key in subDeformers ) {
758
+
759
+ var subDeformer = subDeformers[ key ];
760
+ var indices = subDeformer.indices;
761
+
762
+ for ( var j = 0; j < indices.length; j ++ ) {
763
+
764
+ var index = indices[ j ];
765
+ var weight = subDeformer.weights[ j ];
766
+
767
+ if ( weightTable[ index ] === undefined ) weightTable[ index ] = [];
768
+
769
+ weightTable[ index ].push( {
770
+ id: subDeformer.index,
771
+ weight: weight
772
+ } );
773
+
774
+ }
775
+
776
+ }
777
+
778
+ }
779
+
780
+ var faceVertexBuffer = [];
781
+ var polygonIndex = 0;
782
+ var displayedWeightsWarning = false;
783
+
784
+ for ( var polygonVertexIndex = 0; polygonVertexIndex < indexBuffer.length; polygonVertexIndex ++ ) {
785
+
786
+ var vertexIndex = indexBuffer[ polygonVertexIndex ];
787
+
788
+ var endOfFace = false;
789
+
790
+ if ( vertexIndex < 0 ) {
791
+
792
+ vertexIndex = vertexIndex ^ - 1;
793
+ indexBuffer[ polygonVertexIndex ] = vertexIndex;
794
+ endOfFace = true;
795
+
796
+ }
797
+
798
+ var vertex = new Vertex();
799
+ var weightIndices = [];
800
+ var weights = [];
801
+
802
+ vertex.position.fromArray( vertexBuffer, vertexIndex * 3 );
803
+
804
+ if ( deformer ) {
805
+
806
+ if ( weightTable[ vertexIndex ] !== undefined ) {
807
+
808
+ var array = weightTable[ vertexIndex ];
809
+
810
+ for ( var j = 0, jl = array.length; j < jl; j ++ ) {
811
+
812
+ weights.push( array[ j ].weight );
813
+ weightIndices.push( array[ j ].id );
814
+
815
+ }
816
+
817
+ }
818
+
819
+ if ( weights.length > 4 ) {
820
+
821
+ if ( ! displayedWeightsWarning ) {
822
+
823
+ console.warn( 'FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' );
824
+ displayedWeightsWarning = true;
825
+
826
+ }
827
+
828
+ var WIndex = [ 0, 0, 0, 0 ];
829
+ var Weight = [ 0, 0, 0, 0 ];
830
+
831
+ weights.forEach( function ( weight, weightIndex ) {
832
+
833
+ var currentWeight = weight;
834
+ var currentIndex = weightIndices[ weightIndex ];
835
+
836
+ Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) {
837
+
838
+ if ( currentWeight > comparedWeight ) {
839
+
840
+ comparedWeightArray[ comparedWeightIndex ] = currentWeight;
841
+ currentWeight = comparedWeight;
842
+
843
+ var tmp = WIndex[ comparedWeightIndex ];
844
+ WIndex[ comparedWeightIndex ] = currentIndex;
845
+ currentIndex = tmp;
846
+
847
+ }
848
+
849
+ } );
850
+
851
+ } );
852
+
853
+ weightIndices = WIndex;
854
+ weights = Weight;
855
+
856
+ }
857
+
858
+ for ( var i = weights.length; i < 4; ++ i ) {
859
+
860
+ weights[ i ] = 0;
861
+ weightIndices[ i ] = 0;
862
+
863
+ }
864
+
865
+ vertex.skinWeights.fromArray( weights );
866
+ vertex.skinIndices.fromArray( weightIndices );
867
+
868
+ }
869
+
870
+ if ( normalInfo ) {
871
+
872
+ vertex.normal.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, normalInfo ) );
873
+
874
+ }
875
+
876
+ if ( uvInfo ) {
877
+
878
+ vertex.uv.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, uvInfo ) );
879
+
880
+ }
881
+
882
+ if ( colorInfo ) {
883
+
884
+ vertex.color.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, colorInfo ) );
885
+
886
+ }
887
+
888
+ faceVertexBuffer.push( vertex );
889
+
890
+ if ( endOfFace ) {
891
+
892
+ var face = new Face();
893
+ face.genTrianglesFromVertices( faceVertexBuffer );
894
+
895
+ if ( materialInfo !== undefined ) {
896
+
897
+ var materials = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo );
898
+ face.materialIndex = materials[ 0 ];
899
+
900
+ } else {
901
+
902
+ // Seems like some models don't have materialInfo(subNodes.LayerElementMaterial).
903
+ // Set 0 in such a case.
904
+ face.materialIndex = 0;
905
+
906
+ }
907
+
908
+ geometry.faces.push( face );
909
+ faceVertexBuffer = [];
910
+ polygonIndex ++;
911
+
912
+ endOfFace = false;
913
+
914
+ }
915
+
916
+ }
917
+
918
+ /**
919
+ * @type {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}}
920
+ */
921
+ var bufferInfo = geometry.flattenToBuffers();
922
+
923
+ var geo = new THREE.BufferGeometry();
924
+ geo.name = geometryNode.name;
925
+ geo.addAttribute( 'position', new THREE.Float32BufferAttribute( bufferInfo.vertexBuffer, 3 ) );
926
+
927
+ if ( bufferInfo.normalBuffer.length > 0 ) {
928
+
929
+ geo.addAttribute( 'normal', new THREE.Float32BufferAttribute( bufferInfo.normalBuffer, 3 ) );
930
+
931
+ }
932
+ if ( bufferInfo.uvBuffer.length > 0 ) {
933
+
934
+ geo.addAttribute( 'uv', new THREE.Float32BufferAttribute( bufferInfo.uvBuffer, 2 ) );
935
+
936
+ }
937
+ if ( subNodes.LayerElementColor ) {
938
+
939
+ geo.addAttribute( 'color', new THREE.Float32BufferAttribute( bufferInfo.colorBuffer, 3 ) );
940
+
941
+ }
942
+
943
+ if ( deformer ) {
944
+
945
+ geo.addAttribute( 'skinIndex', new THREE.Float32BufferAttribute( bufferInfo.skinIndexBuffer, 4 ) );
946
+
947
+ geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( bufferInfo.skinWeightBuffer, 4 ) );
948
+
949
+ geo.FBX_Deformer = deformer;
950
+
951
+ }
952
+
953
+ // Convert the material indices of each vertex into rendering groups on the geometry.
954
+
955
+ var materialIndexBuffer = bufferInfo.materialIndexBuffer;
956
+ var prevMaterialIndex = materialIndexBuffer[ 0 ];
957
+ var startIndex = 0;
958
+
959
+ for ( var i = 0; i < materialIndexBuffer.length; ++ i ) {
960
+
961
+ if ( materialIndexBuffer[ i ] !== prevMaterialIndex ) {
962
+
963
+ geo.addGroup( startIndex, i - startIndex, prevMaterialIndex );
964
+
965
+ prevMaterialIndex = materialIndexBuffer[ i ];
966
+ startIndex = i;
967
+
968
+ }
969
+
970
+ }
971
+
972
+ return geo;
973
+
974
+ }
975
+
976
+ /**
977
+ * Parses normal information for geometry.
978
+ * @param {FBXGeometryNode} geometryNode
979
+ * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
980
+ */
981
+ function getNormals( NormalNode ) {
982
+
983
+ var mappingType = NormalNode.properties.MappingInformationType;
984
+ var referenceType = NormalNode.properties.ReferenceInformationType;
985
+ var buffer = parseFloatArray( NormalNode.subNodes.Normals.properties.a );
986
+ var indexBuffer = [];
987
+ if ( referenceType === 'IndexToDirect' ) {
988
+
989
+ if ( 'NormalIndex' in NormalNode.subNodes ) {
990
+
991
+ indexBuffer = parseIntArray( NormalNode.subNodes.NormalIndex.properties.a );
992
+
993
+ } else if ( 'NormalsIndex' in NormalNode.subNodes ) {
994
+
995
+ indexBuffer = parseIntArray( NormalNode.subNodes.NormalsIndex.properties.a );
996
+
997
+ }
998
+
999
+ }
1000
+
1001
+ return {
1002
+ dataSize: 3,
1003
+ buffer: buffer,
1004
+ indices: indexBuffer,
1005
+ mappingType: mappingType,
1006
+ referenceType: referenceType
1007
+ };
1008
+
1009
+ }
1010
+
1011
+ /**
1012
+ * Parses UV information for geometry.
1013
+ * @param {FBXGeometryNode} geometryNode
1014
+ * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
1015
+ */
1016
+ function getUVs( UVNode ) {
1017
+
1018
+ var mappingType = UVNode.properties.MappingInformationType;
1019
+ var referenceType = UVNode.properties.ReferenceInformationType;
1020
+ var buffer = parseFloatArray( UVNode.subNodes.UV.properties.a );
1021
+ var indexBuffer = [];
1022
+ if ( referenceType === 'IndexToDirect' ) {
1023
+
1024
+ indexBuffer = parseIntArray( UVNode.subNodes.UVIndex.properties.a );
1025
+
1026
+ }
1027
+
1028
+ return {
1029
+ dataSize: 2,
1030
+ buffer: buffer,
1031
+ indices: indexBuffer,
1032
+ mappingType: mappingType,
1033
+ referenceType: referenceType
1034
+ };
1035
+
1036
+ }
1037
+
1038
+ /**
1039
+ * Parses Vertex Color information for geometry.
1040
+ * @param {FBXGeometryNode} geometryNode
1041
+ * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
1042
+ */
1043
+ function getColors( ColorNode ) {
1044
+
1045
+ var mappingType = ColorNode.properties.MappingInformationType;
1046
+ var referenceType = ColorNode.properties.ReferenceInformationType;
1047
+ var buffer = parseFloatArray( ColorNode.subNodes.Colors.properties.a );
1048
+ var indexBuffer = [];
1049
+ if ( referenceType === 'IndexToDirect' ) {
1050
+
1051
+ indexBuffer = parseFloatArray( ColorNode.subNodes.ColorIndex.properties.a );
1052
+
1053
+ }
1054
+
1055
+ return {
1056
+ dataSize: 4,
1057
+ buffer: buffer,
1058
+ indices: indexBuffer,
1059
+ mappingType: mappingType,
1060
+ referenceType: referenceType
1061
+ };
1062
+
1063
+ }
1064
+
1065
+ /**
1066
+ * Parses material application information for geometry.
1067
+ * @param {FBXGeometryNode}
1068
+ * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
1069
+ */
1070
+ function getMaterials( MaterialNode ) {
1071
+
1072
+ var mappingType = MaterialNode.properties.MappingInformationType;
1073
+ var referenceType = MaterialNode.properties.ReferenceInformationType;
1074
+
1075
+ if ( mappingType === 'NoMappingInformation' ) {
1076
+
1077
+ return {
1078
+ dataSize: 1,
1079
+ buffer: [ 0 ],
1080
+ indices: [ 0 ],
1081
+ mappingType: 'AllSame',
1082
+ referenceType: referenceType
1083
+ };
1084
+
1085
+ }
1086
+
1087
+ var materialIndexBuffer = parseIntArray( MaterialNode.subNodes.Materials.properties.a );
1088
+
1089
+ // Since materials are stored as indices, there's a bit of a mismatch between FBX and what
1090
+ // we expect. So we create an intermediate buffer that points to the index in the buffer,
1091
+ // for conforming with the other functions we've written for other data.
1092
+ var materialIndices = [];
1093
+
1094
+ for ( var materialIndexBufferIndex = 0, materialIndexBufferLength = materialIndexBuffer.length; materialIndexBufferIndex < materialIndexBufferLength; ++ materialIndexBufferIndex ) {
1095
+
1096
+ materialIndices.push( materialIndexBufferIndex );
1097
+
1098
+ }
1099
+
1100
+ return {
1101
+ dataSize: 1,
1102
+ buffer: materialIndexBuffer,
1103
+ indices: materialIndices,
1104
+ mappingType: mappingType,
1105
+ referenceType: referenceType
1106
+ };
1107
+
1108
+ }
1109
+
1110
+ /**
1111
+ * Function uses the infoObject and given indices to return value array of object.
1112
+ * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
1113
+ * @param {number} polygonIndex - Index of polygon in geometry.
1114
+ * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
1115
+ * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
1116
+ * @returns {number[]}
1117
+ */
1118
+
1119
+ var dataArray = [];
1120
+
1121
+ var GetData = {
1122
+
1123
+ ByPolygonVertex: {
1124
+
1125
+ /**
1126
+ * Function uses the infoObject and given indices to return value array of object.
1127
+ * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
1128
+ * @param {number} polygonIndex - Index of polygon in geometry.
1129
+ * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
1130
+ * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
1131
+ * @returns {number[]}
1132
+ */
1133
+ Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
1134
+
1135
+ var from = ( polygonVertexIndex * infoObject.dataSize );
1136
+ var to = ( polygonVertexIndex * infoObject.dataSize ) + infoObject.dataSize;
1137
+
1138
+ // return infoObject.buffer.slice( from, to );
1139
+ return slice( dataArray, infoObject.buffer, from, to );
1140
+
1141
+ },
1142
+
1143
+ /**
1144
+ * Function uses the infoObject and given indices to return value array of object.
1145
+ * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
1146
+ * @param {number} polygonIndex - Index of polygon in geometry.
1147
+ * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
1148
+ * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
1149
+ * @returns {number[]}
1150
+ */
1151
+ IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
1152
+
1153
+ var index = infoObject.indices[ polygonVertexIndex ];
1154
+ var from = ( index * infoObject.dataSize );
1155
+ var to = ( index * infoObject.dataSize ) + infoObject.dataSize;
1156
+
1157
+ // return infoObject.buffer.slice( from, to );
1158
+ return slice( dataArray, infoObject.buffer, from, to );
1159
+
1160
+ }
1161
+
1162
+ },
1163
+
1164
+ ByPolygon: {
1165
+
1166
+ /**
1167
+ * Function uses the infoObject and given indices to return value array of object.
1168
+ * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
1169
+ * @param {number} polygonIndex - Index of polygon in geometry.
1170
+ * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
1171
+ * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
1172
+ * @returns {number[]}
1173
+ */
1174
+ Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
1175
+
1176
+ var from = polygonIndex * infoObject.dataSize;
1177
+ var to = polygonIndex * infoObject.dataSize + infoObject.dataSize;
1178
+
1179
+ // return infoObject.buffer.slice( from, to );
1180
+ return slice( dataArray, infoObject.buffer, from, to );
1181
+
1182
+ },
1183
+
1184
+ /**
1185
+ * Function uses the infoObject and given indices to return value array of object.
1186
+ * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
1187
+ * @param {number} polygonIndex - Index of polygon in geometry.
1188
+ * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
1189
+ * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
1190
+ * @returns {number[]}
1191
+ */
1192
+ IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
1193
+
1194
+ var index = infoObject.indices[ polygonIndex ];
1195
+ var from = index * infoObject.dataSize;
1196
+ var to = index * infoObject.dataSize + infoObject.dataSize;
1197
+
1198
+ // return infoObject.buffer.slice( from, to );
1199
+ return slice( dataArray, infoObject.buffer, from, to );
1200
+
1201
+ }
1202
+
1203
+ },
1204
+
1205
+ ByVertice: {
1206
+
1207
+ Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
1208
+
1209
+ var from = ( vertexIndex * infoObject.dataSize );
1210
+ var to = ( vertexIndex * infoObject.dataSize ) + infoObject.dataSize;
1211
+
1212
+ // return infoObject.buffer.slice( from, to );
1213
+ return slice( dataArray, infoObject.buffer, from, to );
1214
+
1215
+ }
1216
+
1217
+ },
1218
+
1219
+ AllSame: {
1220
+
1221
+ /**
1222
+ * Function uses the infoObject and given indices to return value array of object.
1223
+ * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
1224
+ * @param {number} polygonIndex - Index of polygon in geometry.
1225
+ * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
1226
+ * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
1227
+ * @returns {number[]}
1228
+ */
1229
+ IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
1230
+
1231
+ var from = infoObject.indices[ 0 ] * infoObject.dataSize;
1232
+ var to = infoObject.indices[ 0 ] * infoObject.dataSize + infoObject.dataSize;
1233
+
1234
+ // return infoObject.buffer.slice( from, to );
1235
+ return slice( dataArray, infoObject.buffer, from, to );
1236
+
1237
+ }
1238
+
1239
+ }
1240
+
1241
+ };
1242
+
1243
+ function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
1244
+
1245
+ return GetData[ infoObject.mappingType ][ infoObject.referenceType ]( polygonVertexIndex, polygonIndex, vertexIndex, infoObject );
1246
+
1247
+ }
1248
+
1249
+ /**
1250
+ * Specialty function for parsing NurbsCurve based Geometry Nodes.
1251
+ * @param {FBXGeometryNode} geometryNode
1252
+ * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships
1253
+ * @returns {THREE.BufferGeometry}
1254
+ */
1255
+ function parseNurbsGeometry( geometryNode ) {
1256
+
1257
+ if ( THREE.NURBSCurve === undefined ) {
1258
+
1259
+ console.error( "THREE.FBXLoader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry." );
1260
+ return new THREE.BufferGeometry();
1261
+
1262
+ }
1263
+
1264
+ var order = parseInt( geometryNode.properties.Order );
1265
+
1266
+ if ( isNaN( order ) ) {
1267
+
1268
+ console.error( "FBXLoader: Invalid Order " + geometryNode.properties.Order + " given for geometry ID: " + geometryNode.id );
1269
+ return new THREE.BufferGeometry();
1270
+
1271
+ }
1272
+
1273
+ var degree = order - 1;
1274
+
1275
+ var knots = parseFloatArray( geometryNode.subNodes.KnotVector.properties.a );
1276
+ var controlPoints = [];
1277
+ var pointsValues = parseFloatArray( geometryNode.subNodes.Points.properties.a );
1278
+
1279
+ for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) {
1280
+
1281
+ controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) );
1282
+
1283
+ }
1284
+
1285
+ var startKnot, endKnot;
1286
+
1287
+ if ( geometryNode.properties.Form === 'Closed' ) {
1288
+
1289
+ controlPoints.push( controlPoints[ 0 ] );
1290
+
1291
+ } else if ( geometryNode.properties.Form === 'Periodic' ) {
1292
+
1293
+ startKnot = degree;
1294
+ endKnot = knots.length - 1 - startKnot;
1295
+
1296
+ for ( var i = 0; i < degree; ++ i ) {
1297
+
1298
+ controlPoints.push( controlPoints[ i ] );
1299
+
1300
+ }
1301
+
1302
+ }
1303
+
1304
+ var curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot );
1305
+ var vertices = curve.getPoints( controlPoints.length * 7 );
1306
+
1307
+ var positions = new Float32Array( vertices.length * 3 );
1308
+
1309
+ for ( var i = 0, l = vertices.length; i < l; ++ i ) {
1310
+
1311
+ vertices[ i ].toArray( positions, i * 3 );
1312
+
1313
+ }
1314
+
1315
+ var geometry = new THREE.BufferGeometry();
1316
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
1317
+
1318
+ return geometry;
1319
+
1320
+ }
1321
+
1322
+ /**
1323
+ * Finally generates Scene graph and Scene graph Objects.
1324
+ * @param {{Objects: {subNodes: {Model: Object.<number, FBXModelNode>}}}} FBXTree
1325
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
1326
+ * @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}>} deformers
1327
+ * @param {Map<number, THREE.BufferGeometry>} geometryMap
1328
+ * @param {Map<number, THREE.Material>} materialMap
1329
+ * @returns {THREE.Group}
1330
+ */
1331
+ function parseScene( FBXTree, connections, deformers, geometryMap, materialMap ) {
1332
+
1333
+ var sceneGraph = new THREE.Group();
1334
+
1335
+ var ModelNode = FBXTree.Objects.subNodes.Model;
1336
+
1337
+ /**
1338
+ * @type {Array.<THREE.Object3D>}
1339
+ */
1340
+ var modelArray = [];
1341
+
1342
+ /**
1343
+ * @type {Map.<number, THREE.Object3D>}
1344
+ */
1345
+ var modelMap = new Map();
1346
+
1347
+ for ( var nodeID in ModelNode ) {
1348
+
1349
+ var id = parseInt( nodeID );
1350
+ var node = ModelNode[ nodeID ];
1351
+ var conns = connections.get( id );
1352
+ var model = null;
1353
+
1354
+ for ( var i = 0; i < conns.parents.length; ++ i ) {
1355
+
1356
+ for ( var FBX_ID in deformers ) {
1357
+
1358
+ var deformer = deformers[ FBX_ID ];
1359
+ var subDeformers = deformer.map;
1360
+ var subDeformer = subDeformers[ conns.parents[ i ].ID ];
1361
+
1362
+ if ( subDeformer ) {
1363
+
1364
+ var model2 = model;
1365
+ model = new THREE.Bone();
1366
+ deformer.bones[ subDeformer.index ] = model;
1367
+
1368
+ // seems like we need this not to make non-connected bone, maybe?
1369
+ // TODO: confirm
1370
+ if ( model2 !== null ) model.add( model2 );
1371
+
1372
+ }
1373
+
1374
+ }
1375
+
1376
+ }
1377
+
1378
+ if ( ! model ) {
1379
+
1380
+ switch ( node.attrType ) {
1381
+
1382
+ case "Mesh":
1383
+ /**
1384
+ * @type {?THREE.BufferGeometry}
1385
+ */
1386
+ var geometry = null;
1387
+
1388
+ /**
1389
+ * @type {THREE.MultiMaterial|THREE.Material}
1390
+ */
1391
+ var material = null;
1392
+
1393
+ /**
1394
+ * @type {Array.<THREE.Material>}
1395
+ */
1396
+ var materials = [];
1397
+
1398
+ for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
1399
+
1400
+ var child = conns.children[ childrenIndex ];
1401
+
1402
+ if ( geometryMap.has( child.ID ) ) {
1403
+
1404
+ geometry = geometryMap.get( child.ID );
1405
+
1406
+ }
1407
+
1408
+ if ( materialMap.has( child.ID ) ) {
1409
+
1410
+ materials.push( materialMap.get( child.ID ) );
1411
+
1412
+ }
1413
+
1414
+ }
1415
+ if ( materials.length > 1 ) {
1416
+
1417
+ material = materials;
1418
+
1419
+ } else if ( materials.length > 0 ) {
1420
+
1421
+ material = materials[ 0 ];
1422
+
1423
+ } else {
1424
+
1425
+ material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
1426
+ materials.push( material );
1427
+
1428
+ }
1429
+ if ( 'color' in geometry.attributes ) {
1430
+
1431
+ for ( var materialIndex = 0, numMaterials = materials.length; materialIndex < numMaterials; ++materialIndex ) {
1432
+
1433
+ materials[ materialIndex ].vertexColors = THREE.VertexColors;
1434
+
1435
+ }
1436
+
1437
+ }
1438
+ if ( geometry.FBX_Deformer ) {
1439
+
1440
+ for ( var materialsIndex = 0, materialsLength = materials.length; materialsIndex < materialsLength; ++ materialsIndex ) {
1441
+
1442
+ materials[ materialsIndex ].skinning = true;
1443
+
1444
+ }
1445
+ model = new THREE.SkinnedMesh( geometry, material );
1446
+
1447
+ } else {
1448
+
1449
+ model = new THREE.Mesh( geometry, material );
1450
+
1451
+ }
1452
+ break;
1453
+
1454
+ case "NurbsCurve":
1455
+ var geometry = null;
1456
+
1457
+ for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
1458
+
1459
+ var child = conns.children[ childrenIndex ];
1460
+
1461
+ if ( geometryMap.has( child.ID ) ) {
1462
+
1463
+ geometry = geometryMap.get( child.ID );
1464
+
1465
+ }
1466
+
1467
+ }
1468
+
1469
+ // FBX does not list materials for Nurbs lines, so we'll just put our own in here.
1470
+ material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 5 } );
1471
+ model = new THREE.Line( geometry, material );
1472
+ break;
1473
+
1474
+ default:
1475
+ model = new THREE.Object3D();
1476
+ break;
1477
+
1478
+ }
1479
+
1480
+ }
1481
+
1482
+ model.name = node.attrName.replace( /:/, '' ).replace( /_/, '' ).replace( /-/, '' );
1483
+ model.FBX_ID = id;
1484
+
1485
+ modelArray.push( model );
1486
+ modelMap.set( id, model );
1487
+
1488
+ }
1489
+
1490
+ for ( var modelArrayIndex = 0, modelArrayLength = modelArray.length; modelArrayIndex < modelArrayLength; ++ modelArrayIndex ) {
1491
+
1492
+ var model = modelArray[ modelArrayIndex ];
1493
+
1494
+ var node = ModelNode[ model.FBX_ID ];
1495
+
1496
+ if ( 'Lcl_Translation' in node.properties ) {
1497
+
1498
+ model.position.fromArray( parseFloatArray( node.properties.Lcl_Translation.value ) );
1499
+
1500
+ }
1501
+
1502
+ if ( 'Lcl_Rotation' in node.properties ) {
1503
+
1504
+ var rotation = parseFloatArray( node.properties.Lcl_Rotation.value ).map( degreeToRadian );
1505
+ rotation.push( 'ZYX' );
1506
+ model.rotation.fromArray( rotation );
1507
+
1508
+ }
1509
+
1510
+ if ( 'Lcl_Scaling' in node.properties ) {
1511
+
1512
+ model.scale.fromArray( parseFloatArray( node.properties.Lcl_Scaling.value ) );
1513
+
1514
+ }
1515
+
1516
+ if ( 'PreRotation' in node.properties ) {
1517
+
1518
+ var preRotations = new THREE.Euler().setFromVector3( parseVector3( node.properties.PreRotation ).multiplyScalar( DEG2RAD ), 'ZYX' );
1519
+ preRotations = new THREE.Quaternion().setFromEuler( preRotations );
1520
+ var currentRotation = new THREE.Quaternion().setFromEuler( model.rotation );
1521
+ preRotations.multiply( currentRotation );
1522
+ model.rotation.setFromQuaternion( preRotations, 'ZYX' );
1523
+
1524
+ }
1525
+
1526
+ var conns = connections.get( model.FBX_ID );
1527
+ for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) {
1528
+
1529
+ var pIndex = findIndex( modelArray, function ( mod ) {
1530
+
1531
+ return mod.FBX_ID === conns.parents[ parentIndex ].ID;
1532
+
1533
+ } );
1534
+ if ( pIndex > - 1 ) {
1535
+
1536
+ modelArray[ pIndex ].add( model );
1537
+ break;
1538
+
1539
+ }
1540
+
1541
+ }
1542
+ if ( model.parent === null ) {
1543
+
1544
+ sceneGraph.add( model );
1545
+
1546
+ }
1547
+
1548
+ }
1549
+
1550
+
1551
+ // Now with the bones created, we can update the skeletons and bind them to the skinned meshes.
1552
+ sceneGraph.updateMatrixWorld( true );
1553
+
1554
+ // Put skeleton into bind pose.
1555
+ var BindPoseNode = FBXTree.Objects.subNodes.Pose;
1556
+ for ( var nodeID in BindPoseNode ) {
1557
+
1558
+ if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) {
1559
+
1560
+ BindPoseNode = BindPoseNode[ nodeID ];
1561
+ break;
1562
+
1563
+ }
1564
+
1565
+ }
1566
+ if ( BindPoseNode ) {
1567
+
1568
+ var PoseNode = BindPoseNode.subNodes.PoseNode;
1569
+ var worldMatrices = new Map();
1570
+
1571
+ for ( var PoseNodeIndex = 0, PoseNodeLength = PoseNode.length; PoseNodeIndex < PoseNodeLength; ++ PoseNodeIndex ) {
1572
+
1573
+ var node = PoseNode[ PoseNodeIndex ];
1574
+
1575
+ var rawMatWrd = parseMatrixArray( node.subNodes.Matrix.properties.a );
1576
+
1577
+ worldMatrices.set( parseInt( node.id ), rawMatWrd );
1578
+
1579
+ }
1580
+
1581
+ }
1582
+
1583
+ for ( var FBX_ID in deformers ) {
1584
+
1585
+ var deformer = deformers[ FBX_ID ];
1586
+ var subDeformers = deformer.map;
1587
+
1588
+ for ( var key in subDeformers ) {
1589
+
1590
+ var subDeformer = subDeformers[ key ];
1591
+ var subDeformerIndex = subDeformer.index;
1592
+
1593
+ /**
1594
+ * @type {THREE.Bone}
1595
+ */
1596
+ var bone = deformer.bones[ subDeformerIndex ];
1597
+ if ( ! worldMatrices.has( bone.FBX_ID ) ) {
1598
+
1599
+ break;
1600
+
1601
+ }
1602
+ var mat = worldMatrices.get( bone.FBX_ID );
1603
+ bone.matrixWorld.copy( mat );
1604
+
1605
+ }
1606
+
1607
+ // Now that skeleton is in bind pose, bind to model.
1608
+ deformer.skeleton = new THREE.Skeleton( deformer.bones );
1609
+
1610
+ var conns = connections.get( deformer.FBX_ID );
1611
+ var parents = conns.parents;
1612
+
1613
+ for ( var parentsIndex = 0, parentsLength = parents.length; parentsIndex < parentsLength; ++ parentsIndex ) {
1614
+
1615
+ var parent = parents[ parentsIndex ];
1616
+
1617
+ if ( geometryMap.has( parent.ID ) ) {
1618
+
1619
+ var geoID = parent.ID;
1620
+ var geoConns = connections.get( geoID );
1621
+
1622
+ for ( var i = 0; i < geoConns.parents.length; ++ i ) {
1623
+
1624
+ if ( modelMap.has( geoConns.parents[ i ].ID ) ) {
1625
+
1626
+ var model = modelMap.get( geoConns.parents[ i ].ID );
1627
+ //ASSERT model typeof SkinnedMesh
1628
+ model.bind( deformer.skeleton, model.matrixWorld );
1629
+ break;
1630
+
1631
+ }
1632
+
1633
+ }
1634
+
1635
+ }
1636
+
1637
+ }
1638
+
1639
+ }
1640
+
1641
+ //Skeleton is now bound, return objects to starting
1642
+ //world positions.
1643
+ sceneGraph.updateMatrixWorld( true );
1644
+
1645
+ // Silly hack with the animation parsing. We're gonna pretend the scene graph has a skeleton
1646
+ // to attach animations to, since FBXs treat animations as animations for the entire scene,
1647
+ // not just for individual objects.
1648
+ /*
1649
+ sceneGraph.skeleton = {
1650
+ bones: modelArray
1651
+ };
1652
+ */
1653
+ sceneGraph.skeleton = new THREE.Skeleton(modelArray);
1654
+
1655
+ var animations = parseAnimations( FBXTree, connections, sceneGraph );
1656
+
1657
+ addAnimations( sceneGraph, animations );
1658
+
1659
+ return sceneGraph;
1660
+
1661
+ }
1662
+
1663
+ /**
1664
+ * Parses animation information from FBXTree and generates an AnimationInfoObject.
1665
+ * @param {{Objects: {subNodes: {AnimationCurveNode: any, AnimationCurve: any, AnimationLayer: any, AnimationStack: any}}}} FBXTree
1666
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
1667
+ */
1668
+ function parseAnimations( FBXTree, connections, sceneGraph ) {
1669
+
1670
+ var rawNodes = FBXTree.Objects.subNodes.AnimationCurveNode;
1671
+ var rawCurves = FBXTree.Objects.subNodes.AnimationCurve;
1672
+ var rawLayers = FBXTree.Objects.subNodes.AnimationLayer;
1673
+ var rawStacks = FBXTree.Objects.subNodes.AnimationStack;
1674
+
1675
+ /**
1676
+ * @type {{
1677
+ curves: Map<number, {
1678
+ T: {
1679
+ id: number;
1680
+ attr: string;
1681
+ internalID: number;
1682
+ attrX: boolean;
1683
+ attrY: boolean;
1684
+ attrZ: boolean;
1685
+ containerBoneID: number;
1686
+ containerID: number;
1687
+ curves: {
1688
+ x: {
1689
+ version: any;
1690
+ id: number;
1691
+ internalID: number;
1692
+ times: number[];
1693
+ values: number[];
1694
+ attrFlag: number[];
1695
+ attrData: number[];
1696
+ };
1697
+ y: {
1698
+ version: any;
1699
+ id: number;
1700
+ internalID: number;
1701
+ times: number[];
1702
+ values: number[];
1703
+ attrFlag: number[];
1704
+ attrData: number[];
1705
+ };
1706
+ z: {
1707
+ version: any;
1708
+ id: number;
1709
+ internalID: number;
1710
+ times: number[];
1711
+ values: number[];
1712
+ attrFlag: number[];
1713
+ attrData: number[];
1714
+ };
1715
+ };
1716
+ },
1717
+ R: {
1718
+ id: number;
1719
+ attr: string;
1720
+ internalID: number;
1721
+ attrX: boolean;
1722
+ attrY: boolean;
1723
+ attrZ: boolean;
1724
+ containerBoneID: number;
1725
+ containerID: number;
1726
+ curves: {
1727
+ x: {
1728
+ version: any;
1729
+ id: number;
1730
+ internalID: number;
1731
+ times: number[];
1732
+ values: number[];
1733
+ attrFlag: number[];
1734
+ attrData: number[];
1735
+ };
1736
+ y: {
1737
+ version: any;
1738
+ id: number;
1739
+ internalID: number;
1740
+ times: number[];
1741
+ values: number[];
1742
+ attrFlag: number[];
1743
+ attrData: number[];
1744
+ };
1745
+ z: {
1746
+ version: any;
1747
+ id: number;
1748
+ internalID: number;
1749
+ times: number[];
1750
+ values: number[];
1751
+ attrFlag: number[];
1752
+ attrData: number[];
1753
+ };
1754
+ };
1755
+ },
1756
+ S: {
1757
+ id: number;
1758
+ attr: string;
1759
+ internalID: number;
1760
+ attrX: boolean;
1761
+ attrY: boolean;
1762
+ attrZ: boolean;
1763
+ containerBoneID: number;
1764
+ containerID: number;
1765
+ curves: {
1766
+ x: {
1767
+ version: any;
1768
+ id: number;
1769
+ internalID: number;
1770
+ times: number[];
1771
+ values: number[];
1772
+ attrFlag: number[];
1773
+ attrData: number[];
1774
+ };
1775
+ y: {
1776
+ version: any;
1777
+ id: number;
1778
+ internalID: number;
1779
+ times: number[];
1780
+ values: number[];
1781
+ attrFlag: number[];
1782
+ attrData: number[];
1783
+ };
1784
+ z: {
1785
+ version: any;
1786
+ id: number;
1787
+ internalID: number;
1788
+ times: number[];
1789
+ values: number[];
1790
+ attrFlag: number[];
1791
+ attrData: number[];
1792
+ };
1793
+ };
1794
+ }
1795
+ }>,
1796
+ layers: Map<number, {
1797
+ T: {
1798
+ id: number;
1799
+ attr: string;
1800
+ internalID: number;
1801
+ attrX: boolean;
1802
+ attrY: boolean;
1803
+ attrZ: boolean;
1804
+ containerBoneID: number;
1805
+ containerID: number;
1806
+ curves: {
1807
+ x: {
1808
+ version: any;
1809
+ id: number;
1810
+ internalID: number;
1811
+ times: number[];
1812
+ values: number[];
1813
+ attrFlag: number[];
1814
+ attrData: number[];
1815
+ };
1816
+ y: {
1817
+ version: any;
1818
+ id: number;
1819
+ internalID: number;
1820
+ times: number[];
1821
+ values: number[];
1822
+ attrFlag: number[];
1823
+ attrData: number[];
1824
+ };
1825
+ z: {
1826
+ version: any;
1827
+ id: number;
1828
+ internalID: number;
1829
+ times: number[];
1830
+ values: number[];
1831
+ attrFlag: number[];
1832
+ attrData: number[];
1833
+ };
1834
+ },
1835
+ },
1836
+ R: {
1837
+ id: number;
1838
+ attr: string;
1839
+ internalID: number;
1840
+ attrX: boolean;
1841
+ attrY: boolean;
1842
+ attrZ: boolean;
1843
+ containerBoneID: number;
1844
+ containerID: number;
1845
+ curves: {
1846
+ x: {
1847
+ version: any;
1848
+ id: number;
1849
+ internalID: number;
1850
+ times: number[];
1851
+ values: number[];
1852
+ attrFlag: number[];
1853
+ attrData: number[];
1854
+ };
1855
+ y: {
1856
+ version: any;
1857
+ id: number;
1858
+ internalID: number;
1859
+ times: number[];
1860
+ values: number[];
1861
+ attrFlag: number[];
1862
+ attrData: number[];
1863
+ };
1864
+ z: {
1865
+ version: any;
1866
+ id: number;
1867
+ internalID: number;
1868
+ times: number[];
1869
+ values: number[];
1870
+ attrFlag: number[];
1871
+ attrData: number[];
1872
+ };
1873
+ },
1874
+ },
1875
+ S: {
1876
+ id: number;
1877
+ attr: string;
1878
+ internalID: number;
1879
+ attrX: boolean;
1880
+ attrY: boolean;
1881
+ attrZ: boolean;
1882
+ containerBoneID: number;
1883
+ containerID: number;
1884
+ curves: {
1885
+ x: {
1886
+ version: any;
1887
+ id: number;
1888
+ internalID: number;
1889
+ times: number[];
1890
+ values: number[];
1891
+ attrFlag: number[];
1892
+ attrData: number[];
1893
+ };
1894
+ y: {
1895
+ version: any;
1896
+ id: number;
1897
+ internalID: number;
1898
+ times: number[];
1899
+ values: number[];
1900
+ attrFlag: number[];
1901
+ attrData: number[];
1902
+ };
1903
+ z: {
1904
+ version: any;
1905
+ id: number;
1906
+ internalID: number;
1907
+ times: number[];
1908
+ values: number[];
1909
+ attrFlag: number[];
1910
+ attrData: number[];
1911
+ };
1912
+ },
1913
+ }
1914
+ }[]>,
1915
+ stacks: Map<number, {
1916
+ name: string,
1917
+ layers: {
1918
+ T: {
1919
+ id: number;
1920
+ attr: string;
1921
+ internalID: number;
1922
+ attrX: boolean;
1923
+ attrY: boolean;
1924
+ attrZ: boolean;
1925
+ containerBoneID: number;
1926
+ containerID: number;
1927
+ curves: {
1928
+ x: {
1929
+ version: any;
1930
+ id: number;
1931
+ internalID: number;
1932
+ times: number[];
1933
+ values: number[];
1934
+ attrFlag: number[];
1935
+ attrData: number[];
1936
+ };
1937
+ y: {
1938
+ version: any;
1939
+ id: number;
1940
+ internalID: number;
1941
+ times: number[];
1942
+ values: number[];
1943
+ attrFlag: number[];
1944
+ attrData: number[];
1945
+ };
1946
+ z: {
1947
+ version: any;
1948
+ id: number;
1949
+ internalID: number;
1950
+ times: number[];
1951
+ values: number[];
1952
+ attrFlag: number[];
1953
+ attrData: number[];
1954
+ };
1955
+ };
1956
+ };
1957
+ R: {
1958
+ id: number;
1959
+ attr: string;
1960
+ internalID: number;
1961
+ attrX: boolean;
1962
+ attrY: boolean;
1963
+ attrZ: boolean;
1964
+ containerBoneID: number;
1965
+ containerID: number;
1966
+ curves: {
1967
+ x: {
1968
+ version: any;
1969
+ id: number;
1970
+ internalID: number;
1971
+ times: number[];
1972
+ values: number[];
1973
+ attrFlag: number[];
1974
+ attrData: number[];
1975
+ };
1976
+ y: {
1977
+ version: any;
1978
+ id: number;
1979
+ internalID: number;
1980
+ times: number[];
1981
+ values: number[];
1982
+ attrFlag: number[];
1983
+ attrData: number[];
1984
+ };
1985
+ z: {
1986
+ version: any;
1987
+ id: number;
1988
+ internalID: number;
1989
+ times: number[];
1990
+ values: number[];
1991
+ attrFlag: number[];
1992
+ attrData: number[];
1993
+ };
1994
+ };
1995
+ };
1996
+ S: {
1997
+ id: number;
1998
+ attr: string;
1999
+ internalID: number;
2000
+ attrX: boolean;
2001
+ attrY: boolean;
2002
+ attrZ: boolean;
2003
+ containerBoneID: number;
2004
+ containerID: number;
2005
+ curves: {
2006
+ x: {
2007
+ version: any;
2008
+ id: number;
2009
+ internalID: number;
2010
+ times: number[];
2011
+ values: number[];
2012
+ attrFlag: number[];
2013
+ attrData: number[];
2014
+ };
2015
+ y: {
2016
+ version: any;
2017
+ id: number;
2018
+ internalID: number;
2019
+ times: number[];
2020
+ values: number[];
2021
+ attrFlag: number[];
2022
+ attrData: number[];
2023
+ };
2024
+ z: {
2025
+ version: any;
2026
+ id: number;
2027
+ internalID: number;
2028
+ times: number[];
2029
+ values: number[];
2030
+ attrFlag: number[];
2031
+ attrData: number[];
2032
+ };
2033
+ };
2034
+ };
2035
+ }[][],
2036
+ length: number,
2037
+ frames: number }>,
2038
+ length: number,
2039
+ fps: number,
2040
+ frames: number
2041
+ }}
2042
+ */
2043
+ var returnObject = {
2044
+ curves: new Map(),
2045
+ layers: {},
2046
+ stacks: {},
2047
+ length: 0,
2048
+ fps: 30,
2049
+ frames: 0
2050
+ };
2051
+
2052
+ /**
2053
+ * @type {Array.<{
2054
+ id: number;
2055
+ attr: string;
2056
+ internalID: number;
2057
+ attrX: boolean;
2058
+ attrY: boolean;
2059
+ attrZ: boolean;
2060
+ containerBoneID: number;
2061
+ containerID: number;
2062
+ }>}
2063
+ */
2064
+ var animationCurveNodes = [];
2065
+ for ( var nodeID in rawNodes ) {
2066
+
2067
+ if ( nodeID.match( /\d+/ ) ) {
2068
+
2069
+ var animationNode = parseAnimationNode( FBXTree, rawNodes[ nodeID ], connections, sceneGraph );
2070
+ animationCurveNodes.push( animationNode );
2071
+
2072
+ }
2073
+
2074
+ }
2075
+
2076
+ /**
2077
+ * @type {Map.<number, {
2078
+ id: number,
2079
+ attr: string,
2080
+ internalID: number,
2081
+ attrX: boolean,
2082
+ attrY: boolean,
2083
+ attrZ: boolean,
2084
+ containerBoneID: number,
2085
+ containerID: number,
2086
+ curves: {
2087
+ x: {
2088
+ version: any,
2089
+ id: number,
2090
+ internalID: number,
2091
+ times: number[],
2092
+ values: number[],
2093
+ attrFlag: number[],
2094
+ attrData: number[],
2095
+ },
2096
+ y: {
2097
+ version: any,
2098
+ id: number,
2099
+ internalID: number,
2100
+ times: number[],
2101
+ values: number[],
2102
+ attrFlag: number[],
2103
+ attrData: number[],
2104
+ },
2105
+ z: {
2106
+ version: any,
2107
+ id: number,
2108
+ internalID: number,
2109
+ times: number[],
2110
+ values: number[],
2111
+ attrFlag: number[],
2112
+ attrData: number[],
2113
+ }
2114
+ }
2115
+ }>}
2116
+ */
2117
+ var tmpMap = new Map();
2118
+ for ( var animationCurveNodeIndex = 0; animationCurveNodeIndex < animationCurveNodes.length; ++ animationCurveNodeIndex ) {
2119
+
2120
+ if ( animationCurveNodes[ animationCurveNodeIndex ] === null ) {
2121
+
2122
+ continue;
2123
+
2124
+ }
2125
+ tmpMap.set( animationCurveNodes[ animationCurveNodeIndex ].id, animationCurveNodes[ animationCurveNodeIndex ] );
2126
+
2127
+ }
2128
+
2129
+
2130
+ /**
2131
+ * @type {{
2132
+ version: any,
2133
+ id: number,
2134
+ internalID: number,
2135
+ times: number[],
2136
+ values: number[],
2137
+ attrFlag: number[],
2138
+ attrData: number[],
2139
+ }[]}
2140
+ */
2141
+ var animationCurves = [];
2142
+ for ( nodeID in rawCurves ) {
2143
+
2144
+ if ( nodeID.match( /\d+/ ) ) {
2145
+
2146
+ var animationCurve = parseAnimationCurve( rawCurves[ nodeID ] );
2147
+
2148
+ // seems like this check would be necessary?
2149
+ if ( ! connections.has( animationCurve.id ) ) continue;
2150
+
2151
+ animationCurves.push( animationCurve );
2152
+
2153
+ var firstParentConn = connections.get( animationCurve.id ).parents[ 0 ];
2154
+ var firstParentID = firstParentConn.ID;
2155
+ var firstParentRelationship = firstParentConn.relationship;
2156
+ var axis = '';
2157
+
2158
+ if ( firstParentRelationship.match( /X/ ) ) {
2159
+
2160
+ axis = 'x';
2161
+
2162
+ } else if ( firstParentRelationship.match( /Y/ ) ) {
2163
+
2164
+ axis = 'y';
2165
+
2166
+ } else if ( firstParentRelationship.match( /Z/ ) ) {
2167
+
2168
+ axis = 'z';
2169
+
2170
+ } else {
2171
+
2172
+ continue;
2173
+
2174
+ }
2175
+
2176
+ tmpMap.get( firstParentID ).curves[ axis ] = animationCurve;
2177
+
2178
+ }
2179
+
2180
+ }
2181
+
2182
+ tmpMap.forEach( function ( curveNode ) {
2183
+
2184
+ var id = curveNode.containerBoneID;
2185
+ if ( ! returnObject.curves.has( id ) ) {
2186
+
2187
+ returnObject.curves.set( id, { T: null, R: null, S: null } );
2188
+
2189
+ }
2190
+ returnObject.curves.get( id )[ curveNode.attr ] = curveNode;
2191
+ if ( curveNode.attr === 'R' ) {
2192
+
2193
+ var curves = curveNode.curves;
2194
+
2195
+ // Seems like some FBX files have AnimationCurveNode
2196
+ // which doesn't have any connected AnimationCurve.
2197
+ // Setting animation parameter for them here.
2198
+
2199
+ if ( curves.x === null ) {
2200
+
2201
+ curves.x = {
2202
+ version: null,
2203
+ times: [ 0.0 ],
2204
+ values: [ 0.0 ]
2205
+ };
2206
+
2207
+ }
2208
+
2209
+ if ( curves.y === null ) {
2210
+
2211
+ curves.y = {
2212
+ version: null,
2213
+ times: [ 0.0 ],
2214
+ values: [ 0.0 ]
2215
+ };
2216
+
2217
+ }
2218
+
2219
+ if ( curves.z === null ) {
2220
+
2221
+ curves.z = {
2222
+ version: null,
2223
+ times: [ 0.0 ],
2224
+ values: [ 0.0 ]
2225
+ };
2226
+
2227
+ }
2228
+
2229
+ curves.x.values = curves.x.values.map( degreeToRadian );
2230
+ curves.y.values = curves.y.values.map( degreeToRadian );
2231
+ curves.z.values = curves.z.values.map( degreeToRadian );
2232
+
2233
+ if ( curveNode.preRotations !== null ) {
2234
+
2235
+ var preRotations = new THREE.Euler().setFromVector3( curveNode.preRotations, 'ZYX' );
2236
+ preRotations = new THREE.Quaternion().setFromEuler( preRotations );
2237
+ var frameRotation = new THREE.Euler();
2238
+ var frameRotationQuaternion = new THREE.Quaternion();
2239
+ for ( var frame = 0; frame < curves.x.times.length; ++ frame ) {
2240
+
2241
+ frameRotation.set( curves.x.values[ frame ], curves.y.values[ frame ], curves.z.values[ frame ], 'ZYX' );
2242
+ frameRotationQuaternion.setFromEuler( frameRotation ).premultiply( preRotations );
2243
+ frameRotation.setFromQuaternion( frameRotationQuaternion, 'ZYX' );
2244
+ curves.x.values[ frame ] = frameRotation.x;
2245
+ curves.y.values[ frame ] = frameRotation.y;
2246
+ curves.z.values[ frame ] = frameRotation.z;
2247
+
2248
+ }
2249
+
2250
+ }
2251
+
2252
+ }
2253
+
2254
+ } );
2255
+
2256
+ for ( var nodeID in rawLayers ) {
2257
+
2258
+ /**
2259
+ * @type {{
2260
+ T: {
2261
+ id: number;
2262
+ attr: string;
2263
+ internalID: number;
2264
+ attrX: boolean;
2265
+ attrY: boolean;
2266
+ attrZ: boolean;
2267
+ containerBoneID: number;
2268
+ containerID: number;
2269
+ curves: {
2270
+ x: {
2271
+ version: any;
2272
+ id: number;
2273
+ internalID: number;
2274
+ times: number[];
2275
+ values: number[];
2276
+ attrFlag: number[];
2277
+ attrData: number[];
2278
+ };
2279
+ y: {
2280
+ version: any;
2281
+ id: number;
2282
+ internalID: number;
2283
+ times: number[];
2284
+ values: number[];
2285
+ attrFlag: number[];
2286
+ attrData: number[];
2287
+ };
2288
+ z: {
2289
+ version: any;
2290
+ id: number;
2291
+ internalID: number;
2292
+ times: number[];
2293
+ values: number[];
2294
+ attrFlag: number[];
2295
+ attrData: number[];
2296
+ };
2297
+ },
2298
+ },
2299
+ R: {
2300
+ id: number;
2301
+ attr: string;
2302
+ internalID: number;
2303
+ attrX: boolean;
2304
+ attrY: boolean;
2305
+ attrZ: boolean;
2306
+ containerBoneID: number;
2307
+ containerID: number;
2308
+ curves: {
2309
+ x: {
2310
+ version: any;
2311
+ id: number;
2312
+ internalID: number;
2313
+ times: number[];
2314
+ values: number[];
2315
+ attrFlag: number[];
2316
+ attrData: number[];
2317
+ };
2318
+ y: {
2319
+ version: any;
2320
+ id: number;
2321
+ internalID: number;
2322
+ times: number[];
2323
+ values: number[];
2324
+ attrFlag: number[];
2325
+ attrData: number[];
2326
+ };
2327
+ z: {
2328
+ version: any;
2329
+ id: number;
2330
+ internalID: number;
2331
+ times: number[];
2332
+ values: number[];
2333
+ attrFlag: number[];
2334
+ attrData: number[];
2335
+ };
2336
+ },
2337
+ },
2338
+ S: {
2339
+ id: number;
2340
+ attr: string;
2341
+ internalID: number;
2342
+ attrX: boolean;
2343
+ attrY: boolean;
2344
+ attrZ: boolean;
2345
+ containerBoneID: number;
2346
+ containerID: number;
2347
+ curves: {
2348
+ x: {
2349
+ version: any;
2350
+ id: number;
2351
+ internalID: number;
2352
+ times: number[];
2353
+ values: number[];
2354
+ attrFlag: number[];
2355
+ attrData: number[];
2356
+ };
2357
+ y: {
2358
+ version: any;
2359
+ id: number;
2360
+ internalID: number;
2361
+ times: number[];
2362
+ values: number[];
2363
+ attrFlag: number[];
2364
+ attrData: number[];
2365
+ };
2366
+ z: {
2367
+ version: any;
2368
+ id: number;
2369
+ internalID: number;
2370
+ times: number[];
2371
+ values: number[];
2372
+ attrFlag: number[];
2373
+ attrData: number[];
2374
+ };
2375
+ },
2376
+ }
2377
+ }[]}
2378
+ */
2379
+ var layer = [];
2380
+ var children = connections.get( parseInt( nodeID ) ).children;
2381
+
2382
+ for ( var childIndex = 0; childIndex < children.length; childIndex ++ ) {
2383
+
2384
+ // Skip lockInfluenceWeights
2385
+ if ( tmpMap.has( children[ childIndex ].ID ) ) {
2386
+
2387
+ var curveNode = tmpMap.get( children[ childIndex ].ID );
2388
+ var boneID = curveNode.containerBoneID;
2389
+ if ( layer[ boneID ] === undefined ) {
2390
+
2391
+ layer[ boneID ] = {
2392
+ T: null,
2393
+ R: null,
2394
+ S: null
2395
+ };
2396
+
2397
+ }
2398
+
2399
+ layer[ boneID ][ curveNode.attr ] = curveNode;
2400
+
2401
+ }
2402
+
2403
+ }
2404
+
2405
+ returnObject.layers[ nodeID ] = layer;
2406
+
2407
+ }
2408
+
2409
+ for ( var nodeID in rawStacks ) {
2410
+
2411
+ var layers = [];
2412
+ var children = connections.get( parseInt( nodeID ) ).children;
2413
+ var timestamps = { max: 0, min: Number.MAX_VALUE };
2414
+
2415
+ for ( var childIndex = 0; childIndex < children.length; ++ childIndex ) {
2416
+
2417
+ var currentLayer = returnObject.layers[ children[ childIndex ].ID ];
2418
+
2419
+ if ( currentLayer !== undefined ) {
2420
+
2421
+ layers.push( currentLayer );
2422
+
2423
+ for ( var currentLayerIndex = 0, currentLayerLength = currentLayer.length; currentLayerIndex < currentLayerLength; ++ currentLayerIndex ) {
2424
+
2425
+ var layer = currentLayer[ currentLayerIndex ];
2426
+
2427
+ if ( layer ) {
2428
+
2429
+ getCurveNodeMaxMinTimeStamps( layer, timestamps );
2430
+
2431
+ }
2432
+
2433
+ }
2434
+
2435
+ }
2436
+
2437
+ }
2438
+
2439
+ // Do we have an animation clip with actual length?
2440
+ if ( timestamps.max > timestamps.min ) {
2441
+
2442
+ returnObject.stacks[ nodeID ] = {
2443
+ name: rawStacks[ nodeID ].attrName,
2444
+ layers: layers,
2445
+ length: timestamps.max - timestamps.min,
2446
+ frames: ( timestamps.max - timestamps.min ) * 30
2447
+ };
2448
+
2449
+ }
2450
+
2451
+ }
2452
+
2453
+ return returnObject;
2454
+
2455
+ }
2456
+
2457
+ /**
2458
+ * @param {Object} FBXTree
2459
+ * @param {{id: number, attrName: string, properties: Object<string, any>}} animationCurveNode
2460
+ * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
2461
+ * @param {{skeleton: {bones: {FBX_ID: number}[]}}} sceneGraph
2462
+ */
2463
+ function parseAnimationNode( FBXTree, animationCurveNode, connections, sceneGraph ) {
2464
+
2465
+ var rawModels = FBXTree.Objects.subNodes.Model;
2466
+
2467
+ var returnObject = {
2468
+ /**
2469
+ * @type {number}
2470
+ */
2471
+ id: animationCurveNode.id,
2472
+
2473
+ /**
2474
+ * @type {string}
2475
+ */
2476
+ attr: animationCurveNode.attrName,
2477
+
2478
+ /**
2479
+ * @type {number}
2480
+ */
2481
+ internalID: animationCurveNode.id,
2482
+
2483
+ /**
2484
+ * @type {boolean}
2485
+ */
2486
+ attrX: false,
2487
+
2488
+ /**
2489
+ * @type {boolean}
2490
+ */
2491
+ attrY: false,
2492
+
2493
+ /**
2494
+ * @type {boolean}
2495
+ */
2496
+ attrZ: false,
2497
+
2498
+ /**
2499
+ * @type {number}
2500
+ */
2501
+ containerBoneID: - 1,
2502
+
2503
+ /**
2504
+ * @type {number}
2505
+ */
2506
+ containerID: - 1,
2507
+
2508
+ curves: {
2509
+ x: null,
2510
+ y: null,
2511
+ z: null
2512
+ },
2513
+
2514
+ /**
2515
+ * @type {number[]}
2516
+ */
2517
+ preRotations: null
2518
+ };
2519
+
2520
+ if ( returnObject.attr.match( /S|R|T/ ) ) {
2521
+
2522
+ for ( var attributeKey in animationCurveNode.properties ) {
2523
+
2524
+ if ( attributeKey.match( /X/ ) ) {
2525
+
2526
+ returnObject.attrX = true;
2527
+
2528
+ }
2529
+ if ( attributeKey.match( /Y/ ) ) {
2530
+
2531
+ returnObject.attrY = true;
2532
+
2533
+ }
2534
+ if ( attributeKey.match( /Z/ ) ) {
2535
+
2536
+ returnObject.attrZ = true;
2537
+
2538
+ }
2539
+
2540
+ }
2541
+
2542
+ } else {
2543
+
2544
+ return null;
2545
+
2546
+ }
2547
+
2548
+ var conns = connections.get( returnObject.id );
2549
+ var containerIndices = conns.parents;
2550
+
2551
+ for ( var containerIndicesIndex = containerIndices.length - 1; containerIndicesIndex >= 0; -- containerIndicesIndex ) {
2552
+
2553
+ var boneID = findIndex( sceneGraph.skeleton.bones, function ( bone ) {
2554
+
2555
+ return bone.FBX_ID === containerIndices[ containerIndicesIndex ].ID;
2556
+
2557
+ } );
2558
+ if ( boneID > - 1 ) {
2559
+
2560
+ returnObject.containerBoneID = boneID;
2561
+ returnObject.containerID = containerIndices[ containerIndicesIndex ].ID;
2562
+ var model = rawModels[ returnObject.containerID.toString() ];
2563
+ if ( 'PreRotation' in model.properties ) {
2564
+
2565
+ returnObject.preRotations = parseVector3( model.properties.PreRotation ).multiplyScalar( Math.PI / 180 );
2566
+
2567
+ }
2568
+ break;
2569
+
2570
+ }
2571
+
2572
+ }
2573
+
2574
+ return returnObject;
2575
+
2576
+ }
2577
+
2578
+ /**
2579
+ * @param {{id: number, subNodes: {KeyTime: {properties: {a: string}}, KeyValueFloat: {properties: {a: string}}, KeyAttrFlags: {properties: {a: string}}, KeyAttrDataFloat: {properties: {a: string}}}}} animationCurve
2580
+ */
2581
+ function parseAnimationCurve( animationCurve ) {
2582
+
2583
+ return {
2584
+ version: null,
2585
+ id: animationCurve.id,
2586
+ internalID: animationCurve.id,
2587
+ times: parseFloatArray( animationCurve.subNodes.KeyTime.properties.a ).map( convertFBXTimeToSeconds ),
2588
+ values: parseFloatArray( animationCurve.subNodes.KeyValueFloat.properties.a ),
2589
+
2590
+ attrFlag: parseIntArray( animationCurve.subNodes.KeyAttrFlags.properties.a ),
2591
+ attrData: parseFloatArray( animationCurve.subNodes.KeyAttrDataFloat.properties.a )
2592
+ };
2593
+
2594
+ }
2595
+
2596
+ /**
2597
+ * Sets the maxTimeStamp and minTimeStamp variables if it has timeStamps that are either larger or smaller
2598
+ * than the max or min respectively.
2599
+ * @param {{
2600
+ T: {
2601
+ id: number,
2602
+ attr: string,
2603
+ internalID: number,
2604
+ attrX: boolean,
2605
+ attrY: boolean,
2606
+ attrZ: boolean,
2607
+ containerBoneID: number,
2608
+ containerID: number,
2609
+ curves: {
2610
+ x: {
2611
+ version: any,
2612
+ id: number,
2613
+ internalID: number,
2614
+ times: number[],
2615
+ values: number[],
2616
+ attrFlag: number[],
2617
+ attrData: number[],
2618
+ },
2619
+ y: {
2620
+ version: any,
2621
+ id: number,
2622
+ internalID: number,
2623
+ times: number[],
2624
+ values: number[],
2625
+ attrFlag: number[],
2626
+ attrData: number[],
2627
+ },
2628
+ z: {
2629
+ version: any,
2630
+ id: number,
2631
+ internalID: number,
2632
+ times: number[],
2633
+ values: number[],
2634
+ attrFlag: number[],
2635
+ attrData: number[],
2636
+ },
2637
+ },
2638
+ },
2639
+ R: {
2640
+ id: number,
2641
+ attr: string,
2642
+ internalID: number,
2643
+ attrX: boolean,
2644
+ attrY: boolean,
2645
+ attrZ: boolean,
2646
+ containerBoneID: number,
2647
+ containerID: number,
2648
+ curves: {
2649
+ x: {
2650
+ version: any,
2651
+ id: number,
2652
+ internalID: number,
2653
+ times: number[],
2654
+ values: number[],
2655
+ attrFlag: number[],
2656
+ attrData: number[],
2657
+ },
2658
+ y: {
2659
+ version: any,
2660
+ id: number,
2661
+ internalID: number,
2662
+ times: number[],
2663
+ values: number[],
2664
+ attrFlag: number[],
2665
+ attrData: number[],
2666
+ },
2667
+ z: {
2668
+ version: any,
2669
+ id: number,
2670
+ internalID: number,
2671
+ times: number[],
2672
+ values: number[],
2673
+ attrFlag: number[],
2674
+ attrData: number[],
2675
+ },
2676
+ },
2677
+ },
2678
+ S: {
2679
+ id: number,
2680
+ attr: string,
2681
+ internalID: number,
2682
+ attrX: boolean,
2683
+ attrY: boolean,
2684
+ attrZ: boolean,
2685
+ containerBoneID: number,
2686
+ containerID: number,
2687
+ curves: {
2688
+ x: {
2689
+ version: any,
2690
+ id: number,
2691
+ internalID: number,
2692
+ times: number[],
2693
+ values: number[],
2694
+ attrFlag: number[],
2695
+ attrData: number[],
2696
+ },
2697
+ y: {
2698
+ version: any,
2699
+ id: number,
2700
+ internalID: number,
2701
+ times: number[],
2702
+ values: number[],
2703
+ attrFlag: number[],
2704
+ attrData: number[],
2705
+ },
2706
+ z: {
2707
+ version: any,
2708
+ id: number,
2709
+ internalID: number,
2710
+ times: number[],
2711
+ values: number[],
2712
+ attrFlag: number[],
2713
+ attrData: number[],
2714
+ },
2715
+ },
2716
+ },
2717
+ }} layer
2718
+ */
2719
+ function getCurveNodeMaxMinTimeStamps( layer, timestamps ) {
2720
+
2721
+ if ( layer.R ) {
2722
+
2723
+ getCurveMaxMinTimeStamp( layer.R.curves, timestamps );
2724
+
2725
+ }
2726
+ if ( layer.S ) {
2727
+
2728
+ getCurveMaxMinTimeStamp( layer.S.curves, timestamps );
2729
+
2730
+ }
2731
+ if ( layer.T ) {
2732
+
2733
+ getCurveMaxMinTimeStamp( layer.T.curves, timestamps );
2734
+
2735
+ }
2736
+
2737
+ }
2738
+
2739
+ /**
2740
+ * Sets the maxTimeStamp and minTimeStamp if one of the curve's time stamps
2741
+ * exceeds the maximum or minimum.
2742
+ * @param {{
2743
+ x: {
2744
+ version: any,
2745
+ id: number,
2746
+ internalID: number,
2747
+ times: number[],
2748
+ values: number[],
2749
+ attrFlag: number[],
2750
+ attrData: number[],
2751
+ },
2752
+ y: {
2753
+ version: any,
2754
+ id: number,
2755
+ internalID: number,
2756
+ times: number[],
2757
+ values: number[],
2758
+ attrFlag: number[],
2759
+ attrData: number[],
2760
+ },
2761
+ z: {
2762
+ version: any,
2763
+ id: number,
2764
+ internalID: number,
2765
+ times: number[],
2766
+ values: number[],
2767
+ attrFlag: number[],
2768
+ attrData: number[],
2769
+ }
2770
+ }} curve
2771
+ */
2772
+ function getCurveMaxMinTimeStamp( curve, timestamps ) {
2773
+
2774
+ if ( curve.x ) {
2775
+
2776
+ getCurveAxisMaxMinTimeStamps( curve.x, timestamps );
2777
+
2778
+ }
2779
+ if ( curve.y ) {
2780
+
2781
+ getCurveAxisMaxMinTimeStamps( curve.y, timestamps );
2782
+
2783
+ }
2784
+ if ( curve.z ) {
2785
+
2786
+ getCurveAxisMaxMinTimeStamps( curve.z, timestamps );
2787
+
2788
+ }
2789
+
2790
+ }
2791
+
2792
+ /**
2793
+ * Sets the maxTimeStamp and minTimeStamp if one of its timestamps exceeds the maximum or minimum.
2794
+ * @param {{times: number[]}} axis
2795
+ */
2796
+ function getCurveAxisMaxMinTimeStamps( axis, timestamps ) {
2797
+
2798
+ timestamps.max = axis.times[ axis.times.length - 1 ] > timestamps.max ? axis.times[ axis.times.length - 1 ] : timestamps.max;
2799
+ timestamps.min = axis.times[ 0 ] < timestamps.min ? axis.times[ 0 ] : timestamps.min;
2800
+
2801
+ }
2802
+
2803
+ /**
2804
+ * @param {{
2805
+ curves: Map<number, {
2806
+ T: {
2807
+ id: number;
2808
+ attr: string;
2809
+ internalID: number;
2810
+ attrX: boolean;
2811
+ attrY: boolean;
2812
+ attrZ: boolean;
2813
+ containerBoneID: number;
2814
+ containerID: number;
2815
+ curves: {
2816
+ x: {
2817
+ version: any;
2818
+ id: number;
2819
+ internalID: number;
2820
+ times: number[];
2821
+ values: number[];
2822
+ attrFlag: number[];
2823
+ attrData: number[];
2824
+ };
2825
+ y: {
2826
+ version: any;
2827
+ id: number;
2828
+ internalID: number;
2829
+ times: number[];
2830
+ values: number[];
2831
+ attrFlag: number[];
2832
+ attrData: number[];
2833
+ };
2834
+ z: {
2835
+ version: any;
2836
+ id: number;
2837
+ internalID: number;
2838
+ times: number[];
2839
+ values: number[];
2840
+ attrFlag: number[];
2841
+ attrData: number[];
2842
+ };
2843
+ };
2844
+ };
2845
+ R: {
2846
+ id: number;
2847
+ attr: string;
2848
+ internalID: number;
2849
+ attrX: boolean;
2850
+ attrY: boolean;
2851
+ attrZ: boolean;
2852
+ containerBoneID: number;
2853
+ containerID: number;
2854
+ curves: {
2855
+ x: {
2856
+ version: any;
2857
+ id: number;
2858
+ internalID: number;
2859
+ times: number[];
2860
+ values: number[];
2861
+ attrFlag: number[];
2862
+ attrData: number[];
2863
+ };
2864
+ y: {
2865
+ version: any;
2866
+ id: number;
2867
+ internalID: number;
2868
+ times: number[];
2869
+ values: number[];
2870
+ attrFlag: number[];
2871
+ attrData: number[];
2872
+ };
2873
+ z: {
2874
+ version: any;
2875
+ id: number;
2876
+ internalID: number;
2877
+ times: number[];
2878
+ values: number[];
2879
+ attrFlag: number[];
2880
+ attrData: number[];
2881
+ };
2882
+ };
2883
+ };
2884
+ S: {
2885
+ id: number;
2886
+ attr: string;
2887
+ internalID: number;
2888
+ attrX: boolean;
2889
+ attrY: boolean;
2890
+ attrZ: boolean;
2891
+ containerBoneID: number;
2892
+ containerID: number;
2893
+ curves: {
2894
+ x: {
2895
+ version: any;
2896
+ id: number;
2897
+ internalID: number;
2898
+ times: number[];
2899
+ values: number[];
2900
+ attrFlag: number[];
2901
+ attrData: number[];
2902
+ };
2903
+ y: {
2904
+ version: any;
2905
+ id: number;
2906
+ internalID: number;
2907
+ times: number[];
2908
+ values: number[];
2909
+ attrFlag: number[];
2910
+ attrData: number[];
2911
+ };
2912
+ z: {
2913
+ version: any;
2914
+ id: number;
2915
+ internalID: number;
2916
+ times: number[];
2917
+ values: number[];
2918
+ attrFlag: number[];
2919
+ attrData: number[];
2920
+ };
2921
+ };
2922
+ };
2923
+ }>;
2924
+ layers: Map<number, {
2925
+ T: {
2926
+ id: number;
2927
+ attr: string;
2928
+ internalID: number;
2929
+ attrX: boolean;
2930
+ attrY: boolean;
2931
+ attrZ: boolean;
2932
+ containerBoneID: number;
2933
+ containerID: number;
2934
+ curves: {
2935
+ x: {
2936
+ version: any;
2937
+ id: number;
2938
+ internalID: number;
2939
+ times: number[];
2940
+ values: number[];
2941
+ attrFlag: number[];
2942
+ attrData: number[];
2943
+ };
2944
+ y: {
2945
+ version: any;
2946
+ id: number;
2947
+ internalID: number;
2948
+ times: number[];
2949
+ values: number[];
2950
+ attrFlag: number[];
2951
+ attrData: number[];
2952
+ };
2953
+ z: {
2954
+ version: any;
2955
+ id: number;
2956
+ internalID: number;
2957
+ times: number[];
2958
+ values: number[];
2959
+ attrFlag: number[];
2960
+ attrData: number[];
2961
+ };
2962
+ };
2963
+ };
2964
+ R: {
2965
+ id: number;
2966
+ attr: string;
2967
+ internalID: number;
2968
+ attrX: boolean;
2969
+ attrY: boolean;
2970
+ attrZ: boolean;
2971
+ containerBoneID: number;
2972
+ containerID: number;
2973
+ curves: {
2974
+ x: {
2975
+ version: any;
2976
+ id: number;
2977
+ internalID: number;
2978
+ times: number[];
2979
+ values: number[];
2980
+ attrFlag: number[];
2981
+ attrData: number[];
2982
+ };
2983
+ y: {
2984
+ version: any;
2985
+ id: number;
2986
+ internalID: number;
2987
+ times: number[];
2988
+ values: number[];
2989
+ attrFlag: number[];
2990
+ attrData: number[];
2991
+ };
2992
+ z: {
2993
+ version: any;
2994
+ id: number;
2995
+ internalID: number;
2996
+ times: number[];
2997
+ values: number[];
2998
+ attrFlag: number[];
2999
+ attrData: number[];
3000
+ };
3001
+ };
3002
+ };
3003
+ S: {
3004
+ id: number;
3005
+ attr: string;
3006
+ internalID: number;
3007
+ attrX: boolean;
3008
+ attrY: boolean;
3009
+ attrZ: boolean;
3010
+ containerBoneID: number;
3011
+ containerID: number;
3012
+ curves: {
3013
+ x: {
3014
+ version: any;
3015
+ id: number;
3016
+ internalID: number;
3017
+ times: number[];
3018
+ values: number[];
3019
+ attrFlag: number[];
3020
+ attrData: number[];
3021
+ };
3022
+ y: {
3023
+ version: any;
3024
+ id: number;
3025
+ internalID: number;
3026
+ times: number[];
3027
+ values: number[];
3028
+ attrFlag: number[];
3029
+ attrData: number[];
3030
+ };
3031
+ z: {
3032
+ version: any;
3033
+ id: number;
3034
+ internalID: number;
3035
+ times: number[];
3036
+ values: number[];
3037
+ attrFlag: number[];
3038
+ attrData: number[];
3039
+ };
3040
+ };
3041
+ };
3042
+ }[]>;
3043
+ stacks: Map<number, {
3044
+ name: string;
3045
+ layers: {
3046
+ T: {
3047
+ id: number;
3048
+ attr: string;
3049
+ internalID: number;
3050
+ attrX: boolean;
3051
+ attrY: boolean;
3052
+ attrZ: boolean;
3053
+ containerBoneID: number;
3054
+ containerID: number;
3055
+ curves: {
3056
+ x: {
3057
+ version: any;
3058
+ id: number;
3059
+ internalID: number;
3060
+ times: number[];
3061
+ values: number[];
3062
+ attrFlag: number[];
3063
+ attrData: number[];
3064
+ };
3065
+ y: {
3066
+ version: any;
3067
+ id: number;
3068
+ internalID: number;
3069
+ times: number[];
3070
+ values: number[];
3071
+ attrFlag: number[];
3072
+ attrData: number[];
3073
+ };
3074
+ z: {
3075
+ version: any;
3076
+ id: number;
3077
+ internalID: number;
3078
+ times: number[];
3079
+ values: number[];
3080
+ attrFlag: number[];
3081
+ attrData: number[];
3082
+ };
3083
+ };
3084
+ };
3085
+ R: {
3086
+ id: number;
3087
+ attr: string;
3088
+ internalID: number;
3089
+ attrX: boolean;
3090
+ attrY: boolean;
3091
+ attrZ: boolean;
3092
+ containerBoneID: number;
3093
+ containerID: number;
3094
+ curves: {
3095
+ x: {
3096
+ version: any;
3097
+ id: number;
3098
+ internalID: number;
3099
+ times: number[];
3100
+ values: number[];
3101
+ attrFlag: number[];
3102
+ attrData: number[];
3103
+ };
3104
+ y: {
3105
+ version: any;
3106
+ id: number;
3107
+ internalID: number;
3108
+ times: number[];
3109
+ values: number[];
3110
+ attrFlag: number[];
3111
+ attrData: number[];
3112
+ };
3113
+ z: {
3114
+ version: any;
3115
+ id: number;
3116
+ internalID: number;
3117
+ times: number[];
3118
+ values: number[];
3119
+ attrFlag: number[];
3120
+ attrData: number[];
3121
+ };
3122
+ };
3123
+ };
3124
+ S: {
3125
+ id: number;
3126
+ attr: string;
3127
+ internalID: number;
3128
+ attrX: boolean;
3129
+ attrY: boolean;
3130
+ attrZ: boolean;
3131
+ containerBoneID: number;
3132
+ containerID: number;
3133
+ curves: {
3134
+ x: {
3135
+ version: any;
3136
+ id: number;
3137
+ internalID: number;
3138
+ times: number[];
3139
+ values: number[];
3140
+ attrFlag: number[];
3141
+ attrData: number[];
3142
+ };
3143
+ y: {
3144
+ version: any;
3145
+ id: number;
3146
+ internalID: number;
3147
+ times: number[];
3148
+ values: number[];
3149
+ attrFlag: number[];
3150
+ attrData: number[];
3151
+ };
3152
+ z: {
3153
+ version: any;
3154
+ id: number;
3155
+ internalID: number;
3156
+ times: number[];
3157
+ values: number[];
3158
+ attrFlag: number[];
3159
+ attrData: number[];
3160
+ };
3161
+ };
3162
+ };
3163
+ }[][];
3164
+ length: number;
3165
+ frames: number;
3166
+ }>;
3167
+ length: number;
3168
+ fps: number;
3169
+ frames: number;
3170
+ }} animations,
3171
+ * @param {{skeleton: { bones: THREE.Bone[]}}} group
3172
+ */
3173
+ function addAnimations( group, animations ) {
3174
+
3175
+ if ( group.animations === undefined ) {
3176
+
3177
+ group.animations = [];
3178
+
3179
+ }
3180
+
3181
+ var stacks = animations.stacks;
3182
+
3183
+ for ( var key in stacks ) {
3184
+
3185
+ var stack = stacks[ key ];
3186
+
3187
+ /**
3188
+ * @type {{
3189
+ * name: string,
3190
+ * fps: number,
3191
+ * length: number,
3192
+ * hierarchy: Array.<{
3193
+ * parent: number,
3194
+ * name: string,
3195
+ * keys: Array.<{
3196
+ * time: number,
3197
+ * pos: Array.<number>,
3198
+ * rot: Array.<number>,
3199
+ * scl: Array.<number>
3200
+ * }>
3201
+ * }>
3202
+ * }}
3203
+ */
3204
+ var animationData = {
3205
+ name: stack.name,
3206
+ fps: 30,
3207
+ length: stack.length,
3208
+ hierarchy: []
3209
+ };
3210
+
3211
+ var bones = group.skeleton.bones;
3212
+
3213
+ for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
3214
+
3215
+ var bone = bones[ bonesIndex ];
3216
+
3217
+ var name = bone.name.replace( /.*:/, '' );
3218
+ var parentIndex = findIndex( bones, function ( parentBone ) {
3219
+
3220
+ return bone.parent === parentBone;
3221
+
3222
+ } );
3223
+ animationData.hierarchy.push( { parent: parentIndex, name: name, keys: [] } );
3224
+
3225
+ }
3226
+
3227
+ for ( var frame = 0; frame <= stack.frames; frame ++ ) {
3228
+
3229
+ for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
3230
+
3231
+ var bone = bones[ bonesIndex ];
3232
+ var boneIndex = bonesIndex;
3233
+
3234
+ var animationNode = stack.layers[ 0 ][ boneIndex ];
3235
+
3236
+ for ( var hierarchyIndex = 0, hierarchyLength = animationData.hierarchy.length; hierarchyIndex < hierarchyLength; ++ hierarchyIndex ) {
3237
+
3238
+ var node = animationData.hierarchy[ hierarchyIndex ];
3239
+
3240
+ if ( node.name === bone.name ) {
3241
+
3242
+ node.keys.push( generateKey( animations, animationNode, bone, frame ) );
3243
+
3244
+ }
3245
+
3246
+ }
3247
+
3248
+ }
3249
+
3250
+ }
3251
+
3252
+ group.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
3253
+
3254
+ }
3255
+
3256
+ }
3257
+
3258
+ var euler = new THREE.Euler();
3259
+ var quaternion = new THREE.Quaternion();
3260
+
3261
+ /**
3262
+ * @param {THREE.Bone} bone
3263
+ */
3264
+ function generateKey( animations, animationNode, bone, frame ) {
3265
+
3266
+ var key = {
3267
+ time: frame / animations.fps,
3268
+ pos: bone.position.toArray(),
3269
+ rot: bone.quaternion.toArray(),
3270
+ scl: bone.scale.toArray()
3271
+ };
3272
+
3273
+ if ( animationNode === undefined ) return key;
3274
+
3275
+ try {
3276
+
3277
+ if ( hasCurve( animationNode, 'T' ) && hasKeyOnFrame( animationNode.T, frame ) ) {
3278
+
3279
+ key.pos = [ animationNode.T.curves.x.values[ frame ], animationNode.T.curves.y.values[ frame ], animationNode.T.curves.z.values[ frame ] ];
3280
+
3281
+ }
3282
+
3283
+ if ( hasCurve( animationNode, 'R' ) && hasKeyOnFrame( animationNode.R, frame ) ) {
3284
+
3285
+ var rotationX = animationNode.R.curves.x.values[ frame ];
3286
+ var rotationY = animationNode.R.curves.y.values[ frame ];
3287
+ var rotationZ = animationNode.R.curves.z.values[ frame ];
3288
+
3289
+ quaternion.setFromEuler( euler.set( rotationX, rotationY, rotationZ, 'ZYX' ) );
3290
+ key.rot = quaternion.toArray();
3291
+
3292
+ }
3293
+
3294
+ if ( hasCurve( animationNode, 'S' ) && hasKeyOnFrame( animationNode.S, frame ) ) {
3295
+
3296
+ key.scl = [ animationNode.S.curves.x.values[ frame ], animationNode.S.curves.y.values[ frame ], animationNode.S.curves.z.values[ frame ] ];
3297
+
3298
+ }
3299
+
3300
+ } catch ( error ) {
3301
+
3302
+ // Curve is not fully plotted.
3303
+ console.log( bone );
3304
+ console.log( error );
3305
+
3306
+ }
3307
+
3308
+ return key;
3309
+
3310
+ }
3311
+
3312
+ var AXES = [ 'x', 'y', 'z' ];
3313
+
3314
+ function hasCurve( animationNode, attribute ) {
3315
+
3316
+ if ( animationNode === undefined ) {
3317
+
3318
+ return false;
3319
+
3320
+ }
3321
+
3322
+ var attributeNode = animationNode[ attribute ];
3323
+
3324
+ if ( ! attributeNode ) {
3325
+
3326
+ return false;
3327
+
3328
+ }
3329
+
3330
+ return AXES.every( function ( key ) {
3331
+
3332
+ return attributeNode.curves[ key ] !== null;
3333
+
3334
+ } );
3335
+
3336
+ }
3337
+
3338
+ function hasKeyOnFrame( attributeNode, frame ) {
3339
+
3340
+ return AXES.every( function ( key ) {
3341
+
3342
+ return isKeyExistOnFrame( attributeNode.curves[ key ], frame );
3343
+
3344
+ } );
3345
+
3346
+ }
3347
+
3348
+ function isKeyExistOnFrame( curve, frame ) {
3349
+
3350
+ return curve.values[ frame ] !== undefined;
3351
+
3352
+ }
3353
+
3354
+ /**
3355
+ * An instance of a Vertex with data for drawing vertices to the screen.
3356
+ * @constructor
3357
+ */
3358
+ function Vertex() {
3359
+
3360
+ /**
3361
+ * Position of the vertex.
3362
+ * @type {THREE.Vector3}
3363
+ */
3364
+ this.position = new THREE.Vector3();
3365
+
3366
+ /**
3367
+ * Normal of the vertex
3368
+ * @type {THREE.Vector3}
3369
+ */
3370
+ this.normal = new THREE.Vector3();
3371
+
3372
+ /**
3373
+ * UV coordinates of the vertex.
3374
+ * @type {THREE.Vector2}
3375
+ */
3376
+ this.uv = new THREE.Vector2();
3377
+
3378
+ /**
3379
+ * Color of the vertex
3380
+ * @type {THREE.Vector3}
3381
+ */
3382
+ this.color = new THREE.Vector3();
3383
+
3384
+ /**
3385
+ * Indices of the bones vertex is influenced by.
3386
+ * @type {THREE.Vector4}
3387
+ */
3388
+ this.skinIndices = new THREE.Vector4( 0, 0, 0, 0 );
3389
+
3390
+ /**
3391
+ * Weights that each bone influences the vertex.
3392
+ * @type {THREE.Vector4}
3393
+ */
3394
+ this.skinWeights = new THREE.Vector4( 0, 0, 0, 0 );
3395
+
3396
+ }
3397
+
3398
+ Object.assign( Vertex.prototype, {
3399
+
3400
+ copy: function ( target ) {
3401
+
3402
+ var returnVar = target || new Vertex();
3403
+
3404
+ returnVar.position.copy( this.position );
3405
+ returnVar.normal.copy( this.normal );
3406
+ returnVar.uv.copy( this.uv );
3407
+ returnVar.skinIndices.copy( this.skinIndices );
3408
+ returnVar.skinWeights.copy( this.skinWeights );
3409
+
3410
+ return returnVar;
3411
+
3412
+ },
3413
+
3414
+ flattenToBuffers: function ( vertexBuffer, normalBuffer, uvBuffer, colorBuffer, skinIndexBuffer, skinWeightBuffer ) {
3415
+
3416
+ this.position.toArray( vertexBuffer, vertexBuffer.length );
3417
+ this.normal.toArray( normalBuffer, normalBuffer.length );
3418
+ this.uv.toArray( uvBuffer, uvBuffer.length );
3419
+ this.color.toArray( colorBuffer, colorBuffer.length );
3420
+ this.skinIndices.toArray( skinIndexBuffer, skinIndexBuffer.length );
3421
+ this.skinWeights.toArray( skinWeightBuffer, skinWeightBuffer.length );
3422
+
3423
+ }
3424
+
3425
+ } );
3426
+
3427
+ /**
3428
+ * @constructor
3429
+ */
3430
+ function Triangle() {
3431
+
3432
+ /**
3433
+ * @type {{position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}
3434
+ */
3435
+ this.vertices = [];
3436
+
3437
+ }
3438
+
3439
+ Object.assign( Triangle.prototype, {
3440
+
3441
+ copy: function ( target ) {
3442
+
3443
+ var returnVar = target || new Triangle();
3444
+
3445
+ for ( var i = 0; i < this.vertices.length; ++ i ) {
3446
+
3447
+ this.vertices[ i ].copy( returnVar.vertices[ i ] );
3448
+
3449
+ }
3450
+
3451
+ return returnVar;
3452
+
3453
+ },
3454
+
3455
+ flattenToBuffers: function ( vertexBuffer, normalBuffer, uvBuffer, colorBuffer, skinIndexBuffer, skinWeightBuffer ) {
3456
+
3457
+ var vertices = this.vertices;
3458
+
3459
+ for ( var i = 0, l = vertices.length; i < l; ++ i ) {
3460
+
3461
+ vertices[ i ].flattenToBuffers( vertexBuffer, normalBuffer, uvBuffer, colorBuffer, skinIndexBuffer, skinWeightBuffer );
3462
+
3463
+ }
3464
+
3465
+ }
3466
+
3467
+ } );
3468
+
3469
+ /**
3470
+ * @constructor
3471
+ */
3472
+ function Face() {
3473
+
3474
+ /**
3475
+ * @type {{vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[]}
3476
+ */
3477
+ this.triangles = [];
3478
+ this.materialIndex = 0;
3479
+
3480
+ }
3481
+
3482
+ Object.assign( Face.prototype, {
3483
+
3484
+ copy: function ( target ) {
3485
+
3486
+ var returnVar = target || new Face();
3487
+
3488
+ for ( var i = 0; i < this.triangles.length; ++ i ) {
3489
+
3490
+ this.triangles[ i ].copy( returnVar.triangles[ i ] );
3491
+
3492
+ }
3493
+
3494
+ returnVar.materialIndex = this.materialIndex;
3495
+
3496
+ return returnVar;
3497
+
3498
+ },
3499
+
3500
+ genTrianglesFromVertices: function ( vertexArray ) {
3501
+
3502
+ for ( var i = 2; i < vertexArray.length; ++ i ) {
3503
+
3504
+ var triangle = new Triangle();
3505
+ triangle.vertices[ 0 ] = vertexArray[ 0 ];
3506
+ triangle.vertices[ 1 ] = vertexArray[ i - 1 ];
3507
+ triangle.vertices[ 2 ] = vertexArray[ i ];
3508
+ this.triangles.push( triangle );
3509
+
3510
+ }
3511
+
3512
+ },
3513
+
3514
+ flattenToBuffers: function ( vertexBuffer, normalBuffer, uvBuffer, colorBuffer, skinIndexBuffer, skinWeightBuffer, materialIndexBuffer ) {
3515
+
3516
+ var triangles = this.triangles;
3517
+ var materialIndex = this.materialIndex;
3518
+
3519
+ for ( var i = 0, l = triangles.length; i < l; ++ i ) {
3520
+
3521
+ triangles[ i ].flattenToBuffers( vertexBuffer, normalBuffer, uvBuffer, colorBuffer, skinIndexBuffer, skinWeightBuffer );
3522
+ append( materialIndexBuffer, [ materialIndex, materialIndex, materialIndex ] );
3523
+
3524
+ }
3525
+
3526
+ }
3527
+
3528
+ } );
3529
+
3530
+ /**
3531
+ * @constructor
3532
+ */
3533
+ function Geometry() {
3534
+
3535
+ /**
3536
+ * @type {{triangles: {vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[], materialIndex: number}[]}
3537
+ */
3538
+ this.faces = [];
3539
+
3540
+ /**
3541
+ * @type {{}|THREE.Skeleton}
3542
+ */
3543
+ this.skeleton = null;
3544
+
3545
+ }
3546
+
3547
+ Object.assign( Geometry.prototype, {
3548
+
3549
+ /**
3550
+ * @returns {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}}
3551
+ */
3552
+ flattenToBuffers: function () {
3553
+
3554
+ var vertexBuffer = [];
3555
+ var normalBuffer = [];
3556
+ var uvBuffer = [];
3557
+ var colorBuffer = [];
3558
+ var skinIndexBuffer = [];
3559
+ var skinWeightBuffer = [];
3560
+
3561
+ var materialIndexBuffer = [];
3562
+
3563
+ var faces = this.faces;
3564
+
3565
+ for ( var i = 0, l = faces.length; i < l; ++ i ) {
3566
+
3567
+ faces[ i ].flattenToBuffers( vertexBuffer, normalBuffer, uvBuffer, colorBuffer, skinIndexBuffer, skinWeightBuffer, materialIndexBuffer );
3568
+
3569
+ }
3570
+
3571
+ return {
3572
+ vertexBuffer: vertexBuffer,
3573
+ normalBuffer: normalBuffer,
3574
+ uvBuffer: uvBuffer,
3575
+ colorBuffer: colorBuffer,
3576
+ skinIndexBuffer: skinIndexBuffer,
3577
+ skinWeightBuffer: skinWeightBuffer,
3578
+ materialIndexBuffer: materialIndexBuffer
3579
+ };
3580
+
3581
+ }
3582
+
3583
+ } );
3584
+
3585
+ function TextParser() {}
3586
+
3587
+ Object.assign( TextParser.prototype, {
3588
+
3589
+ getPrevNode: function () {
3590
+
3591
+ return this.nodeStack[ this.currentIndent - 2 ];
3592
+
3593
+ },
3594
+
3595
+ getCurrentNode: function () {
3596
+
3597
+ return this.nodeStack[ this.currentIndent - 1 ];
3598
+
3599
+ },
3600
+
3601
+ getCurrentProp: function () {
3602
+
3603
+ return this.currentProp;
3604
+
3605
+ },
3606
+
3607
+ pushStack: function ( node ) {
3608
+
3609
+ this.nodeStack.push( node );
3610
+ this.currentIndent += 1;
3611
+
3612
+ },
3613
+
3614
+ popStack: function () {
3615
+
3616
+ this.nodeStack.pop();
3617
+ this.currentIndent -= 1;
3618
+
3619
+ },
3620
+
3621
+ setCurrentProp: function ( val, name ) {
3622
+
3623
+ this.currentProp = val;
3624
+ this.currentPropName = name;
3625
+
3626
+ },
3627
+
3628
+ // ----------parse ---------------------------------------------------
3629
+ parse: function ( text ) {
3630
+
3631
+ this.currentIndent = 0;
3632
+ this.allNodes = new FBXTree();
3633
+ this.nodeStack = [];
3634
+ this.currentProp = [];
3635
+ this.currentPropName = '';
3636
+
3637
+ var split = text.split( "\n" );
3638
+
3639
+ for ( var line in split ) {
3640
+
3641
+ var l = split[ line ];
3642
+
3643
+ // short cut
3644
+ if ( l.match( /^[\s\t]*;/ ) ) {
3645
+
3646
+ continue;
3647
+
3648
+ } // skip comment line
3649
+ if ( l.match( /^[\s\t]*$/ ) ) {
3650
+
3651
+ continue;
3652
+
3653
+ } // skip empty line
3654
+
3655
+ // beginning of node
3656
+ var beginningOfNodeExp = new RegExp( "^\\t{" + this.currentIndent + "}(\\w+):(.*){", '' );
3657
+ var match = l.match( beginningOfNodeExp );
3658
+ if ( match ) {
3659
+
3660
+ var nodeName = match[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, "" );
3661
+ var nodeAttrs = match[ 2 ].split( ',' );
3662
+
3663
+ for ( var i = 0, l = nodeAttrs.length; i < l; i ++ ) {
3664
+ nodeAttrs[ i ] = nodeAttrs[ i ].trim().replace( /^"/, '' ).replace( /"$/, '' );
3665
+ }
3666
+
3667
+ this.parseNodeBegin( l, nodeName, nodeAttrs || null );
3668
+ continue;
3669
+
3670
+ }
3671
+
3672
+ // node's property
3673
+ var propExp = new RegExp( "^\\t{" + ( this.currentIndent ) + "}(\\w+):[\\s\\t\\r\\n](.*)" );
3674
+ var match = l.match( propExp );
3675
+ if ( match ) {
3676
+
3677
+ var propName = match[ 1 ].replace( /^"/, '' ).replace( /"$/, "" ).trim();
3678
+ var propValue = match[ 2 ].replace( /^"/, '' ).replace( /"$/, "" ).trim();
3679
+
3680
+ this.parseNodeProperty( l, propName, propValue );
3681
+ continue;
3682
+
3683
+ }
3684
+
3685
+ // end of node
3686
+ var endOfNodeExp = new RegExp( "^\\t{" + ( this.currentIndent - 1 ) + "}}" );
3687
+ if ( l.match( endOfNodeExp ) ) {
3688
+
3689
+ this.nodeEnd();
3690
+ continue;
3691
+
3692
+ }
3693
+
3694
+ // for special case,
3695
+ //
3696
+ // Vertices: *8670 {
3697
+ // a: 0.0356229953467846,13.9599733352661,-0.399196773.....(snip)
3698
+ // -0.0612030513584614,13.960485458374,-0.409748703241348,-0.10.....
3699
+ // 0.12490539252758,13.7450733184814,-0.454119384288788,0.09272.....
3700
+ // 0.0836158767342567,13.5432004928589,-0.435397416353226,0.028.....
3701
+ //
3702
+ // these case the lines must contiue with previous line
3703
+ if ( l.match( /^[^\s\t}]/ ) ) {
3704
+
3705
+ this.parseNodePropertyContinued( l );
3706
+
3707
+ }
3708
+
3709
+ }
3710
+
3711
+ return this.allNodes;
3712
+
3713
+ },
3714
+
3715
+ parseNodeBegin: function ( line, nodeName, nodeAttrs ) {
3716
+
3717
+ // var nodeName = match[1];
3718
+ var node = { 'name': nodeName, properties: {}, 'subNodes': {} };
3719
+ var attrs = this.parseNodeAttr( nodeAttrs );
3720
+ var currentNode = this.getCurrentNode();
3721
+
3722
+ // a top node
3723
+ if ( this.currentIndent === 0 ) {
3724
+
3725
+ this.allNodes.add( nodeName, node );
3726
+
3727
+ } else {
3728
+
3729
+ // a subnode
3730
+
3731
+ // already exists subnode, then append it
3732
+ if ( nodeName in currentNode.subNodes ) {
3733
+
3734
+ var tmp = currentNode.subNodes[ nodeName ];
3735
+
3736
+ // console.log( "duped entry found\nkey: " + nodeName + "\nvalue: " + propValue );
3737
+ if ( this.isFlattenNode( currentNode.subNodes[ nodeName ] ) ) {
3738
+
3739
+
3740
+ if ( attrs.id === '' ) {
3741
+
3742
+ currentNode.subNodes[ nodeName ] = [];
3743
+ currentNode.subNodes[ nodeName ].push( tmp );
3744
+
3745
+ } else {
3746
+
3747
+ currentNode.subNodes[ nodeName ] = {};
3748
+ currentNode.subNodes[ nodeName ][ tmp.id ] = tmp;
3749
+
3750
+ }
3751
+
3752
+ }
3753
+
3754
+ if ( attrs.id === '' ) {
3755
+
3756
+ currentNode.subNodes[ nodeName ].push( node );
3757
+
3758
+ } else {
3759
+
3760
+ currentNode.subNodes[ nodeName ][ attrs.id ] = node;
3761
+
3762
+ }
3763
+
3764
+ } else if ( typeof attrs.id === 'number' || attrs.id.match( /^\d+$/ ) ) {
3765
+
3766
+ currentNode.subNodes[ nodeName ] = {};
3767
+ currentNode.subNodes[ nodeName ][ attrs.id ] = node;
3768
+
3769
+ } else {
3770
+
3771
+ currentNode.subNodes[ nodeName ] = node;
3772
+
3773
+ }
3774
+
3775
+ }
3776
+
3777
+ // for this ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
3778
+ // NodeAttribute: 1001463072, "NodeAttribute::", "LimbNode" {
3779
+ if ( nodeAttrs ) {
3780
+
3781
+ node.id = attrs.id;
3782
+ node.attrName = attrs.name;
3783
+ node.attrType = attrs.type;
3784
+
3785
+ }
3786
+
3787
+ this.pushStack( node );
3788
+
3789
+ },
3790
+
3791
+ parseNodeAttr: function ( attrs ) {
3792
+
3793
+ var id = attrs[ 0 ];
3794
+
3795
+ if ( attrs[ 0 ] !== "" ) {
3796
+
3797
+ id = parseInt( attrs[ 0 ] );
3798
+
3799
+ if ( isNaN( id ) ) {
3800
+
3801
+ // PolygonVertexIndex: *16380 {
3802
+ id = attrs[ 0 ];
3803
+
3804
+ }
3805
+
3806
+ }
3807
+
3808
+ var name = '', type = '';
3809
+
3810
+ if ( attrs.length > 1 ) {
3811
+
3812
+ name = attrs[ 1 ].replace( /^(\w+)::/, '' );
3813
+ type = attrs[ 2 ];
3814
+
3815
+ }
3816
+
3817
+ return { id: id, name: name, type: type };
3818
+
3819
+ },
3820
+
3821
+ parseNodeProperty: function ( line, propName, propValue ) {
3822
+
3823
+ var currentNode = this.getCurrentNode();
3824
+ var parentName = currentNode.name;
3825
+
3826
+ // special case parent node's is like "Properties70"
3827
+ // these chilren nodes must treat with careful
3828
+ if ( parentName !== undefined ) {
3829
+
3830
+ var propMatch = parentName.match( /Properties(\d)+/ );
3831
+ if ( propMatch ) {
3832
+
3833
+ this.parseNodeSpecialProperty( line, propName, propValue );
3834
+ return;
3835
+
3836
+ }
3837
+
3838
+ }
3839
+
3840
+ // special case Connections
3841
+ if ( propName == 'C' ) {
3842
+
3843
+ var connProps = propValue.split( ',' ).slice( 1 );
3844
+ var from = parseInt( connProps[ 0 ] );
3845
+ var to = parseInt( connProps[ 1 ] );
3846
+
3847
+ var rest = propValue.split( ',' ).slice( 3 );
3848
+
3849
+ propName = 'connections';
3850
+ propValue = [ from, to ];
3851
+ append( propValue, rest );
3852
+
3853
+ if ( currentNode.properties[ propName ] === undefined ) {
3854
+
3855
+ currentNode.properties[ propName ] = [];
3856
+
3857
+ }
3858
+
3859
+ }
3860
+
3861
+ // special case Connections
3862
+ if ( propName == 'Node' ) {
3863
+
3864
+ var id = parseInt( propValue );
3865
+ currentNode.properties.id = id;
3866
+ currentNode.id = id;
3867
+
3868
+ }
3869
+
3870
+ // already exists in properties, then append this
3871
+ if ( propName in currentNode.properties ) {
3872
+
3873
+ // console.log( "duped entry found\nkey: " + propName + "\nvalue: " + propValue );
3874
+ if ( Array.isArray( currentNode.properties[ propName ] ) ) {
3875
+
3876
+ currentNode.properties[ propName ].push( propValue );
3877
+
3878
+ } else {
3879
+
3880
+ currentNode.properties[ propName ] += propValue;
3881
+
3882
+ }
3883
+
3884
+ } else {
3885
+
3886
+ // console.log( propName + ": " + propValue );
3887
+ if ( Array.isArray( currentNode.properties[ propName ] ) ) {
3888
+
3889
+ currentNode.properties[ propName ].push( propValue );
3890
+
3891
+ } else {
3892
+
3893
+ currentNode.properties[ propName ] = propValue;
3894
+
3895
+ }
3896
+
3897
+ }
3898
+
3899
+ this.setCurrentProp( currentNode.properties, propName );
3900
+
3901
+ },
3902
+
3903
+ // TODO:
3904
+ parseNodePropertyContinued: function ( line ) {
3905
+
3906
+ this.currentProp[ this.currentPropName ] += line;
3907
+
3908
+ },
3909
+
3910
+ parseNodeSpecialProperty: function ( line, propName, propValue ) {
3911
+
3912
+ // split this
3913
+ // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
3914
+ // into array like below
3915
+ // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ]
3916
+ var props = propValue.split( '",' );
3917
+
3918
+ for ( var i = 0, l = props.length; i < l; i ++ ) {
3919
+ props[ i ] = props[ i ].trim().replace( /^\"/, '' ).replace( /\s/, '_' );
3920
+ }
3921
+
3922
+ var innerPropName = props[ 0 ];
3923
+ var innerPropType1 = props[ 1 ];
3924
+ var innerPropType2 = props[ 2 ];
3925
+ var innerPropFlag = props[ 3 ];
3926
+ var innerPropValue = props[ 4 ];
3927
+
3928
+ /*
3929
+ if ( innerPropValue === undefined ) {
3930
+ innerPropValue = props[3];
3931
+ }
3932
+ */
3933
+
3934
+ // cast value in its type
3935
+ switch ( innerPropType1 ) {
3936
+
3937
+ case "int":
3938
+ innerPropValue = parseInt( innerPropValue );
3939
+ break;
3940
+
3941
+ case "double":
3942
+ innerPropValue = parseFloat( innerPropValue );
3943
+ break;
3944
+
3945
+ case "ColorRGB":
3946
+ case "Vector3D":
3947
+ innerPropValue = parseFloatArray( innerPropValue );
3948
+ break;
3949
+
3950
+ }
3951
+
3952
+ // CAUTION: these props must append to parent's parent
3953
+ this.getPrevNode().properties[ innerPropName ] = {
3954
+
3955
+ 'type': innerPropType1,
3956
+ 'type2': innerPropType2,
3957
+ 'flag': innerPropFlag,
3958
+ 'value': innerPropValue
3959
+
3960
+ };
3961
+
3962
+ this.setCurrentProp( this.getPrevNode().properties, innerPropName );
3963
+
3964
+ },
3965
+
3966
+ nodeEnd: function () {
3967
+
3968
+ this.popStack();
3969
+
3970
+ },
3971
+
3972
+ /* ---------------------------------------------------------------- */
3973
+ /* util */
3974
+ isFlattenNode: function ( node ) {
3975
+
3976
+ return ( 'subNodes' in node && 'properties' in node ) ? true : false;
3977
+
3978
+ }
3979
+
3980
+ } );
3981
+
3982
+ // Binary format specification:
3983
+ // https://code.blender.org/2013/08/fbx-binary-file-format-specification/
3984
+ // https://wiki.rogiken.org/specifications/file-format/fbx/ (more detail but Japanese)
3985
+ function BinaryParser() {}
3986
+
3987
+ Object.assign( BinaryParser.prototype, {
3988
+
3989
+ /**
3990
+ * Parses binary data and builds FBXTree as much compatible as possible with the one built by TextParser.
3991
+ * @param {ArrayBuffer} buffer
3992
+ * @returns {THREE.FBXTree}
3993
+ */
3994
+ parse: function ( buffer ) {
3995
+
3996
+ var reader = new BinaryReader( buffer );
3997
+ reader.skip( 23 ); // skip magic 23 bytes
3998
+
3999
+ var version = reader.getUint32();
4000
+
4001
+ console.log( 'FBX binary version: ' + version );
4002
+
4003
+ var allNodes = new FBXTree();
4004
+
4005
+ while ( ! this.endOfContent( reader ) ) {
4006
+
4007
+ var node = this.parseNode( reader, version );
4008
+ if ( node !== null ) allNodes.add( node.name, node );
4009
+
4010
+ }
4011
+
4012
+ return allNodes;
4013
+
4014
+ },
4015
+
4016
+ /**
4017
+ * Checks if reader has reached the end of content.
4018
+ * @param {BinaryReader} reader
4019
+ * @returns {boolean}
4020
+ */
4021
+ endOfContent: function( reader ) {
4022
+
4023
+ // footer size: 160bytes + 16-byte alignment padding
4024
+ // - 16bytes: magic
4025
+ // - padding til 16-byte alignment (at least 1byte?)
4026
+ // (seems like some exporters embed fixed 15bytes?)
4027
+ // - 4bytes: magic
4028
+ // - 4bytes: version
4029
+ // - 120bytes: zero
4030
+ // - 16bytes: magic
4031
+ if ( reader.size() % 16 === 0 ) {
4032
+
4033
+ return ( ( reader.getOffset() + 160 + 16 ) & ~0xf ) >= reader.size();
4034
+
4035
+ } else {
4036
+
4037
+ return reader.getOffset() + 160 + 15 >= reader.size();
4038
+
4039
+ }
4040
+
4041
+ },
4042
+
4043
+ /**
4044
+ * Parses Node as much compatible as possible with the one parsed by TextParser
4045
+ * TODO: could be optimized more?
4046
+ * @param {BinaryReader} reader
4047
+ * @param {number} version
4048
+ * @returns {Object} - Returns an Object as node, or null if NULL-record.
4049
+ */
4050
+ parseNode: function ( reader, version ) {
4051
+
4052
+ // The first three data sizes depends on version.
4053
+ var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32();
4054
+ var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32();
4055
+ var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32();
4056
+ var nameLen = reader.getUint8();
4057
+ var name = reader.getString( nameLen );
4058
+
4059
+ // Regards this node as NULL-record if endOffset is zero
4060
+ if ( endOffset === 0 ) return null;
4061
+
4062
+ var propertyList = [];
4063
+
4064
+ for ( var i = 0; i < numProperties; i ++ ) {
4065
+
4066
+ propertyList.push( this.parseProperty( reader ) );
4067
+
4068
+ }
4069
+
4070
+ // Regards the first three elements in propertyList as id, attrName, and attrType
4071
+ var id = propertyList.length > 0 ? propertyList[ 0 ] : '';
4072
+ var attrName = propertyList.length > 1 ? propertyList[ 1 ] : '';
4073
+ var attrType = propertyList.length > 2 ? propertyList[ 2 ] : '';
4074
+
4075
+ var subNodes = {};
4076
+ var properties = {};
4077
+
4078
+ var isSingleProperty = false;
4079
+
4080
+ // if this node represents just a single property
4081
+ // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]}
4082
+ if ( numProperties === 1 && reader.getOffset() === endOffset ) {
4083
+
4084
+ isSingleProperty = true;
4085
+
4086
+ }
4087
+
4088
+ while ( endOffset > reader.getOffset() ) {
4089
+
4090
+ var node = this.parseNode( reader, version );
4091
+
4092
+ if ( node === null ) continue;
4093
+
4094
+ // special case: child node is single property
4095
+ if ( node.singleProperty === true ) {
4096
+
4097
+ var value = node.propertyList[ 0 ];
4098
+
4099
+ if ( Array.isArray( value ) ) {
4100
+
4101
+ // node represents
4102
+ // Vertices: *3 {
4103
+ // a: 0.01, 0.02, 0.03
4104
+ // }
4105
+ // of text format here.
4106
+
4107
+ node.properties[ node.name ] = node.propertyList[ 0 ];
4108
+ subNodes[ node.name ] = node;
4109
+
4110
+ // Later phase expects single property array is in node.properties.a as String.
4111
+ // TODO: optimize
4112
+ node.properties.a = value.toString();
4113
+
4114
+ } else {
4115
+
4116
+ // node represents
4117
+ // Version: 100
4118
+ // of text format here.
4119
+
4120
+ properties[ node.name ] = value;
4121
+
4122
+ }
4123
+
4124
+ continue;
4125
+
4126
+ }
4127
+
4128
+ // special case: connections
4129
+ if ( name === 'Connections' && node.name === 'C' ) {
4130
+
4131
+ var array = [];
4132
+
4133
+ // node.propertyList would be like
4134
+ // ["OO", 111264976, 144038752, "d|x"] (?, from, to, additional values)
4135
+ for ( var i = 1, il = node.propertyList.length; i < il; i ++ ) {
4136
+
4137
+ array[ i - 1 ] = node.propertyList[ i ];
4138
+
4139
+ }
4140
+
4141
+ if ( properties.connections === undefined ) {
4142
+
4143
+ properties.connections = [];
4144
+
4145
+ }
4146
+
4147
+ properties.connections.push( array );
4148
+
4149
+ continue;
4150
+
4151
+ }
4152
+
4153
+ // special case: child node is Properties\d+
4154
+ if ( node.name.match( /^Properties\d+$/ ) ) {
4155
+
4156
+ // move child node's properties to this node.
4157
+
4158
+ var keys = Object.keys( node.properties );
4159
+
4160
+ for ( var i = 0, il = keys.length; i < il; i ++ ) {
4161
+
4162
+ var key = keys[ i ];
4163
+ properties[ key ] = node.properties[ key ];
4164
+
4165
+ }
4166
+
4167
+ continue;
4168
+
4169
+ }
4170
+
4171
+ // special case: properties
4172
+ if ( name.match( /^Properties\d+$/ ) && node.name === 'P' ) {
4173
+
4174
+ var innerPropName = node.propertyList[ 0 ];
4175
+ var innerPropType1 = node.propertyList[ 1 ];
4176
+ var innerPropType2 = node.propertyList[ 2 ];
4177
+ var innerPropFlag = node.propertyList[ 3 ];
4178
+ var innerPropValue;
4179
+
4180
+ if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' );
4181
+ if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' );
4182
+
4183
+ if ( innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' ||
4184
+ innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) {
4185
+
4186
+ innerPropValue = [
4187
+ node.propertyList[ 4 ],
4188
+ node.propertyList[ 5 ],
4189
+ node.propertyList[ 6 ]
4190
+ ];
4191
+
4192
+ } else {
4193
+
4194
+ innerPropValue = node.propertyList[ 4 ];
4195
+
4196
+ }
4197
+
4198
+ if ( innerPropType1.indexOf( 'Lcl_' ) === 0 ) {
4199
+
4200
+ innerPropValue = innerPropValue.toString();
4201
+
4202
+ }
4203
+
4204
+ // this will be copied to parent. see above.
4205
+ properties[ innerPropName ] = {
4206
+
4207
+ 'type': innerPropType1,
4208
+ 'type2': innerPropType2,
4209
+ 'flag': innerPropFlag,
4210
+ 'value': innerPropValue
4211
+
4212
+ };
4213
+
4214
+ continue;
4215
+
4216
+ }
4217
+
4218
+ // standard case
4219
+ // follows TextParser's manner.
4220
+ if ( subNodes[ node.name ] === undefined ) {
4221
+
4222
+ if ( typeof node.id === 'number' ) {
4223
+
4224
+ subNodes[ node.name ] = {};
4225
+ subNodes[ node.name ][ node.id ] = node;
4226
+
4227
+ } else {
4228
+
4229
+ subNodes[ node.name ] = node;
4230
+
4231
+ }
4232
+
4233
+ } else {
4234
+
4235
+ if ( node.id === '' ) {
4236
+
4237
+ if ( ! Array.isArray( subNodes[ node.name ] ) ) {
4238
+
4239
+ subNodes[ node.name ] = [ subNodes[ node.name ] ];
4240
+
4241
+ }
4242
+
4243
+ subNodes[ node.name ].push( node );
4244
+
4245
+ } else {
4246
+
4247
+ if ( subNodes[ node.name ][ node.id ] === undefined ) {
4248
+
4249
+ subNodes[ node.name ][ node.id ] = node;
4250
+
4251
+ } else {
4252
+
4253
+ // conflict id. irregular?
4254
+
4255
+ if ( ! Array.isArray( subNodes[ node.name ][ node.id ] ) ) {
4256
+
4257
+ subNodes[ node.name ][ node.id ] = [ subNodes[ node.name ][ node.id ] ];
4258
+
4259
+ }
4260
+
4261
+ subNodes[ node.name ][ node.id ].push( node );
4262
+
4263
+ }
4264
+
4265
+ }
4266
+
4267
+ }
4268
+
4269
+ }
4270
+
4271
+ return {
4272
+
4273
+ singleProperty: isSingleProperty,
4274
+ id: id,
4275
+ attrName: attrName,
4276
+ attrType: attrType,
4277
+ name: name,
4278
+ properties: properties,
4279
+ propertyList: propertyList, // raw property list, would be used by parent
4280
+ subNodes: subNodes
4281
+
4282
+ };
4283
+
4284
+ },
4285
+
4286
+ parseProperty: function ( reader ) {
4287
+
4288
+ var type = reader.getChar();
4289
+
4290
+ switch ( type ) {
4291
+
4292
+ case 'F':
4293
+ return reader.getFloat32();
4294
+
4295
+ case 'D':
4296
+ return reader.getFloat64();
4297
+
4298
+ case 'L':
4299
+ return reader.getInt64();
4300
+
4301
+ case 'I':
4302
+ return reader.getInt32();
4303
+
4304
+ case 'Y':
4305
+ return reader.getInt16();
4306
+
4307
+ case 'C':
4308
+ return reader.getBoolean();
4309
+
4310
+ case 'f':
4311
+ case 'd':
4312
+ case 'l':
4313
+ case 'i':
4314
+ case 'b':
4315
+
4316
+ var arrayLength = reader.getUint32();
4317
+ var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed
4318
+ var compressedLength = reader.getUint32();
4319
+
4320
+ if ( encoding === 0 ) {
4321
+
4322
+ switch ( type ) {
4323
+
4324
+ case 'f':
4325
+ return reader.getFloat32Array( arrayLength );
4326
+
4327
+ case 'd':
4328
+ return reader.getFloat64Array( arrayLength );
4329
+
4330
+ case 'l':
4331
+ return reader.getInt64Array( arrayLength );
4332
+
4333
+ case 'i':
4334
+ return reader.getInt32Array( arrayLength );
4335
+
4336
+ case 'b':
4337
+ return reader.getBooleanArray( arrayLength );
4338
+
4339
+ }
4340
+
4341
+ }
4342
+
4343
+ if ( typeof Zlib == 'undefined' ) {
4344
+
4345
+ throw new Error( 'FBXLoader: Import inflate.min.js from https://github.com/imaya/zlib.js' );
4346
+
4347
+ }
4348
+
4349
+ var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) );
4350
+ var reader2 = new BinaryReader( inflate.decompress().buffer );
4351
+
4352
+ switch ( type ) {
4353
+
4354
+ case 'f':
4355
+ return reader2.getFloat32Array( arrayLength );
4356
+
4357
+ case 'd':
4358
+ return reader2.getFloat64Array( arrayLength );
4359
+
4360
+ case 'l':
4361
+ return reader2.getInt64Array( arrayLength );
4362
+
4363
+ case 'i':
4364
+ return reader2.getInt32Array( arrayLength );
4365
+
4366
+ case 'b':
4367
+ return reader2.getBooleanArray( arrayLength );
4368
+
4369
+ }
4370
+
4371
+ case 'S':
4372
+ var length = reader.getUint32();
4373
+ return reader.getString( length );
4374
+
4375
+ case 'R':
4376
+ var length = reader.getUint32();
4377
+ return reader.getArrayBuffer( length );
4378
+
4379
+ default:
4380
+ throw new Error( 'FBXLoader: Unknown property type ' + type );
4381
+
4382
+ }
4383
+
4384
+ }
4385
+
4386
+ } );
4387
+
4388
+
4389
+ function BinaryReader( buffer, littleEndian ) {
4390
+
4391
+ this.dv = new DataView( buffer );
4392
+ this.offset = 0;
4393
+ this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true;
4394
+
4395
+ }
4396
+
4397
+ Object.assign( BinaryReader.prototype, {
4398
+
4399
+ getOffset: function () {
4400
+
4401
+ return this.offset;
4402
+
4403
+ },
4404
+
4405
+ size: function () {
4406
+
4407
+ return this.dv.buffer.byteLength;
4408
+
4409
+ },
4410
+
4411
+ skip: function ( length ) {
4412
+
4413
+ this.offset += length;
4414
+
4415
+ },
4416
+
4417
+ // seems like true/false representation depends on exporter.
4418
+ // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54)
4419
+ // then sees LSB.
4420
+ getBoolean: function () {
4421
+
4422
+ return ( this.getUint8() & 1 ) === 1;
4423
+
4424
+ },
4425
+
4426
+ getBooleanArray: function ( size ) {
4427
+
4428
+ var a = [];
4429
+
4430
+ for ( var i = 0; i < size; i ++ ) {
4431
+
4432
+ a.push( this.getBoolean() );
4433
+
4434
+ }
4435
+
4436
+ return a;
4437
+
4438
+ },
4439
+
4440
+ getInt8: function () {
4441
+
4442
+ var value = this.dv.getInt8( this.offset );
4443
+ this.offset += 1;
4444
+ return value;
4445
+
4446
+ },
4447
+
4448
+ getInt8Array: function ( size ) {
4449
+
4450
+ var a = [];
4451
+
4452
+ for ( var i = 0; i < size; i ++ ) {
4453
+
4454
+ a.push( this.getInt8() );
4455
+
4456
+ }
4457
+
4458
+ return a;
4459
+
4460
+ },
4461
+
4462
+ getUint8: function () {
4463
+
4464
+ var value = this.dv.getUint8( this.offset );
4465
+ this.offset += 1;
4466
+ return value;
4467
+
4468
+ },
4469
+
4470
+ getUint8Array: function ( size ) {
4471
+
4472
+ var a = [];
4473
+
4474
+ for ( var i = 0; i < size; i ++ ) {
4475
+
4476
+ a.push( this.getUint8() );
4477
+
4478
+ }
4479
+
4480
+ return a;
4481
+
4482
+ },
4483
+
4484
+ getInt16: function () {
4485
+
4486
+ var value = this.dv.getInt16( this.offset, this.littleEndian );
4487
+ this.offset += 2;
4488
+ return value;
4489
+
4490
+ },
4491
+
4492
+ getInt16Array: function ( size ) {
4493
+
4494
+ var a = [];
4495
+
4496
+ for ( var i = 0; i < size; i ++ ) {
4497
+
4498
+ a.push( this.getInt16() );
4499
+
4500
+ }
4501
+
4502
+ return a;
4503
+
4504
+ },
4505
+
4506
+ getUint16: function () {
4507
+
4508
+ var value = this.dv.getUint16( this.offset, this.littleEndian );
4509
+ this.offset += 2;
4510
+ return value;
4511
+
4512
+ },
4513
+
4514
+ getUint16Array: function ( size ) {
4515
+
4516
+ var a = [];
4517
+
4518
+ for ( var i = 0; i < size; i ++ ) {
4519
+
4520
+ a.push( this.getUint16() );
4521
+
4522
+ }
4523
+
4524
+ return a;
4525
+
4526
+ },
4527
+
4528
+ getInt32: function () {
4529
+
4530
+ var value = this.dv.getInt32( this.offset, this.littleEndian );
4531
+ this.offset += 4;
4532
+ return value;
4533
+
4534
+ },
4535
+
4536
+ getInt32Array: function ( size ) {
4537
+
4538
+ var a = [];
4539
+
4540
+ for ( var i = 0; i < size; i ++ ) {
4541
+
4542
+ a.push( this.getInt32() );
4543
+
4544
+ }
4545
+
4546
+ return a;
4547
+
4548
+ },
4549
+
4550
+ getUint32: function () {
4551
+
4552
+ var value = this.dv.getUint32( this.offset, this.littleEndian );
4553
+ this.offset += 4;
4554
+ return value;
4555
+
4556
+ },
4557
+
4558
+ getUint32Array: function ( size ) {
4559
+
4560
+ var a = [];
4561
+
4562
+ for ( var i = 0; i < size; i ++ ) {
4563
+
4564
+ a.push( this.getUint32() );
4565
+
4566
+ }
4567
+
4568
+ return a;
4569
+
4570
+ },
4571
+
4572
+ // JavaScript doesn't support 64-bit integer so attempting to calculate by ourselves.
4573
+ // 1 << 32 will return 1 so using multiply operation instead here.
4574
+ // There'd be a possibility that this method returns wrong value if the value
4575
+ // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER.
4576
+ // TODO: safely handle 64-bit integer
4577
+ getInt64: function () {
4578
+
4579
+ var low, high;
4580
+
4581
+ if ( this.littleEndian ) {
4582
+
4583
+ low = this.getUint32();
4584
+ high = this.getUint32();
4585
+
4586
+ } else {
4587
+
4588
+ high = this.getUint32();
4589
+ low = this.getUint32();
4590
+
4591
+ }
4592
+
4593
+ // calculate negative value
4594
+ if ( high & 0x80000000 ) {
4595
+
4596
+ high = ~high & 0xFFFFFFFF;
4597
+ low = ~low & 0xFFFFFFFF;
4598
+
4599
+ if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF;
4600
+
4601
+ low = ( low + 1 ) & 0xFFFFFFFF;
4602
+
4603
+ return - ( high * 0x100000000 + low );
4604
+
4605
+ }
4606
+
4607
+ return high * 0x100000000 + low;
4608
+
4609
+ },
4610
+
4611
+ getInt64Array: function ( size ) {
4612
+
4613
+ var a = [];
4614
+
4615
+ for ( var i = 0; i < size; i ++ ) {
4616
+
4617
+ a.push( this.getInt64() );
4618
+
4619
+ }
4620
+
4621
+ return a;
4622
+
4623
+ },
4624
+
4625
+ // Note: see getInt64() comment
4626
+ getUint64: function () {
4627
+
4628
+ var low, high;
4629
+
4630
+ if ( this.littleEndian ) {
4631
+
4632
+ low = this.getUint32();
4633
+ high = this.getUint32();
4634
+
4635
+ } else {
4636
+
4637
+ high = this.getUint32();
4638
+ low = this.getUint32();
4639
+
4640
+ }
4641
+
4642
+ return high * 0x100000000 + low;
4643
+
4644
+ },
4645
+
4646
+ getUint64Array: function ( size ) {
4647
+
4648
+ var a = [];
4649
+
4650
+ for ( var i = 0; i < size; i ++ ) {
4651
+
4652
+ a.push( this.getUint64() );
4653
+
4654
+ }
4655
+
4656
+ return a;
4657
+
4658
+ },
4659
+
4660
+ getFloat32: function () {
4661
+
4662
+ var value = this.dv.getFloat32( this.offset, this.littleEndian );
4663
+ this.offset += 4;
4664
+ return value;
4665
+
4666
+ },
4667
+
4668
+ getFloat32Array: function ( size ) {
4669
+
4670
+ var a = [];
4671
+
4672
+ for ( var i = 0; i < size; i ++ ) {
4673
+
4674
+ a.push( this.getFloat32() );
4675
+
4676
+ }
4677
+
4678
+ return a;
4679
+
4680
+ },
4681
+
4682
+ getFloat64: function () {
4683
+
4684
+ var value = this.dv.getFloat64( this.offset, this.littleEndian );
4685
+ this.offset += 8;
4686
+ return value;
4687
+
4688
+ },
4689
+
4690
+ getFloat64Array: function ( size ) {
4691
+
4692
+ var a = [];
4693
+
4694
+ for ( var i = 0; i < size; i ++ ) {
4695
+
4696
+ a.push( this.getFloat64() );
4697
+
4698
+ }
4699
+
4700
+ return a;
4701
+
4702
+ },
4703
+
4704
+ getArrayBuffer: function ( size ) {
4705
+
4706
+ var value = this.dv.buffer.slice( this.offset, this.offset + size );
4707
+ this.offset += size;
4708
+ return value;
4709
+
4710
+ },
4711
+
4712
+ getChar: function () {
4713
+
4714
+ return String.fromCharCode( this.getUint8() );
4715
+
4716
+ },
4717
+
4718
+ getString: function ( size ) {
4719
+
4720
+ var s = '';
4721
+
4722
+ while ( size > 0 ) {
4723
+
4724
+ var value = this.getUint8();
4725
+ size--;
4726
+
4727
+ if ( value === 0 ) break;
4728
+
4729
+ s += String.fromCharCode( value );
4730
+
4731
+ }
4732
+
4733
+ this.skip( size );
4734
+
4735
+ return s;
4736
+
4737
+ }
4738
+
4739
+ } );
4740
+
4741
+
4742
+ function FBXTree() {}
4743
+
4744
+ Object.assign( FBXTree.prototype, {
4745
+
4746
+ add: function ( key, val ) {
4747
+
4748
+ this[ key ] = val;
4749
+
4750
+ },
4751
+
4752
+ searchConnectionParent: function ( id ) {
4753
+
4754
+ if ( this.__cache_search_connection_parent === undefined ) {
4755
+
4756
+ this.__cache_search_connection_parent = [];
4757
+
4758
+ }
4759
+
4760
+ if ( this.__cache_search_connection_parent[ id ] !== undefined ) {
4761
+
4762
+ return this.__cache_search_connection_parent[ id ];
4763
+
4764
+ } else {
4765
+
4766
+ this.__cache_search_connection_parent[ id ] = [];
4767
+
4768
+ }
4769
+
4770
+ var conns = this.Connections.properties.connections;
4771
+
4772
+ var results = [];
4773
+ for ( var i = 0; i < conns.length; ++ i ) {
4774
+
4775
+ if ( conns[ i ][ 0 ] == id ) {
4776
+
4777
+ // 0 means scene root
4778
+ var res = conns[ i ][ 1 ] === 0 ? - 1 : conns[ i ][ 1 ];
4779
+ results.push( res );
4780
+
4781
+ }
4782
+
4783
+ }
4784
+
4785
+ if ( results.length > 0 ) {
4786
+
4787
+ append( this.__cache_search_connection_parent[ id ], results );
4788
+ return results;
4789
+
4790
+ } else {
4791
+
4792
+ this.__cache_search_connection_parent[ id ] = [ - 1 ];
4793
+ return [ - 1 ];
4794
+
4795
+ }
4796
+
4797
+ },
4798
+
4799
+ searchConnectionChildren: function ( id ) {
4800
+
4801
+ if ( this.__cache_search_connection_children === undefined ) {
4802
+
4803
+ this.__cache_search_connection_children = [];
4804
+
4805
+ }
4806
+
4807
+ if ( this.__cache_search_connection_children[ id ] !== undefined ) {
4808
+
4809
+ return this.__cache_search_connection_children[ id ];
4810
+
4811
+ } else {
4812
+
4813
+ this.__cache_search_connection_children[ id ] = [];
4814
+
4815
+ }
4816
+
4817
+ var conns = this.Connections.properties.connections;
4818
+
4819
+ var res = [];
4820
+ for ( var i = 0; i < conns.length; ++ i ) {
4821
+
4822
+ if ( conns[ i ][ 1 ] == id ) {
4823
+
4824
+ // 0 means scene root
4825
+ res.push( conns[ i ][ 0 ] === 0 ? - 1 : conns[ i ][ 0 ] );
4826
+ // there may more than one kid, then search to the end
4827
+
4828
+ }
4829
+
4830
+ }
4831
+
4832
+ if ( res.length > 0 ) {
4833
+
4834
+ append( this.__cache_search_connection_children[ id ], res );
4835
+ return res;
4836
+
4837
+ } else {
4838
+
4839
+ this.__cache_search_connection_children[ id ] = [ ];
4840
+ return [ ];
4841
+
4842
+ }
4843
+
4844
+ },
4845
+
4846
+ searchConnectionType: function ( id, to ) {
4847
+
4848
+ var key = id + ',' + to; // TODO: to hash
4849
+ if ( this.__cache_search_connection_type === undefined ) {
4850
+
4851
+ this.__cache_search_connection_type = {};
4852
+
4853
+ }
4854
+
4855
+ if ( this.__cache_search_connection_type[ key ] !== undefined ) {
4856
+
4857
+ return this.__cache_search_connection_type[ key ];
4858
+
4859
+ } else {
4860
+
4861
+ this.__cache_search_connection_type[ key ] = '';
4862
+
4863
+ }
4864
+
4865
+ var conns = this.Connections.properties.connections;
4866
+
4867
+ for ( var i = 0; i < conns.length; ++ i ) {
4868
+
4869
+ if ( conns[ i ][ 0 ] == id && conns[ i ][ 1 ] == to ) {
4870
+
4871
+ // 0 means scene root
4872
+ this.__cache_search_connection_type[ key ] = conns[ i ][ 2 ];
4873
+ return conns[ i ][ 2 ];
4874
+
4875
+ }
4876
+
4877
+ }
4878
+
4879
+ this.__cache_search_connection_type[ id ] = null;
4880
+ return null;
4881
+
4882
+ }
4883
+
4884
+ } );
4885
+
4886
+
4887
+ /**
4888
+ * @param {ArrayBuffer} buffer
4889
+ * @returns {boolean}
4890
+ */
4891
+ function isFbxFormatBinary( buffer ) {
4892
+
4893
+ var CORRECT = 'Kaydara FBX Binary \0';
4894
+
4895
+ return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length );
4896
+
4897
+ }
4898
+
4899
+ /**
4900
+ * @returns {boolean}
4901
+ */
4902
+ function isFbxFormatASCII( text ) {
4903
+
4904
+ var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ];
4905
+
4906
+ var cursor = 0;
4907
+
4908
+ function read( offset ) {
4909
+
4910
+ var result = text[ offset - 1 ];
4911
+ text = text.slice( cursor + offset );
4912
+ cursor ++;
4913
+ return result;
4914
+
4915
+ }
4916
+
4917
+ for ( var i = 0; i < CORRECT.length; ++ i ) {
4918
+
4919
+ var num = read( 1 );
4920
+ if ( num == CORRECT[ i ] ) {
4921
+
4922
+ return false;
4923
+
4924
+ }
4925
+
4926
+ }
4927
+
4928
+ return true;
4929
+
4930
+ }
4931
+
4932
+ /**
4933
+ * @returns {number}
4934
+ */
4935
+ function getFbxVersion( text ) {
4936
+
4937
+ var versionRegExp = /FBXVersion: (\d+)/;
4938
+ var match = text.match( versionRegExp );
4939
+ if ( match ) {
4940
+
4941
+ var version = parseInt( match[ 1 ] );
4942
+ return version;
4943
+
4944
+ }
4945
+ throw new Error( 'FBXLoader: Cannot find the version number for the file given.' );
4946
+
4947
+ }
4948
+
4949
+ /**
4950
+ * Converts FBX ticks into real time seconds.
4951
+ * @param {number} time - FBX tick timestamp to convert.
4952
+ * @returns {number} - FBX tick in real world time.
4953
+ */
4954
+ function convertFBXTimeToSeconds( time ) {
4955
+
4956
+ // Constant is FBX ticks per second.
4957
+ return time / 46186158000;
4958
+
4959
+ }
4960
+
4961
+ /**
4962
+ * Parses comma separated list of float numbers and returns them in an array.
4963
+ * @example
4964
+ * // Returns [ 5.6, 9.4, 2.5, 1.4 ]
4965
+ * parseFloatArray( "5.6,9.4,2.5,1.4" )
4966
+ * @returns {number[]}
4967
+ */
4968
+ function parseFloatArray( string ) {
4969
+
4970
+ var array = string.split( ',' );
4971
+
4972
+ for ( var i = 0, l = array.length; i < l; i ++ ) {
4973
+
4974
+ array[ i ] = parseFloat( array[ i ] );
4975
+
4976
+ }
4977
+
4978
+ return array;
4979
+
4980
+ }
4981
+
4982
+ /**
4983
+ * Parses comma separated list of int numbers and returns them in an array.
4984
+ * @example
4985
+ * // Returns [ 5, 8, 2, 3 ]
4986
+ * parseFloatArray( "5,8,2,3" )
4987
+ * @returns {number[]}
4988
+ */
4989
+ function parseIntArray( string ) {
4990
+
4991
+ var array = string.split( ',' );
4992
+
4993
+ for ( var i = 0, l = array.length; i < l; i ++ ) {
4994
+
4995
+ array[ i ] = parseInt( array[ i ] );
4996
+
4997
+ }
4998
+
4999
+ return array;
5000
+
5001
+ }
5002
+
5003
+ /**
5004
+ * Parses Vector3 property from FBXTree. Property is given as .value.x, .value.y, etc.
5005
+ * @param {FBXVector3} property - Property to parse as Vector3.
5006
+ * @returns {THREE.Vector3}
5007
+ */
5008
+ function parseVector3( property ) {
5009
+
5010
+ return new THREE.Vector3().fromArray( property.value );
5011
+
5012
+ }
5013
+
5014
+ /**
5015
+ * Parses Color property from FBXTree. Property is given as .value.x, .value.y, etc.
5016
+ * @param {FBXVector3} property - Property to parse as Color.
5017
+ * @returns {THREE.Color}
5018
+ */
5019
+ function parseColor( property ) {
5020
+
5021
+ return new THREE.Color().fromArray( property.value );
5022
+
5023
+ }
5024
+
5025
+ function parseMatrixArray( floatString ) {
5026
+
5027
+ return new THREE.Matrix4().fromArray( parseFloatArray( floatString ) );
5028
+
5029
+ }
5030
+
5031
+ /**
5032
+ * Converts ArrayBuffer to String.
5033
+ * @param {ArrayBuffer} buffer
5034
+ * @param {number} from
5035
+ * @param {number} to
5036
+ * @returns {String}
5037
+ */
5038
+ function convertArrayBufferToString( buffer, from, to ) {
5039
+
5040
+ if ( from === undefined ) from = 0;
5041
+ if ( to === undefined ) to = buffer.byteLength;
5042
+
5043
+ var array = new Uint8Array( buffer, from, to );
5044
+
5045
+ if ( typeof TextDecoder == 'undefined' ) {
5046
+
5047
+ return new TextDecoder().decode( array );
5048
+
5049
+ }
5050
+
5051
+ var s = '';
5052
+
5053
+ for ( var i = 0, il = array.length; i < il; i ++ ) {
5054
+
5055
+ s += String.fromCharCode( array[ i ] );
5056
+
5057
+ }
5058
+
5059
+ return s;
5060
+
5061
+ }
5062
+
5063
+ /**
5064
+ * Converts number from degrees into radians.
5065
+ * @param {number} value
5066
+ * @returns {number}
5067
+ */
5068
+ function degreeToRadian( value ) {
5069
+
5070
+ return value * DEG2RAD;
5071
+
5072
+ }
5073
+
5074
+ var DEG2RAD = Math.PI / 180;
5075
+
5076
+ //
5077
+
5078
+ function findIndex( array, func ) {
5079
+
5080
+ for ( var i = 0, l = array.length; i < l; i ++ ) {
5081
+
5082
+ if ( func( array[ i ] ) ) return i;
5083
+
5084
+ }
5085
+
5086
+ return -1;
5087
+
5088
+ }
5089
+
5090
+ function append( a, b ) {
5091
+
5092
+ for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) {
5093
+
5094
+ a[ j ] = b[ i ];
5095
+
5096
+ }
5097
+
5098
+ }
5099
+
5100
+ function slice( a, b, from, to ) {
5101
+
5102
+ for ( var i = from, j = 0; i < to; i ++, j ++ ) {
5103
+
5104
+ a[ j ] = b[ i ];
5105
+
5106
+ }
5107
+
5108
+ return a;
5109
+
5110
+ }
5111
+
5112
+ } )();