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