GLTFExporter.js 28.3 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 908 909 910 911 912
				if ( trackBinding.objectName === 'bones' ) {

					trackNode = THREE.PropertyBinding.findBone( trackNode, trackBinding.objectIndex );

				}

913 914 915
				if ( ! trackNode || ! trackProperty ) {

					console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name );
916
					return null;
917 918 919

				}

D
Don McCurdy 已提交
920 921 922 923 924 925 926 927 928
				var inputItemSize = 1;
				var outputItemSize = track.values.length / track.times.length;

				if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) {

					outputItemSize /= trackNode.morphTargetInfluences.length;

				}

929 930
				samplers.push( {

D
Don McCurdy 已提交
931 932
					input: processAccessor( new THREE.BufferAttribute( track.times, inputItemSize ) ),
					output: processAccessor( new THREE.BufferAttribute( track.values, outputItemSize ) ),
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
					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 已提交
961 962 963 964 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 993 994 995 996 997 998 999 1000
		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 已提交
1001 1002 1003 1004 1005
		/**
		 * Process Object3D node
		 * @param  {THREE.Object3D} node Object3D to processNode
		 * @return {Integer}      Index of the node in the nodes list
		 */
M
Mr.doob 已提交
1006
		function processNode( object ) {
F
Fernando Serrano 已提交
1007

1008 1009 1010
			if ( object instanceof THREE.Light ) {

				console.warn( 'GLTFExporter: Unsupported node type:', object.constructor.name );
1011
				return null;
1012 1013 1014

			}

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

F
Fernando Serrano 已提交
1017
				outputJSON.nodes = [];
F
Fernando Serrano 已提交
1018

F
Fernando Serrano 已提交
1019 1020
			}

F
Fernando Serrano 已提交
1021 1022 1023
			var gltfNode = {};

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

F
Fernando Serrano 已提交
1025 1026 1027 1028
				var rotation = object.quaternion.toArray();
				var position = object.position.toArray();
				var scale = object.scale.toArray();

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

F
Fernando Serrano 已提交
1031
					gltfNode.rotation = rotation;
F
Fernando Serrano 已提交
1032

F
Fernando Serrano 已提交
1033 1034
				}

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

D
Don McCurdy 已提交
1037
					gltfNode.translation = position;
F
Fernando Serrano 已提交
1038

F
Fernando Serrano 已提交
1039 1040
				}

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

F
Fernando Serrano 已提交
1043
					gltfNode.scale = scale;
F
Fernando Serrano 已提交
1044

F
Fernando Serrano 已提交
1045 1046 1047
				}

			} else {
F
Fernando Serrano 已提交
1048

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

F
Fernando Serrano 已提交
1052
					gltfNode.matrix = object.matrix.elements;
F
Fernando Serrano 已提交
1053

F
Fernando Serrano 已提交
1054
				}
F
Fernando Serrano 已提交
1055

F
Fernando Serrano 已提交
1056 1057 1058 1059
			}

			if ( object.name ) {

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

F
Fernando Serrano 已提交
1062 1063
			}

1064 1065 1066 1067 1068 1069
			if ( object.userData && Object.keys( object.userData ).length > 0 ) {

				try {

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

M
Mugen87 已提交
1070
				} catch ( e ) {
1071

M
Mugen87 已提交
1072
					throw new Error( 'THREE.GLTFExporter: userData can\'t be serialized' );
1073 1074 1075 1076 1077

				}

			}

F
Fernando Serrano 已提交
1078 1079 1080 1081
			if ( object instanceof THREE.Mesh ||
				object instanceof THREE.Line ||
				object instanceof THREE.Points ) {

F
Fernando Serrano 已提交
1082
				gltfNode.mesh = processMesh( object );
F
Fernando Serrano 已提交
1083

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

F
Fernando Serrano 已提交
1086
				gltfNode.camera = processCamera( object );
F
Fernando Serrano 已提交
1087

F
Fernando Serrano 已提交
1088 1089
			}

D
Don McCurdy 已提交
1090 1091 1092 1093 1094 1095
			if ( object instanceof THREE.SkinnedMesh ) {

				skins.push( object );

			}

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

1098
				var children = [];
F
Fernando Serrano 已提交
1099 1100

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

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

1104 1105
					if ( child.visible || options.onlyVisible === false ) {

1106 1107
						var node = processNode( child );

1108
						if ( node !== null ) {
1109

1110
							children.push( node );
1111 1112

						}
F
Fernando Serrano 已提交
1113

F
Fernando Serrano 已提交
1114
					}
F
Fernando Serrano 已提交
1115

F
Fernando Serrano 已提交
1116
				}
F
Fernando Serrano 已提交
1117

1118 1119 1120 1121 1122 1123 1124
				if ( children.length > 0 ) {

					gltfNode.children = children;

				}


F
Fernando Serrano 已提交
1125 1126 1127 1128
			}

			outputJSON.nodes.push( gltfNode );

1129 1130 1131
			var nodeIndex = nodeMap[ object.uuid ] = outputJSON.nodes.length - 1;

			return nodeIndex;
F
Fernando Serrano 已提交
1132

F
Fernando Serrano 已提交
1133 1134 1135
		}

		/**
F
Fernando Serrano 已提交
1136
		 * Process Scene
F
Fernando Serrano 已提交
1137 1138 1139
		 * @param  {THREE.Scene} node Scene to process
		 */
		function processScene( scene ) {
F
Fernando Serrano 已提交
1140

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

F
Fernando Serrano 已提交
1143 1144
				outputJSON.scenes = [];
				outputJSON.scene = 0;
F
Fernando Serrano 已提交
1145

F
Fernando Serrano 已提交
1146 1147 1148
			}

			var gltfScene = {
F
Fernando Serrano 已提交
1149

F
Fernando Serrano 已提交
1150
				nodes: []
F
Fernando Serrano 已提交
1151

F
Fernando Serrano 已提交
1152 1153
			};

F
Fernando Serrano 已提交
1154 1155
			if ( scene.name ) {

F
Fernando Serrano 已提交
1156
				gltfScene.name = scene.name;
F
Fernando Serrano 已提交
1157

F
Fernando Serrano 已提交
1158 1159
			}

F
Fernando Serrano 已提交
1160
			outputJSON.scenes.push( gltfScene );
F
Fernando Serrano 已提交
1161

1162 1163
			var nodes = [];

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

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

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

1170 1171
					var node = processNode( child );

1172
					if ( node !== null ) {
1173

1174
						nodes.push( node );
1175 1176

					}
1177 1178 1179

				}

1180
			}
1181

1182
			if ( nodes.length > 0 ) {
F
Fernando Serrano 已提交
1183

1184
				gltfScene.nodes = nodes;
F
Fernando Serrano 已提交
1185

F
Fernando Serrano 已提交
1186
			}
F
Fernando Serrano 已提交
1187

F
Fernando Serrano 已提交
1188 1189
		}

1190 1191 1192 1193
		/**
		 * Creates a THREE.Scene to hold a list of objects and parse it
		 * @param  {Array} objects List of objects to process
		 */
M
Mr.doob 已提交
1194
		function processObjects( objects ) {
1195 1196

			var scene = new THREE.Scene();
1197
			scene.name = 'AuxScene';
1198

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

1201 1202 1203
				// 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 ] );
1204 1205 1206 1207 1208 1209 1210

			}

			processScene( scene );

		}

1211
		function processInput( input ) {
1212

1213
			input = input instanceof Array ? input : [ input ];
F
Fernando Serrano 已提交
1214

1215
			var objectsWithoutScene = [];
M
Mr.doob 已提交
1216

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

1219
				if ( input[ i ] instanceof THREE.Scene ) {
1220 1221 1222

					processScene( input[ i ] );

1223
				} else {
1224

1225
					objectsWithoutScene.push( input[ i ] );
1226

1227
				}
F
Fernando Serrano 已提交
1228

F
Fernando Serrano 已提交
1229
			}
F
Fernando Serrano 已提交
1230

1231
			if ( objectsWithoutScene.length > 0 ) {
1232

1233
				processObjects( objectsWithoutScene );
1234 1235

			}
F
Fernando Serrano 已提交
1236

D
Don McCurdy 已提交
1237 1238 1239 1240 1241 1242
			for ( var i = 0; i < skins.length; ++ i ) {

				processSkin( skins[ i ] );

			}

1243 1244 1245 1246 1247 1248
			for ( var i = 0; i < options.animations.length; ++ i ) {

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

			}

F
Fernando Serrano 已提交
1249
		}
F
Fernando Serrano 已提交
1250

1251 1252
		processInput( input );

F
Fernando Serrano 已提交
1253
		// Generate buffer
F
Fernando Serrano 已提交
1254
		// Create a new blob with all the dataviews from the buffers
1255
		var blob = new Blob( dataViews, { type: 'application/octet-stream' } );
F
Fernando Serrano 已提交
1256 1257

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

1260 1261 1262
			outputJSON.buffers[ 0 ].byteLength = blob.size;

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

1264 1265 1266 1267 1268 1269 1270 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
			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 已提交
1315

1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331
					};

				};

			} else {

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

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

				};

			}
F
Fernando Serrano 已提交
1332

1333
		} else {
F
Fernando Serrano 已提交
1334

M
Mugen87 已提交
1335
			onDone( outputJSON );
F
Fernando Serrano 已提交
1336

1337
		}
M
Mr.doob 已提交
1338

F
Fernando Serrano 已提交
1339
	}
M
Mr.doob 已提交
1340

F
Fernando Serrano 已提交
1341
};