GLTFExporter.js 28.4 KB
Newer Older
F
Fernando Serrano 已提交
1 2 3 4
/**
 * @author fernandojsg / http://fernandojsg.com
 */

M
Mugen87 已提交
5 6 7
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
8 9
var WEBGL_CONSTANTS = {
	POINTS: 0x0000,
F
Fernando Serrano 已提交
10 11 12 13 14 15
	LINES: 0x0001,
	LINE_LOOP: 0x0002,
	LINE_STRIP: 0x0003,
	TRIANGLES: 0x0004,
	TRIANGLE_STRIP: 0x0005,
	TRIANGLE_FAN: 0x0006,
16 17 18 19 20 21 22 23 24

	UNSIGNED_BYTE: 0x1401,
	UNSIGNED_SHORT: 0x1403,
	FLOAT: 0x1406,
	UNSIGNED_INT: 0x1405,
	ARRAY_BUFFER: 0x8892,
	ELEMENT_ARRAY_BUFFER: 0x8893,

	NEAREST: 0x2600,
F
Fernando Serrano 已提交
25 26 27 28 29
	LINEAR: 0x2601,
	NEAREST_MIPMAP_NEAREST: 0x2700,
	LINEAR_MIPMAP_NEAREST: 0x2701,
	NEAREST_MIPMAP_LINEAR: 0x2702,
	LINEAR_MIPMAP_LINEAR: 0x2703
30 31 32 33
};

var THREE_TO_WEBGL = {
	// @TODO Replace with computed property name [THREE.*] when available on es6
F
Fernando Serrano 已提交
34
	1003: WEBGL_CONSTANTS.NEAREST,
35 36 37 38
	1004: WEBGL_CONSTANTS.NEAREST_MIPMAP_NEAREST,
	1005: WEBGL_CONSTANTS.NEAREST_MIPMAP_LINEAR,
	1006: WEBGL_CONSTANTS.LINEAR,
	1007: WEBGL_CONSTANTS.LINEAR_MIPMAP_NEAREST,
F
Fernando Serrano 已提交
39
	1008: WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR
M
Mugen87 已提交
40
};
41

42 43 44 45 46 47 48
var PATH_PROPERTIES = {
	scale: 'scale',
	position: 'translation',
	quaternion: 'rotation',
	morphTargetInfluences: 'weights'
};

F
Fernando Serrano 已提交
49 50 51
//------------------------------------------------------------------------------
// GLTF Exporter
//------------------------------------------------------------------------------
52
THREE.GLTFExporter = function () {};
F
Fernando Serrano 已提交
53 54 55 56

THREE.GLTFExporter.prototype = {

	constructor: THREE.GLTFExporter,
F
Fernando Serrano 已提交
57

F
Fernando Serrano 已提交
58 59 60
	/**
	 * Parse scenes and generate GLTF output
	 * @param  {THREE.Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
F
Fernando Serrano 已提交
61 62
	 * @param  {Function} onDone  Callback on completed
	 * @param  {Object} options options
F
Fernando Serrano 已提交
63
	 */
F
Fernando Serrano 已提交
64 65
	parse: function ( input, onDone, options ) {

66 67
		var DEFAULT_OPTIONS = {
			trs: false,
68
			onlyVisible: true,
69
			truncateDrawRange: true,
70 71
			embedImages: true,
			animations: []
72 73 74 75
		};

		options = Object.assign( {}, DEFAULT_OPTIONS, options );

D
Don McCurdy 已提交
76 77 78 79 80 81 82
		if ( options.animations.length > 0 ) {

			// Only TRS properties, and not matrices, may be targeted by animation.
			options.trs = true;

		}

F
Fernando Serrano 已提交
83
		var outputJSON = {
F
Fernando Serrano 已提交
84

F
Fernando Serrano 已提交
85
			asset: {
F
Fernando Serrano 已提交
86

F
Fernando Serrano 已提交
87
				version: "2.0",
M
Mr.doob 已提交
88
				generator: "THREE.GLTFExporter"
F
Fernando Serrano 已提交
89

M
Mr.doob 已提交
90
			}
F
Fernando Serrano 已提交
91

M
Mr.doob 已提交
92
		};
F
Fernando Serrano 已提交
93 94 95

		var byteOffset = 0;
		var dataViews = [];
96
		var nodeMap = {};
D
Don McCurdy 已提交
97
		var skins = [];
98 99 100 101 102 103
		var cachedData = {

			images: {},
			materials: {}

		};
F
Fernando Serrano 已提交
104

105 106
		var cachedCanvas;

F
Fernando Serrano 已提交
107 108 109
		/**
		 * Compare two arrays
		 */
F
Fernando Serrano 已提交
110 111 112 113 114 115
		/**
		 * Compare two arrays
		 * @param  {Array} array1 Array 1 to compare
		 * @param  {Array} array2 Array 2 to compare
		 * @return {Boolean}        Returns true if both arrays are equal
		 */
M
Mr.doob 已提交
116
		function equalArray( array1, array2 ) {
F
Fernando Serrano 已提交
117

M
Mugen87 已提交
118
			return ( array1.length === array2.length ) && array1.every( function ( element, index ) {
F
Fernando Serrano 已提交
119

M
Mr.doob 已提交
120
				return element === array2[ index ];
F
Fernando Serrano 已提交
121

M
Mugen87 已提交
122
			} );
F
Fernando Serrano 已提交
123

F
Fernando Serrano 已提交
124 125
		}

126 127 128 129 130 131 132 133 134 135 136 137 138
		/**
		 * Converts a string to an ArrayBuffer.
		 * @param  {string} text
		 * @return {ArrayBuffer}
		 */
		function stringToArrayBuffer( text ) {

			if ( window.TextEncoder !== undefined ) {

				return new TextEncoder().encode( text ).buffer;

			}

139
			var buffer = new ArrayBuffer( text.length );
140

141
			var bufferView = new Uint8Array( buffer );
142 143 144 145 146 147 148 149 150 151 152

			for ( var i = 0; i < text.length; ++ i ) {

				bufferView[ i ] = text.charCodeAt( i );

			}

			return buffer;

		}

F
Fernando Serrano 已提交
153
		/**
154 155
		 * Get the min and max vectors from the given attribute
		 * @param  {THREE.BufferAttribute} attribute Attribute to find the min/max
F
Fernando Serrano 已提交
156 157
		 * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
		 */
M
Mr.doob 已提交
158
		function getMinMax( attribute ) {
F
Fernando Serrano 已提交
159

F
Fernando Serrano 已提交
160
			var output = {
F
Fernando Serrano 已提交
161

F
Fernando Serrano 已提交
162 163
				min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ),
				max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY )
F
Fernando Serrano 已提交
164

F
Fernando Serrano 已提交
165 166
			};

M
Mugen87 已提交
167
			for ( var i = 0; i < attribute.count; i ++ ) {
F
Fernando Serrano 已提交
168

M
Mugen87 已提交
169
				for ( var a = 0; a < attribute.itemSize; a ++ ) {
F
Fernando Serrano 已提交
170

F
Fernando Serrano 已提交
171
					var value = attribute.array[ i * attribute.itemSize + a ];
F
Fernando Serrano 已提交
172 173 174
					output.min[ a ] = Math.min( output.min[ a ], value );
					output.max[ a ] = Math.max( output.max[ a ], value );

F
Fernando Serrano 已提交
175
				}
F
Fernando Serrano 已提交
176

F
Fernando Serrano 已提交
177 178
			}

F
Fernando Serrano 已提交
179
			return output;
M
Mugen87 已提交
180

F
Fernando Serrano 已提交
181 182
		}

F
Fernando Serrano 已提交
183
		/**
F
Fernando Serrano 已提交
184 185 186 187
		 * Process a buffer to append to the default one.
		 * @param  {THREE.BufferAttribute} attribute     Attribute to store
		 * @param  {Integer} componentType Component type (Unsigned short, unsigned int or float)
		 * @return {Integer}               Index of the buffer created (Currently always 0)
F
Fernando Serrano 已提交
188
		 */
M
Mr.doob 已提交
189
		function processBuffer( attribute, componentType, start, count ) {
F
Fernando Serrano 已提交
190

M
Mugen87 已提交
191
			if ( ! outputJSON.buffers ) {
F
Fernando Serrano 已提交
192

F
Fernando Serrano 已提交
193
				outputJSON.buffers = [
F
Fernando Serrano 已提交
194

F
Fernando Serrano 已提交
195
					{
F
Fernando Serrano 已提交
196

F
Fernando Serrano 已提交
197 198
						byteLength: 0,
						uri: ''
F
Fernando Serrano 已提交
199

F
Fernando Serrano 已提交
200
					}
F
Fernando Serrano 已提交
201

F
Fernando Serrano 已提交
202
				];
F
Fernando Serrano 已提交
203

F
Fernando Serrano 已提交
204 205
			}

206 207 208
			var offset = 0;
			var componentSize = componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ? 2 : 4;

F
Fernando Serrano 已提交
209
			// Create a new dataview and dump the attribute's array into it
210
			var byteLength = count * attribute.itemSize * componentSize;
F
Fernando Serrano 已提交
211

212
			var dataView = new DataView( new ArrayBuffer( byteLength ) );
F
Fernando Serrano 已提交
213

M
Mugen87 已提交
214
			for ( var i = start; i < start + count; i ++ ) {
F
Fernando Serrano 已提交
215

M
Mugen87 已提交
216
				for ( var a = 0; a < attribute.itemSize; a ++ ) {
F
Fernando Serrano 已提交
217

F
Fernando Serrano 已提交
218
					var value = attribute.array[ i * attribute.itemSize + a ];
F
Fernando Serrano 已提交
219

220
					if ( componentType === WEBGL_CONSTANTS.FLOAT ) {
F
Fernando Serrano 已提交
221

F
Fernando Serrano 已提交
222
						dataView.setFloat32( offset, value, true );
F
Fernando Serrano 已提交
223

224
					} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_INT ) {
F
Fernando Serrano 已提交
225

F
Fernando Serrano 已提交
226
						dataView.setUint8( offset, value, true );
F
Fernando Serrano 已提交
227

228
					} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ) {
F
Fernando Serrano 已提交
229

F
Fernando Serrano 已提交
230
						dataView.setUint16( offset, value, true );
F
Fernando Serrano 已提交
231

F
Fernando Serrano 已提交
232
					}
F
Fernando Serrano 已提交
233

234
					offset += componentSize;
F
Fernando Serrano 已提交
235

F
Fernando Serrano 已提交
236
				}
F
Fernando Serrano 已提交
237

F
Fernando Serrano 已提交
238 239
			}

F
Fernando Serrano 已提交
240
			// We just use one buffer
F
Fernando Serrano 已提交
241
			dataViews.push( dataView );
F
Fernando Serrano 已提交
242

F
Fernando Serrano 已提交
243
			// Always using just one buffer
F
Fernando Serrano 已提交
244
			return 0;
M
Mugen87 已提交
245

F
Fernando Serrano 已提交
246 247 248
		}

		/**
F
Fernando Serrano 已提交
249
		 * Process and generate a BufferView
250 251 252 253 254 255
		 * @param  {THREE.BufferAttribute} data
		 * @param  {number} componentType
		 * @param  {number} start
		 * @param  {number} count
		 * @param  {number} target (Optional) Target usage of the BufferView
		 * @return {Object}
F
Fernando Serrano 已提交
256
		 */
257
		function processBufferView( data, componentType, start, count, target ) {
F
Fernando Serrano 已提交
258

M
Mugen87 已提交
259
			if ( ! outputJSON.bufferViews ) {
F
Fernando Serrano 已提交
260

F
Fernando Serrano 已提交
261
				outputJSON.bufferViews = [];
F
Fernando Serrano 已提交
262

F
Fernando Serrano 已提交
263 264
			}

265 266 267 268 269
			var componentSize = componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ? 2 : 4;

			// Create a new dataview and dump the attribute's array into it
			var byteLength = count * data.itemSize * componentSize;

F
Fernando Serrano 已提交
270
			var gltfBufferView = {
F
Fernando Serrano 已提交
271

272
				buffer: processBuffer( data, componentType, start, count ),
F
Fernando Serrano 已提交
273
				byteOffset: byteOffset,
274
				byteLength: byteLength
F
Fernando Serrano 已提交
275

F
Fernando Serrano 已提交
276 277
			};

278 279
			if ( target !== undefined ) gltfBufferView.target = target;

280 281 282 283 284 285 286
			if ( target === WEBGL_CONSTANTS.ARRAY_BUFFER ) {

				// Only define byteStride for vertex attributes.
				gltfBufferView.byteStride = data.itemSize * componentSize;

			}

287
			byteOffset += byteLength;
F
Fernando Serrano 已提交
288

F
Fernando Serrano 已提交
289
			outputJSON.bufferViews.push( gltfBufferView );
F
Fernando Serrano 已提交
290 291

			// @TODO Ideally we'll have just two bufferviews: 0 is for vertex attributes, 1 for indices
F
Fernando Serrano 已提交
292
			var output = {
F
Fernando Serrano 已提交
293

F
Fernando Serrano 已提交
294 295
				id: outputJSON.bufferViews.length - 1,
				byteLength: 0
F
Fernando Serrano 已提交
296

F
Fernando Serrano 已提交
297
			};
F
Fernando Serrano 已提交
298

F
Fernando Serrano 已提交
299
			return output;
F
Fernando Serrano 已提交
300

F
Fernando Serrano 已提交
301 302 303
		}

		/**
F
Fernando Serrano 已提交
304
		 * Process attribute to generate an accessor
305 306
		 * @param  {THREE.BufferAttribute} attribute Attribute to process
		 * @param  {THREE.BufferGeometry} geometry (Optional) Geometry used for truncated draw range
F
Fernando Serrano 已提交
307
		 * @return {Integer}           Index of the processed accessor on the "accessors" array
F
Fernando Serrano 已提交
308
		 */
M
Mr.doob 已提交
309
		function processAccessor( attribute, geometry ) {
F
Fernando Serrano 已提交
310

M
Mugen87 已提交
311
			if ( ! outputJSON.accessors ) {
F
Fernando Serrano 已提交
312

F
Fernando Serrano 已提交
313
				outputJSON.accessors = [];
F
Fernando Serrano 已提交
314

F
Fernando Serrano 已提交
315 316
			}

D
Don McCurdy 已提交
317
			var types = {
F
Fernando Serrano 已提交
318

D
Don McCurdy 已提交
319 320 321 322 323
				1: 'SCALAR',
				2: 'VEC2',
				3: 'VEC3',
				4: 'VEC4',
				16: 'MAT4'
F
Fernando Serrano 已提交
324

D
Don McCurdy 已提交
325
			};
F
Fernando Serrano 已提交
326

327 328
			var componentType;

F
Fernando Serrano 已提交
329
			// Detect the component type of the attribute array (float, uint or ushort)
330
			if ( attribute.array.constructor === Float32Array ) {
F
Fernando Serrano 已提交
331

332
				componentType = WEBGL_CONSTANTS.FLOAT;
F
Fernando Serrano 已提交
333

334
			} else if ( attribute.array.constructor === Uint32Array ) {
F
Fernando Serrano 已提交
335

336
				componentType = WEBGL_CONSTANTS.UNSIGNED_INT;
F
Fernando Serrano 已提交
337

338
			} else if ( attribute.array.constructor === Uint16Array ) {
F
Fernando Serrano 已提交
339

340
				componentType = WEBGL_CONSTANTS.UNSIGNED_SHORT;
F
Fernando Serrano 已提交
341

342
			} else {
F
Fernando Serrano 已提交
343

344
				throw new Error( 'THREE.GLTFExporter: Unsupported bufferAttribute component type.' );
F
Fernando Serrano 已提交
345

346
			}
F
Fernando Serrano 已提交
347 348

			var minMax = getMinMax( attribute );
349 350 351 352 353

			var start = 0;
			var count = attribute.count;

			// @TODO Indexed buffer geometry with drawRange not supported yet
354
			if ( options.truncateDrawRange && geometry !== undefined && geometry.index === null ) {
M
Mugen87 已提交
355

356 357
				start = geometry.drawRange.start;
				count = geometry.drawRange.count !== Infinity ? geometry.drawRange.count : attribute.count;
M
Mugen87 已提交
358

359 360
			}

361 362 363 364 365 366 367 368 369 370 371 372
			var bufferViewTarget;

			// If geometry isn't provided, don't infer the target usage of the bufferView. For
			// animation samplers, target must not be set.
			if ( geometry !== undefined ) {

				var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
				bufferViewTarget = isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER;

			}

			var bufferView = processBufferView( attribute, componentType, start, count, bufferViewTarget );
F
Fernando Serrano 已提交
373

F
Fernando Serrano 已提交
374
			var gltfAccessor = {
F
Fernando Serrano 已提交
375

F
Fernando Serrano 已提交
376 377 378
				bufferView: bufferView.id,
				byteOffset: bufferView.byteOffset,
				componentType: componentType,
379
				count: count,
F
Fernando Serrano 已提交
380 381
				max: minMax.max,
				min: minMax.min,
D
Don McCurdy 已提交
382
				type: types[ attribute.itemSize ]
F
Fernando Serrano 已提交
383

F
Fernando Serrano 已提交
384 385 386 387 388
			};

			outputJSON.accessors.push( gltfAccessor );

			return outputJSON.accessors.length - 1;
F
Fernando Serrano 已提交
389

F
Fernando Serrano 已提交
390 391 392
		}

		/**
F
Fernando Serrano 已提交
393 394 395 396
		 * Process image
		 * @param  {Texture} map Texture to process
		 * @return {Integer}     Index of the processed texture in the "images" array
		 */
M
Mr.doob 已提交
397
		function processImage( map ) {
F
Fernando Serrano 已提交
398

T
Takahiro 已提交
399
			if ( cachedData.images[ map.uuid ] !== undefined ) {
400 401 402 403 404

				return cachedData.images[ map.uuid ];

			}

M
Mugen87 已提交
405
			if ( ! outputJSON.images ) {
F
Fernando Serrano 已提交
406

F
Fernando Serrano 已提交
407
				outputJSON.images = [];
F
Fernando Serrano 已提交
408

F
Fernando Serrano 已提交
409 410
			}

411 412
			var mimeType = map.format === THREE.RGBAFormat ? 'image/png' : 'image/jpeg';
			var gltfImage = {mimeType: mimeType};
413

F
Fernando Serrano 已提交
414
			if ( options.embedImages ) {
F
Fernando Serrano 已提交
415

416 417 418 419
				var canvas = cachedCanvas = cachedCanvas || document.createElement( 'canvas' );
				canvas.width = map.image.width;
				canvas.height = map.image.height;
				var ctx = canvas.getContext( '2d' );
420 421 422 423 424 425 426 427

				if ( map.flipY === true ) {

					ctx.translate( 0, map.image.height );
					ctx.scale( 1, -1 );

				}

428 429
				ctx.drawImage( map.image, 0, 0 );

430 431 432
				// @TODO Embed in { bufferView } if options.binary set.

				gltfImage.uri = canvas.toDataURL( mimeType );
F
Fernando Serrano 已提交
433

F
Fernando Serrano 已提交
434
			} else {
F
Fernando Serrano 已提交
435

436
				gltfImage.uri = map.image.src;
F
Fernando Serrano 已提交
437

F
Fernando Serrano 已提交
438 439 440 441
			}

			outputJSON.images.push( gltfImage );

442 443 444 445
			var index = outputJSON.images.length - 1;
			cachedData.images[ map.uuid ] = index;

			return index;
F
Fernando Serrano 已提交
446

F
Fernando Serrano 已提交
447 448 449 450 451 452 453
		}

		/**
		 * Process sampler
		 * @param  {Texture} map Texture to process
		 * @return {Integer}     Index of the processed texture in the "samplers" array
		 */
M
Mr.doob 已提交
454
		function processSampler( map ) {
F
Fernando Serrano 已提交
455

M
Mugen87 已提交
456
			if ( ! outputJSON.samplers ) {
F
Fernando Serrano 已提交
457

F
Fernando Serrano 已提交
458
				outputJSON.samplers = [];
F
Fernando Serrano 已提交
459

F
Fernando Serrano 已提交
460 461 462
			}

			var gltfSampler = {
F
Fernando Serrano 已提交
463

464 465 466 467
				magFilter: THREE_TO_WEBGL[ map.magFilter ],
				minFilter: THREE_TO_WEBGL[ map.minFilter ],
				wrapS: THREE_TO_WEBGL[ map.wrapS ],
				wrapT: THREE_TO_WEBGL[ map.wrapT ]
F
Fernando Serrano 已提交
468

F
Fernando Serrano 已提交
469 470 471 472 473
			};

			outputJSON.samplers.push( gltfSampler );

			return outputJSON.samplers.length - 1;
F
Fernando Serrano 已提交
474

F
Fernando Serrano 已提交
475 476 477 478 479 480 481
		}

		/**
		 * Process texture
		 * @param  {Texture} map Map to process
		 * @return {Integer}     Index of the processed texture in the "textures" array
		 */
M
Mr.doob 已提交
482
		function processTexture( map ) {
F
Fernando Serrano 已提交
483

M
Mugen87 已提交
484
			if ( ! outputJSON.textures ) {
F
Fernando Serrano 已提交
485

F
Fernando Serrano 已提交
486
				outputJSON.textures = [];
F
Fernando Serrano 已提交
487

F
Fernando Serrano 已提交
488 489 490
			}

			var gltfTexture = {
F
Fernando Serrano 已提交
491

F
Fernando Serrano 已提交
492 493
				sampler: processSampler( map ),
				source: processImage( map )
F
Fernando Serrano 已提交
494

F
Fernando Serrano 已提交
495 496 497 498 499
			};

			outputJSON.textures.push( gltfTexture );

			return outputJSON.textures.length - 1;
F
Fernando Serrano 已提交
500

F
Fernando Serrano 已提交
501 502 503 504 505 506
		}

		/**
		 * Process material
		 * @param  {THREE.Material} material Material to process
		 * @return {Integer}      Index of the processed material in the "materials" array
F
Fernando Serrano 已提交
507
		 */
M
Mr.doob 已提交
508
		function processMaterial( material ) {
F
Fernando Serrano 已提交
509

T
Takahiro 已提交
510
			if ( cachedData.materials[ material.uuid ] !== undefined ) {
511 512 513 514 515

				return cachedData.materials[ material.uuid ];

			}

M
Mugen87 已提交
516
			if ( ! outputJSON.materials ) {
F
Fernando Serrano 已提交
517

F
Fernando Serrano 已提交
518
				outputJSON.materials = [];
F
Fernando Serrano 已提交
519

F
Fernando Serrano 已提交
520
			}
F
Fernando Serrano 已提交
521

522 523 524 525 526 527 528 529
			if ( material instanceof THREE.ShaderMaterial ) {

				console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' );
				return null;

			}


M
Mugen87 已提交
530
			if ( ! ( material instanceof THREE.MeshStandardMaterial ) ) {
531

532
				console.warn( 'GLTFExporter: Currently just THREE.StandardMaterial is supported. Material conversion may lose information.' );
533

F
Fernando Serrano 已提交
534 535 536
			}

			// @QUESTION Should we avoid including any attribute that has the default value?
537
			var gltfMaterial = {
F
Fernando Serrano 已提交
538

539
				pbrMetallicRoughness: {}
F
Fernando Serrano 已提交
540

541
			};
542

543 544
			// pbrMetallicRoughness.baseColorFactor
			var color = material.color.toArray().concat( [ material.opacity ] );
F
Fernando Serrano 已提交
545

M
Mugen87 已提交
546
			if ( ! equalArray( color, [ 1, 1, 1, 1 ] ) ) {
547

548
				gltfMaterial.pbrMetallicRoughness.baseColorFactor = color;
549 550 551

			}

552
			if ( material instanceof THREE.MeshStandardMaterial ) {
553

554 555
				gltfMaterial.pbrMetallicRoughness.metallicFactor = material.metalness;
				gltfMaterial.pbrMetallicRoughness.roughnessFactor = material.roughness;
556

M
Mr.doob 已提交
557
			} else {
558

M
Mugen87 已提交
559 560
				gltfMaterial.pbrMetallicRoughness.metallicFactor = 0.5;
				gltfMaterial.pbrMetallicRoughness.roughnessFactor = 0.5;
F
Fernando Serrano 已提交
561

562
			}
563

564 565
			// pbrMetallicRoughness.baseColorTexture
			if ( material.map ) {
566

567
				gltfMaterial.pbrMetallicRoughness.baseColorTexture = {
F
Fernando Serrano 已提交
568

569
					index: processTexture( material.map )
F
Fernando Serrano 已提交
570

571
				};
572

573
			}
574

F
Fernando Serrano 已提交
575 576 577
			if ( material instanceof THREE.MeshBasicMaterial ||
				material instanceof THREE.LineBasicMaterial ||
				material instanceof THREE.PointsMaterial ) {
578 579 580 581

			} else {

				// emissiveFactor
582
				var emissive = material.emissive.clone().multiplyScalar( material.emissiveIntensity ).toArray();
F
Fernando Serrano 已提交
583

M
Mugen87 已提交
584
				if ( ! equalArray( emissive, [ 0, 0, 0 ] ) ) {
585 586 587

					gltfMaterial.emissiveFactor = emissive;

F
Fernando Serrano 已提交
588
				}
589 590 591 592 593 594

				// emissiveTexture
				if ( material.emissiveMap ) {

					gltfMaterial.emissiveTexture = {

595
						index: processTexture( material.emissiveMap )
596 597 598 599 600 601 602 603 604 605 606

					};

				}

			}

			// normalTexture
			if ( material.normalMap ) {

				gltfMaterial.normalTexture = {
F
Fernando Serrano 已提交
607

608
					index: processTexture( material.normalMap )
F
Fernando Serrano 已提交
609

610 611
				};

M
Mugen87 已提交
612
				if ( material.normalScale.x !== - 1 ) {
613 614 615

					if ( material.normalScale.x !== material.normalScale.y ) {

M
Mugen87 已提交
616
						console.warn( 'THREE.GLTFExporter: Normal scale components are different, ignoring Y and exporting X.' );
617 618 619 620 621 622 623

					}

					gltfMaterial.normalTexture.scale = material.normalScale.x;

				}

F
Fernando Serrano 已提交
624 625
			}

626 627 628 629
			// occlusionTexture
			if ( material.aoMap ) {

				gltfMaterial.occlusionTexture = {
F
Fernando Serrano 已提交
630

631
					index: processTexture( material.aoMap )
F
Fernando Serrano 已提交
632

633 634
				};

635 636 637 638 639 640
				if ( material.aoMapIntensity !== 1.0 ) {

					gltfMaterial.occlusionTexture.strength = material.aoMapIntensity;

				}

641 642 643
			}

			// alphaMode
644
			if ( material.transparent || material.alphaTest > 0.0 ) {
645

646
				gltfMaterial.alphaMode = material.opacity < 1.0 ? 'BLEND' : 'MASK';
647

648 649
				// Write alphaCutoff if it's non-zero and different from the default (0.5).
				if ( material.alphaTest > 0.0 && material.alphaTest !== 0.5 ) {
650 651 652 653

					gltfMaterial.alphaCutoff = material.alphaTest;

				}
654 655 656 657

			}

			// doubleSided
F
Fernando Serrano 已提交
658
			if ( material.side === THREE.DoubleSide ) {
659

F
Fernando Serrano 已提交
660
				gltfMaterial.doubleSided = true;
661

F
Fernando Serrano 已提交
662 663
			}

F
Fernando Serrano 已提交
664
			if ( material.name ) {
665

F
Fernando Serrano 已提交
666
				gltfMaterial.name = material.name;
667

F
Fernando Serrano 已提交
668 669
			}

F
Fernando Serrano 已提交
670
			outputJSON.materials.push( gltfMaterial );
F
Fernando Serrano 已提交
671

672 673 674 675
			var index = outputJSON.materials.length - 1;
			cachedData.materials[ material.uuid ] = index;

			return index;
676

F
Fernando Serrano 已提交
677 678 679
		}

		/**
F
Fernando Serrano 已提交
680 681 682
		 * Process mesh
		 * @param  {THREE.Mesh} mesh Mesh to process
		 * @return {Integer}      Index of the processed mesh in the "meshes" array
F
Fernando Serrano 已提交
683 684
		 */
		function processMesh( mesh ) {
F
Fernando Serrano 已提交
685

M
Mugen87 已提交
686
			if ( ! outputJSON.meshes ) {
F
Fernando Serrano 已提交
687 688 689

				outputJSON.meshes = [];

F
Fernando Serrano 已提交
690 691 692
			}

			var geometry = mesh.geometry;
F
Fernando Serrano 已提交
693

M
Mr.doob 已提交
694 695
			var mode;

696 697 698
			// Use the correct mode
			if ( mesh instanceof THREE.LineSegments ) {

699
				mode = WEBGL_CONSTANTS.LINES;
700 701 702

			} else if ( mesh instanceof THREE.LineLoop ) {

703
				mode = WEBGL_CONSTANTS.LINE_LOOP;
704 705 706

			} else if ( mesh instanceof THREE.Line ) {

707
				mode = WEBGL_CONSTANTS.LINE_STRIP;
708 709 710

			} else if ( mesh instanceof THREE.Points ) {

711
				mode = WEBGL_CONSTANTS.POINTS;
712 713 714

			} else {

M
Mugen87 已提交
715
				if ( ! geometry.isBufferGeometry ) {
716 717 718 719 720 721 722

					var geometryTemp = new THREE.BufferGeometry();
					geometryTemp.fromGeometry( geometry );
					geometry = geometryTemp;

				}

723 724
				if ( mesh.drawMode === THREE.TriangleFanDrawMode ) {

F
Fernando Serrano 已提交
725
					console.warn( 'GLTFExporter: TriangleFanDrawMode and wireframe incompatible.' );
726
					mode = WEBGL_CONSTANTS.TRIANGLE_FAN;
727 728 729

				} else if ( mesh.drawMode === THREE.TriangleStripDrawMode ) {

730
					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINE_STRIP : WEBGL_CONSTANTS.TRIANGLE_STRIP;
731 732 733

				} else {

734
					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINES : WEBGL_CONSTANTS.TRIANGLES;
735 736 737 738

				}

			}
F
Fernando Serrano 已提交
739 740 741 742 743 744 745 746 747 748

			var gltfMesh = {
				primitives: [
					{
						mode: mode,
						attributes: {},
					}
				]
			};

749
			var material = processMaterial( mesh.material );
750
			if ( material !== null ) {
751 752 753 754 755 756

				gltfMesh.primitives[ 0 ].material = material;

			}


757 758
			if ( geometry.index ) {

759
				gltfMesh.primitives[ 0 ].indices = processAccessor( geometry.index, geometry );
760 761 762

			}

F
Fernando Serrano 已提交
763 764
			// We've just one primitive per mesh
			var gltfAttributes = gltfMesh.primitives[ 0 ].attributes;
F
Fernando Serrano 已提交
765

F
Fernando Serrano 已提交
766 767
			// Conversion between attributes names in threejs and gltf spec
			var nameConversion = {
F
Fernando Serrano 已提交
768

F
Fernando Serrano 已提交
769 770
				uv: 'TEXCOORD_0',
				uv2: 'TEXCOORD_1',
771 772 773
				color: 'COLOR_0',
				skinWeight: 'WEIGHTS_0',
				skinIndex: 'JOINTS_0'
F
Fernando Serrano 已提交
774

F
Fernando Serrano 已提交
775 776
			};

777
			// @QUESTION Detect if .vertexColors = THREE.VertexColors?
F
Fernando Serrano 已提交
778
			// For every attribute create an accessor
F
Fernando Serrano 已提交
779 780
			for ( var attributeName in geometry.attributes ) {

F
Fernando Serrano 已提交
781
				var attribute = geometry.attributes[ attributeName ];
F
Fernando Serrano 已提交
782
				attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
D
Don McCurdy 已提交
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811

				if ( attributeName.substr( 0, 5 ) !== 'MORPH' ) {

					gltfAttributes[ attributeName ] = processAccessor( attribute, geometry );

				}

			}

			// Morph targets
			if ( mesh.morphTargetInfluences !== undefined && mesh.morphTargetInfluences.length > 0 ) {

				gltfMesh.primitives[ 0 ].targets = [];

				for ( var i = 0; i < mesh.morphTargetInfluences.length; ++ i ) {

					var target = {};

					for ( var attributeName in geometry.morphAttributes ) {

						var attribute = geometry.morphAttributes[ attributeName ][ i ];
						attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
						target[ attributeName ] = processAccessor( attribute, geometry );

					}

					gltfMesh.primitives[ 0 ].targets.push( target );

				}
F
Fernando Serrano 已提交
812

F
Fernando Serrano 已提交
813 814 815 816 817
			}

			outputJSON.meshes.push( gltfMesh );

			return outputJSON.meshes.length - 1;
M
Mugen87 已提交
818

F
Fernando Serrano 已提交
819 820
		}

F
Fernando Serrano 已提交
821 822 823 824 825 826
		/**
		 * Process camera
		 * @param  {THREE.Camera} camera Camera to process
		 * @return {Integer}      Index of the processed mesh in the "camera" array
		 */
		function processCamera( camera ) {
F
Fernando Serrano 已提交
827

M
Mugen87 已提交
828
			if ( ! outputJSON.cameras ) {
F
Fernando Serrano 已提交
829

F
Fernando Serrano 已提交
830
				outputJSON.cameras = [];
F
Fernando Serrano 已提交
831

F
Fernando Serrano 已提交
832 833 834 835 836
			}

			var isOrtho = camera instanceof THREE.OrthographicCamera;

			var gltfCamera = {
F
Fernando Serrano 已提交
837

F
Fernando Serrano 已提交
838
				type: isOrtho ? 'orthographic' : 'perspective'
F
Fernando Serrano 已提交
839

F
Fernando Serrano 已提交
840 841 842 843 844 845 846 847 848 849 850
			};

			if ( isOrtho ) {

				gltfCamera.orthographic = {

					xmag: camera.right * 2,
					ymag: camera.top * 2,
					zfar: camera.far,
					znear: camera.near

F
Fernando Serrano 已提交
851
				};
F
Fernando Serrano 已提交
852 853 854 855 856 857 858 859 860 861 862 863 864 865

			} else {

				gltfCamera.perspective = {

					aspectRatio: camera.aspect,
					yfov: THREE.Math.degToRad( camera.fov ) / camera.aspect,
					zfar: camera.far,
					znear: camera.near

				};

			}

F
Fernando Serrano 已提交
866 867
			if ( camera.name ) {

F
Fernando Serrano 已提交
868
				gltfCamera.name = camera.type;
F
Fernando Serrano 已提交
869

F
Fernando Serrano 已提交
870 871 872 873 874
			}

			outputJSON.cameras.push( gltfCamera );

			return outputJSON.cameras.length - 1;
M
Mugen87 已提交
875

F
Fernando Serrano 已提交
876 877
		}

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
		/**
		 * Creates glTF animation entry from AnimationClip object.
		 *
		 * Status:
		 * - Only properties listed in PATH_PROPERTIES may be animated.
		 * - Only LINEAR and STEP interpolation currently supported.
		 *
		 * @param {THREE.AnimationClip} clip
		 * @param {THREE.Object3D} root
		 * @return {number}
		 */
		function processAnimation ( clip, root ) {

			if ( ! outputJSON.animations ) {

				outputJSON.animations = [];

			}

			var channels = [];
			var samplers = [];

			for ( var i = 0; i < clip.tracks.length; ++ i ) {

				var track = clip.tracks[ i ];
				var trackBinding = THREE.PropertyBinding.parseTrackName( track.name );
				var trackNode = THREE.PropertyBinding.findNode( root, trackBinding.nodeName );
				var trackProperty = PATH_PROPERTIES[ trackBinding.propertyName ];

907
				if ( trackBinding.objectName === 'bones' ) {
908

909 910 911 912 913 914 915 916 917
					if ( trackNode.isSkinnedMesh === true ) {

						trackNode = trackNode.skeleton.getBoneByName( trackBinding.objectIndex );

					} else {

						trackNode = undefined;

					}
918 919 920

				}

921 922 923
				if ( ! trackNode || ! trackProperty ) {

					console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name );
924
					return null;
925 926 927

				}

D
Don McCurdy 已提交
928 929 930 931 932 933 934 935 936
				var inputItemSize = 1;
				var outputItemSize = track.values.length / track.times.length;

				if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) {

					outputItemSize /= trackNode.morphTargetInfluences.length;

				}

937 938
				samplers.push( {

D
Don McCurdy 已提交
939 940
					input: processAccessor( new THREE.BufferAttribute( track.times, inputItemSize ) ),
					output: processAccessor( new THREE.BufferAttribute( track.values, outputItemSize ) ),
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
					interpolation: track.interpolation === THREE.InterpolateDiscrete ? 'STEP' : 'LINEAR'

				} );

				channels.push( {

					sampler: samplers.length - 1,
					target: {
						node: nodeMap[ trackNode.uuid ],
						path: trackProperty
					}

				} );

			}

			outputJSON.animations.push( {

				name: clip.name || 'clip_' + outputJSON.animations.length,
				samplers: samplers,
				channels: channels

			} );

			return outputJSON.animations.length - 1;

		}

D
Don McCurdy 已提交
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
		function processSkin( object ) {

			var node = outputJSON.nodes[ nodeMap[ object.uuid ] ];

			var skeleton = object.skeleton;
			var rootJoint = object.skeleton.bones[ 0 ];

			if ( rootJoint === undefined ) return null;

			var joints = [];
			var inverseBindMatrices = new Float32Array( skeleton.bones.length * 16 );

			for ( var i = 0; i < skeleton.bones.length; ++ i ) {

				joints.push( nodeMap[ skeleton.bones[ i ].uuid ] );

				skeleton.boneInverses[ i ].toArray( inverseBindMatrices, i * 16 );

			}

			if ( outputJSON.skins === undefined ) {

				outputJSON.skins = [];

			}

			outputJSON.skins.push( {

				inverseBindMatrices: processAccessor( new THREE.BufferAttribute( inverseBindMatrices, 16 ) ),
				joints: joints,
				skeleton: nodeMap[ rootJoint.uuid ]

			} );

			var skinIndex = node.skin = outputJSON.skins.length - 1;

			return skinIndex;

		}

F
Fernando Serrano 已提交
1009 1010 1011 1012 1013
		/**
		 * Process Object3D node
		 * @param  {THREE.Object3D} node Object3D to processNode
		 * @return {Integer}      Index of the node in the nodes list
		 */
M
Mr.doob 已提交
1014
		function processNode( object ) {
F
Fernando Serrano 已提交
1015

1016 1017 1018
			if ( object instanceof THREE.Light ) {

				console.warn( 'GLTFExporter: Unsupported node type:', object.constructor.name );
1019
				return null;
1020 1021 1022

			}

M
Mugen87 已提交
1023
			if ( ! outputJSON.nodes ) {
F
Fernando Serrano 已提交
1024

F
Fernando Serrano 已提交
1025
				outputJSON.nodes = [];
F
Fernando Serrano 已提交
1026

F
Fernando Serrano 已提交
1027 1028
			}

F
Fernando Serrano 已提交
1029 1030 1031
			var gltfNode = {};

			if ( options.trs ) {
F
Fernando Serrano 已提交
1032

F
Fernando Serrano 已提交
1033 1034 1035 1036
				var rotation = object.quaternion.toArray();
				var position = object.position.toArray();
				var scale = object.scale.toArray();

M
Mugen87 已提交
1037
				if ( ! equalArray( rotation, [ 0, 0, 0, 1 ] ) ) {
F
Fernando Serrano 已提交
1038

F
Fernando Serrano 已提交
1039
					gltfNode.rotation = rotation;
F
Fernando Serrano 已提交
1040

F
Fernando Serrano 已提交
1041 1042
				}

M
Mugen87 已提交
1043
				if ( ! equalArray( position, [ 0, 0, 0 ] ) ) {
F
Fernando Serrano 已提交
1044

D
Don McCurdy 已提交
1045
					gltfNode.translation = position;
F
Fernando Serrano 已提交
1046

F
Fernando Serrano 已提交
1047 1048
				}

M
Mugen87 已提交
1049
				if ( ! equalArray( scale, [ 1, 1, 1 ] ) ) {
F
Fernando Serrano 已提交
1050

F
Fernando Serrano 已提交
1051
					gltfNode.scale = scale;
F
Fernando Serrano 已提交
1052

F
Fernando Serrano 已提交
1053 1054 1055
				}

			} else {
F
Fernando Serrano 已提交
1056

F
Fernando Serrano 已提交
1057
				object.updateMatrix();
M
Mugen87 已提交
1058
				if ( ! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
F
Fernando Serrano 已提交
1059

F
Fernando Serrano 已提交
1060
					gltfNode.matrix = object.matrix.elements;
F
Fernando Serrano 已提交
1061

F
Fernando Serrano 已提交
1062
				}
F
Fernando Serrano 已提交
1063

F
Fernando Serrano 已提交
1064 1065 1066 1067
			}

			if ( object.name ) {

F
Fernando Serrano 已提交
1068
				gltfNode.name = object.name;
F
Fernando Serrano 已提交
1069

F
Fernando Serrano 已提交
1070 1071
			}

1072 1073 1074 1075 1076 1077
			if ( object.userData && Object.keys( object.userData ).length > 0 ) {

				try {

					gltfNode.extras = JSON.parse( JSON.stringify( object.userData ) );

M
Mugen87 已提交
1078
				} catch ( e ) {
1079

M
Mugen87 已提交
1080
					throw new Error( 'THREE.GLTFExporter: userData can\'t be serialized' );
1081 1082 1083 1084 1085

				}

			}

F
Fernando Serrano 已提交
1086 1087 1088 1089
			if ( object instanceof THREE.Mesh ||
				object instanceof THREE.Line ||
				object instanceof THREE.Points ) {

F
Fernando Serrano 已提交
1090
				gltfNode.mesh = processMesh( object );
F
Fernando Serrano 已提交
1091

F
Fernando Serrano 已提交
1092
			} else if ( object instanceof THREE.Camera ) {
F
Fernando Serrano 已提交
1093

F
Fernando Serrano 已提交
1094
				gltfNode.camera = processCamera( object );
F
Fernando Serrano 已提交
1095

F
Fernando Serrano 已提交
1096 1097
			}

D
Don McCurdy 已提交
1098 1099 1100 1101 1102 1103
			if ( object instanceof THREE.SkinnedMesh ) {

				skins.push( object );

			}

F
Fernando Serrano 已提交
1104
			if ( object.children.length > 0 ) {
F
Fernando Serrano 已提交
1105

1106
				var children = [];
F
Fernando Serrano 已提交
1107 1108

				for ( var i = 0, l = object.children.length; i < l; i ++ ) {
F
Fernando Serrano 已提交
1109

F
Fernando Serrano 已提交
1110
					var child = object.children[ i ];
F
Fernando Serrano 已提交
1111

1112 1113
					if ( child.visible || options.onlyVisible === false ) {

1114 1115
						var node = processNode( child );

1116
						if ( node !== null ) {
1117

1118
							children.push( node );
1119 1120

						}
F
Fernando Serrano 已提交
1121

F
Fernando Serrano 已提交
1122
					}
F
Fernando Serrano 已提交
1123

F
Fernando Serrano 已提交
1124
				}
F
Fernando Serrano 已提交
1125

1126 1127 1128 1129 1130 1131 1132
				if ( children.length > 0 ) {

					gltfNode.children = children;

				}


F
Fernando Serrano 已提交
1133 1134 1135 1136
			}

			outputJSON.nodes.push( gltfNode );

1137 1138 1139
			var nodeIndex = nodeMap[ object.uuid ] = outputJSON.nodes.length - 1;

			return nodeIndex;
F
Fernando Serrano 已提交
1140

F
Fernando Serrano 已提交
1141 1142 1143
		}

		/**
F
Fernando Serrano 已提交
1144
		 * Process Scene
F
Fernando Serrano 已提交
1145 1146 1147
		 * @param  {THREE.Scene} node Scene to process
		 */
		function processScene( scene ) {
F
Fernando Serrano 已提交
1148

M
Mugen87 已提交
1149
			if ( ! outputJSON.scenes ) {
F
Fernando Serrano 已提交
1150

F
Fernando Serrano 已提交
1151 1152
				outputJSON.scenes = [];
				outputJSON.scene = 0;
F
Fernando Serrano 已提交
1153

F
Fernando Serrano 已提交
1154 1155 1156
			}

			var gltfScene = {
F
Fernando Serrano 已提交
1157

F
Fernando Serrano 已提交
1158
				nodes: []
F
Fernando Serrano 已提交
1159

F
Fernando Serrano 已提交
1160 1161
			};

F
Fernando Serrano 已提交
1162 1163
			if ( scene.name ) {

F
Fernando Serrano 已提交
1164
				gltfScene.name = scene.name;
F
Fernando Serrano 已提交
1165

F
Fernando Serrano 已提交
1166 1167
			}

F
Fernando Serrano 已提交
1168
			outputJSON.scenes.push( gltfScene );
F
Fernando Serrano 已提交
1169

1170 1171
			var nodes = [];

F
Fernando Serrano 已提交
1172
			for ( var i = 0, l = scene.children.length; i < l; i ++ ) {
F
Fernando Serrano 已提交
1173

F
Fernando Serrano 已提交
1174 1175
				var child = scene.children[ i ];

1176
				if ( child.visible || options.onlyVisible === false ) {
F
Fernando Serrano 已提交
1177

1178 1179
					var node = processNode( child );

1180
					if ( node !== null ) {
1181

1182
						nodes.push( node );
1183 1184

					}
1185 1186 1187

				}

1188
			}
1189

1190
			if ( nodes.length > 0 ) {
F
Fernando Serrano 已提交
1191

1192
				gltfScene.nodes = nodes;
F
Fernando Serrano 已提交
1193

F
Fernando Serrano 已提交
1194
			}
F
Fernando Serrano 已提交
1195

F
Fernando Serrano 已提交
1196 1197
		}

1198 1199 1200 1201
		/**
		 * Creates a THREE.Scene to hold a list of objects and parse it
		 * @param  {Array} objects List of objects to process
		 */
M
Mr.doob 已提交
1202
		function processObjects( objects ) {
1203 1204

			var scene = new THREE.Scene();
1205
			scene.name = 'AuxScene';
1206

M
Mugen87 已提交
1207
			for ( var i = 0; i < objects.length; i ++ ) {
1208

1209 1210 1211
				// We push directly to children instead of calling `add` to prevent
				// modify the .parent and break its original scene and hierarchy
				scene.children.push( objects[ i ] );
1212 1213 1214 1215 1216 1217 1218

			}

			processScene( scene );

		}

1219
		function processInput( input ) {
1220

1221
			input = input instanceof Array ? input : [ input ];
F
Fernando Serrano 已提交
1222

1223
			var objectsWithoutScene = [];
M
Mr.doob 已提交
1224

M
Mugen87 已提交
1225
			for ( var i = 0; i < input.length; i ++ ) {
F
Fernando Serrano 已提交
1226

1227
				if ( input[ i ] instanceof THREE.Scene ) {
1228 1229 1230

					processScene( input[ i ] );

1231
				} else {
1232

1233
					objectsWithoutScene.push( input[ i ] );
1234

1235
				}
F
Fernando Serrano 已提交
1236

F
Fernando Serrano 已提交
1237
			}
F
Fernando Serrano 已提交
1238

1239
			if ( objectsWithoutScene.length > 0 ) {
1240

1241
				processObjects( objectsWithoutScene );
1242 1243

			}
F
Fernando Serrano 已提交
1244

D
Don McCurdy 已提交
1245 1246 1247 1248 1249 1250
			for ( var i = 0; i < skins.length; ++ i ) {

				processSkin( skins[ i ] );

			}

1251 1252 1253 1254 1255 1256
			for ( var i = 0; i < options.animations.length; ++ i ) {

				processAnimation( options.animations[ i ], input[ 0 ] );

			}

F
Fernando Serrano 已提交
1257
		}
F
Fernando Serrano 已提交
1258

1259 1260
		processInput( input );

F
Fernando Serrano 已提交
1261
		// Generate buffer
F
Fernando Serrano 已提交
1262
		// Create a new blob with all the dataviews from the buffers
1263
		var blob = new Blob( dataViews, { type: 'application/octet-stream' } );
F
Fernando Serrano 已提交
1264 1265

		// Update the bytlength of the only main buffer and update the uri with the base64 representation of it
1266
		if ( outputJSON.buffers && outputJSON.buffers.length > 0 ) {
F
Fernando Serrano 已提交
1267

1268 1269 1270
			outputJSON.buffers[ 0 ].byteLength = blob.size;

			var reader = new window.FileReader();
F
Fernando Serrano 已提交
1271

1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
			if ( options.binary === true ) {

				// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification

				var GLB_HEADER_BYTES = 12;
				var GLB_HEADER_MAGIC = 0x46546C67;
				var GLB_VERSION = 2;

				var GLB_CHUNK_PREFIX_BYTES = 8;
				var GLB_CHUNK_TYPE_JSON = 0x4E4F534A;
				var GLB_CHUNK_TYPE_BIN = 0x004E4942;

				reader.readAsArrayBuffer( blob );
				reader.onloadend = function () {

					// Binary chunk.
					var binaryChunk = reader.result;
					var binaryChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
					binaryChunkPrefix.setUint32( 0, binaryChunk.byteLength, true );
					binaryChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_BIN, true );

					// JSON chunk.
					delete outputJSON.buffers[ 0 ].uri; // Omitted URI indicates use of binary chunk.
					var jsonChunk = stringToArrayBuffer( JSON.stringify( outputJSON ) );
					var jsonChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
					jsonChunkPrefix.setUint32( 0, jsonChunk.byteLength, true );
					jsonChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_JSON, true );

					// GLB header.
					var header = new ArrayBuffer( GLB_HEADER_BYTES );
					var headerView = new DataView( header );
					headerView.setUint32( 0, GLB_HEADER_MAGIC, true );
					headerView.setUint32( 4, GLB_VERSION, true );
					var totalByteLength = GLB_HEADER_BYTES
						+ jsonChunkPrefix.byteLength + jsonChunk.byteLength
						+ binaryChunkPrefix.byteLength + binaryChunk.byteLength;
					headerView.setUint32( 8, totalByteLength, true );

					var glbBlob = new Blob( [
						header,
						jsonChunkPrefix,
						jsonChunk,
						binaryChunkPrefix,
						binaryChunk
					], { type: 'application/octet-stream' } );

					var glbReader = new window.FileReader();
					glbReader.readAsArrayBuffer( glbBlob );
					glbReader.onloadend = function () {

						onDone( glbReader.result );
F
Fernando Serrano 已提交
1323

1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339
					};

				};

			} else {

				reader.readAsDataURL( blob );
				reader.onloadend = function () {

					var base64data = reader.result;
					outputJSON.buffers[ 0 ].uri = base64data;
					onDone( outputJSON );

				};

			}
F
Fernando Serrano 已提交
1340

1341
		} else {
F
Fernando Serrano 已提交
1342

M
Mugen87 已提交
1343
			onDone( outputJSON );
F
Fernando Serrano 已提交
1344

1345
		}
M
Mr.doob 已提交
1346

F
Fernando Serrano 已提交
1347
	}
M
Mr.doob 已提交
1348

F
Fernando Serrano 已提交
1349
};