GLTFExporter.js 29.1 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

399 400 401 402 403 404
			if ( cachedData.images[ map.uuid ] ) {

				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

510 511 512 513 514 515
			if ( cachedData.materials[ material.uuid ] ) {

				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

				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 = {};

801 802
					var warned = false;

D
Don McCurdy 已提交
803 804
					for ( var attributeName in geometry.morphAttributes ) {

T
Takahiro 已提交
805
						// glTF 2.0 morph supports only POSITION/NORMAL/TANGENT.
806
						// Three.js doesn't support TANGENT yet.
T
Takahiro 已提交
807 808 809

						if ( attributeName !== 'position' && attributeName !== 'normal' ) {

810 811 812 813 814 815 816
							if ( ! warned ) {

								console.warn( 'GLTFExporter: Only POSITION and NORMAL morph are supported.' );
								warned = true;

							}

T
Takahiro 已提交
817 818 819 820
							continue;

						}

D
Don McCurdy 已提交
821
						var attribute = geometry.morphAttributes[ attributeName ][ i ];
T
Takahiro 已提交
822

823
						// Three.js morph attribute has absolute values while the one of glTF has relative values.
T
Takahiro 已提交
824 825 826 827 828 829
						//
						// glTF 2.0 Specification:
						// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#morph-targets

						var baseAttribute = geometry.attributes[ attributeName ];
						// Clones attribute not to override
830
						var relativeAttribute = attribute.clone();
T
Takahiro 已提交
831 832 833

						for ( var j = 0, jl = attribute.count; j < jl; j ++ ) {

834
							relativeAttribute.setXYZ(
T
Takahiro 已提交
835 836 837 838 839 840 841 842
								j,
								attribute.getX( j ) - baseAttribute.getX( j ),
								attribute.getY( j ) - baseAttribute.getY( j ),
								attribute.getZ( j ) - baseAttribute.getZ( j )
							);

						}

843
						target[ attributeName.toUpperCase() ] = processAccessor( relativeAttribute, geometry );
D
Don McCurdy 已提交
844 845 846 847 848 849

					}

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

				}
F
Fernando Serrano 已提交
850

F
Fernando Serrano 已提交
851 852 853 854 855
			}

			outputJSON.meshes.push( gltfMesh );

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

F
Fernando Serrano 已提交
857 858
		}

F
Fernando Serrano 已提交
859 860 861 862 863 864
		/**
		 * 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 已提交
865

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

F
Fernando Serrano 已提交
868
				outputJSON.cameras = [];
F
Fernando Serrano 已提交
869

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

			var isOrtho = camera instanceof THREE.OrthographicCamera;

			var gltfCamera = {
F
Fernando Serrano 已提交
875

F
Fernando Serrano 已提交
876
				type: isOrtho ? 'orthographic' : 'perspective'
F
Fernando Serrano 已提交
877

F
Fernando Serrano 已提交
878 879 880 881 882 883 884 885 886 887 888
			};

			if ( isOrtho ) {

				gltfCamera.orthographic = {

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

F
Fernando Serrano 已提交
889
				};
F
Fernando Serrano 已提交
890 891 892 893 894 895 896 897 898 899 900 901 902 903

			} else {

				gltfCamera.perspective = {

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

				};

			}

F
Fernando Serrano 已提交
904 905
			if ( camera.name ) {

F
Fernando Serrano 已提交
906
				gltfCamera.name = camera.type;
F
Fernando Serrano 已提交
907

F
Fernando Serrano 已提交
908 909 910 911 912
			}

			outputJSON.cameras.push( gltfCamera );

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

F
Fernando Serrano 已提交
914 915
		}

916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
		/**
		 * 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 ];

				if ( ! trackNode || ! trackProperty ) {

					console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name );
948
					return null;
949 950 951

				}

D
Don McCurdy 已提交
952 953 954 955 956 957 958 959 960
				var inputItemSize = 1;
				var outputItemSize = track.values.length / track.times.length;

				if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) {

					outputItemSize /= trackNode.morphTargetInfluences.length;

				}

961 962
				samplers.push( {

D
Don McCurdy 已提交
963 964
					input: processAccessor( new THREE.BufferAttribute( track.times, inputItemSize ) ),
					output: processAccessor( new THREE.BufferAttribute( track.values, outputItemSize ) ),
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
					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 已提交
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
		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 已提交
1033 1034 1035 1036 1037
		/**
		 * Process Object3D node
		 * @param  {THREE.Object3D} node Object3D to processNode
		 * @return {Integer}      Index of the node in the nodes list
		 */
M
Mr.doob 已提交
1038
		function processNode( object ) {
F
Fernando Serrano 已提交
1039

1040 1041 1042
			if ( object instanceof THREE.Light ) {

				console.warn( 'GLTFExporter: Unsupported node type:', object.constructor.name );
1043
				return null;
1044 1045 1046

			}

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

F
Fernando Serrano 已提交
1049
				outputJSON.nodes = [];
F
Fernando Serrano 已提交
1050

F
Fernando Serrano 已提交
1051 1052
			}

F
Fernando Serrano 已提交
1053 1054 1055
			var gltfNode = {};

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

F
Fernando Serrano 已提交
1057 1058 1059 1060
				var rotation = object.quaternion.toArray();
				var position = object.position.toArray();
				var scale = object.scale.toArray();

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

F
Fernando Serrano 已提交
1063
					gltfNode.rotation = rotation;
F
Fernando Serrano 已提交
1064

F
Fernando Serrano 已提交
1065 1066
				}

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

D
Don McCurdy 已提交
1069
					gltfNode.translation = position;
F
Fernando Serrano 已提交
1070

F
Fernando Serrano 已提交
1071 1072
				}

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

F
Fernando Serrano 已提交
1075
					gltfNode.scale = scale;
F
Fernando Serrano 已提交
1076

F
Fernando Serrano 已提交
1077 1078 1079
				}

			} else {
F
Fernando Serrano 已提交
1080

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

F
Fernando Serrano 已提交
1084
					gltfNode.matrix = object.matrix.elements;
F
Fernando Serrano 已提交
1085

F
Fernando Serrano 已提交
1086
				}
F
Fernando Serrano 已提交
1087

F
Fernando Serrano 已提交
1088 1089 1090 1091
			}

			if ( object.name ) {

F
Fernando Serrano 已提交
1092
				gltfNode.name = object.name;
F
Fernando Serrano 已提交
1093

F
Fernando Serrano 已提交
1094 1095
			}

1096 1097 1098 1099 1100 1101
			if ( object.userData && Object.keys( object.userData ).length > 0 ) {

				try {

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

M
Mugen87 已提交
1102
				} catch ( e ) {
1103

M
Mugen87 已提交
1104
					throw new Error( 'THREE.GLTFExporter: userData can\'t be serialized' );
1105 1106 1107 1108 1109

				}

			}

F
Fernando Serrano 已提交
1110 1111 1112 1113
			if ( object instanceof THREE.Mesh ||
				object instanceof THREE.Line ||
				object instanceof THREE.Points ) {

F
Fernando Serrano 已提交
1114
				gltfNode.mesh = processMesh( object );
F
Fernando Serrano 已提交
1115

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

F
Fernando Serrano 已提交
1118
				gltfNode.camera = processCamera( object );
F
Fernando Serrano 已提交
1119

F
Fernando Serrano 已提交
1120 1121
			}

D
Don McCurdy 已提交
1122 1123 1124 1125 1126 1127
			if ( object instanceof THREE.SkinnedMesh ) {

				skins.push( object );

			}

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

1130
				var children = [];
F
Fernando Serrano 已提交
1131 1132

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

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

1136 1137
					if ( child.visible || options.onlyVisible === false ) {

1138 1139
						var node = processNode( child );

1140
						if ( node !== null ) {
1141

1142
							children.push( node );
1143 1144

						}
F
Fernando Serrano 已提交
1145

F
Fernando Serrano 已提交
1146
					}
F
Fernando Serrano 已提交
1147

F
Fernando Serrano 已提交
1148
				}
F
Fernando Serrano 已提交
1149

1150 1151 1152 1153 1154 1155 1156
				if ( children.length > 0 ) {

					gltfNode.children = children;

				}


F
Fernando Serrano 已提交
1157 1158 1159 1160
			}

			outputJSON.nodes.push( gltfNode );

1161 1162 1163
			var nodeIndex = nodeMap[ object.uuid ] = outputJSON.nodes.length - 1;

			return nodeIndex;
F
Fernando Serrano 已提交
1164

F
Fernando Serrano 已提交
1165 1166 1167
		}

		/**
F
Fernando Serrano 已提交
1168
		 * Process Scene
F
Fernando Serrano 已提交
1169 1170 1171
		 * @param  {THREE.Scene} node Scene to process
		 */
		function processScene( scene ) {
F
Fernando Serrano 已提交
1172

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

F
Fernando Serrano 已提交
1175 1176
				outputJSON.scenes = [];
				outputJSON.scene = 0;
F
Fernando Serrano 已提交
1177

F
Fernando Serrano 已提交
1178 1179 1180
			}

			var gltfScene = {
F
Fernando Serrano 已提交
1181

F
Fernando Serrano 已提交
1182
				nodes: []
F
Fernando Serrano 已提交
1183

F
Fernando Serrano 已提交
1184 1185
			};

F
Fernando Serrano 已提交
1186 1187
			if ( scene.name ) {

F
Fernando Serrano 已提交
1188
				gltfScene.name = scene.name;
F
Fernando Serrano 已提交
1189

F
Fernando Serrano 已提交
1190 1191
			}

F
Fernando Serrano 已提交
1192
			outputJSON.scenes.push( gltfScene );
F
Fernando Serrano 已提交
1193

1194 1195
			var nodes = [];

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

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

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

1202 1203
					var node = processNode( child );

1204
					if ( node !== null ) {
1205

1206
						nodes.push( node );
1207 1208

					}
1209 1210 1211

				}

1212
			}
1213

1214
			if ( nodes.length > 0 ) {
F
Fernando Serrano 已提交
1215

1216
				gltfScene.nodes = nodes;
F
Fernando Serrano 已提交
1217

F
Fernando Serrano 已提交
1218
			}
F
Fernando Serrano 已提交
1219

F
Fernando Serrano 已提交
1220 1221
		}

1222 1223 1224 1225
		/**
		 * Creates a THREE.Scene to hold a list of objects and parse it
		 * @param  {Array} objects List of objects to process
		 */
M
Mr.doob 已提交
1226
		function processObjects( objects ) {
1227 1228

			var scene = new THREE.Scene();
1229
			scene.name = 'AuxScene';
1230

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

1233 1234 1235
				// 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 ] );
1236 1237 1238 1239 1240 1241 1242

			}

			processScene( scene );

		}

1243
		function processInput( input ) {
1244

1245
			input = input instanceof Array ? input : [ input ];
F
Fernando Serrano 已提交
1246

1247
			var objectsWithoutScene = [];
M
Mr.doob 已提交
1248

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

1251
				if ( input[ i ] instanceof THREE.Scene ) {
1252 1253 1254

					processScene( input[ i ] );

1255
				} else {
1256

1257
					objectsWithoutScene.push( input[ i ] );
1258

1259
				}
F
Fernando Serrano 已提交
1260

F
Fernando Serrano 已提交
1261
			}
F
Fernando Serrano 已提交
1262

1263
			if ( objectsWithoutScene.length > 0 ) {
1264

1265
				processObjects( objectsWithoutScene );
1266 1267

			}
F
Fernando Serrano 已提交
1268

D
Don McCurdy 已提交
1269 1270 1271 1272 1273 1274
			for ( var i = 0; i < skins.length; ++ i ) {

				processSkin( skins[ i ] );

			}

1275 1276 1277 1278 1279 1280
			for ( var i = 0; i < options.animations.length; ++ i ) {

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

			}

F
Fernando Serrano 已提交
1281
		}
F
Fernando Serrano 已提交
1282

1283 1284
		processInput( input );

F
Fernando Serrano 已提交
1285
		// Generate buffer
F
Fernando Serrano 已提交
1286
		// Create a new blob with all the dataviews from the buffers
1287
		var blob = new Blob( dataViews, { type: 'application/octet-stream' } );
F
Fernando Serrano 已提交
1288 1289

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

1292 1293 1294
			outputJSON.buffers[ 0 ].byteLength = blob.size;

			var reader = new window.FileReader();
F
Fernando Serrano 已提交
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 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346
			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 已提交
1347

1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363
					};

				};

			} else {

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

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

				};

			}
F
Fernando Serrano 已提交
1364

1365
		} else {
F
Fernando Serrano 已提交
1366

M
Mugen87 已提交
1367
			onDone( outputJSON );
F
Fernando Serrano 已提交
1368

1369
		}
M
Mr.doob 已提交
1370

F
Fernando Serrano 已提交
1371
	}
M
Mr.doob 已提交
1372

F
Fernando Serrano 已提交
1373
};